Can QListWidget be refreshed after an addition? - python

I'm adding an item to a QListWidget, and although I can get the specifics of the item back from the QListQidget, the new item never appears on the screen. Is it possible to refresh the QListWidget to have it update to display newer contents?
>>>myQListWidget.addItem("Hello")
>>>print self.myQListWidget.item(0).text()
Hello
I'm doing this in Python, but if you have the solution in C++ I can easily convert it.
Thanks!
--Erin

You can update the widget's view by calling update() or repaint(), the second function is asynchronous and forces widget to update immediately. But the QListWidget should update automatically after insertion without calling any extra functions, if not, then the problem could be that Qt can't process the paint events. Then you have to call QCoreApplication::processEvents(), but I am not sure if it is your problem.

Hmm... I'm not seeing that behavior.
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
a = QtGui.QApplication(sys.argv)
w = QtGui.QListWidget()
w.setWindowTitle('example')
w.show()
w.addItem("test 1") # shows up
w.addItem("test 2") # also shows up
EDIT: Removed chevrons so the code can be copied/pasted

I saw similar behavior as user671110 with PyQt5 (5.9.2). In my case using the suggestion from this qt forum worked.
Using the adapted sample code from jkerian:
import sys
from PyQt5 import QtWidgets

a = QtWidgets.QApplication(sys.argv)

w = QtWidgets.QListWidget()
w.setWindowTitle('example')
w.show()
w.addItem("test 1") # Item 1 does NOT show up
w.repaint() # Item 1 does NOT show up
QtCore.QCoreApplication.processEvents()
 # Item 1 DOES show up

w.addItem("test 2") 
 # Item 2 does NOT show up
QtCore.QCoreApplication.processEvents()
 # Item 2 DOES show up

w.addItem("test 3") 
 # Item 3 does NOT show up
a.exec_() # All items shows up
This is kind of expected as the GUI is refreshed with event processing. Still THe doc mentioned that repaint() could be use to perform an asynchronous refresh but it does not seems to be working, or I use it wrongly.

Related

PyQT5 Designer add checkbox i Python when I have separate file for code

My problem is that I have a file with my UI called xxx.ui. Then as many have suggested I created another python file called test.py where I have put code to use my xxx.ui:
# imports
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import uic
import sys
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('xxx.ui', self)
self.show()
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
Up until this stage everything works ok. But now I would like to add a checkbox to my UI whe program starts without messing inside xxx.ui (so the checkbox will be created dynamicaly when the program runs).
How can I do that ???
Thank You in advance.
After many fail attempts I have found how to do it:
To add Checkbox outside of xxx.ui file. I went to my test.py file and added code below this line:
uic.loadUi('xxx.ui', self)
The code looks like that (I am using horizontal layout widget created in designer called seasonLayout and my checkbox is inside that layout):
self.checkBox = QtWidgets.QCheckBox(self.horizontalLayoutWidget)
self.checkBox.setObjectName("checkBox_0")
self.checkBox.setText('Hello')
self.seasonLayout.addWidget(self.checkBox)
Then if You want to get to that object all You have to do is to use code below:
(here i change text of this newly created checkbox):
self.checkBoxs = self.findChild(QtWidgets.QCheckBox, 'checkBox_0')
self.checkBoxs.setText('test')
Hopefuly it will be helpful for other because I have really tried to find answer for that almost everywhere and everyone were just using widgets from designer - noone explain how to add them outside of it.

Using a checkbox in pyqt

