Auto resize QLabel to fit updated QTableWidget span - python

I have a QTableWidget, and I have a QLabel that I put inside a cell of the table using .setCellWidget().
During run-time I change the span of the QTableWidget cell where the QLabel is, using .setSpan()
BUT when I change the span of the cell where the QLabel is, the QLabel does not resize.
Some code and screenshot below:
def generate_table(self):
global gtable
table = QTableWidget(20, 60)
gtable = table
def create_task(self):
task_widget = QWidget()
task_layout = QHBoxLayout()
task_widget.setLayout(task_layout)
task = QLabelClickable(the_task_name)
task_layout.addWidget(task)
gtable.setCellWidget(selected_row_column[0], selected_row_column[1], task_widget)
// if I include this part of the code, everything looks fine, both cell, widget and label scale properly, as visible through background color, below line is not the problem, notice its in the same function as where I set the cell widget
the_duration = 3
gtable.setSpan(selected_row, selected_column, 1, the_duration)
// Below is how I change the cell span. The rrow and ccolumn are integers, basically just cell coordinates
def save_task(self):
gtable.setSpan(rrow, ccolumn, 1, w_dr.value())
(there's a ton of code all over the place so I included what I thought is relevant, let me know what other parts of the code I should include)
This is what it should look like: (this is what the first span-changing line does)
This is what it looks like: (this is what the last line does)
My question is, How do I resize the QLabel / QWidget to auto fit the cell's updated size?

You can see the first span as correct because it's applied in the same event loop that would update the geometries of the view.
Spanning does not automatically do that (which could be a bug), so the solution is to use updateGeometries(), which:
Updates the geometry of the child widgets of the view.
Which means that all widgets of the view will be correctly resized and updated, including scroll bars and cell widgets.
def save_task(self):
gtable.setSpan(rrow, ccolumn, 1, w_dr.value())
gtable.updateGeometries()
A very important suggestion: avoid using globals, they are not handy as one would think, and in fact the often cause problems and bugs that are difficult to track; use instance members instead (eg. self.gtable).

Related

Resize column to text QT Table Widget Python PyQt5 [duplicate]

I am new to Qt and I have just managed to make a QTableView work with my model. It has fixed 3 columns. When I open a window, it look ok but when i resize the window, the QTableView itself gets resized but columns' width remains the same. Is there any build-in way to make it work? I want columns to resize to fit the edges of QTableView every the the window gets resized.
This code equally stretches each column so that they fit the table's width.
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
Docs:
QHeaderView::setSectionResizeMode
See resize modes here.
There is a header flag to ensure that the QTableView's last column fills up its parent if resized. You can set it like so:
table_view->horizontalHeader()->setStretchLastSection(true);
However, that does not resize the other columns proportionately. If you want to do that as well, you could handle it inside the resizeEvent of your parent thusly:
void QParent::resizeEvent(QResizeEvent *event) {
table_view->setColumnWidth(0, this->width()/3);
table_view->setColumnWidth(1, this->width()/3);
table_view->setColumnWidth(2, this->width()/3);
QMainWindow::resizeEvent(event);
}
QParent class is subclass of QMainWindow.
Widgets QTableView, QTreeView and their derived classes (such as QTableWidget) have this two usefull methods:
QHeaderView* horizontalHeader() const;
QHeaderView* verticalHeader() const;
If you open documentation for a class QHeaderView, you will find methods that set up appearance and behavior of row or column header for item views. You can resolve your problem by one of these methods:
void QHeaderView::stretchLastSection( bool stretch )
As Davy Jones mentioned.
Example:
QTableView *table = new QTableView();
table->horizontalHeader()->setStretchLastSection(true);
void QHeaderView::setResizeMode( ResizeMode mode )
As mode you can set QHeaderView::Stretch or QHeaderView::ResizeToContents.
Unfortunately this method have a drawback - after it's apply you will not be able to change size of columns (or rows) manually (in GUI) or programmatically.
Example:
QTableView *table = new QTableView();
table->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
In PyQt5 you can achieve this in your table_widget by doing:
header = table_widget.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)

