What is the PyQt Equivalent to WinForms Activated Event? - python

I'm writing a small PyQt application that needs to run a few checks before the application starts and if any of the checks fail, the application needs to inform the user it can't run and then quit.
In WinForms. I can achieve this simply:
var form = new Form();
form.Activated += (s, e) =>
{
var condition = true;
if (condition)
{
MessageBox.Show("oh dear, something's wrong.");
Application.Exit();
}
};
PyQt doesn't seem to have a signal that can be connected to when the main application window has loaded and my current attempts don't even fire off the dialog when the condition fails.
This is what I have at the moment (MRE(. Program flow is as follows:
import sys
import winreg
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
def loadData(self):
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Path\To\Nonexistant\Key')
x = winreg.QueryValueEx(key, 'some_key')[0]
print(x)
except FileNotFoundError:
error = QMessageBox()
error.setIcon(QMessageBox.Critical)
error.setText('Cannot locate installation directory.')
error.setWindowTitle('Cannot find registry key')
error.setStandardButtons(QMessageBox.Ok)
error.show()
QApplication.quit()
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
main_window.loadData()
sys.exit(app.exec_())
main()
My end goal is to be able to let loadData() run when the application has loaded fully and is displayed to the user.
Update: Program now works as expected. I had to:
Change error.show() to error.exec_()
Change QApplication.quit() to sys.exit()
Call main_window.loadData() after main_window.show()

The show method is not blocking, so as soon as it is executed, the QMessageBox will be displayed at QApplication.quit(), instead you should use exec_() that will wait until the user closes the QMessageBox through the buttons.
# ...
error.setStandardButtons(QMessageBox.Ok)
# error.show()
error.exec_()
QApplication.quit()
Update:
On the other hand, QApplication.quit() cannot be used when the eventloop is not started yet, so the solution in this case is to remove the application a moment later using QTimer.singleShot():
class MainWindow(QWidget):
# UI stuff omitted...
def loadData(self):
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Path\To\Key')
path = winreg.QueryValueEx(key, 'install_path')[0]
# I'll do stuff with path here...
except FileNotFoundError:
error = QMessageBox()
error.setIcon(QMessageBox.Critical)
error.setText('Cannot locate directory.')
error.setWindowTitle('Cannot find registry key')
error.setStandardButtons(QMessageBox.Ok)
error.exec_()
QTimer.singleShot(0, QApplication.quit)
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
main_window.loadData()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Another equivalent code is as follows
class MainWindow(QWidget):
# UI stuff omitted...
def loadData(self):
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Path\To\Key')
path = winreg.QueryValueEx(key, 'install_path')[0]
# I'll do stuff with path here...
except FileNotFoundError:
error = QMessageBox()
error.setIcon(QMessageBox.Critical)
error.setText('Cannot locate directory.')
error.setWindowTitle('Cannot find registry key')
error.setStandardButtons(QMessageBox.Ok)
error.exec_()
QApplication.quit()
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
QTimer.singleShot(0, main_window.loadData)
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Related

How to jump from 1 qt window to other in pyqt5.?

I am working on pyqt5 project where I have login window and registration window. I have a button on login window which when clicked should open the registration window. I have designed the ui in qt desginer and have converted the .ui files to .py files using pyuic.
So I have two files login_ui.py and register_window_ui.py. To use them, I have also created two separate files i.e. login.py and register.py which contains all the functional code of the ui files.
Below is the files code:
login.py
import sys
import os
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from ui.login_ui import Ui_login_main_window
from ui.register_window_ui import Ui_register_window
curr_path = os.path.dirname(os.path.abspath(__file__))
class Login(QMainWindow, Ui_login_main_window):
def __init__(self):
QMainWindow.__init__(self)
self.login_ui = Ui_login_main_window()
self.login_ui.setupUi(self)
self.login_ui.register_settings_btn.clicked.connect(self.show_registration_window)
def show_registration_window(self):
self.window = QtWidgets.QMainWindow()
self.ui = Ui_register_window()
self.ui.setupUi(self.window)
self.window.show()
app = QApplication(sys.argv)
main_window = Login()
main_window.show()
sys.exit(app.exec_())
register.py
import sys
import os
from PyQt5.QtWidgets import QApplication, QMainWindow
from ui.register_window_ui import Ui_register_window
curr_path = os.path.dirname(os.path.abspath(__file__))
class Register(QMainWindow, Ui_register_window):
def __init__(self):
QMainWindow.__init__(self)
self.register_win_ui = Ui_register_window()
self.register_win_ui.setupUi(self)
self.register_win_ui.register_trial_btn.clicked.connect(self.print_data)
def print_data(self):
print("Clicked")
app = QApplication(sys.argv)
main_window = Register()
main_window.show()
sys.exit(app.exec_())
As you can see that in login.py I have created a button click event for register_settings_btn which when clicked will show Ui_register_window() which is register_window. So when I click it, it shows me the window but the window is not functional i.e. there is no button click event happening. For ex in register.py I have created a button click event which is not working.
Can anyone please explain me why is it not working. Please help. Thanks
I have a button on login window which when clicked should open the registration window
instead of creating 2 QApplications create only 1 and show or exec 1st the login, validate the input and if everything is ok, then show the Register
i.e. instead do something like:
app = QApplication(sys.argv)
login_window = Login()
result = login_window.exec()
if result == ???:
register_window = Register()
register_window.show()
else:
showMsg('Login failed!')
sys.exit(app.exec_())
update! create another file i.e. myProject.py and add this!
#!/usr/local/bin/python
# coding: utf-8
import os, sys
add your imports here!
if __name__ == "__main__":
app = QApplication(sys.argv)
#create login view nd register view
login_window = Login()
register_window = Register()
#execute the view login and read the doc, this will block the code until the user quit the view!
result = login_window.exec()
#define a condition to proof the validity of login process
if result == ???:
#show the register
register_window.show()
else:
#or show the error msg
showMsg('Login failed!')
sys.exit(app.exec_())

How to reboot PyQt5 application

I'm trying to restart my application after an update from the client side. I was able to achieve till the auto update part. I tried to surf on How to restart PyQt application?. There are few similar questions earlier, but none of them have good explanation or example with a button click event. Could you guys help me understand on how to reboot a PyQt application. Basically I want to restart the application from if __name__ == '__main__': everytime there is an update.
Note: AppLogin is my private module I created to handle application login. So basically that would be the landing QDialog once application is opened.
from PyQt5.QtWidgets import *
import sys
import AppLogin
class App:
def __init__(self):
btn = QPushButton(main_window)
btn.setText('close')
btn.pressed.connect(self.restart)
main_window.show()
def restart(self):
# Here goes the code for restart
pass
if __name__ == '__main__':
appctxt = QApplication(sys.argv)
log_in = AppLogin.Login()
if log_in.exec_() == QDialog.Accepted:
main_window = QMainWindow()
ui = App()
exit_code = appctxt.exec_()
sys.exit(exit_code)
The logic is to end the eventloop and launch the application an instant before it closes:
import sys
from PyQt5 import QtCore, QtWidgets
def restart():
QtCore.QCoreApplication.quit()
status = QtCore.QProcess.startDetached(sys.executable, sys.argv)
print(status)
def main():
app = QtWidgets.QApplication(sys.argv)
print("[PID]:", QtCore.QCoreApplication.applicationPid())
window = QtWidgets.QMainWindow()
window.show()
button = QtWidgets.QPushButton("Restart")
button.clicked.connect(restart)
window.setCentralWidget(button)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
A solution is to close (or forget) the QMainWindow and recreate it.
If you just "show()" a single widget, the same idea works fine.
import sys
import uuid
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
singleton: 'MainWindow' = None
def __init__(self):
super().__init__()
btn = QPushButton(f'RESTART\n{uuid.uuid4()}')
btn.clicked.connect(MainWindow.restart)
self.setCentralWidget(btn)
self.show()
#staticmethod
def restart():
MainWindow.singleton = MainWindow()
def main():
app = QApplication([])
MainWindow.restart()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

PyQt error “QProcess: Destroyed while process is still running”

When I try to run the following PyQt code for running processes and tmux, I encounter the error QProcess: Destroyed while process is still running. How can I fix this?
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class embeddedTerminal(QWidget):
def __init__(self):
QWidget.__init__(self)
self._processes = []
self.resize(800, 600)
self.terminal = QWidget(self)
layout = QVBoxLayout(self)
layout.addWidget(self.terminal)
self._start_process(
'xterm',
['-into', str(self.terminal.winId()),
'-e', 'tmux', 'new', '-s', 'my_session']
)
button = QPushButton('list files')
layout.addWidget(button)
button.clicked.connect(self._list_files)
def _start_process(self, prog, args):
child = QProcess()
self._processes.append(child)
child.start(prog, args)
def _list_files(self):
self._start_process(
'tmux', ['send-keys', '-t', 'my_session:0', 'ls', 'Enter']
)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = embeddedTerminal()
main.show()
You usually get the error QProcess: Destroyed while process is still running when the application closes and the process hadn't finished.
In your current code, your application ends at soon as it starts, because you didn't call app.exec_(). You should do something like:
if __name__ == "__main__":
app = QApplication(sys.argv)
main = embeddedTerminal()
main.show()
sys.exit(app.exec_())
Now, it works fine, but when you close the application you will still get the error message. You need to overwrite the close event to end the process properly. This works, given you replace child by self.child:
def closeEvent(self,event):
self.child.terminate()
self.child.waitForFinished()
event.accept()

Does closing a GUI in PyQt4 destroy its children Qobjects by default

I'm trying to make a GUI that used QTimer to create a state machine, but when ever I close the GUI window the timer continues. I think I'm properly making the object that creates my Qtimer a child of the GUI but with the behavior I'm seeing it doesn't seem like it. Here is some code
class Ui_Form(QtGui.QWidget):
def __init__(self):
super(Ui_Form, self).__init__()
self.backEnd = BackEnd(self)
self.backEnd.start()
class BackEnd(QtCore.QObject):
def __init__(self,parent=None):
super(BackEnd,self).__init__(parent)
self.setParent(parent)
self.timer = QtCore.QTimer()
self.timer.setSingleShot(True)
QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.timerHandler)
def timerHandler(self):
print "Im here"
self.timer.start(1000)
def start(self):
self.timer.start(1000)
def stop(self):
self.timer.stop()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
Form = QtGui.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
Timer does not continue when I close GUI window, it works fine, as desired... anyway, try to override close event for your Ui_Form like this:
def closeEvent(self):
self.backEnd.stop()
I hope that helps.
Also, I've changed your main like this:
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
ui = Ui_Form()
ui.show()
sys.exit(app.exec_())
in your case it might be that Form = QtGui.QWidget() stays alive after you close GUI window. So try that modification first.

PyQt window closes immediately after opening

I am getting an issue when trying to open a PyQt window.
The code below is an example of my original code. When I imported the module in import Test and ran test.Start(), I got the following error:
QCoreApplication::exec: The event loop is already running
After some research, I found out it was because I had already already made a QApplication.
test.py....
import sys
def Start():
app = QApplication(sys.argv)
m = myWindow()
m.show()
app.exec_()
class myWindow():....
if __name__ == "__main__":
Start()
So then I read that I could rewrite my code like this and it would fix the error:
test.py....
def Start():
m = myWindow()
m.show()
class myWindow():....
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
Start()
app.exec_()
Now I no longer get the QCoreApplication::exec: The event loop is already running error, but my window closes almost immediately after opening.
You need to keep a reference to the opened window, otherwise it goes out of scope and is garbage collected, which will destroy the underlying C++ object also. Try:
def Start():
m = myWindow()
m.show()
return m
class myWindow():....
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
window = Start()
app.exec_()
You can also do:
def Start():
global m
m = myWindow()
m.show()
class myWindow():....
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
window = Start()
app.exec_()
Use the following code. Your problem is in your imports and using "show" as a name for function as far as I assume. You haven't provided what you have written in your class, so it's difficult to guess. But following code works like a charm. ;-)
Best wishes, good luck!
import sys
from PyQt5 import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
app = QApplication(sys.argv)
def Start():
m = myWindow()
m.showWid()
sys.exit(app.exec())
class myWindow:
def __init__(self):
self.window = QWidget()
self.window.setWindowTitle("Program Title")
self.window.setFixedWidth(600)
self.window.setStyleSheet("background: #18BEBE;")
def showWid(self):
self.window.show()
if __name__ == "__main__":
Start()

Categories