I'm using PyQt5, I'm trying to create an application that involves a QPushButton. Now, when the user clicks this button, a function is supposed to be executed.
When I try to use button.clicked.connect(self.button_clicked_slot) it works as expected only and only if button_clicked_slot() is a static method, i.e. doesn't take self as an argument. In case it is a non-static method, the function isn't executed when the button is clicked.
I've tried various similar answers from StackOver and elsewhere, none of them could solve the problem I'm facing. I'm creating and using this button inside a class, the slot function is a method of this same class. I've tried to make the class inherit from QWidget and QObject, neither of those solutions worked. It always seems to boil down to using a staticmethod.
The entire code would be very big, however, here is an over-simplified code snippet with the exact same
import sys
from PyQt5 import QtWidgets, QtCore
class activity(QtWidgets.QWidget):
def __init__(self):
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
button = QtWidgets.QPushButton('test button')
button.clicked.connect(self.temp_print)
window.setCentralWidget(button)
window.show()
sys.exit(app.exec_())
#staticmethod
def temp_print():
print('Reached here')
activity()
In the above code, the method temp_print() is executed whenever the button is clicked (because it is a staticmethod). However, if I rewrite the function as:
def temp_print(self):
print('Reached here')
And suddenly, this function is never executed regardless of how many times I click the button.
In several other code snippets and the official documentation, I've seen a non-static function being used as a slot and things seem to be going smoothly for them. The code snippet I shared above is an overly-simplified version of the problem that I'm facing.
And yes, in the code that I shared above, I don't need the self parameter inside the function and thus should be able to use a static method too. However, like I mentioned, this is a simplified version of the code I'm using (my actual code is over 500+ lines and thus it would be very stupid to paste the whole thing here), but, in my actual code, I am using the self parameter and thus need a non-static function to be used as the slot for click events.
Static methods can be invoked without the need to have an instance unlike the methods of the class that need the object to be created.
In your example, app.exec_() prevents the constructor from running, so the other methods of the class such as temp_print can not be called.
So there are 2 possible solutions:
Use a lambda method to invoke temp_print:
import sys
from PyQt5 import QtWidgets
class Activity:
def __init__(self):
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
button = QtWidgets.QPushButton("test button")
button.clicked.connect(lambda: self.temp_print())
window.setCentralWidget(button)
window.show()
sys.exit(app.exec_())
def temp_print(self):
print("Reached here")
if __name__ == "__main__":
Activity()
Let the object finish building:
import sys
from PyQt5 import QtWidgets
class Activity:
def __init__(self):
self.m_app = QtWidgets.QApplication(sys.argv)
self.window = QtWidgets.QMainWindow()
button = QtWidgets.QPushButton("test button")
button.clicked.connect(self.temp_print)
self.window.setCentralWidget(button)
self.window.show()
def temp_print(self):
print("Reached here")
def run(self):
return self.m_app.exec_()
if __name__ == "__main__":
a = Activity()
sys.exit(a.run())
Related
I have been trying to get a QComboBox in PyQt5 to become populated from a database table. The problem is trying to find a method that recognizes a click event on it.
In my GUI, my combo-box is initially empty, but upon clicking on it I wish for the click event to activate my method for communicating to the database and populating the drop-down list. It seems so far that there is no built-in event handler for a click-event for the combo-box. I am hoping that I am wrong on this. I hope someone will be able to tell me that there is a way to do this.
The best article I could find on my use-case here is from this link referring to PyQt4 QComboBox:
dropdown event/callback in combo-box in pyqt4
I also found another link that contains a nice image of a QComboBox.
The first element seems to be a label followed by a list:
Catch mouse button pressed signal from QComboBox popup menu
You can override the showPopup method to achieve this, which will work no matter how the drop-down list is opened (i.e. via the mouse, keyboard, or shortcuts):
from PyQt5 import QtCore, QtWidgets
class ComboBox(QtWidgets.QComboBox):
popupAboutToBeShown = QtCore.pyqtSignal()
def showPopup(self):
self.popupAboutToBeShown.emit()
super(ComboBox, self).showPopup()
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.combo = ComboBox(self)
self.combo.popupAboutToBeShown.connect(self.populateConbo)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.combo)
def populateConbo(self):
if not self.combo.count():
self.combo.addItems('One Two Three Four'.split())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
However, for your particular use-case, I think a better solution might be to set a QSqlQueryModel on the combo-box, so that the items are updated from the database automatically.
Alternative Solution I :
We can use frame click, the code is to be used in the container of the combo box (windows/dialog/etc.)
def mousePressEvent(self, event):
print("Hello world !")
or
def mousePressEvent():
print("Hello world !")
Alternative Solution II :
We could connect a handler to the pressed signal of the combo's view
self.uiComboBox.view().pressed.connect(self.handleItemPressed)
...
def handleItemPressed(self, index):
item = self.uiComboBox.model().itemFromIndex(index)
print("Do something with the selected item")
Why would you want to populate it when it's activated rather than when the window is loaded?
I am currently developing an application with PySide (another Python binding for the Qt framework), and I populate my comboboxes in the mainwindow class __init__ function, which seems to be the way to go, judging by many examples.
Look at the example code under "QCombobox" over at Zetcode.
I can't get the GUI for my application to run in the manner that I need it to. My question is, given the below criteria, how do I go about setting up and running the GUI properly. The lack of good documentation that I have found is insanely frustrating (maybe I'm looking in the wrong places?).
I have a main window in a file called MainCustomerWindow.py containing a class by the same name. This is where all the code from the qt designer is. I have another class file called GUIController. The GUIController class does just that, controls the multiple GUI windows. It is in this GUIController class that I am trying to instantiate and run the MainCustomerWindow. Here is the code I have been trying.
def setup_window(self):
APP = QtWidgets.QApplication(sys.argv)
Window = MainCustomerWindow()
Window.setupUi(QtWidgets.QMainWindow)
Window.show()
sys.exit(APP.exec_())
Just as a side note, I come from JavaFX and Swing, and don't fully understand the workflow for pyqt5. So if someone could add an explanation for that as well it would be greatly appreciated.
The class generated by Qt Designer is not a widget, it is a class used to fill an existing widget, so you must create an object in the window, assuming you have used the "Main Window" template, then the widget must be QMainWindow (if it is another maybe you should use QDialog or QWidget), then you have to create another class that belongs to the design, and using the method setupUi() you must pass the widget to fill it:
def setup_window(self):
app = QtWidgets.QApplication(sys.argv)
# create window
window = QtWidgets.QMainWindow()
ui = MainCustomerWindow()
# fill window
ui.setupUi(window)
window.show()
sys.exit(app.exec_())
Although a better option is to create a new class and have it inherit from both:
class MainWindow(QtWidgets.QMainWindow, MainCustomerWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
If you want to get detailed information I recommend you read the following:
http://pyqt.sourceforge.net/Docs/PyQt5/designer.html
You can try taking the code you have there and adding it to the main statement at the end of your app's script. You can also have this statement instantiate your class where the init method contains the setupui() call. For example:
if __name__ == '__main__':
app = QWidgets.QApplication(sys.argv)
window = QMainWindow()
main_window = MainCustomerWindow()
window.show()
sys.exit(app.exec())
This code first sets up the PyQt app as an instance of QApplication. Next it instantiates an instance of QMainWindow so that PyQt knows what to display as the main app starts. In my experience, I've put setupui() in the init method of the app class. In your case, in the init method of MainCustomerWindow Finally, window.show() tells PyQt to begin rendering the main window.
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")
I spent longer than I'd care to admit think of a suitable 'question' heading for this topic, as my issue is somewhat hard to articulate.
Here is a quick summary of the situation:
I'm writing a basic GUI with Python 3.4 and PySide
I'm using QFileSystemWatcher to monitor a particular file
When the file is changed, QFileSystemWatcher calls a method, which in turn calls a method within a PySide Class
All of the above seems to be working perfectly, except the GUI-specific actions detailed in the PySide Class method aren't being executed (I'll explain in more detail below).
Example code:
#Establishing the PySide GUI Class
class GUI(QMainWindow, Ui_GUI):
def __init__(self, parent=None)
super(GUI, self).__init__(parent)
self.setupUi(self)
QtCore.QObject.connect(self.Button, QtCore.SIGNAL("clicked()"), self.Run)
def Run(self):
print("1")
self.treeWidget1.clear()
self.treeWidget2.clear()
print("2")
self.label1.setText("Text 1")
self.label2.setText("Text 2")
print("3")
for y in range(0, 5):
self.treeWidget1.resizeColumnsToContents()
print("Finished")
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
Script = GUI()
Script.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
#Setting up the GUI
if __name__ == '__main__':
app = QApplication(sys.argv)
showGUI = GUI()
showGUI.show()
app.exec_()
As I mentioned above, the above code doesn't return any errors. When I change the file (listed in the path), FileChanged does indeed call the Run() method from the GUI class. However, it won't actually do any of the 'stuff', it will only execute the print commands in between the 'stuff'.
If I then click on the 'Button' in the GUI, it will execute Run() correctly, and properly execute all the 'stuff'.
My question: is there something I'm missing here? If it's calling the method correctly, and is able to execute the various 'print' commands, why is it not executing the actual 'stuff'?
Thanks!
EDIT 1: I've removed the -do stuff- tags and put in some example code. All the 'stuff' code relates to updating various PySide QLabels, QTreeWidgets, etc.
EDIT 2: I forget the () at the end of the treeWidget clear commands.
The Script object created in the FileChanged function has local scope, and will be garbage-collected as soon as the function returns.
If the Run slot gets called when the signal fires, it will carry out all of the changes correctly, but you won't get to see any of those changes, because Script will be deleted before it is ever shown.
In order to for the example script to begin to make any sense, it would need to be re-arranged to something like this:
#Setting up the GUI
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
showGUI = GUI()
#Establish the file monitoring mechanism, *outside* the PySide class
def FileChanged():
showGUI.Run()
Paths = ['path/to/file']
Watch = QtCore.QFileSystemWatcher(Paths)
Watch.fileChanged.connect(FileChanged)
showGUI.show()
app.exec_()
Of course, it's possible that your real code is nothing like the example in your question (which has numerous other errors preventing it from being run), and so this might be no help. But if that is the case, you will have to post a fully working, self-contained example that properly demonstrates the problems you are having.
In my Qt-based application (built using PyQt 4.8.6), I have a class that is a subclass of QtGui.QDialog:
class ModelDialog(QtGui.QDialog):
...
When I run the application's user interface, I can display the QDialog like so:
def main():
app = QtGui.QApplication(sys.argv)
dialog = ModelDialog()
dialog.exec_()
According to the Qt docs and the PyQt docs, exec_() is a blocking function for this QDialog, which defaults to a modal window (which by definition prevents the user from interacting with any other windows within the application). This is exactly what happens under normal circumstances.
Recently, however, I've been working on a way to call through the entire QApplication using defaults for all input values, and not asking the user for any input. The application behaves as expected except for one single aspect: calling dialog.exec_() causes the modal dialog to be shown.
The only workaround I've been able to find has been to catch the showEvent function and to promptly hide the window, but this still allows the QDialog object to be shown for a split second:
class ModelDialog(QtGui.QDialog):
...
def showEvent(self, data=None):
self.hide()
Is there a way to prevent the modal window from being shown altogether, while continuing to block the main event loop? I'd love for there to be something like:
def main():
app = QtGui.QApplication(sys.argv)
dialog = ModelDialog()
dialog.setHideNoMatterWhat(True)
dialog.exec_()
(to that end, I tried using QWidget.setVisible(False), but dialog.exec_() sets the dialog to be visible anyways, which is expected according to the Qt docs)
Use app.exec_() instead of dialog.exec_().