I have a sample Python program that sub classes a PyQt5 window. I am teaching myself and still a little new PyQt5 and python Classes.
The program does what I want to do I ran into an error that I don't understand how to fix. To start this program functions as is, I am currently learning how to run Threads. I imported and sub classed the PyQt5 window. In the __init__ section of the subclass I can set the window title and it works fine.
If I move the statement to a function "def initUI(self):" I am unable to set the window title, mind you I have tried various versions of the statement and nothing works. It's the first line of the def and I have it commented out.
My questions are:
Is this property setable in a def.
If it io what is the proper format of the statement.
from PyQt5.QtCore import pyqtSlot, pyqtSignal
from PyQt5 import QtCore, QtGui, QtWidgets
from Threads import Ui_MainWindow
import sys, time
from time import sleep
class MainWindow_EXEC():
def __init__(self): # This section has to be laid out this way
app = QtWidgets.QApplication(sys.argv)
win = QtWidgets.QMainWindow()
self.ui = Ui_MainWindow()
self.ui.setupUi(win)
self.initUI() # Inits that need to happen before start up
win.setWindowTitle("This is the Title!") # Works fine
win.resize(800,600)
win.show()
sys.exit(app.exec_())
def initUI(self): # Button assignments
#self.ui.setWindowTitle("This is the Title!") # AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'
self.ui.btn_Start.clicked.connect(self.start_progressbar)
self.ui.btn_Stop.clicked.connect(self.stop_progressbar)
self.ui.btn_Reset.clicked.connect(self.reset_progressbar)
self.progress_value = 0
self.stop_progress = False
def progressbar_counter(self, start_value=0):
# have to use member: self.run_thread NOT local var: run_thread
self.run_thread = RunThread(parent=None, counter_start=start_value)
self.run_thread.start()
self.run_thread.counter_value.connect(self.get_thread_value)
def get_thread_value(self, counter): #This updates the progress bar
print(counter)
if not self.stop_progress:
self.ui.progressBar.setValue(counter)
def start_progressbar(self): # This is the button that starts the progress bar
self.stop_progress = False # This is a switch
self.progress_value = self.ui.progressBar.value() # Updating the progress bar
self.progressbar_counter(self.progress_value)
def stop_progressbar(self): # This is a button to stop the progress bar
self.stop_progress = True
self.run_thread.stop()
def reset_progressbar(self): # This is a button to reset the progress bar
self.stop_progressbar()
self.progress_value = 0
self.stop_progress = False
self.ui.progressBar.reset()
class RunThread(QtCore.QThread):
counter_value = QtCore.pyqtSignal(int) # define new Signal
def __init__(self, parent=None, counter_start=0):
super(RunThread, self).__init__(parent)
self.counter = counter_start
self.isRunning = True
def run(self):
while self.counter < 100 and self.isRunning == True:
sleep(0.1)
self.counter += 1
print(self.counter)
self.counter_value.emit(self.counter) # emit new Signal with value
def stop(self):
self.isRunning = False
print('stopping thread...')
self.terminate()
if __name__ == "__main__":
MainWindow_EXEC()
The objective of the following classes must be distinguished:
QMainWindow is a widget that has the setWindowTitle method.
Ui_MainWindow is not a widget but a class that is used to fill a widget so it does not have the setWindowTitle method.
The solution is to make win a class member and then use that object to modify the title:
class MainWindow_EXEC():
def __init__(self): # This section has to be laid out this way
app = QtWidgets.QApplication(sys.argv)
self.win = QtWidgets.QMainWindow()
self.ui = Ui_MainWindow()
self.ui.setupUi(self.win)
self.initUI()
self.win.setWindowTitle("This is the Title!")
self.win.resize(800,600)
self.win.show()
sys.exit(app.exec_())
def initUI(self): # Button assignments
self.win.setWindowTitle("This is the Title!")
self.ui.btn_Start.clicked.connect(self.start_progressbar)
# ...
Related
If I click Back from the second window, the program will just exit. How do I go back to mainwindow in this case? I assume I will need some more code in that clickMethodBack function.
import os
import PyQt5
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QWidget, QLabel, QPushButton
import time
from PyQt5.QtCore import QSize
class GUI_Window():
def __init__(self):
self.main_window()
return
def main_window(self):
app = PyQt5.QtWidgets.QApplication(sys.argv)
self.MainWindow = MainWindow_()
self.MainWindow.show()
app.exec_()
return
class MainWindow_(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.TestAButton = QPushButton("TestA", self)
self.TestAButton.clicked.connect(self.TestA_clickMethod)
self.TestAButton.move(20, 0)
self.CloseButton = QPushButton("Close", self)
self.CloseButton.clicked.connect(self.Close_clickMethod)
self.CloseButton.move(20, 40)
self.TestingA = TestA_MainWindow()
def TestA_clickMethod(self):
self.TestAButton.setEnabled(False)
time.sleep(0.2)
self.TestingA.show()
self.hide()
try:
if self.TestingA.back == True:
self.show()
except:
None
def Close_clickMethod(self):
self.Test_Choice = 'Exit'
self.close()
class TestA_MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setMinimumSize(QSize(980,700))
self.setWindowTitle("TestA")
self.Back_Button = False
self.closeButton = QPushButton("Close", self)
self.closeButton.clicked.connect(self.clickMethodClose)
self.returnButton = QPushButton("Back", self)
self.returnButton.clicked.connect(self.clickMethodBack)
self.returnButton.move(0,30)
def clickMethodClose(self):
self.Back_Button = False
self.close()
def clickMethodBack(self):
self.returnButton.setEnabled(False)
time.sleep(0.5)
self.back = True
self.close()
# Run if Script
if __name__ == "__main__":
main = GUI_Window() # Initialize GUI
Your code has two very important issues.
you're using a blocking function, time.sleep; Qt, as almost any UI toolkit, is event driven, which means that it has to be able to constantly receive and handle events (coming from the system or after user interaction): when something blocks the event queue, it completely freezes the whole program until that block releases control;
you're checking for the variable too soon: even assuming the sleep would work, you cannot know if the window is closed after that sleep timer has ended;
The solution is to use signals and slots. Since you need to know when the second window has been closed using the "back" button, create a custom signal for the second window that will be emitted whenever the function that is called by the button is closed.
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
central = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout(central)
self.testButton = QtWidgets.QPushButton('Test A')
self.closeButton = QtWidgets.QPushButton('Close')
layout.addWidget(self.testButton)
layout.addWidget(self.closeButton)
self.setCentralWidget(central)
self.testButton.clicked.connect(self.launchWindow)
self.closeButton.clicked.connect(self.close)
def launchWindow(self):
self.test = TestA_MainWindow()
self.test.backSignal.connect(self.show)
self.hide()
self.test.show()
class TestA_MainWindow(QtWidgets.QWidget):
backSignal = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
layout = QtWidgets.QHBoxLayout(self)
self.closeButton = QtWidgets.QPushButton('Close')
self.backButton = QtWidgets.QPushButton('Back')
layout.addWidget(self.closeButton)
layout.addWidget(self.backButton)
self.closeButton.clicked.connect(self.close)
self.backButton.clicked.connect(self.goBack)
def goBack(self):
self.close()
self.backSignal.emit()
def GUI_Window():
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
GUI_Window()
Notes:
I removed the GUI_Window class and made a function, as using a class for that is not really useful;
you should always prefer layout managers instead of setting manual geometries;
widgets should not be added to a QMainWindow as direct children, and a central widget should always be used (see the creation and use of central in the example); read more about it in the documentation;
only classes and constants should be capitalized, while variables, attributes and functions should always have names starting with a lowercase letter;
I want to start printing numbers in a GUI in pyqt4 but when I pressed the start button it freezes and I have to close de window, I want start the psocess clicking on a button and stoped with another button, here I let my code
This is the part where I tried to start and stop the process
class LecturaFrecuencia(QMainWindow):
"""docstring for LecturaFrecuencia"""
def __init__(self, parent):
super(LecturaFrecuencia, self).__init__(parent)
uic.loadUi('frecuencia.ui', self)
global START
self.button_volver.clicked.connect(self.regresar)
self.button_terminar.clicked.connect(self.terminar_monitoreo)
self.button_guardar.clicked.connect(self.guardar)
self.button_iniciar.clicked.connect(self.iniciarLectura)
def iniciarLectura(self):
START = 1
while START :
pqrst= random.randint(55,101)
time.sleep(1.0)
print(str(pqrst))
self.pqrst.setText(str(pqrst))
if self.button_terminar.isChecked():
START = 0
else:
pass
Never use time.sleep() in the GUI thread since it will freeze it. If you want to do periodic tasks use a QTimer:
import random
from PyQt4 import QtCore, QtGui, uic
class LecturaFrecuencia(QtGui.QMainWindow):
"""docstring for LecturaFrecuencia"""
def __init__(self, parent=None):
super(LecturaFrecuencia, self).__init__(parent)
uic.loadUi('frecuencia.ui', self)
timer = QtCore.QTimer(
self,
interval=1000,
timeout=self.processing
)
self.button_iniciar.clicked.connect(timer.start)
self.button_terminar.clicked.connect(timer.stop)
#QtCore.pyqtSlot()
def processing(self):
pqrst = random.randint(55,101)
self.pqrst.setText("{}".format(pqrst))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = LecturaFrecuencia()
w.show()
sys.exit(app.exec_())
In the code below, after any sort of editing or completion in the line edit box, the modify function will be called. Then, the program will be stuck in an infinite loop, resulting in continuous QMessageBox pop-ups and 'modifying..' print statements, followed by the eventual crash of the program.
I've tried to put self.win.processEvents() at different places, but it doesn't help.
from PyQt5 import QtWidgets
class Test:
def __init__(self):
self.app = QtWidgets.QApplication([])
self.win = QtWidgets.QMainWindow()
self.le_dwell_filter = QtWidgets.QLineEdit()
self.le_dwell_filter.editingFinished.connect(self.modify)
self.win.setCentralWidget(self.le_dwell_filter)
self.win.show()
def modify(self):
print('Modifying...')
msgbox = QtWidgets.QMessageBox()
msgbox.setText('modification done!')
msgbox.show()
def start(self):
self.app.exec()
if __name__ == '__main__':
my_test = Test()
my_test.start()
I would have thought this will print one 'Modifying...', but somehow QMessageBox keeps popping up and the printing keeps happening.. I think it has to do with PyQt Event loop?
You want to have a single QMessageBox so why do you create a new QMessageBox in the modify method ?, what you have to do is reuse:
class Test:
def __init__(self):
self.app = QtWidgets.QApplication([])
self.win = QtWidgets.QMainWindow()
self.le_dwell_filter = QtWidgets.QLineEdit()
self.le_dwell_filter.editingFinished.connect(self.modify)
self.win.setCentralWidget(self.le_dwell_filter)
self.win.show()
self.msgbox = QtWidgets.QMessageBox()
def modify(self):
print('Modifying...')
self.msgbox.setText('modification done!')
self.msgbox.show()
def start(self):
self.app.exec()
I wish to open a PySide/PyQt window and automatically start executing a method, which will show a progress in the UI while it is executing.
With the code below, the ui is not shown until the process has completed, thus you cannot see the progress. How can I change the code to see the progress before the process has completed?
from PySide import QtGui
import time
class MyApp(QtGui.QMainWindow)
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
# Setup
self.centralWidget = QtGui.QWidget(self)
self.setCentralWidget(self.centralWidget)
self.setup_UI()
# Execute process!
self.process()
def setup_UI(self):
''' Attach widgets to window '''
self.mainLayout=QtGui.QVBoxLayout(self.centralWidget)
self.list_widget = QtGui.QListWidget()
self.progress_bar = QtGui.QProgressBar()
self.mainLayout.addWidget(self.list_widget)
self.mainLayout.addWidget(self.progress_bar)
def process(self):
''' Manipulate the ui '''
self.progress_bar.setMaximum(0)
self.progress_bar.setMaximum(10)
for x in range(0, 10):
time.sleep(1)
self.list_widget.addItem('Item ' + str(x))
self.progress_bar.setValue(x)
my_app = MyApp()
my_app.show()
Your main problem that you are blocking qt main thread by calling time.sleep. To solve this issue you have two options. One of them is using threading. Another option is change your code to asynchronous like this:
from PySide import QtGui, QtCore
import time
import sys
class MyApp(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
# Setup
self.centralWidget = QtGui.QWidget(self)
self.setCentralWidget(self.centralWidget)
self.setup_UI()
# Execute process!
self.set_process()
self.timer = QtCore.QTimer()
self.i = 0
self.timer.timeout.connect(self.update)
self.timer.start(1000)
def setup_UI(self):
''' Attach widgets to window '''
self.mainLayout=QtGui.QVBoxLayout(self.centralWidget)
self.list_widget = QtGui.QListWidget()
self.progress_bar = QtGui.QProgressBar()
self.mainLayout.addWidget(self.list_widget)
self.mainLayout.addWidget(self.progress_bar)
def set_process(self):
''' Manipulate the ui '''
self.progress_bar.setMaximum(0)
self.progress_bar.setMaximum(10)
def update(self):
if self.i > 9:
self.timer.stop()
self.list_widget.addItem('Item ' + str(self.i))
self.progress_bar.setValue(self.i)
self.i += 1
def main():
app = QtGui.QApplication(sys.argv)
my_win = MyApp()
my_win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
This example are using Qtimer object for updating progress bar with delay.
The example can be made to work quite easily by starting the processing with a single-shot timer and then calling processEvents within the loop to update the GUI:
# Execute process!
QtCore.QTimer.singleShot(100, self.process)
...
for x in range(0, 10):
time.sleep(1)
...
QtGui.qApp.processEvents(QtCore.QEventLoop.AllEvents, 50)
However, there is no guarantee that this type of approach will work with a more realistic example. You may end up needing to use threads or multiprocessing - it all depends on the specific kind of processing you are going to do.
I have this simplified code that doesn't work and I can't understand why... I expect MySlot.slt() to be called every time i press a key in my QTextEdit but it doesn't! Could you please have a look?
#!/usr/bin/env python2
import sys
from PySide import QtGui, QtCore
class MySlot(object):
def __init__(self, qte):
qte.textChanged.connect(self.soc)
def slt(self):
print("got signal")
class MainWid(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWid, self).__init__(parent)
self.initgui()
def initgui(self):
lay = QtGui.QVBoxLayout()
txt = QtGui.QTextEdit(self)
MySoc(txt)
lay.addWidget(txt)
self.setLayout(lay)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
wid = MainWid()
sys.exit(app.exec_())
if __name__=="__main__":
main()
Your MySoc object in initgui has local scope and is therefore destroyed at the end of initgui.
Assign the object to a variable:
...
self.soc = MySoc(txt);
...
and you will see the "got signal" output each time you press a key.