pyqt: how to use scroll area that contains an outsized label [duplicate]

I want to overlay two widgets in QtDesigner:
There is the big QTextBrowser, and below in the down right corner should be a non-interactiv label that I am going to use as a drag-grip to resize the window (the main widget is frameless so I need to implement it).
Usually this label will sit below the QTextBrowser, which leaves on the left of the grip-label a lot of unused space. So I want to put the grip-label above the QTextBrowser. I want to achieve this in QtDesigner. But the code would look like:
QHBoxLayout *layout = new QHBoxLayout(videoWidget);
QLabel *overlayWidget = new QLabel();
overlay->setAlignment(Qt::AlignCenter);
overlay->setText("Overlaid Text");
layout->addWidget(overlay);
Or as I already did in python:
self.textedit = QTextBrowser(self);
...
gripImage=QLabel(self.textedit);
There the part between the brackets is the parent widget.
That's how it looks right now, but this is not what I want:
This is usually simplest to achieve by using QGridLayout. It allows widgets to occupy the same grid cells. For this particular problem, a 1x1 grid is enough.
Steps to try it out with designer:
Create new form, plain Widget for simplicity
Add a text edit to it (drag and drop from Widget Box), and from Object Inspector you should see it becomes child of the root widget
Add a label to it (drag and drop from Widget Box), and from Object Inspector you should see it becomes child of the root widget
Right click on the root widget (easiest in the Object Inspector), and from the bottom of context menu, select Lay out > - Lay out in Grid
Right click on the label, and from Layout alignment > set it aligned to the corner you want
Done. Here's what it looks like in my Designer:
Now adapt above to your real form.
Ok, it appears achieving above with Designer is hard, and perhaps a bit a matter of luck of doing things just right... Designer just doesn't support doing what you want, it seems.
For clarity this is a complete source code:
QGridLayout *layout = new QGridLayout(widget);
QTextBrowser *textBrowser = new QTextBrowser();
QLabel *label = new QLabel();
label->setText("Overlaid Text");
//label gets positioned above textBrowser and is an overlay
layout->addWidget(textBrowser, 0, 0, Qt::AlignLeft | Qt::AlignTop);
layout->addWidget(label, 0, 0, Qt::AlignRight | Qt::AlignBottom);
I had the same problem but I did not manage to add Overlapping widgets in QtDesigner. Instead, I had to create the overlapping one dynamically after initializing my MainWindow.
I've got two widgets:
dataset_tableWidget (tableWidget)
spinner_dataset_tableWidget (QtWaitingSpinner)
and I wanted to make spinner_dataset_tableWidget spin over the dataset_tableWidget.
After initializing the MainWindow you can do:
#Crating QtWaitingSpinners dinamically and positioning it over the tableWidgets
dataset_tableWidget = QtWaitingSpinner(dataset_tableWidget)
dataset_tableWidget.setSizePolicy(dataset_tableWidget.sizePolicy())

Trouble with widget size and adding new widgets

I ran into an issue tonight that involves resizing a QTextBox in a QGridLayout. The code is mostly self-explanatory. I set the QTextBox to span 10 cells down in my init_ui. I have a window size of 500x500.
def init_ui(self):
self.message_body = QTextEdit()
self.layout.addWidget(self.message_body, 2, 0, 9, 7)
self.show()
And then in a later method, changeSize, I am successfully able to change the size of QTextEdit. From this method I call the next method to load titles that will appear below the 445x280 QTextEdit. Loading these titles calls for new widgets to be added to the layout. Which is apparently a problem. I don't know if this is poor styling or not. Should all of the widgets be added in init? Either way when
self.layout.addWidget(lbl_title, 10, i)
runs. I lose my resized QTextEdit. Even if I follow it with another resize function.
Here are the two methods implicated
def changeSize(self):
self.message_body.resize(445,280)
self.loadTitles()
def loadTitles(self):
titles = Movie.title_library
for i, my_title in enumerate(titles):
lbl_title = QLabel(my_title)
## This is the line that refreshes the size to span 10x8 units
self.layout.addWidget(lbl_title, 10, i)
self.message_body.resize(445,280)
I figured out the solution. I needed to remove the message body widget before reinstating it. The code looked like
self.layout.removeWidget(self.message_body)
self.layout.addWidget(self.message_body,2,0,4,7)

