Well, I'm writing a small PyQt4 app, it's just a single Yes/No dialog which has to execute an external command (e.g. 'eject /dev/sr0') and quit.
The app runs, it executes the command after pressing the "Yes" button, but I cannot make the dialog itself exit when executing the command.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import os
import subprocess
from PyQt4 import QtGui
from PyQt4 import QtCore
from subprocess import call
cmd = 'eject /dev/sr0'
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
btn = QtGui.QPushButton('Yes', self)
btn.clicked.connect(lambda: os.system(cmd))
btn.resize(180, 40)
btn.move(20, 35)
qbtn = QtGui.QPushButton('No', self)
qbtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
qbtn.resize(180, 40)
qbtn.move(20, 80)
self.setWindowTitle('Test')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here is my code. When I click "Yes", it calls the 'eject /dev/sr0' command properly, but after that the dialog is still visible. I have to click "No" to close the app I would like it to close automatically when the command is executed. What should I add/modify?
btn.clicked.connect(self.close)
That would be my suggestion
Replace lambda: os.system(cmd) with a function/method that has multiple statements.
def buttonClicked(self):
os.system(cmd)
QtCore.QCoreApplication.instance().quit()
...
btn = QtGui.QPushButton('Yes', self)
btn.clicked.connect(self.buttonClicked)
...
Step1: in the Main Class needs to be build a "connection":
self.ui.closeButton.clicked.connect(self.closeIt)
Step2: Creating a function like to close:
def closeIt(self):
self.close()
I named to "closeIt" on purpose because if you name it "close" a conflict will occur.
This solution has the advantage if the created GUI is a plugin for another program (like in my case QGIS), only the active GUI will be closed and not the whole program.
Subclass QDialog() and then close it using your object.
class Dialog(QDialog):
"""
Subclassing QDialog class.
"""
def __init__(self):
QDialog.__init__(self)
def close_clicked(self):
self.close()
In your main function write following code
dialogbox = Dialog() # we subclasses QDialog into Dialog
b1= QPushButton("Close",dialogbox)
b1.clicked.connect(dialogbox.close_clicked)
dialogbox.exec_()
Related
I am currently coding an application using the Qt4 python bindings that requires the user to provide login credentials. At startup of my application I show a modal dialog in which the user can enter their data. Since the application cannot provide useful services when the user is not logged in, I want to close the app if the user clicks the cancel button.
So, if the dialog returns a negative result, I just call the close() method of my QMainWindow. Normally this leads to the exit of the application since there are no interactable windows any more.
However, if the modal Dialog has been shown before, the application simply continues to run and I have to kill it manually.
Below is the code for an minimal example
main.py:
import sys
from PyQt4.QtGui import QApplication
from MyMainWindow import MyMainWindow
app = QApplication(sys.argv)
window = MyMainWindow()
window.show()
app.exec_()
print "Terminated"
MyMainWindow.py:
from PyQt4.QtGui import QMainWindow
from PyQt4 import QtGui
from MyDialog import MyDialog
class MyMainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setWindowTitle("Close App Test")
self.centralwidget = QtGui.QWidget(self)
self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
self.closeButton = QtGui.QPushButton(self.centralwidget)
self.closeButton.setText("Close")
self.closeButton.clicked.connect(self.exit)
def show(self):
QMainWindow.show(self)
myDialog = MyDialog(self)
res = myDialog.exec_()
if res == 0:
self.exit()
else:
print "Continue"
def exit(self):
self.close()
MyDialog.py:
from PyQt4.QtGui import QDialog
from PyQt4 import QtGui
class MyDialog(QDialog):
def __init__(self, parent = None):
QDialog.__init__(self, parent)
self.setWindowTitle("Modal Dialog")
self.resize(200, 50)
self.closeButton = QtGui.QPushButton(self)
self.closeButton.setText("Close")
self.closeButton.move(10, 10)
self.otherButton = QtGui.QPushButton(self)
self.otherButton.setText("Do Nothing")
self.otherButton.move(100, 10)
self.closeButton.clicked.connect(self.reject)
self.otherButton.clicked.connect(self.accept)
If the Close button on the modal dialog is clicked, the application continues to run even if all windows are closed. If the "Do Nothing" button is clicked, and the application is closed with the close button on the main window, everything works as expected and the application terminates.
I cannot really see the difference between both cases, since each time I just call close on the main window. I can just assume that I have errors in handling the modal dialog. I even tried tinkering around with myDialog.close(), myDialog.destroy(), as well as the quitOnCloseand deleteOnClose window attributes without any positive effects.
Any help is appreciated.
self.exit() is called before app.exec_() if the Close button on the modal dialog is clicked, so the application continues to run. self.exit() should be called when the main event loop is running.
The following is a possible solution for the case:
from PyQt4.QtGui import QMainWindow
from PyQt4 import QtGui
from pyQt4 import QtCore
from MyDialog import MyDialog
class MyMainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setWindowTitle("Close App Test")
self.centralwidget = QtGui.QWidget(self)
self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
self.closeButton = QtGui.QPushButton(self.centralwidget)
self.closeButton.setText("Close")
self.closeButton.clicked.connect(self.exit)
def login(self):
myDialog = MyDialog(self)
res = myDialog.exec_()
if res == 0:
self.exit()
else:
print "Continue"
def show(self):
QMainWindow.show(self)
QtCore.QTimer.singleShot(0, self.login)
def exit(self):
self.close()
I am having problems showing a QWidget window for the user to input some data.
My script has not GUI, but I just want to show this small QWidget window.
I created the window with QtDesigner, and now I am trying to show the QWidget window like this:
from PyQt4 import QtGui
from input_data_window import Ui_Form
class childInputData(QtGui.QWidget ):
def __init__(self, parent=None):
super(childInputData, self).__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
self.setFocus(True)
self.show()
And then, from my main class, I am doing like that:
class myMainClass():
childWindow = childInputData()
That gave me the error:
QWidget: Must construct a QApplication before a QPaintDevice
So now I am doing, from my main class:
class myMainClass():
app = QtGui.QApplication(sys.argv)
childWindow = childInputData()
Now there is no error, but the window is showed twice and the script does not wait until the data is entered, it just shows the window and continues without waiting.
What is wrong here?
It's perfectly normal that the window is shown and the script goes on: you never told the script to wait for the user to answer. You just told it to show a window.
What you would like is the script to stop until the user is done and the window is closed.
Here's one way to do it:
from PyQt4 import QtGui,QtCore
import sys
class childInputData(QtGui.QWidget):
def __init__(self, parent=None):
super(childInputData, self).__init__()
self.show()
class mainClass():
def __init__(self):
app=QtGui.QApplication(sys.argv)
win=childInputData()
print("this will print even if the window is not closed")
app.exec_()
print("this will be print after the window is closed")
if __name__ == "__main__":
m=mainClass()
The exec() method "Enters the main event loop and waits until exit() is called" (doc):
the script will be blocked on the line app.exec_() until the window is closed.
NB: using sys.exit(app.exec_()) would cause the script to end when the window is closed.
An other way is to use QDialog instead of QWidget. You then replace self.show() by self.exec(), which will block the script
From the doc:
int QDialog::exec()
Shows the dialog as a modal dialog, blocking until the user closes it
Finally, this answer of a related question advocates not to use exec, but to set the window modality with win.setWindowModality(QtCore.Qt.ApplicationModal). However this doesn't work here: it blocks inputs in other windows, but do not block the script.
you dont need the myMainClass...do something like this:
import sys
from PyQt4 import QtGui
from input_data_window import Ui_Form
class childInputData(QtGui.QWidget):
def __init__(self, parent=None):
super(childInputData, self).__init__(parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
self.setFocus(True)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
win = childInputData()
win.show()
sys.exit(app.exec_())
I use KDE on Manjaro linux. I have this script in python to turn off touchpad:
#!/usr/bin/python
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication
from PyQt5.QtCore import QCoreApplication
from subprocess import call
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
qbtn = QPushButton('On', self)
qbtn.clicked.connect(self.handleButtonOn)
qbtn.resize(qbtn.sizeHint())
qbtn.move(25, 50)
qbtn = QPushButton('Off', self)
qbtn.clicked.connect(self.handleButtonOff)
qbtn.resize(qbtn.sizeHint())
qbtn.move(125, 50)
self.setGeometry(300, 300, 250, 100)
self.setWindowTitle('Touchpad On/Off')
self.show()
def handleButtonOn(self, event):
print ('On')
call(["synclient", "touchpadoff=0"])
call(["notify-send", "Your touchpad is set to ON"])
self.destroy()
def handleButtonOff(self, event):
print ('Off')
call(["synclient", "touchpadoff=1"])
call(["notify-send", "Your touchpad is set to OFF"])
self.destroy()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
It's works almost perfect (turn off my touchpad) but when I start script it's turn off my power button so I can't turn off computer by this button.
There is a problem with Yakuake too, can't write in this terminal. Finally i've start some other terminal for example "konsole" and turn off computer by shutdown command.
I'm new in python. How to make this work OK. I need turn off my touchpad, I use external mouse.
I can't reproduce your issue with power button, but I found out that self.destroy() is causing your script to freeze in some corrupted-not-responding-to-SIGINT state. Replacing self.destroy() with self.close() makes it work on my machine.
Please try replacing self.destroy() with self.close().
I am wondering how one would create a GUI application, and interact with it from the console that started it.
As an example, I would like to create a GUI in PyQt and work with it from the console. This could be for testing settings without restarting the app, but in larger projects also for calling functions etc.
Here is a simple example using PyQt:
import sys
from PyQt4 import QtGui
def main():
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
when this is run with python -i example.py the console is blocked as long as the main-loop is executed.
How can I call w.resize(100,100) while the GUI is running?
ops, posted wrong answer before
there is a post in Stack about that
Execute Python code from within PyQt event loop
The following example uses the code module to run a console in the command prompt (be sure to run the script from the command line). Subclassing QThread provides a route by which the console can be run in a separate thread from that of the main GUI and enables some interaction with it. The stub example below should be easy enough to incorporate into a larger packaged PyQt program.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import threading #used only to id the active thread
import code
import sys
class Worker(QThread): #Subclass QThread and re-define run()
signal = pyqtSignal()
def __init__(self):
super().__init__()
def raise_sys_exit(self): #more gracefully exit the console
print('(Deactivated Console)')
raise SystemExit
def setup_console(self,global_dict):
console_exit = {'exit': self.raise_sys_exit}
self.console = code.InteractiveConsole(locals=dict(global_dict,**console_exit))
def run(self):
try:
print('worker', threading.get_ident())
self.console.interact()
except SystemExit:
self.signal.emit()
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args,**kwargs)
self.data = [1,2,3,4] #some data we might want to look at
layout = QVBoxLayout()
self.b = QPushButton("Interact")
self.b.clicked.connect(self.b_clicked)
layout.addWidget(self.b)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.worker = Worker()
self.worker.signal.connect(self.finished)
def finished(self):
self.b.setEnabled(True)
def b_clicked(self):
print('main',threading.get_ident())
self.worker.setup_console(globals()) #pass the global variables to the worker
self.worker.start()
self.b.setEnabled(False) #disable the GUI button until console is exited
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
The easiest way is to use IPython:
ipython --gui=qt4
See ipython --help or the online documentation for more options (e.g. gtk, tk, etc).
I defined a print function using QPrinter and QDialog. However, when I launch the printer dialog and then press cancel, the entire mainwindow goes into not responding mode. I have tried to do QtGui.QPrintDialog.close() but does not work.
Code:
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class QButton(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.button = QtGui.QPushButton('Button', self)
self.name='me'
self.button.clicked.connect(self.calluser)
def calluser(self):
Appli=QtGui.QApplication(sys.argv)
printer= QtGui.QPrinter()
doc=QtGui.QTextDocument("Set local variables in this printing slot." )
dialog = QtGui.QPrintDialog(printer)
dialog.setModal(True)
dialog.setWindowTitle("Print Document" )
if dialog.exec_() == True:
doc.print_(printer)
# dialog.addEnabledOption(QAbstractPrintDialog.PrintSelection)
def demo_QButton():
app = QtGui.QApplication(sys.argv)
tb = QButton()
tb.show()
app.exec_()
if __name__=='__main__':
demo_QButton()
You created a new application in the calluser method. Delete or comment the line:
Appli=QtGui.QApplication(sys.argv)
and try again. I think this time your main window will remain responsive.