Pyqt Terminal hangs after excuting close window command - python

I have read lots of threads online, but still I could not find the solution. My question should be very simple: how to close a Pyqt window WITHOUT clicking a button or using a timer.
The code I tried is pasted below
from PyQt4 import QtGui, QtCore
import numpy as np
import progressMeter_simple
import sys
import time
import pdb
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
QtGui.QApplication.processEvents()
def termination(self):
time.sleep(5)
self.close()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Dialog = ProgressMeter()
Dialog.show()
Dialog.termination()
sys.exit(app.exec_())
My Pyqt GUI is designed using Qt designer, and it is nothing but a progress bar that keeps moving from left to right (busy indication).
However, when I run the code above, the terminal still hangs after the Pyqt window is closed. Ctrl+C also couldn't kill the process.
In short, how can I properly close/terminate a Pyqt window without clicking a button or using a timer?

It's not working because you're calling GUI methods on the dialog (close()) outside of the event loop. The event loop doesn't start until you call app.exec_().
If you really want to close the dialog immediately after it opens without using a QTimer, you can override the showEvent() method and call termination() from there, which gets called when the dialog is first displayed.
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
def showEvent(self, event):
super(ProgressMeter, self).showEvent(event)
self.termination()

Related

In PyQt5 How to call function or method after load aplication?

from PyQt5 import QtWidgets
import time
def show_message(self):
time.sleep(5)
self.label.setText("It's me")
class Main(QtWidgets.QMainWindow):
def __init__(self):
super(Main, self).__init__()
self.label = QtWidgets.QLabel('Hello', self)
#How to call this func after load application
show_message(self)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication([])
application = Main()
application.show()
sys.exit(app.exec_())
How to call show_message(self) after load application
Does pyqt5 have a function or method like self.afterLoad(application, show_message)
It seems to me something like this available on tkinter
I am guessing you want to start the application and then after 5 seconds the text in the label will get changed to the new text.
What you have right now is almost working, however the problem is that when you call time.sleep(5) then the whole execution of the program will get paused and nothing gets shown for that amount of time. If you still wanna be able to interact with the program during those 5 seconds then you will need to use a timer that is running in the background instead.
PyQt already has something like that in PyQt5.QtCore.QTimer. If you use that then your code could look something like this
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer # Importing QTimer
import time
def show_message(self):
self.label.setText("It's me")
# Stopping the timer. Otherwise it will run over and over again.
self.timer.stop()
class Main(QtWidgets.QMainWindow):
def __init__(self):
super(Main, self).__init__()
self.label = QtWidgets.QLabel('Hello', self)
# Create a new QTimer
self.timer = QTimer(self)
# Tell the timer that it should call show_message(self) when the time runs out
self.timer.timeout.connect(lambda: show_message(self))
# Start the timer which then starts running in the background for 5 seconds
self.timer.start(5000)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication([])
application = Main()
application.show()
sys.exit(app.exec_())
One last thing that I would recommend doing is putting your functions that belong to a class inside of it. So here it would probably be better to put show_message inside of the main class because right now you can call the function from every part of the code and that can lead to errors.

How can I self hide and show QDialog() in PyQT5?

I have a GUI that was generated using Qt Designer, I used pyuic5 to generate a .py file. In a separate py (program.py) file I import my UI a do all my work there.
program.py
import sys, os, time
from subprocess import call
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyCred_GUI import Ui_Dialog
class MyGUI(Ui_Dialog):
def __init__(self, dialog):
Ui_Dialog.__init__(self)
self.setupUi(dialog)
self.pushButton_2.clicked.connect(self.cancelbutton)
def cancelbutton(self):
exit()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = QtWidgets.QDialog()
dialog.setWindowFlags(QtCore.Qt.WindowSystemMenuHint)
prog = MyGUI(dialog)
dialog.show()
sys.exit(app.exec_())
I pulled a lot out just to focus on the issue here. When I click my Cancel button, I want the window to hide, set a timer, and then reappear after so many seconds. I have tried every combination of self.close() self.hide() self.destroy() and none of them hide my window. I get an error that says
"AttributeError: 'MyGUI' object has no attribute 'hide'"
Which makes sense because MyGUI doesn't have a hide() function. I am at a complete loss on how to hide this window.
EDIT (Solved)
For future people, as suggested by Hi Im Frogatto dialog.hide() worked.
In your code snippet, dialog is of type QDialog and thereby having hide method. However instances of MyGUI class seem to not have such a method. So, if you write dialog.hide() in that __init__() function, you can hide it.

