I have an application with a button for which the clicked signal is connected to a slot that opens a QFileDialog. I want to manipulate the state of the button (sender) within the slot depending on the actions taken by the user in the QFileDialog.
However, with the code I have presently, my application do not starts correctly. It starts immediately with QFileDialogOpen and I do not understand why. When I comment the line that connect the button's clicked signal to the slot, the application starts normally though.
How can I correctly pass the button as an argument when I want to connect a clicked signal of a button to a slot? Here is a MCWE of my problem:
from PySide import QtGui
import sys
class MyApplication(QtGui.QWidget):
def __init__(self, parent=None):
super(MyApplication, self).__init__(parent)
self.fileButton = QtGui.QPushButton('Select File')
self.fileButton.clicked.connect(self.select_file(self.fileButton))
layout = QtGui.QGridLayout()
layout.addWidget(self.fileButton)
self.setLayout(layout)
def select_file(self, button):
file_name = QtGui.QFileDialog.getOpenFileName()
if str(file_name[0]) is not "":
button.setEnabled(True)
else:
button.setDisabled(True)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = MyApplication()
w.show()
sys.exit(app.exec_())
You don't bind an actual function call using PySide signals/slots, you bind a function, method, or function-like object using PySide's signals/slots.
You have:
self.fileButton.clicked.connect(self.select_file(self.fileButton))
This tells Qt to bind the click event to something that is returned from the self.select_file function call, which presumably has no __call__ attribute and is called immediately (causing the opening of the QFileDialog)
What you want is the following:
from functools import partial
self.fileButton.clicked.connect(partial(self.select_file, self.fileButton))
This creates a callable, frozen function-like object with arguments for Qt to call.
This is comparable to saying:
self.fileButton.clicked.connect(self.select_file)
Rather than saying:
self.fileButton.clicked.connect(self.select_file())
Related
First File first.py
import pyqt5py
ret=pyqt5py.confirm()
print(ret)
Second File Having PYQT5 name: pyqt5py.py
import sys
from PyQt5 import QtWidgets, uic
class Ui(QtWidgets.QDialog):
def __init__(self,button1='Ok',button2='Cancel',text='Are You Sure?'):
super(Ui, self).__init__() # Call the inherited classes __init__ method
uic.loadUi('dialog.ui', self) # Load the .ui file
# Show the GUI
self.pushButton1.clicked.connect(lambda: self.click(1))
self.pushButton2.clicked.connect(lambda: self.click(2))
self.label.setText(text)
self.pushButton1.setText(button1)
self.pushButton2.setText(button2)
self.show()
def click(self,args):
print(self)
return self.sender().text()
app = QtWidgets.QApplication(sys.argv) # Create an instance of QtWidgets.QApplication
def confirm():
def pressed():
return 'clicked'
window = Ui(button1='Ok',button2='Cancel',text='Are You Sure?') # Create an instance of our class
print(window)
window.pushButton1.clicked.connect(pressed)
app.exec_() # Start the application
but i dont know what changes should i do make my first.py to work,i have correctly made the pyqt5 file but i dont know how to add def to call it for confirm
#######################
I Updated My Second File
As suggested by bfris you should rewrite the last lines of pyqt5py.py as follows:
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Ui(button1='Ok',button2='Cancel',text='Are You Sure?')
app.exec_()
That way you can run this file directly for debbugging purposes, but also import it elsewhere.
To use your widget in first.py you need to create an instance of it there.
first.py:
from pyqt5py.py import UI
app = QtWidgets.QApplication(sys.argv)
window = Ui(button1='Ok',button2='Cancel',text='Are You Sure?')
app.exec_()
Usually I use a QDialog within a Qt environment where it is opened from a QMainWindow and also returning which button was clicked to the QMainWindow.
However as I understand you would like to run another program and in between open your UI? I am not experienced doing that but it seems to me that its exec method does exactly that though you should read this discussion about a bug related to it.
Alternatively in first.py you connect the pushbutton's clicked signal to a slot, a function there.
I have a QPushbutton:
btn = QPushButton("Click me")
btn.clicked.connect(lambda: print("one"))
Later in my program, I want to rebind its click handler, I tried to achieve this by calling connect again:
btn.clicked.connect(lambda: print("two"))
I expected to see that the console only prints two, but actually it printed both one and two. In other words, I actually bound two click handlers to the button.
How can I rebind the click handler?
Signals and slots in Qt are observer pattern (pub-sub) implementation, many objects can subscribe to same signal and subscribe many times. And they can unsubscribe with disconnect function.
from PyQt5 import QtWidgets, QtCore
if __name__ == "__main__":
app = QtWidgets.QApplication([])
def handler1():
print("one")
def handler2():
print("two")
button = QtWidgets.QPushButton("test")
button.clicked.connect(handler1)
button.show()
def change_handler():
print("change_handler")
button.clicked.disconnect(handler1)
button.clicked.connect(handler2)
QtCore.QTimer.singleShot(2000, change_handler)
app.exec()
In case of lambda you can only disconnect all subscribers at once with disconnect() (without arguments), which is fine for button case.
On launch, my Qt application displays a dialog box with some options, before displaying the main window. The dialog box has a 'Start' and 'Cancel' button. The same dialog box is used at a later time, after the main window is displayed, when the user clicks on a 'New Game' button.
I'm trying to have the 'Cancel' button quit the application if it's the only interface element being displayed (i.e. on application launch). In my current code, however, the main window is still displayed.
If I replace self.view.destroy() with self.view.deleteLater() the main window briefly flashes into existence before disappearing (and quitting properly), but this can't be the solution.
If I move the view.show() call inside the dialog.exec_() block, it doesn't work either. Mainly because I would be calling view.show() every time the dialog is displayed again from within the main window, but also because even in this case the app doesn't really quit. The main window, in this case, is not displayed but the process is keeps running (Python application icon still visible in the Dock).
What am I doing wrong here? I've read other similar questions but I don't understand how to apply these solutions in my case.
(PySide2 5.15.1 on macOS 10.15.6)
class App:
def __init__(self, app, game, view):
self.app = app
self.game = game
self.view = view
# Display the dialog
# Same callback is bound to a QPushButton in MainWindow
self.cb_start_dialog()
def cb_start_dialog(self):
# Do some initialisation
dialog = DialogNewGame() # A subclass of QDialog
if dialog.exec_():
# Setup the interface
else:
if not self.view.isVisible():
# Condition evaluates correctly
# (False on app launch,
# True if main window is displayed)
self.view.destroy()
self.app.quit() # Doesn't work, main window still displayed
def main():
application = QApplication()
view = MainWindow() # A QWidget with the main window
model = Game() # Application logic
App(application, model, view)
view.show()
application.exec_()
If the code is analyzed well, it is observed that "quit" is invoked before the eventloop starts, so it makes no sense to terminate an eventloop that never started. The solution is to invoke X an instant after the eventloop starts. On the other hand, the quit method is static so it is not necessary to access "self.app"
from PySide2.QtCore import QTimer
from PySide2.QtWidgets import QApplication, QDialog, QMainWindow
class MainWindow(QMainWindow):
pass
class DialogNewGame(QDialog):
pass
class Game:
pass
class App:
def __init__(self, game, view):
self.game = game
self.view = view
QTimer.singleShot(0, self.cb_start_dialog)
def cb_start_dialog(self):
dialog = DialogNewGame()
if dialog.exec_():
pass
else:
QApplication.quit()
def main():
application = QApplication()
view = MainWindow()
model = Game()
app = App(model, view)
view.show()
application.exec_()
if __name__ == "__main__":
main()
I have a GUI App with a Main Dialog and I added a button to it.
Pushing the button adds another "dialog" where the user has to input some values.
Both Ui-files are written with the QTDesigner and "dialog" has a "QtableWidget" with the object name "tableCo" I am not sure why I cannot change the properties of this tableWidget:
from PyQt4 import QtGui, QtCore, Qt
from Main_Window import Ui_Dialog as Dlg
from dialog import Ui_MyDialog
class MainDialog(QtGui.QDialog, Dlg):
def __init__(self):
QtGui.QDialog.__init__(self)
self.setupUi(self)
self.connect(self.buttonOK,
QtCore.SIGNAL("clicked()"), self.onOK)
self.connect(self.buttonAbbrechen,
QtCore.SIGNAL("clicked()"), self.onClose)
self.connect(self.Button,
QtCore.SIGNAL("clicked()"), self.on_Button_clicked)
def on_Button_clicked(self, checked=None):
if checked==None: return
dialog = QtGui.QDialog()
dialog.ui = Ui_MyDialog()
dialog.ui.setupUi(dialog)
dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
dialog.exec_()
some_list=["A","B","C"]
#a list in another python class from another script that changes so
#the table properties have to be changed dynamically
#here I just take a simple list as an example
#the following two lines do not work (they work if tableCo is an
#object in the Main Dialog
self.tableCo.setColumnCount(len(some_list))
self.tableCo.setHorizontalHeaderLabels(some_list)
def onOK:
...
def onClose:
...
If I push the button i see my "tableCo" widget, but the properties of the header have not changed, and after closing this sub-dialog I get the following error-message
Traceback (most recent call last):
File "C:/gui.py", line 88, in on_Button_clicked
self.tableCo.setColumnCount(len(some_list))
AttributeError: 'MainDialog' object has no attribute 'tableCo'
What do i have to change in my code to configure a Widget in a sub-Dialog?
There are two problems with the code in your on_Button_clicked.
Firstly, you are attempting to call methods after the dialog has closed. When exec_ is called, the dialog enters a blocking loop until the user closes the dialog. When the dialog closes, the following lines will get executed, but the dialog will be immediately garbage-collected after that when the function returns.
Secondly, you are attempting to access methods of the dialog using self, rather than via the local name dialog, which is why you are getting the AttributeError.
You can fix these problems by creating a subclass for the second dialog in the same way that you have for your MainDialog class:
class SubDialog(QtGui.QDialog, Ui_MyDialog):
def __init__(self, some_list, parent=None):
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.tableCo.setColumnCount(len(some_list))
self.tableCo.setHorizontalHeaderLabels(some_list)
class MainDialog(QtGui.QDialog, Dlg):
...
def on_Button_clicked(self, checked=None):
if checked is None: return
dialog = SubQDialog(some_list)
dialog.exec_()
are you sure tableCo has that exact name and it's parented directly to the MainWindow? Seems like the properties are not being updated simply because there is no self.tableCo.
I'm trying to set up an app in Linux using PyQt4 designer and I'm struggling to connect signals and slots to it. Right now all I want it to do is connect a button clicked signal to a custom slot, saveEnergyScheme which simply prints 'energy list' to the terminal.
I've translated the .ui code for my app to a python class with pyuic4 -w sumcorr.ui > sumcorr_ui.py. This created a class in the sumcorr_ui.py module called SumCorr_ui:
class SumCorr_ui(QtGui.QMainWindow, Ui_SumCorr_ui):
def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
QtGui.QMainWindow.__init__(self, parent, f)
self.setupUi(self)
I then made my app as a custom widget and tried to add a simple signal-slot connection to a button to show it works:
from PyQt4 import QtGui, QtCore
from sumcorr_ui import SumCorr_ui
class SumCorr(SumCorr_ui):
def __init__(self):
SumCorr_ui.__init__(self)
self.save_energies_button.clicked.connect(self.saveEnergyScheme)
def saveEnergyScheme(self):
print 'energyList'
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mySumCorr = QtGui.QMainWindow()
ui = SumCorr()
ui.setupUi(mySumCorr)
mySumCorr.show()
sys.exit(app.exec_())
I expect to get the line 'energy list' when I click the button named save_energies_button, but nothing happens. Could this be because I haven't built the UI as a widget, but as a main window? Why doesn't it print out??
Try to add ui.show() and you'll see that your code is creating two different windows, one should have the signal connected and one doesn't. That's because you are showing only the mySumCorr window, but you call only setupUi on it, which does not connect the signal.
When you create the SumCorr instance, you are creating a window and setting it up, then, by no reason, you do ui.setupUi(mySumCorr), which setups the mySumCorr instance without connecting the signal, and you show this last window.
I believe your code should be like this:
class SumCorr(QtGui.QMainWindow, Ui_SumCorr_ui):
def __init__(self):
SumCorr_ui.__init__(self)
self.setupUi(self)
self.save_energies_button.clicked.connect(self.saveEnergyScheme)
def saveEnergyScheme(self):
print 'energyList'
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mySumCorr = SumCorr()
mySumCorr.show()
sys.exit(app.exec_())
Note that it doesn't make any sense to have a SumCorr_ui class, that's because Qt is a UI library so you are just introducing a worthless level of abstraction. The designer file already gives you an abstraction over the ui layout.