QTableWidget Integer - python

I am trying to insert and display integers in my QTableWidget. They don't display. It works if I convert everything to strings, but then I can't sort columns numerically--only lexically (1, 10, 100, etc.). This is using PyQt.
I've tried some of the suggested solutions, using QTableWidgetItem.setData(someRole,intValue), bu then nothing at all displays. I've tried, Qt.UserRole, DisplayRole and Edit Role. (I don't understand why these Roles are needed to display integers, but have just followed the examples). My specific code is:
item = QTableWidgetItem()
item.setData = (Qt.DisplayRole,intValue)
myTable.setItem(row, column, item)
The following code works, but for strings only:
item = QTableWidgetItem(str(intValue))
myTable.setItem(row, column, item)
Also, the suggestions for reading the data back, only show the object location, not the actual data. Example, using Eric as an interpreter shell:
item.data(Qt.DisplayRole)
Response: PyQt4.QtCore.QVariant object at 0x1f01fa60
or this:
item.data(Qt.EditRole).data()
Response: sip.voidptr object at 0x1e904a80
Any insight is appreciated.

You were on the right track. Your code doesn't work because you're not calling the QTableWidgetItem's setData() function but trying to assign it a value. You have
item.setData = (Qt.DisplayRole,intValue)
instead of
item.setData(Qt.DisplayRole,intValue)
Also, when reading the data back it's not just the location that's shown but the data itself as a QVariant. You should find that item.data(Qt.DisplayRole).toString() will return your data back as a string by converting the QVariant (via its .toString() method).
Here's a quick working example just to demonstrate:
import sys
from PyQt4.QtGui import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout
from PyQt4.QtCore import Qt
class Widget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.widget_layout = QVBoxLayout()
self.table_widget = QTableWidget(101, 1)
self.table_widget.setSortingEnabled(True)
self.widget_layout.addWidget(self.table_widget)
self.setLayout(self.widget_layout)
for num in xrange(101):
item = QTableWidgetItem()
item.setData(Qt.EditRole, num)
self.table_widget.setItem(num, 0, item)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = Widget()
widget.show()
sys.exit(app.exec_())

Related

"Python has stopped working" error when instantiating subclass of QStyledItemDelegate twice

I have the following code to align text of QTableWidget's column.
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem, QStyledItemDelegate
class AlignRightDelegate(QStyledItemDelegate):
def initStyleOption(self, option, index):
super(AlignRightDelegate, self).initStyleOption(option, index)
option.displayAlignment = Qt.AlignRight
class Table(QTableWidget):
def __init__(self, data, alignColumns = 1, *args):
QTableWidget.__init__(self, *args)
self.setRowCount(len(data))
self.setColumnCount(3)
self.setData(data)
self.resizeColumnsToContents()
for colIndex in range(1, 1 + alignColumns):
print("Align Column [", colIndex, "]", sep="")
self.setItemDelegateForColumn(colIndex, AlignRightDelegate())
def setData(self, data):
horizontalHeaders = []
for m, rowContent in enumerate(data):
for n, cellContent in enumerate(data[m]):
if (m == 0):
horizontalHeaders.append(cellContent)
else:
self.setItem(m - 1, n, QTableWidgetItem(cellContent))
self.setHorizontalHeaderLabels(horizontalHeaders)
if __name__ == "__main__":
data = [["col1", "col2", "col3"],
[ "1", "1", "a"],
[ "-1", "2", "b"],
[ "0", "3", "c"]]
print("python QTableWidgetAlignRight.py[ <AlignColumnCount=1]")
app = QApplication(sys.argv)
table = Table(data, int(sys.argv[1])) if (len(sys.argv) > 1) else Table(data)
table.show()
sys.exit(app.exec_())
If I execute the above code with python QTableWidgetAlignRight.py 1 it sort of works as it should as shown below with col2 aligned to the right but apparently to the top also:
However when I execute the same code with python QTableWidgetAlignRight.py 2 where I try to align 2 columns to the right, I ran into Python has stopped working error. The following screenshot is actually on my Japanese Windows OS (Win 10 Pro 20H2 (OS Build 19402.1165))
However I searched the net for the same error message in English and I found a screenshot albeit not due to my code above (burrowed from this page: Python has stopped working).
So what is the correct way of aligning 2 columns of my QTableWidget (without an error and without aligning to the top vertically)? Also is this a bug in PyQt5?
For your information, I am using the following: Python 3.7.6, conda 4.8.2, pyqt 5.9.2 py37h6538335_2. For column alignment, I looked at this for reference: How to align all items in a column to center in QTableWidget
Due to the way python and PyQt keep references to objects, only one unparented and unreferenced delegate can theoretically be set for each view.
But, while it is possible to set one delegate like this, it should not be done: delegates should always have a persistent reference or valid parent since the view does not take ownership of the delegate (see the docs for all setItemDelegate* functions).
Also, considering that you are using the same delegate for multiple columns, there's no point in creating more than one.
self.alignDelegate = AlignRightDelegate(self)
for colIndex in range(1, 1 + alignColumns):
print("Align Column [", colIndex, "]", sep="")
self.setItemDelegateForColumn(colIndex, self.alignDelegate)
Note that you could either use the parent argument (self) or create an instance attribute:
self.alignDelegate = AlignRightDelegate()
# or
alignDelegate = AlignRightDelegate(self)
Using both, though, is not an issue, and specifying the parent is a good choice anyway when dealing with Qt objects.
In any case, at least one of the methods above should always be used.
You get a wrong alignment because you only set the horizontal alignment: use option.displayAlignment = Qt.AlignRight | Qt.AlignVCenter.

Python retrieve QComboBox from QTablewidget

I am now a month into Python and half a month into QDesigner/Qt.
Within QDesigner, I have a QTableWidget, which I promote to my own InputTable(QTableWidget) class.
class InputTable(QTableWidget):
def __init__(self, parent=None):
self.parent = parent
super(QTableWidget, self).__init__(parent)
...
I inserted some QComboboxes, which allow the user to select the units of the values in the table. This is at the initialization within the InputTable class.
pressureunits = [ "barg","bara", "psig", "psia"]
pressurecombobox = QComboBox()
for i in range(len(pressureunits)):
pressurecombobox.addItem(pressureunits[i])
self.setCellWidget(2, 0, pressurecombobox)
So far, so good.
Now I want to convert all the inputdata to a parallel table (master table) with values in SI units for further analysis.
units = self.tableInputs.cellWidget(2,0)
if units.currentIndex()==0: # this means the combobox is at the first entry
mastertable[0,2] = float(self.tableInputs.item(2,1).text())
This fails, because units is of type QWidget, which has no attribute currentIndex. On some forums I read that the QComboBox is a child of the QWidget and I have played with the children() functions, but helas no solution here. Some people recommend switching to QTableView structure. I spend my day searching how to do this, nothing found yet. Any smart ideas?
Update: After reducing all complexity from my question, I was able to find the solution below.
from PyQt5.QtWidgets import QComboBox, QTableWidget, QTableWidgetItem
Mytable = QTableWidget(3,3)
Mytable.setItem(0, 0, QTableWidgetItem("1"))
print("Now retrieve the data :", Mytable.item(0, 0).text())
pressureunits = [ "barg","bara", "psig", "psia"]
pressurecombobox = QComboBox()
for i in range(len(pressureunits)):
pressurecombobox.addItem(pressureunits[i])
Mytable.setCellWidget(2, 0, pressurecombobox)
print("Now retrieve the active (first) entry :", Mytable.cellWidget(2,0).currentText())

How to display values from Database into QlineEdit pyqt4 python

myresult = ('sandeep pawar','1234','haveri','581110','karnatak')
I want display each of these values into separate QlineEdit.
myresult = ['sandeep pawar','1234','haveri','581110','karnatak']
for i in myresult:
value = ' '.join(map(str,x))
a,b,c,d,e = value.split(" ")
self.lineEdit.setText(a)
self.lineEdit_2.setText(b)
self.lineEdit_3.setText(c)
self.lineEdit_4.setText(d)
self.lineEdit_5.setText(e)
I have tried with this method but i get this following error
a,b,c,d,e = value.split(" ") ValueError too many values to unpack.
Please guide me how to display values into lineEdit without using split() function.
For a lineEdit you can just reference each element in the list, this of course assumes you always have 5 elements in your list. Which is okay but likely could be done more dynamic (see below).
myresult = ['sandeep pawar','1234','haveri','581110','karnatak']
self.lineEdit.setText(myresult[0])
self.lineEdit_2.setText(myresult[1])
self.lineEdit_3.setText(myresult[2])
self.lineEdit_4.setText(myresult[3])
self.lineEdit_5.setText(myresult[4])
The above code would work and get you a line edit for each element in your list. But if that list were to grow, you would need to add a new lineEdit to the GUI and then populate it with myresult[5]. I would rather do something like my example below.
This is for PyQt5 since I only have it installed on my PC. But why not use something more dynamic than lineedits? Such as a QlistWidget with editable items.
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import *
import sys
if __name__ == '__main__':
app = QApplication(sys.argv)
listWidget = QListWidget()
listWidget.show()
myresult = ['sandeep pawar','1234','haveri','581110','karnatak']
listWidget.addItems(myresult)
for index in range(listWidget.count()):
item = listWidget.item(index)
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
sys.exit(app.exec_())
You can then bind the itemChanged event which will let you know when one of the items in the list has changed. So you can then go in and update your list after the edits are made. I hope this helps, please let me know if you have questions or need a push in the right direction.
This is my opinion:
myresult = ['sandeep pawar','1234','haveri','581110','karnatak']
line_edit = [self.lineEdit,self.lineEdit_2,self.lineEdit_3,self.lineEdit_4,self.lineEdit_5]
for i in range(len(line_edit)):
line_edit[i].setText(myresult[i])

Inserting row in QTableView

I have a PostgreSQL parent table called 'rooms' and a related table called 'configurations'. I have a form which would allow the user to add edit and add new configurations to individual rooms in a QTableView. In the configuration table, room is a foreign key to the rooms table. The form looks like this:
As far as editing the configurations, there seems to be no issue. The problem arises when I add a new record in the configurations model. If I insert a row and do not set the room cell, the user can edit any of the fields and it will save to the database. Although, when finished editing, the row clears and ! shows up in the row header. If I reload the form, the record edited correctly.
What I want to do, however, is to set the room number for the user and keep the room number field in the configuration view hidden from the user. When I add a row and set the room number cell's value, I cannot edit the row. Any ideas?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtSql import *
from rooms import *
from config import *
class Setup_rooms(QDialog):
def __init__(self):
QDialog.__init__(self)
self.ui = Ui_rooms_setup()
self.ui.setupUi(self)
self.rooms_model = QSqlRelationalTableModel(self)
self.rooms_model.setTable('rooms')
self.rooms_model.setRelation(4, QSqlRelation('room_types1', 'room_type', 'room_type'))
self.rooms_model.setSort(int(self.rooms_model.fieldIndex("room")), Qt.AscendingOrder)
self.rooms_model.select()
self.rooms_mapper = QDataWidgetMapper(self)
self.rooms_mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)
self.rooms_mapper.setModel(self.rooms_model)
self.rooms_mapper.setItemDelegate(QSqlRelationalDelegate(self))
self.rooms_mapper.addMapping(self.ui.room_num, self.rooms_model.fieldIndex("room"))
self.rooms_mapper.addMapping(self.ui.room_description, self.rooms_model.fieldIndex("description"))
self.rooms_mapper.addMapping(self.ui.deep_clean_date, self.rooms_model.fieldIndex("deepcleandate"))
self.rooms_mapper.addMapping(self.ui.deep_clean_cycle, self.rooms_model.fieldIndex("deepcleancycle"))
room_types_model = self.rooms_model.relationModel(4)
self.ui.room_type.setModel(room_types_model)
self.ui.room_type.setModelColumn(room_types_model.fieldIndex('room_type'))
self.rooms_mapper.addMapping(self.ui.room_type, self.rooms_model.fieldIndex('room_type'))
self.rooms_mapper.toFirst()
self.ui.current_index.setText(str(self.rooms_mapper.currentIndex()))
self.config_model = QSqlRelationalTableModel(self)
self.config_model.setTable('room_configurations')
self.config_model.setRelation(self.config_model.fieldIndex("configuration"), QSqlRelation('configurations', 'configuration', 'configuration'))
self.room_model = self.config_model.relationModel(self.config_model.fieldIndex('room'))
self.config_view = self.ui.configurations
self.config_view.setModel(self.config_model)
self.config_view.horizontalHeader().setStretchLastSection(True)
self.config_model.select()
self.config_model.setHeaderData(self.config_model.fieldIndex("sort_order"), Qt.Horizontal, "sort order")
self.config_view.setItemDelegate(View_room_config(self))
self.config_view.setItemDelegate(QSqlRelationalDelegate(self.config_view))
self.config_view.resizeColumnsToContents()
room = self.rooms_model.record(self.rooms_mapper.currentIndex()).value('room')
self.config_model.setFilter("room = '{0}'".format(room))
#self.config_view.setColumnHidden(0, True)
#self.config_view.setColumnHidden(2, True)
self.ui.add_config.clicked.connect(self.add_config)
def add_config(self):
row=int(self.config_model.rowCount())
self.config_model.insertRow(row)
self.index = QModelIndex(self.config_model.index(row, 2))
self.config_model.setData(self.index, self.ui.room_num.text(), Qt.EditRole)
class View_room_config(QSqlRelationalDelegate):
def __init__(self, parent=None):
QItemDelegate.__init__(self)
def createEditor(self, parent, option, index):
if index.column() == 3:
combo = super().createEditor(parent, option, index)
if __name__=="__main__":
app=QApplication(sys.argv)
db = QSqlDatabase.addDatabase("QPSQL")
db.setHostName(host)
db.setDatabaseName(database)
db.setUserName(user)
db.setPassword(password)
if (db.open()==False):
QMessageBox.critical(None, "Database Error", db.lastError().text())
myapp = Setup_rooms()
myapp.show()
sys.exit(app.exec_())
I kind of sorted my problem. I restored a back up of the database into the development area. I am not aware of any underlying tables which would have caused an issue but my theory is that a database error was occurring. I am now testing for errors on self.config_model using self.config_model.lastError().text() but I haven't found any.
Nonetheless, without changing my code the program is kind of working although the user interface when editing is rather clunky. I will now look into that.

