Assign different widths to columns of a QTableWidget - python

I'm developing a small interface in pyqt5 from QT Designer, this includes a QTableWidget but I want to assign different widths to the columns, I have found topics that talk about the same but I don't know exactly where to insert the code they provide, I don't know if it's because of the version, I'm quite new in QT Designer.
I leave the questions I mention for what it's worth.
PyQt:How do i set different header sizes for individual headers?
PyQt Set column widths
The structure of my files is as follows:
app.py: Stores the functionalities of the application
SGS.py: The code that is generated after converting the .ui file to .py
SGS.ui
I leave the SGS.py part where the table headers are generated for what it's worth.
item = self.TableDocs.horizontalHeaderItem(0)
item.setText(_translate("MainWindow", "IDsystem"))
item = self.TableDocs.horizontalHeaderItem(2)
item.setText(_translate("MainWindow", "IDpeople"))
item = self.TableDocs.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Work"))
item = self.TableDocs.horizontalHeaderItem(4)
item.setText(_translate("MainWindow", "Hours"))
I also leave the code with which I fill the table
result = Cur.execute("SELECT idsystem,IDpeople,work,hours FROM workers")
self.TableDocs.setRowCount(0)
for row_number, row_data in enumerate(result):
self.TableDocs.insertRow(row_number)
for column_number, data in enumerate(row_data):
self.TableDocs.setItem(row_number, column_number, QtWidgets.QTableWidgetItem(str(data)))

The python file generated from the ui should never be edited. Consider it as a resource file (like an image or a json file) that is used to "create" the interface. Everything that you can't do from Designer you will have to implement in your application code files.
You can set the size (or resize mode, for example automatic resizing, or the "stretching" to the available width) whenever a model is applied to the item view. Since you're using a QTableWidget (which has its internal private model), you can do that as soon as the widget is created in the interface, which is right after setupUi (or loadUi) if using a designer file.
In order to set the size and behavior of the sections, you need to access the table headers: the horizontal one for the columns, the vertical for the rows.
class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.setupUi(self)
horizontalHeader = self.TableDocs.horizontalHeader()
# resize the first column to 100 pixels
horizontalHeader.resizeSection(0, 100)
# adjust the second column to its contents
horizontalHeader.setSectionResizeMode(
1, QtWidgets.QHeaderView.ResizeToContents)
# adapt the third column to fill all available space
horizontalHeader.setSectionResizeMode(
2, QtWidgets.QHeaderView.Stretch)
Note that if you remove a column and insert another one in the same place, you'll need to set its section size or mode once again.

Related

Looping over grid to get label and entry text values in tkinter

I have created a window using tkinter, this window contains a grid of Labels and Entries. In the Entries I have edited some text that I want to save back to the source.
Everything I'm reading so far says that I need to create a separate list and save each entry text variable in the list.
But isn't there a better way to get the values directly from the controls themselves? I know I can loop over mywindow.winfo_children or mywindow.children. So if I can do this, then I should be able to get the text values directly, no?
I just don't know which property to get the value from.
Any ideas out there?
This is the answer.
for child in context.grid_slaves():
if(type(child) is label):
print (child['text'])
if(type(child) is entry):
print(child.get())
I can also find out where I am in the grid like this: child.grid_info() and so I can synch back to the source.
Just to elaborate on the answer.
Loop through the window grid items using the grid_slaves method.
Get the row and column using the widget's grid_info method:
for child in window.grid_slaves():
g_info = child.grid_info()
if type(child) is Button:
row = g_info['row']
col = g_info['column']
text = child['text']
print(row, col, text)

How to clear table widget pyqt5 in order to add new content

I've been trying to figure out how to clear tableWidget so i can add new content. The snippet below shows the widget and how the information is added. However, anytime i refresh it, instead of clearing the screen it rather add more information.
Any help will be appreciated.
Thanks
def addNewContent(results):
header = self.tableWidget.horizontalHeader()
self.tableWidget.clearContents()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
numrows = len(results)
numcols = len(results[0])
self.tableWidget.setRowCount(numrows)
self.tableWidget.setColumnCount(numcols)
for row in range(numrows):
for column in range(numcols):
self.tableWidget.setItem(row, column, QTableWidgetItem((str(results[row][column]))))
addNewContent(results)
I've searched a lot for this problem the only solution that I came up with, was to call model.clear() and then setting the header labels once again
I think problem is in the variable result i believe its a list so all what you have to do is reinitialize it then append it with the new values somewhere in your code

QTableWidget: remove or extend to entire row the cell's active highlight