Is it possible to change the colour of a QTableWidget row label?

I have a class that inherits from QTableWidget and I'm wondering if it's possible to change the colour of the row label for each row in the table?
I don't want to change the colour of any of the cells or column headings.
Thanks :)
P.S. I would like each row label to have a different colour. The motivation is that I can use these colours as a key/legend as each row in the table corresponds to a differently coloured line on a plot.
EDIT: Image illustrating the elements of the table I am referring to:
Yes it is possible but only with a slight trick. With setVerticalHeaderItem of QTableWidget you can set a QTableWidgetItem even for header rows and there you can define a background brush for each row. However most of the times the background will be ignored because the actual QStyle will override it. Setting the style of the vertical header widget to a style that doesn't change the background however does the trick.
Example:
from PySide import QtGui
app = QtGui.QApplication([])
table = QtGui.QTableWidget(4, 2)
table.show()
for i in range(0, 4):
item = QtGui.QTableWidgetItem('Text')
item.setBackground(QtGui.QColor(i*50, i*30, 200-i*40))
table.setVerticalHeaderItem(i, item)
# print(QtGui.QStyleFactory.keys())
table.verticalHeader().setStyle(QtGui.QStyleFactory.create('CleanLooks'))
app.exec_()
Yes it appears that you can do so, using the QTableWidgetItem functions such as setForeground which you pass through a QBrush object that I believe you can use the QBrush's setColor function to pass it a color. The QTableWidgetItem documentation has a lot of aesthetic-based functions so it looks like what you're looking for especially since it seems to be able to target specific cells/items on the table. If you want to look more into the QTableWidget documentation itself, here's a link to that as well.

How to set a QTableWidget to consistently scroll to bottom during live data input

I currently have a working GUI that implements a QTableWidget (not to be confused with a QTableView). This QTableWidget takes in a live stream of data and I want to have it so the vertical scroll bar that appears defaults to the bottom. I have tried several methods including setToBottom() with no success. I understand that there are many ways to do this by using a QTableView, however I am looking for a way in which to do this via QTableWidget.
You can use self.ui.tableWidget.scrollToBottom just after inserting a new item. But it does not always work as expected. It moves the scroll bar only to the beginning of the last row inserted which is not always necessarily the bottom of the table because sometimes the height of the last row inserted is really large.
So i think this is a good method to always scroll to the bottom of tablewidget :
item = self.ui.tableWidget.item(lastIndex, 0)
self.ui.tableWidget.scrollToItem(item, QtGui.QAbstractItemView.PositionAtTop)
self.ui.tableWidget.selectRow(lastIndex)
scrollToItem scrolls the view to ensure that the item is visible. The hint parameter specifies more precisely where the item should be located after the operation. Here PositionAtTop scrolls to position the item at the top of the viewport and lastIndex is the index of the last item inserted to the table.
As far as I remeber layout invalidation is delayed in Qt views. So you cannot scroll to some element or to bottom just after you added data. You should delay scrolling with timer. It is a bit dirty but the only working way I came up with
Like this (C++ code)
void getData()
{
addDataToWidget();
QTimer::singleshort(10, widget, SLOT(scrollToBottom());
}
If you are using PyQt6, you can use:
self.tableWidget.setItem(row, column, itemMeasVal)
self.tableWidget.scrollToItem(itemMeasVal,QtWidgets.QAbstractItemView.ScrollHint.EnsureVisible) # This is for the bar to scroll automatically and then the current item added is always visible
Scrolls the view if necessary to ensure that the item is visible.
The hint parameter specifies more precisely where the item should be located after the operation.
You can check more here: https://doc.qt.io/qtforpython/PySide6/QtWidgets/QTableWidget.html
https://doc.qt.io/qtforpython/PySide6/QtWidgets/QAbstractItemView.html#PySide6.QtWidgets.PySide6.QtWidgets.QAbstractItemView.scrollTo

Categories

Resources