1) I have a checkbox called "ch_check" in my UI created with Qt designer that needs to be tested
2) There is also a button, "bt_button", which triggers a simple function:
self.dlg.bt_button.clicked.connect(self.doCheck)
3) The function:
def doCheck(self):
if ch_check.isChecked():
self.dlg.le_text.setText("Hello")
else:
self.dlg.le_text.setText("Nope")
However I can't figure out how to reference the box properly. How would I do that? Do I need to connect the checkbox somehow first? All the examples I found so far use checkboxes to fire off functions and whatnot while completely ignoring this basic usage. I found this question but it's not answering how to address the existing checkbox: How to check if a checkbox is checked in pyqt
You can do this utilizing the StateChanged signal. For this example we have a simple .ui and a simple .py file:
The .ui file defines two widgets. A check box (ch_check) and a single QLabel (my_label)
The python file:
from PyQt4 import QtCore
from PyQt4 import QtGui
import sys
from test_ui import Ui_MainWindow
class CheckDialog(QtGui.QMainWindow):
def __init__(self):
QtGui.QWidget.__init__(self)
# Set up the user interface from Designer.
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.ch_check.stateChanged.connect(self.state_changed)
def state_changed(self, int):
if self.ui.ch_check.isChecked():
self.ui.my_label.setText("CHECKED!")
else:
self.ui.my_label.setText("UNCHECKED!")
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = CheckDialog()
window.show()
sys.exit(app.exec_())
Explanation:
We set up our signal with this line:
self.ui.ch_check.stateChanged.connect(self.state_changed)
When the state of the checkbox changes, it will call the state_changed function. This is where your logic to check whether the box is checked or unchecked goes.
def state_changed(self, int):
if self.ui.ch_check.isChecked():
self.ui.my_label.setText("CHECKED!")
else:
self.ui.my_label.setText("UNCHECKED!")
In the function, we determine if the check box has been checked. If so, we change our label to say "CHECKED", if it is unchecked the label changes to "UNCHECKED".
Example:
When the application is launched the UI looks like this:
Checking the box, changes the label:
Unchecking the box, also changes the label:
As mentioned in the comment above, I just made a small mistake. The correct code would be:
def doCheck(self):
checker = self.dlg.ch_check
if self.dlg.ch_check.isChecked():
self.dlg.le_text.setText("Hello")
else:
self.dlg.le_text.setText("Nope")

Lock a choice in PyQt4 QComboBox using a QCheckBox

I am beginning to write a GUI using PyQt4. This is my first experience with GUIs (and also oo-programming is somewhat new to me). Part of that GUI will be like 4 to 5 instances of QComboBox. As many choices are to be made, I want the user to be able to lock a choice, such that is not being changed unintenionally later. For one QComboBox I can solve the problem with this code that I wrote:
import sys
from PyQt4 import QtGui, QtCore
class MyGui(QtGui.QWidget):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(250, 50)
# vertical layout for widgets
self.vbox = QtGui.QVBoxLayout()
self.setLayout(self.vbox)
# Create a combo box with some choices
self.combo_color = QtGui.QComboBox()
self.vbox.addWidget(self.combo_color)
items = 'Red Yellow Purple'.split()
self.combo_color.addItems(items)
self.connect(self.combo_color, QtCore.SIGNAL('activated(QString)'), self.use_choice)
# add a checkbox next to the combobox which (un-)locks the the combo-choice
self.checkbox_color = QtGui.QCheckBox('Lock Choice', self)
self.vbox.addWidget(self.checkbox_color)
self.connect(self.checkbox_color, QtCore.SIGNAL('stateChanged(int)'), self.lock_choice)
def use_choice(self, text):
# do something very useful with the choice
print 'The current choice is: {choice}'.format(choice=text)
def lock_choice(self):
if self.checkbox_color.isChecked():
self.combo_color.setEnabled(False)
print 'Choice {choice} locked'.format(choice=self.combo_color.currentText())
else:
self.combo_color.setEnabled(True)
print 'Choice unlocked'
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
mygui = MyGui()
mygui.show()
app.exec_()
This code does what it should, but I am very unhappy with its design, because the method lock_choice is hard-coded to only lock the choice of the QComboBox combo_color. What if I now want to do the same thing for another QComboBox (say combo_name) and a second QCheckBox (say checkbox_name) which could be realized by appending the following code to the classes __init__(self) code block:
# create second combo box with some other choices
self.combo_name = QtGui.QComboBox()
self.vbox.addWidget(self.combo_name)
items = 'Bob Peter'.split()
self.combo_name.addItems(items)
self.connect(self.combo_name, QtCore.SIGNAL('activated(QString)'), self.use_choice)
# add a checkbox next to the combobox which (un-)locks the the combo-choice
self.checkbox_name = QtGui.QCheckBox('Lock Choice', self)
self.vbox.addWidget(self.checkbox_name)
self.connect(self.checkbox_name, QtCore.SIGNAL('stateChanged(int)'), self.lock_choice) # <-- obviously wrong, as it (un-)locks color choice at the moment
Both QComboBoxes can share the method use_choice() right now, but they cannot share the method lock_choice(), as both checkboxes lock the color-choice. I want the checkbox checkbox_name to lock the name-choice, without copy and pasting the current lock_choice()-method and switching the hardcoded combobox.
I am sure there is an easy way, like passing the target-combobox to the method, which i just don't know yet. Help would be appreciated!
Try partial functions-
from functools import partial
with the connect signal, pass the QWidget name:
self.connect(self.checkbox_color, QtCore.SIGNAL('stateChanged(int)'), partial(self.lock_choice, self.combo_color))
And in your method, you may add an argument.
We'll add an argument in the method like below, to handle the QWidget(QComboBox) that we'll pass through the partial function above.
def lock_choice(self, combos):
if combos.isEnabled(True):
combos.setEnabled(False)
print 'Choice {choice} locked'.format(choice=combos.currentText())
The simplest solution would be to use the toggled signal with the setDisabled slot:
self.checkbox_color.toggled.connect(self.combo_color.setDisabled)
(And note how much cleaner the new-style sytax is when making signal connections).
It's also worth pointing out that you can also use a lambda for making inline signal connections, like this:
self.checkbox_color.toggled.connect(
lambda checked: self.combo_color.setDisabled(checked))
This is probably the most idiomatic solution when there are no convenient signal/slot pairings (and of course partial functions can achieve more or less the same thing in different way).