PyQt: ListWidget.insertItem not shown

I have a fairly simply PyQt question. (Python 3.4, PyQt 4.11.3, Qt 4.8.5) I built a very simple dialog using Qt Designer (Ui_Dialog). This object has a QPushButton, a QLineEdit, and a QListWidget. I wrote another object that inherits from Ui_Dialog, and sets up a returnPressed signal from QLineEdit that should add some text to the QListWidget. Unfortunately, this does not work.
Here's my code:
import sys
from PyQt4 import QtGui
from dialog import Ui_Dialog
class ImDialog(QtGui.QDialog, Ui_Dialog):
def __init__(self):
super(ImDialog, self).__init__()
self.setupUi(self)
self.lineEdit.returnPressed.connect(self.additem)
self.pushButton.clicked.connect(self.listWidget.clear)
def additem(self):
text = self.lineEdit.text()
print(text)
self.listWidget.insertItem(0, text)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
ui = ImDialog()
ui.show()
sys.exit(app.exec_())
The text in the line editor prints fine to the terminal, but it is not added to the listWidget.
Interestingly, if I comment out the sys.exit line and run this in an IPython terminal, I can add as much text as I like to the listWidget without a problem.
[In 1]: %run that_program.py
[In 2]: ui.listWidget.insertItem(0, "Test") # This works fine
If anyone has any suggestions to get this to work (outside IPython), I would appreciate the help. Thanks
There is only one button in your dialog, and so it will become the auto-default. This means that whenever you press enter in the dialog, the button will receive a press event, even if it doesn't currently have the keyboard focus.
So the item does get added to the list-widget - it's just that it then immediately gets cleared by the auto-default button.
To fix this, reset the auto-default like so:
self.pushButton.setAutoDefault(False)
(NB: you can also change this property in Qt Designer).

QDialog.show() method has a delayed reaction

