3

I'm currently working on a program where, in a QFormLayout, I have two columns of QLineEdits. The first column I have stored in a list (meaning they are easily accessible), while the second column isn't stored in a variable or list. I'm trying to access the texts of the QLineEdits in the second column, but am always running into an error.

I currently have no intentions of using a second list/ dictionary to grant access to the second column, as using the getWidgetPosition and itemAt functions should provide an easier route for access to these values.

from PyQt5.QtWidgets import *

app = QApplication([])
window = QWidget()

layout = QFormLayout()
entry1 = QLineEdit()
layout.addRow(entry1,QLineEdit())

ePos = layout.getWidgetPosition(entry1)
text = layout.itemAt(ePos[1],ePos[0]+1).text()
print(text)


window.setLayout(layout)
window.show()
app.exec_()

The above code is just an example that's close to the code that I'm trying to use. For some reason, accessing the text of the second column of QLineEdits isn't possible as I get this error message:

Traceback (most recent call last):
    File "sampleProblem.py", line 11, in <module>
      text = layout.itemAt(ePos[1],ePos[0]+1).text()
 AttributeError: 'QWidgetItem' object has no attribute 'text'
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Okay first why would you do this when you can access that object directly by using self.entry1 -- are you testing this functionality? – Dennis Jensen Jul 23 '19 at 20:33
  • Okay read your description a bit more -- no intention of doing it the simpler and smarter way instead do it the harder more complex way --- okay why? – Dennis Jensen Jul 23 '19 at 20:37

2 Answers2

3

The itemAt() method of the layouts returns a QLayoutItem, the QLayoutItem encapsulates another layout or another widget, then to get the widget (in your case QLineEdit) you must use the widget() method, and then just get the text.

import sys
from PyQt5 import QtWidgets

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = QtWidgets.QWidget()
    layout = QtWidgets.QFormLayout(window)
    entry1 = QtWidgets.QLineEdit()
    layout.addRow(entry1, QtWidgets.QLineEdit())

    i, j = layout.getWidgetPosition(entry1)
    widget_item = layout.itemAt(i, j+1)
    widget = widget_item.widget()
    text = widget.text()
    print(text)

    window.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • For anyone trying this and wondering why it doesn't work, [`QFormLayout.itemAt()`](https://doc.qt.io/qt-5/qformlayout.html#itemAt) requires [`QFormLayout.ItemRole`](https://doc.qt.io/qt-5/qformlayout.html#ItemRole-enum) instead of an int. Turning `j+1` into an enum fixes the issue: `widget_item = layout.itemAt(i, QtWidgets.QFormLayout.ItemRole(j+1))` – Umbral Reaper Apr 13 '21 at 04:07
  • @UmbralReaper Are you using pyqt5 or pyqt6? – eyllanesc Apr 13 '21 at 04:09
  • PySide6. Does it make a difference? – Umbral Reaper Apr 13 '21 at 04:10
  • @UmbralReaper Yes, it makes a lot of difference since in Qt6 some methods have been removed, they changed the type of argument, etc. which is reflected in the bindings: PyQt6 and PySide6. Also PyQt6 and PySide6 are more strict with the types. – eyllanesc Apr 13 '21 at 04:14
0

Okay I got a slightly different answer more of a helper to the answer but I took the code and did the following: when printing the text failed I printed the object at those coordinates and got a PyQt5.QtWidgets.QWidgetItem. I then verified this by looking up the QWidgetItem and getting one of its known attributes and printing that. So yes while eyllanesc's answer is most likely correct I am hoping that this added information helps you with it a bit more.

from sys import exit as sysExit

from PyQt5.QtCore import *
from PyQt5.QtGui  import *
from PyQt5.QtWidgets import *

class MainWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        self.LaidOut = QFormLayout()
        self.Entry = QLineEdit()
        self.LaidOut.addRow(self.Entry, QLineEdit())

        self.setLayout(self.LaidOut)

        ePos = self.LaidOut.getWidgetPosition(self.Entry)
        print('EPos :',ePos)
#        text = self.LaidOut.itemAt(ePos[1],ePos[0]+1).text()
        print('Text :',self.LaidOut.itemAt(ePos[1],ePos[0]+1).isEmpty())

if __name__ == "__main__":
    MainThred = QApplication([])

    MainGUI = MainWindow()
    MainGUI.show()
    sysExit(MainThred.exec_())

Still curious as to the why do it the harder more complex way?

Dennis Jensen
  • 214
  • 1
  • 14
  • `isEmpty()` does not return if the QLineEdit text is empty, it only indicates if the QLayoutItem does not have a layout or a widget, read the docs: https://doc.qt.io/qt-5/qlayoutitem.html#isEmpty – eyllanesc Jul 23 '19 at 21:05
  • Thanks for the answer, Dennis. Although this isn't the method I'll be using I really appreciate your responses – FreshOuttaTheBag Jul 23 '19 at 22:31
  • @DennisJensen Your code is confusing: `print('Text :',...isEmpty())`, don't you think that? Perhaps something clearer would be `print("Is there QLayoutItem?:", ..isEmpty())`. I do not point out that this is not an answer, for me it is, but that your code can bring confusion. – eyllanesc Jul 24 '19 at 15:13
  • @eyllanesc now you are nit-picking -- code that is designed to give you information you did not have does not need to make 100% sense as that is not its purpose -- originally what I was attempting to get back was Text remember this -- `text = layout.itemAt(ePos[1],ePos[0]+1).text()` but because that failed it morphed into getting information about the object to understand why I was not getting text -- the 'Text :' is just a line visual for the print to make it easier to see when that is printed to the screen -- changing that value is unnecessary as it is inconsequential to the purpose – Dennis Jensen Jul 24 '19 at 15:23
  • Note: that was all explained prior to posting the code btw – Dennis Jensen Jul 24 '19 at 15:25