How to hide QComboBox items instead of clearing them out

I can't find a way to hide QComboBox items. So far the only way to filter its items out is to delete the existing ones (with .clear() method). And then to rebuild the entire QComboBox again using its .addItem() method.
I would rather temporary hide the items. And when they are needed to unhide them back.
Is hide/unhide on QCombobox items could be accomplished?
In case someone still looking for an answer:
By default, QComboBox uses QListView to display the popup list and QListView has the setRowHidden() method:
qobject_cast<QListView *>(comboBox->view())->setRowHidden(0, true);
Edit: fix code according to #Tobias Leupold's comment.
Edit: Python version:
# hide row
view = comboBox.view()
view.setRowHidden(row, True)
# disable item
model = comboBox.model()
item = model.item(row)
item.setFlags(item.flags() & ~Qt.ItemIsEnabled)
# enable item
view.setRowHidden(row, false)
item.setFlags(item.flags() | Qt.ItemIsEnabled)
To build on what #kef answered:
(excuse the C++ on the python question)
By default the QComboBox will use a QListView for the view, thus you can do the following:
QListView* view = qobject_cast<QListView *>(combo->view());
Q_ASSERT(view != nullptr);
view->setRowHidden(row, true);
The one drawback with the above is, that even though the item will be hidden from the popup, the user can still select it using the mouse wheel. To overcome this add the following for the hidden row:
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(combo->model());
Q_ASSERT(model != nullptr);
QStandardItem* item = model->item(row);
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
With the above the row will be hidden and the user will not be able to scroll to it with the mouse wheel.
To unhide it, just do the reverse:
view->setRowHidden(row, false);
item->setFlags(item->flags() | Qt::ItemIsEnabled);
You can use the removeItem() method to remove an item from the QComboBox.
void QComboBox::removeItem ( int index )
Removes the item at the given index from the combobox. This will update the current index if the index is removed.
This function does nothing if index is out of range.
If you don't know the index, use the findText() method.
There are no hide/unhide methods for QComboBox items.
Althought there is no direct way to hide the item of the QComboBox, but you can use QComboBox::setItemData and set the size to (0,0) to hide the item of QComboBox:
comboBox->setItemData(row, QSize(0,0), Qt::SizeHintRole);
Use the setVisible() to alter the visibility of your object:
.setVisible(False) # Not Visible
.setVisible(True) # Visible
To show the item again:
comboBox->setItemData(row, QVariant(), Qt::SizeHintRole);
Note: changing the SizeHintRole doesn't work on OS X.
I came across this thread after getting frustrated with a lack of hide functionality that would keep the item indexing etc.
I got it to work based on #Kef and #CJCombrink answers. This is basically just a python translation.
I had a problem with qobject_cast.
Solved it by setting .setView(QListView()) to the QComboBox.
combo=QComboBox()
combo.setView(QListView())
hide:
combo.view().setRowHidden(rowindex,True)
tmp_item=combo.model().item(rowindex)
tmp_item.setFlags(tmp_item.flags() & ~Qt.ItemIsEnabled)
unhide:
combo.view().setRowHidden(rowindex,False)
tmp_item=combo.model().item(rowindex)
tmp_item.setFlags(tmp_item.flags() | Qt.ItemIsEnabled)
I decided to subclass the QComboBox and add the hide functionality. Bellow is an use example for testing.
You are welcome to use it. I make no assurances.
import sys
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
#subclassed QComboBox with added hide row functionality
class ComboBox_whide(QComboBox):
def __init__(self):
super().__init__()
self.setView(QListView())#default self.view() is a QAbstractItemView object which is missing setRowHidden, therefore a QListView needs to be set
def hide_row_set(self,row,value=True):
"""sets the row accesibility
value=True hides the row"""
self.view().setRowHidden(row,value)#hides the item from dropdown, however the item is stil accesible by moving down with arrow keys or mouse wheel. The following disables solves that
tmp_item=self.model().item(row)
if value:#hide -> disable
tmp_item.setFlags(tmp_item.flags() & ~Qt.ItemIsEnabled)
else:#enable
tmp_item.setFlags(tmp_item.flags() | Qt.ItemIsEnabled)
def hide_row_toggle(self,row):
"""toggles the row accesibility"""
if self.view().isRowHidden(row):#is hidden, therefore make available
self.hide_row_set(row,False)
else:#is not hidden, therefore hide
self.hide_row_set(row,True)
class Main(QMainWindow):
def __init__(self):
super().__init__()
cwidg=QWidget()
clayer=QVBoxLayout()
cwidg.setLayout(clayer)
self.setCentralWidget(cwidg)
#button for testing
self.btn=QPushButton('Button')
self.btn.setCheckable(True)
clayer.addWidget(self.btn)
#subclassed QComboBox
self.combo=ComboBox_whide()
for n in range(3):#add 3 items with tooltips
self.combo.addItem('item%i'%n)
self.combo.setItemData(n,'tip%i'%n,Qt.ToolTipRole)
clayer.addWidget(self.combo)
#button test function - choose either or for testing
self.btn.clicked.connect(self.btn_clicked)
#uncomment for add/remove example self.btn.clicked.connect(self.remove_add_item)
def btn_clicked(self):
self.combo.hide_row_toggle(1)
def remove_add_item(self):# here for naive comparison and to show why removing and adding is not ok
if self.combo.count()==3:
self.combo.removeItem(1)
else:
self.combo.addItem('new')#new "item1" withouth the ToolTip
if __name__ == '__main__':
app = QApplication.instance()
if app is None:#Pyside2 ipython notebook check
app = QApplication(sys.argv)
main = Main()
main.show()
app.exec_()

