I want to enable a combobox - which is disabled from the properties editor in Qt Designer - but, only if the user checks the checkbox. I wrote the following, but it is not working. It is put inside the __init__ method of my mainclass. Could you please help me to understand why?
if self.dlg.checkBox.isChecked():
self.dlg.cmbvectorLayer6.setEnabled(True)
EDIT:
I now have the following in the __init__ method of my main class:
self.dlg.checkBox.stateChanged[int].connect(self.enablecombo)
with enablecombo being:
def enablecombo(self):
self.dlg.cmbvectorLayer6.setEnabled(True)
and it works fine in order to activate the comboboxes. But I am not sure how to do the equivalent in order to disactivate the comboboxes when the checkbox is unchecked...
The QCheckBox class inherits QAbstractButton, so you can use the toggled signal to do what you want:
self.dlg.checkBox.toggled.connect(self.enablecombo)
...
def enablecombo(self, checked):
self.dlg.cmbvectorLayer6.setEnabled(checked)
Or connect to the combo-box directly:
self.dlg.checkBox.toggled.connect(self.dlg.cmbvectorLayer6.setEnabled)
(You can also set up these kinds of direct connections in Qt Designer, by using the Signals and Slots Editing Mode)
if self.dlg.checkBox.isEnabled():
self.dlg.cmbvectorLayer6.setEnabled(True)
You checking the state is checked but you need to check isEnabled
self.dlg.checkBox.stateChanged[int].connect(self.checkcombo)
whatewer is the current state , just call a function which checks it and then based on its output enable/disable it
def checkcombo():
if self.dlg.checkBox.isChecked():
self.dlg.cmbvectorLayer6.setEnabled(True)
else:
self.dlg.cmbvectorLayer6.setEnabled(False)
Related
My app allows double-clicking on a QTableWidget cell to edit the content. However, when I double-click on a cell, the existing content is selected.
How can I allow a cell's content to be edited without having the existing text automatically selected?
Qt item views automatically call selectAll() when an editor is created and it's a QLineEdit or a Q[Double]SpinBox.
Since the call to selectAll() is done after the editor is created, a simple solution would be to connect to the selectionChanged signal and deselect the text automatically. This can be done in an item delegate that would just override createEditor().
Note that this must be done only once, otherwise any user selection would become impossible. This also means that the signal must be disconnected immediately, and before deselecting (otherwise the function would be called again).
class NoSelectDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = super().createEditor(parent, option, index)
if isinstance(editor, (QLineEdit, QSpinBox, QDoubleSpinBox)):
def deselect():
# Important! First disconnect, otherwise editor.deselect()
# will call again this function
editor.selectionChanged.disconnect(deselect)
editor.deselect()
editor.selectionChanged.connect(deselect)
return editor
# ...
table.setItemDelegate(NoSelectDelegate(table))
I'm having troubles with a Qt Application (managed through PyQt5) which used to be reliable, until a bunch of updates (where I ended up with PyQt6). PyQt6 is not the culprit here, as I began to have those problems with later PyQt5 versions.
I'm suspecting my problems are instead linked to an abuse of exec() methods call. The full traceback is not available (I've only a RuntimeError with a cryptic message telling me the Dialog is not available anymore); what (I think) I'm seeing are the parent windows randomly disappearing. This behaviour is indeed mentionned in the Qt documentation:
Note: Avoid using this function; instead, use open(). Unlike exec(), open() is asynchronous, and does not spin an additional event loop. This prevents a series of dangerous bugs from happening (e.g. deleting the dialog's parent while the dialog is open via exec()). When using open() you can connect to the finished() signal of QDialog to be notified when the dialog is closed.
Initially, my app was constructed this way:
QMainWindow A > QDialog B > QDialog C > ...
Each QDialog was launched using .exec() method.
The pseudo code could be summarized this way [edit] :
class A(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ui = Some_Ui_From_QtDesigner()
self.ui.setupUi(self)
self.ui.some_menu.triggered.connect(self.load_popup)
def load_popup():
B = QDialog(A)
if B.exec() == 1:
# Do some things (extract data from B and update A)
if C.exec() == 1:
# Do some things (extract data from C and update B)
qApp = QtWidgets.QApplication.instance()
a = A()
a.show()
qApp.setQuitOnLastWindowClosed(True)
qApp.exec()
I've tried to use .open() methods instead of .exec() and came up with something like this (using QLoopEvent's and events handling):
def process_C_qdialog(parent):
C = QDialog(parent)
# Construct your QDialog some more ...
loop = QtCore.QEventLoop()
my_result = None
def _accept():
my_result = "blah"
loop.exit(0)
def _reject():
loop.exit(-1)
f.accepted.connect(_accept)
f.rejected.connect(_reject)
f.open()
loop.exec()
return my_result
My troubles are far from over as yet. I suspect that recreating manually a QEventLoop causes the same problems than using .exec() in the first place.
So some questions:
Am I right about the QEventLoop?
Would it be safer/enough to leave the parent out of the QDialog generator?
Any advice on how to handle this the right way (keeping a modal dialog if possible)?
More generally, are they any safe ways to reproduce the static methods of Qt through a custom python function? (ie, a function which would load a customized popup, wait for the user's input and returning it)
Note: as this behaviour happens erratically (and without the full traceback available), I'm not 100% sure of this; but I suspect that even a simple QMessageBox().exec() has the same effects, even if this is not stated in the doc.
I got a couple of questions regarding qDialogButtonBox. While my code still works, I believed that there are a few parts that can be better refined/ I am not finding much info online
class testDialog(QtGui.QDialog):
def __init_(self, parent=None):
...
self.init_ui()
self.signals_connection()
def init_ui(self):
...
self.buttonBox = QtGui.QDialogButtonBox()
self.buttonBox.addButton("Help", QtGui.QDialogButtonBox.HelpRole)
self.buttonBox.addButton("Apply", QtGui.QDialogButtonBox.AcceptRole)
self.buttonBox.addButton("Cancel", QtGui.QDialogButtonBox.RejectRole)
#
def signals_connection(self):
self.test_random.clicked.connect(self.test_rand)
# Is this the latest/correct way to write it?
self.buttonBox.accepted.connect(self.test_apply)
self.buttonBox.rejected.connect(self.test_cancel)
self.buttonBox.helpRequested.connect(self.test_help)
def test_apply(self):
print "I am clicking on Apply"
def test_cancel(self):
print "I am clicking on Cancel"
self.close()
def test_help(self):
print "I am clicking for Help!"
My questions are as follows:
Under my function - signals_connection(), the lines that I wrote for
the buttonBox (though the code still works) are quite different
for the signal I have wrote for the self.test_random and I am
unable to find any similar online for the qdialogbuttonbox.. There
is another style that I have found - self.connect(self.buttonBox,
QtCore.SIGNAL("accepted()"), self, QtCore.SLOT("accept()")) but I
think that is the old style?? Otherwise what should be the right way
to write it?
In my test_cancel() function, is writing self.close() the best
way to close the application? The way that I run my program is as
follows:
dialog = testDialog();dialog.show()
Lastly, is it possible to add 3 different tool tips to the 3 buttons I have created? I saw that there is a command for it - self.buttonBox.setToolTip("Buttons for life!"), but this will results in all 3 buttons to have the same tool tip. Can I make it as individual?
Yes, that is the correct way to write signal connections (the other syntax you found is indeed the old way of doing it). You can find all the signals in the pyqt documentation for QDialogButtonBox. Different widgets and objects have different signals. QPushButton's and QDialogButtonBox's have different signals.
Yes, close() will close the dialog. The QApplication will exit by default if there are no other windows open. However, if this is a modal dialog, you typically want to close a dialog with either the accept or reject command. This will alert the calling function as to whether the dialog was closed with the Ok/Yes/Apply button or closed with the No/Cancel button.
You can set different tooltips for different buttons in the QDialogButtonBox. You just need to get a reference to the specific button you want to set the tooltip for.
For example
self.buttonBox.button(QDialogButtonBox.Help).setToolTip('Help Tooltip')
self.buttonBox.button(QDialogButtonBox.Ok).setToolTip('Apply Tooltip')
Or you could loop through all the buttons
for button in self.buttonBox.buttons():
if button.text() == 'Help':
button.setToolTip('Help Tooltip')
elif button.text() == 'Apply':
button.setToolTip('Apply Tooltip')
Also, you could connect the accepted and rejected signals from the QDialogButtonBox to the accept and reject slots on the QDialog
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
That way, you won't have to manually connect the Ok and Cancel buttons to your callbacks for closing the dialog.
I am trying to replace a widget for another. I am using a StackedWidget. I have the following.
First, I register add some widgets to the StackedWidget:
self.stackedWidget.addWidget(w1)
self.stackedWidget.addWidget(w2)
self.stackedWidget.addWidget(w3)
The I bind the click of a button of w1:
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self.stackedWidget,SLOT(self.stackedWidget.setCurrentWidget(w2)))
For Slot I have also tried "setCurrentIndex". I checked if the signal is being received, and it is ok.
Finally, I show w1.
self.stackedWidget.setCurrentWidget(w1)
Although the "clicked()" signal is received when the button of w1 is pressed, the widget w2 never appears in the StackedWidget.
UPDATE:
I am doing
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self.stackedWidget,SLOT('w1Clicked()'))
as suggested; however, I get
Object::connect: No such slot QStackedWidget::w1Clicked()
Object::connect: (sender name: 'pushButton')
Object::connect: (receiver name: 'stackedWidget')
I guess I have to create the slot "w1Clicked" somehow, but I am using the designer I can't figure it out.
I have one Main Window with the StackedWidget and separate forms with the buttons, so I don't see how to make the connection or create slots.
Also, I discovered that the problem with the way I was doing:
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self.stackedWidget,SLOT(self.stackedWidget.setCurrentWidget(w2)))
is that "self.stackedWidget.setCurrentWidget(w2)" gets executed immediately, it does not wait for the signal! That's why w2 was never shown.
Still I have no idea.
Your connect call is wrong. You can't define what values are going to be passed to your slot when you make the connection. Instead you need to create your own slot and handle the signal as you desire (apologies if my python syntax is off):
QObject.connect(w1.pushButton,SIGNAL("clicked()"),self,SLOT("w1Clicked()"))
def w1Clicked(self):
self.stackedWidget.setCurrentWidget(w2)
Finally, it worked this way:
w1.pushButton.clicked.connect(self.w1Clicked)
At least the signal is received and the method is called correctly.
Using Python3 and PyQt4 I have a function (run) that takes as an input a callable to provide status updates.
class Windows(QtGui.QWidget):
# Creates a widget containing:
# - a QLineEdit (status_widget)
# - a button, connected to on_run_clicked
def on_run_clicked(self):
def update(text):
self.widget.setText(text)
threading.Thread(target=run, args=(update, )).start()
This works ok (i.e. the text updates are displayed properly in the widget). However, when I replace QLineEdit by QTextEdit and use the append method to add text, I get:
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
It still works but point to the fact that I am doing something wrong, and I am not sure that I will keep working when more threads are active. Usually, I do this type of updates using signals and slots, but the run function is not PyQt specific. The questions are:
Why does it work without a warning for QLineEdit and not for
QTextEdit?
What is the right way to deal with a situation like this?
I don't know the specific reason why one class works and the other doesn't - nor do I really know the difference between using Python threading vs. Qt's threading...however, I can tell you that it is very tempremental if you don't set it up properly. Namely, you cannot (or at the very least, should not) modify GUI objects from a thread. Again, not sure the difference of a python vs. a Qt thread on that. But, the safe way to modify your interface from a GUI is by sending signals to your window...easiest way I know to do this is via the Qt threading.
class MyThread(QtCore.QThread):
updated = QtCore.pyqtSignal(str)
def run( self ):
# do some functionality
for i in range(10000):
self.updated.emit(str(i))
class Windows(QtGui.QWidget):
def __init__( self, parent = None ):
super(Windows, self).__init__(parent)
self._thread = MyThread(self)
self._thread.updated.connect(self.updateText)
# create a line edit and a button
self._button.clicked.connect(self._thread.start)
def updateText( self, text ):
self.widget.setText(text)