I'm currently developing a cross-platform app (current developing it's under Ubuntu 18.10 and Windows 10) with PyQT 5.13.1 on Python 3.6/3.7. I'm using a QTableWidget that I've initialized in this way:
myTable = QTableWidget()
myTable.setSelectionBehavior(QAbstractItemView.SelectRows)
myTable.setSelectionMode(QAbstractItemView.SingleSelection)
myTable.setAlternatingRowColors(True)
At app's start, my table has two columns but is empty (no rows) and I've a button to add rows one by one. When I do this the first time, the first row is added and it shows with the first cell with a sort of light blue highlight (in both Ubuntu and Windows) and the second cell without it, as in this screenshot:
If I click on any of those cells, the entire row is selected and shows a blue background, accordingly with the setSelectionBehavior initialization. If I CTRL+Click that selected row, it got deselected and goes back with the first cell active/highlighted and the second not. My question is: is there a way to completely remove that highlight (what I'd prefer) or to extend it also to adjacent cell? I've read around that one possible solution wuold be to disable table's focus, but I wish to avoid such a solution.
Thank you all in advance!
The currentIndex exists even when no index is selected, and by default it is highlighted. So to eliminate this behavior you can use a delegate to eliminate that behavior:
class Delegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
option.state &= ~QtWidgets.QStyle.State_HasFocus
delegate = Delegate(myTable)
myTable.setItemDelegate(delegate)

Display a huge sheet with QTableWidget

I need to display data using the PyQt class QTableWidget. But there could be dozens of thousand lines. Displaying the whole sheet makes the appli to hang...
So I found a solution consisting on loading data (meaning that the sheet is created) but hidding most of the rows and, when moving the scroll bar, show new rows and hidding previous ones.
In the bellow code, I setted self.rowsShown = 50 and at the init the lines from 1 to 50 are shown. And I previously did self.verticalScrollBar().actionTriggered.connect(self.refreshDisplay).
So the code is:
def refreshDisplay(self):
"""
This is to refresh the display when the scrollbar is moved
"""
# Minimum is reached and a previous row exists
if self.verticalScrollBar().value() <= self.verticalScrollBar().minimum() and self.isRowHidden(self.firstRowShown-1):# Minimum is reached and a previous row exists
for row in xrange(self.lastRowShown-1, self.lastRowShown):
self.hideRow(row)
self.lastRowShown -= 1
for row in xrange(self.firstRowShown-1, self.firstRowShown):
self.showRow(row)
self.firstRowShown -= 1
# Maximum is reached and a next row exists
if self.verticalScrollBar().value() >= self.verticalScrollBar().maximum() and self.isRowHidden(self.lastRowShown+1):
for row in xrange(self.firstRowShown, self.firstRowShown+1):
self.hideRow(row)
self.firstRowShown += 1
for row in xrange(self.lastRowShown, self.lastRowShown+1):
self.showRow(row)
self.lastRowShown += 1
This is working very well when I use the roll of the mouse or click on buttons of the scrollbar. But when I grab the slider or use the keypad to move the case selected, I'm blocked in the area shown.
Moreover, the size of the scrolling area correspond to the rows shown only. I would like to redifine it to set my own bar (for example with a size based on the real number of lines), so that I can move fastly to another part of the sheet. But the size of the scroll bar is always updated with the number of lines shown.
So, what I expect from you is to help me to resolve this two issues, OR give me another idea for displaying such huge sheet. Maybe I missed a module or class that already bear this functionnality of loading/showing lines whilemoving the cursor.
Thanks
I have written a tool that displays 10^6+ lines in an Excel-like table without resource problems. The solution is to use QTableView and QAbstractTableModel. You have to derive from QAbstractTableModel and implement the necessary functions it requires you to implement (I remember headerData and data, but I think there are more). You then plug the model into your view by doing view.setModel(model). More information how this is done can be found here.

Create QTableWidget with 2 or more header rows

I'd like to have a QTableWidget that has 2 'header' rows. Essentially I'd like to have the top 2 rows of the table not scroll vertically. For example:
Header 1 | Header 2
__________________
Header 3 | Header 4
__________________
Data | Data
__________________
Data | Data
__________________
Data | Data
__________________
Data | Data
__________________
I'd like to prevent any of the 4 headers (first two rows) not scroll off the screen as the user scrolls down.
I don't see anyway in Qt to add additional header rows or prevent scrolling of a single row. Maybe there is a tricky way to do this by having 2 actual tables and 1 of those tables having a single row which is a header?
I found a way to do this albeit in a somewhat obscure way. I did find a great example on how to do something similar, but with columns instead of header rows.
http://doc.qt.nokia.com/4.7-snapshot/itemviews-frozencolumn.html
I created two tables, one for the header rows and one for the data. I then hid the horizontal headers for the data table, set the margins/spacing for everything to 0. This squished the tables close enough together to look like a single table.
Make sure to hide the horizontal scroll bars for each individual table and then adding a new scrollbar that connected the two hidden scroll bars. So, when the user scrolls with the stand-alone scrollbar it triggers events on the 'real' hidden scroll bars. This way users can only have a single scrollbar to interact with.
I also had to catch all the signals from the QHeaderView class
and make sure to apply the changes requested by the signals to both tables at the same time.
The trickest part was making sure the width of the vertical header items were the same length in both tables. The width of a vertical header item gets set on an event called resizeEvent, http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qwidget.html#resizeEvent. So I had to override this method in my class to set the headers on both tables to the same width.
Code:
def resize(self):
"""
Called when we know the data table has been setup by Qt so we are
guaranteed that the headers now have a width, etc.
There is no other way to guarantee that your elements have been sized,
etc. by Qt other than this event.
"""
# Make the width of the vertical headers on the header table the same
# size as the initialized width of the data table (data table widths
# are setup automatically to fit the content)
width = self._data_table.verticalHeader().width()
self._header_table.verticalHeader().setFixedWidth(width)
Override the selectAll() method so when a user clicks the corner button, if enabled, all data in both tables will be selected:
Code:
def selectAll(self):
"""Select all data in both tables"""
for table in [self._header_table, self._data_table]:
for row in xrange(table.rowCount()):
for col in xrange(table.columnCount()):
item = table.item(row, col)
table.setItemSelected(item, True)

Categories