I have a problem i can't quite figure out for some time. I have a main window application and a QDialog that should pop out after clicking one of the buttons, but the show() method on QDialog seems to be waiting for the funcion connected to the "clicked()" signal to end!
I want the dialog to show right after calling the QDialog.show() method, not after all the other code instructions in that function...
Of course in my code I am going to replace the sleep(5) part with much more complicated code, but this pictures the problem and the code I put there is irrelevant to the issue, i think (database connections and updates)
being more specific:
# -*- coding: utf-8 -*-
import sys
import PyQt4
from PyQt4 import QtCore, QtGui
from twython import Twython, TwythonError
from project import Ui_MainWindow
from time import sleep
import psycopg2, globalvals, updater
import updating, noconnection
class UpWindow(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
self.ui = updating.Ui_updating()
self.ui.setupUi(self)
class NoConnection(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
self.ui = noconnection.Ui_noConnection()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.noConnectionClose, QtCore.SIGNAL("clicked()"), self.close)
class MyCounter(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.noConn = NoConnection(self)
self.upWin = UpWindow(self)
QtCore.QObject.connect(self.ui.refreshButton,QtCore.SIGNAL("clicked()"), self.refresh)
QtCore.QObject.connect(self.ui.manageButton,QtCore.SIGNAL("clicked()"), self.manage)
def refresh(self):
self.upWin.show()
self.upWin.show
self.upWin.setVisible(True)
self.setEnabled(False)
self.upWin.setEnabled(True)
#Thats the issue - the sleep instruction is being held
#BEFORE the showing of upWin QDialog
sleep(5)
def manage(self):
print 'ok'
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyCounter()
myapp.upcontent()
myapp.show()
sys.exit(app.exec_())
Think of the any Qt program as a cooperative-multitasking system. Graphics and events in general are handled by the main loop. You don't want to stay long in any slot, because the library won't process signals (say button clicks, repaints, but also other stuff) in the mean time.
If you want to do some heavy processing, or anything that needs to wait for resources while the rest of the program is chugging along, use a QThread.
Another option is to force the event processing with qApp.processEvents() (you can find qApp in QtGui), just before your sleep(5) (or whatever code you're going to put in place of it).
Edit: Now, keep in mind that forcing the event processing will just show the QDialog you're trying to popup. You can't do anything with it (remember, no event processing) without calling again qApp.processEvents() or returning from the slot.
If MyCounter represents a widget that does a long computation and updates a dialog during that time, then sleep(5) is not representative of it, because during those 5 seconds the GUI can't handle events. For a "long running" function you would move the blocking part to a QThread and either poll the thread or connect to a signal it emits as it progresses, either way you would not hold up the GUI event loop during that time (for example, the polling, which takes very little time, would occur in an idle callback). The simplest way to create your test would be to use a timed callback into your MyCounter:
def refresh(self):
... show stuff, then:
self.timer = QTimer()
self.timer.timeout.connect(self.updateDialog)
timer.start(100) # 10 times per sec
def updateDialog(self):
#get thread status
if self.thread.status != self.oldStatus:
self.upWin.updateStatus( self.thread.status )

Why is my PyQt4 program exiting the main loop for no apparent reason?

I'm (trying to) make a small program that resides in the system tray and checks a list of Twitch channels to see if they're online every once in a while.
I'm currently doing the GUI (in PyQt4), but it's exiting for no reason.
Here's my code so far:
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class TwitchWatchTray(QtGui.QSystemTrayIcon):
def __init__(self, icon, parent=None):
super(TwitchWatchTray, self).__init__(icon, parent)
self.menu = QtGui.QMenu(parent)
settings_action = self.menu.addAction("Settings")
settings_action.triggered.connect(self.open_settings)
self.menu.addSeparator()
exit_action = self.menu.addAction("Exit")
exit_action.triggered.connect(QtCore.QCoreApplication.instance().quit)
self.setContextMenu(self.menu)
self.show()
def open_settings(self):
settings = SettingsDialog()
settings.show()
class SettingsDialog(QtGui.QWidget):
def __init__(self):
super(SettingsDialog, self).__init__()
self.resize(300, 300)
self.setWindowTitle('TwitchWatch Settings')
vbox = QtGui.QHBoxLayout()
self.channels_list = QtGui.QListView(self)
vbox.addWidget(self.channels_list)
self.add_box = QtGui.QLineEdit(self)
vbox.addWidget(self.add_box)
self.setLayout(vbox)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
widget = QtGui.QWidget()
tw = TwitchWatchTray(QtGui.QIcon("icon.png"), widget)
app.exec_()
print("Done!")
if __name__ == '__main__':
main()
When I right click the tray icon and click "Settings", it flashes a white box (my dialog), then immediately exits and prints "Done!".
Why is this, and how do I fix it?
There are two reasons why your code exits immediately after you open the settings dialog.
The first problem is with your open_settings method:
def open_settings(self):
settings = SettingsDialog()
settings.show()
This creates a dialog and makes it visible. show() returns immediately after showing the window; it doesn't wait for the window to be closed. The settings variable goes out of scope at the end of the method, and this causes the reference count of your SettingsDialog to drop to zero and hence become eligible for garbage collection. When Python deletes the SettingsDialog object, PyQt will delete the underlying C++ object, and this is what causes the dialog to close again.
I would recommend having your settings dialog subclass QDialog rather than QWidget (it is a dialog, after all). Instead of calling settings.show() you can then call settings.exec_(). settings.exec_() does wait for the dialog to be closed before it returns. It also returns QDialog.Accepted or QDialog.Rejected depending on whether the user clicked OK or Cancel. I'd also recommend getting rid of the call to self.show() in your SettingsDialog constructor.
The second problem is that your QApplication is set to quit when the last window is closed. This is the default behaviour, which is what a lot of applications need, but not yours. Even if your dialog stayed open and you could close it, you wouldn't want your application to exit immediately after you close the settings dialog. Call app.setQuitOnLastWindowClosed(False) to fix this.

Categories

Resources