Error in model view implemention of GUI in pyqt

the following sample code is crashing with this error when I close the application:
QBasicTimer::start: QBasicTimer can only be used with threads started with QThread
here is my code :
import sys
from PyQt4 import QtGui ,QtCore
app = QtGui.QApplication(sys.argv)
data=[]
data.append("one")
model=QtGui.QStringListModel(data)
combobox=QtGui.QComboBox()
combobox.show()
combobox.setModel(model)
sys.exit(app.exec_())
I found out that's about using model but I don't know how to fix it.
edited:
os : win 7 64bit
pyqt4
The program is not "crashing": it is merely printing an error message during the normal shutdown process.
The reason the message is being shown is a side-effect of garbage-collection. When python shuts down, the order in which objects get deleted can be unpredictable. This may result in objects on the C++ side being deleted in the "wrong" order, and so Qt will sometimes complain when this happens.
One way to "fix" the sample code would be to simply rename some of the PyQt objects. If I change the name combobox to combo, for instance, the error message goes away. There's nothing mysterious about this - it just changes the order in which the objects are deleted.
But another, much more robust, way to fix the problem would be to make sure the QStringListModel has a parent, since it's possible that Qt doesn't take ownership of it when it's passed to the combo-box. Qt should always handle the deletion of child objects correctly when they're linked together in this way. So the code example would become:
import sys
from PyQt4 import QtGui, QtCore
app = QtGui.QApplication(sys.argv)
combobox = QtGui.QComboBox()
data = []
data.append("one")
model = QtGui.QStringListModel(data, combobox)
combobox.setModel(model)
combobox.show()
sys.exit(app.exec_())

Categories