How to sort items in Qt QListview using Qt.UserRole

I'm having some problem sorting the items in my QListView using values in a field I specified.
Basically what I'm trying to do is this:
Detect faces in a collection of photos and display them in a QListView
Cluster the faces (images)
Update the view by placing items in the list (which are face images) belonging to the same cluster in together. Concretely, if item 1, 3, 5 are in one cluster and items 2, 4, 6 are in another, then items 1, 3, 5 should be displayed (in whatever permutations) before any of items 2, 4, 6 are displayed or vice versa.
The way I went about doing this is to set one of the UserRole field for each QStandardItem in my list to the cluster label and then try to get the QStandardModel to sort according to this UserRole. This would then display items in the same cluster (i.e. with the same cluster label in the UserRole) next to each other.
I'm able to set the UserRole successfully for the items but calling the sort function on the QStandardModel did not sort the items even though when I set the sort role to be the default DisplayRole (i.e. sort according to the text label of each face) it worked as intended.
Can anyone tell me what is wrong with my code or offer an alternative method? I've googled sorting list and I found the following link on QSortFilterProxyModel but as I'm quite new to Qt, I'm not able to adapt it to my situation.
Thanks in advance to any replies.
Here is the relevant code:
import os
from PySide.QtGui import QListView, QStandardItemModel, QStandardItem, QIcon
from PySide.QtCore import Qt
class FacesView(QListView):
"""
View to display detected faces for user to see and label.
"""
UNCLUSTERED_LABEL = -1
CLUSTER_ROLE = Qt.UserRole + 1
def __init__(self, *args):
super(FacesView, self).__init__(*args)
self._dataModel = QStandardItemModel()
self.setModel(self._dataModel)
# Layout items in batches instead of waiting for all items to be
# loaded before user is allowed to interact with them.
self.setLayoutMode(QListView.Batched)
def updateFaceClusters(self, labels):
"""Update the cluster label for each face.
#param labels: [1 x N] array where each element is an integer
for the cluster the face belongs to."""
assert(len(labels) == self._dataModel.rowCount())
# Put the cluster label each item/face belong to in the
# CLUSTER_ROLE field.
for i in xrange(self._dataModel.rowCount()):
index = self._dataModel.index(i, 0)
self._dataModel.setData(index, labels[i], self.CLUSTER_ROLE)
# Use cluster label as sort role
self._dataModel.setSortRole(self.CLUSTER_ROLE)
# This does NOT seem to sort the items even though it works fine
# when sort role is the default Qt.DisplayRole.
self._dataModel.sort(0)
print("Finished updating face clusters")
def itemsInList(self):
"""Returns the label for a face and the path to its image.
#return: (label, path)"""
items = []
for i in xrange(self._dataModel.rowCount()):
label = self._dataModel.index(i, 0).data(Qt.DisplayRole)
imagePath = self._dataModel.index(i, 0).data(Qt.UserRole)
clusterLabel = self._dataModel.index(i, 0).data(self.CLUSTER_ROLE)
items.append((imagePath, label, clusterLabel))
return items
def addItem(self, label, imagePath):
"""Add an item to list view
#param label: The label associated with the item.
#param imagePath: Path to image for the icon."""
if os.path.exists(imagePath):
icon = QIcon(imagePath)
else:
icon = QIcon(':/res/Unknown-person.gif')
item = QStandardItem(icon, label)
item.setEditable(True)
# Add image path to the UserRole field.
item.setData(imagePath, Qt.UserRole)
# Add cluster label to image. CLUSTER_ROLE is where I intend
# to put the item's cluster label.
item.setData(self.UNCLUSTERED_LABEL, self.CLUSTER_ROLE)
# Prevent an item from dropping into another item.
item.setDropEnabled(False)
# Add item to list indirectly by adding it to the model.
self._dataModel.appendRow(item)
def clear(self):
self._dataModel.clear()
There's nothing wrong with the code you posted. So there must be something wrong with how you are using it. How are you generating the cluster labels?
Here's a test script using your FacesView class that sorts as you intended:
from random import randint
from PySide.QtGui import QWidget, QPushButton, QVBoxLayout, QApplication
from facesview import FacesView
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
self.list = FacesView(self)
self.button = QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout = QVBoxLayout(self)
layout.addWidget(self.list)
layout.addWidget(self.button)
def handleButton(self):
labels = []
self.list.model().setRowCount(0)
for row in range(10):
labels.append(randint(0, 3))
text = 'Item(%d) - Cluster(%d)' % (row, labels[-1])
self.list.addItem(text, 'icon.png')
self.list.updateFaceClusters(labels)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Categories