I have an application on pyqt5. And if I click on button time lcd display start to countdown with thread I created. After the countdown finished I cant use the same thread for the same action. How can I terminate that thread and start it again with clicking button
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window,self).__init__()
self.button1=QPushButton(self)
self.lcd=QLCDNumber(self)
self.tbutton1=threading.Thread(target=self.timing)
def initUI(self):
self.lcd.setVisible(False)
self.button1.clicked.connect(self.timing)
def timing(self):
self.tbutton1.start()
self.lcd.setVisible(True)
timing=self.spin.value()
for i in range(timing,-1,-1):
time.sleep(1)
if(i<10):
self.lcd.display("00:0{}".format(i))
else:
self.lcd.display("00:{}".format(i))
self.lcd.setVisible(False)
if __name__=='__main__':
app=QApplication(sys.argv)
win=window()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import threading
class window(QtWidgets.QMainWindow):
def __init__(self):
super(window,self).__init__()
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.button1 = QPushButton("Start to countdown", self)
self.lcd = QLCDNumber(self)
self.spin = QSpinBox(self)
grid = QGridLayout(centralWidget)
grid.addWidget(self.lcd)
grid.addWidget(self.spin)
grid.addWidget(self.button1)
self.initUI()
def initUI(self):
# self.lcd.setVisible(False)
self.button1.clicked.connect(self.timing)
def timing(self):
self.tbutton1 = threading.Thread(target=self.timingThread)
self.tbutton1.start()
# self.lcd.setVisible(True)
def timingThread(self):
timing=self.spin.value()
for i in range(timing, -1, -1):
# time.sleep(1)
QtCore.QThread.msleep(1000)
if(i<10):
self.lcd.display("00:0{}".format(i))
else:
self.lcd.display("00:{}".format(i))
# self.lcd.setVisible(False)
if __name__=='__main__':
app = QApplication(sys.argv)
win = window()
win.show()
sys.exit(app.exec_())
Related
I need to execute a method whenever the user clicks the down arrow on the combo box. I've tried the signals listed in the documentations but none of the worked.
from PyQt5.QtWidgets import *
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.combo = QComboBox(self)
self.combo.signal.connect(self.mymethod)
self.show()
def mymethod(self):
print('hello world')
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
There is no signal that is emitted when the down arrow is pressed but you can create override the mousePressEvent method and verify that this element was pressed:
import sys
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QStyle,
QStyleOptionComboBox,
QVBoxLayout,
QWidget,
)
class ComboBox(QComboBox):
arrowClicked = pyqtSignal()
def mousePressEvent(self, event):
super().mousePressEvent(event)
opt = QStyleOptionComboBox()
self.initStyleOption(opt)
sc = self.style().hitTestComplexControl(
QStyle.CC_ComboBox, opt, event.pos(), self
)
if sc == QStyle.SC_ComboBoxArrow:
self.arrowClicked.emit()
class Window(QWidget):
def __init__(self):
super().__init__()
self.combo = ComboBox()
self.combo.arrowClicked.connect(self.mymethod)
lay = QVBoxLayout(self)
lay.addWidget(self.combo)
lay.setAlignment(Qt.AlignTop)
def mymethod(self):
print("hello world")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
what I am trying to do with a python script: Use a pytest test method to print a text line to a label in the pyqt GUI.
When running the main python file, the GUI starts and a click on the "test" button runs the test without blocking the GUI (see full code example below). But I have no clue how to proceed now.
Code:
import sys
import pytest
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
class Window(QtWidgets.QMainWindow):
signal_start_background_job = QtCore.pyqtSignal()
def __init__(self):
super(Window, self).__init__()
layout = QVBoxLayout()
self.button = QtWidgets.QPushButton("test", self)
self.label = QtWidgets.QLabel("console output")
layout.addWidget(self.button)
layout.addWidget(self.label)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.worker = WorkerObject()
self.thread = QtCore.QThread()
self.worker.moveToThread(self.thread)
self.signal_start_background_job.connect(self.worker.background_job)
self.button.clicked.connect(self.start_background_job)
def start_background_job(self):
self.thread.start()
self.signal_start_background_job.emit()
class WorkerObject(QtCore.QObject):
#QtCore.pyqtSlot()
def background_job(self):
pytest.main(["-s", "-k test_something"])
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
def test_something():
print("unit test some stuff")
assert 0 == 0
Instead of using pytest directly you could use QProcess to launch it and then capture the output:
import os
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.button = QtWidgets.QPushButton("test", self)
self.label = QtWidgets.QLabel("console output")
self.textedit = QtWidgets.QTextEdit(readOnly=True)
widget = QWidget()
layout = QVBoxLayout(widget)
layout.addWidget(self.button)
layout.addWidget(self.label)
layout.addWidget(self.textedit)
self.setCentralWidget(widget)
self.process = QtCore.QProcess()
self.process.setProgram(sys.executable)
self.process.readyReadStandardError.connect(self.on_readyReadStandardError)
self.process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
self.button.clicked.connect(self.on_clicked)
#QtCore.pyqtSlot()
def on_clicked(self):
self.process.setWorkingDirectory(CURRENT_DIR)
self.process.setArguments(["-m", "pytest", "-s", "-k", "test_something"])
self.process.start()
#QtCore.pyqtSlot()
def on_readyReadStandardError(self):
err = self.process.readAllStandardError().data().decode()
self.textedit.append(err)
#QtCore.pyqtSlot()
def on_readyReadStandardOutput(self):
out = self.process.readAllStandardOutput().data().decode()
self.textedit.append(out)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I'm thinking you need to check out sys.stdout, and route that to an io object that you can route to the label in your widget. Then I would set a timer and every 0.1 seconds or so set the text of your label to that object. Alternatively you can implement a widget that grabs the stdout text in qt, example here: https://stackoverflow.com/a/1220002/6615517 I haven't tried it but the other answers to that question should help. You'll probably want to clear the label on each test to prevent it from getting too long.
I want to create a QApplication which is then exited using a keyboard shortcut. Then the python script should call another QApplication.
My issues currently is that I get this error when the second QApplication is about to run:
app2 = QApplication()
RuntimeError: Please destroy the QApplication singleton before creating a new QApplication instance.
I have the following structure:
| main.py
| Q1.py
| Q2.py
This is main.py:
import Q1 as record
import Q2 as display
def main():
record.main()
display.main()
if __name__ == "__main__":
main()
This is Q1 which creates the problem:
import sys
from PySide2 import QtWidgets as qtw
from PySide2 import QtGui as qtg
from PySide2 import QtCore as qtc
from PySide2 import QtMultimedia as qtmm
class MainWindow(qtw.QMainWindow):
def __init__(self):
super().__init__()
#Create Window layout with a sound widget
soundboard = qtw.QWidget()
soundboard.setLayout(qtw.QGridLayout())
self.setCentralWidget(soundboard)
sw = SoundWidget()
soundboard.layout().addWidget(sw)
#Window Dimensions
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.MinimumExpanding)
# Code ends here
self.show()
class SendOrderButton(qtw.QPushButton):
button_stylesheet = 'background-color: blue; color: white;'
def __init__(self):
super().__init__('Send Order')
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
self.setStyleSheet(self.button_stylesheet)
#self.clicked.connect(qtc.QCoreApplication.instance().quit)
def press_button(self):
if self.isEnabled():
self.setEnabled(False)
self.setText('Send Order')
else:
self.setEnabled(True)
self.setText('Sent')
class SoundWidget(qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(qtw.QGridLayout())
#Send Order Button
self.sendorder_button = SendOrderButton()
self.sendorder_button.setShortcut(qtg.QKeySequence('Tab'))
self.layout().addWidget(self.sendorder_button, 5, 0, 1, 2)
self.sendorder_button.clicked.connect(qtc.QCoreApplication.instance().quit)
def main():
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
This is Q2.py which has the second QApplication:
import sys
from PySide2.QtCore import (QAbstractTableModel, Slot)
from PySide2.QtWidgets import (QAction, QApplication, QMainWindow,QWidget)
class MainWindow(QMainWindow):
def __init__(self, widget):
QMainWindow.__init__(self)
# Exit QAction
exit_action = QAction("Exit", self)
exit_action.setShortcut("Ctrl+Q")
exit_action.triggered.connect(self.exit_app)
#Slot()
def exit_app(self, checked):
sys.exit()
class CustomTableModel(QAbstractTableModel):
def __init__(self, data=None):
QAbstractTableModel.__init__(self)
class Widget(QWidget):
def __init__(self):
QWidget.__init__(self)
# Getting the Model
self.model = CustomTableModel()
def main():
app2 = QApplication()
widget = Widget()
window2 = MainWindow(widget)
window2.show()
sys.exit(app2.exec_())
if __name__ == "__main__":
app = QApplication()
widget = Widget()
window = MainWindow(widget)
window.show()
sys.exit(app.exec_())
As noted in the comments a Qt application can only and should have a QApplication (you might not follow this rule but nothing guarantees that it works correctly) so you will have to restructure your code.
Assuming that you want the Q1 window to be first and when that window is closed then the Q2 window opens that does not imply at any time that you have to use several QApplication. The idea is to know when a window is closed and to be notified of it, to know when a window is closed then you must override the closeEvent method of the window and to make the notification you must send a signal.
Considering the above, the solution is:
├── main.py
├── Q1.py
└── Q2.py
main.py
import sys
from PySide2 import QtWidgets as qtw
import Q1 as record
import Q2 as display
def main():
app = qtw.QApplication(sys.argv)
w1 = record.get_mainwindow()
w2 = display.get_mainwindow()
w1.closed.connect(w2.show)
w1.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Q1.py
from PySide2 import QtWidgets as qtw
from PySide2 import QtGui as qtg
from PySide2 import QtCore as qtc
class MainWindow(qtw.QMainWindow):
closed = qtc.Signal()
def __init__(self):
super().__init__()
# Create Window layout with a sound widget
soundboard = qtw.QWidget()
soundboard.setLayout(qtw.QGridLayout())
self.setCentralWidget(soundboard)
sw = SoundWidget()
soundboard.layout().addWidget(sw)
# Window Dimensions
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.MinimumExpanding)
sw.sendorder_button.clicked.connect(self.close)
def closeEvent(self, event):
self.closed.emit()
super().closeEvent(event)
class SendOrderButton(qtw.QPushButton):
button_stylesheet = "background-color: blue; color: white;"
def __init__(self):
super().__init__("Send Order")
self.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
self.setStyleSheet(self.button_stylesheet)
def press_button(self):
if self.isEnabled():
self.setEnabled(False)
self.setText("Send Order")
else:
self.setEnabled(True)
self.setText("Sent")
class SoundWidget(qtw.QWidget):
def __init__(self):
super().__init__()
self.setLayout(qtw.QGridLayout())
# Send Order Button
self.sendorder_button = SendOrderButton()
self.sendorder_button.setShortcut(qtg.QKeySequence("Tab"))
self.layout().addWidget(self.sendorder_button, 5, 0, 1, 2)
def get_mainwindow():
window = MainWindow()
return window
if __name__ == "__main__":
import sys
app = qtw.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Q2.py
from PySide2 import QtCore as qtc
from PySide2 import QtWidgets as qtw
class MainWindow(qtw.QMainWindow):
def __init__(self, widget):
super().__init__()
file_menu = self.menuBar().addMenu("&File")
# Exit QAction
exit_action = qtw.QAction("Exit", self)
exit_action.setShortcut("Ctrl+Q")
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
class CustomTableModel(qtc.QAbstractTableModel):
pass
class Widget(qtw.QWidget):
def __init__(self):
super().__init__()
# Getting the Model
self.model = CustomTableModel()
def get_mainwindow():
widget = Widget()
window2 = MainWindow(widget)
return window2
if __name__ == "__main__":
import sys
app = qtw.QApplication()
widget = Widget()
window = MainWindow(widget)
window.show()
sys.exit(app.exec_())
I have a simple application, one label and 2 buttons ( start increment, stop increment), write in pyqt5.
When I pres start button want to see in ui, value update in real time and have acces at stop button.
Now when I press start button, not see an update in UI and when try to press on stop receive not responding.
One solution probably is Threads, but I don't understand how work thread's in Python
import sys
import time
from PyQt5 import QtWidgets, Qt
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QLineEdit
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWindow(QMainWindow):
running = True
value = 1
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(600, 500))
self.setMaximumSize(QSize(600, 500))
self.setWindowTitle("Demo app")
QApplication.setStyle("fusion")
self.move(1000, 200)
self.button_start = QPushButton('Start', self)
self.button_start.setFixedHeight(40)
self.button_start.setFixedWidth(170)
self.button_start.move(10, 215)
self.button_start.clicked.connect(self.start_function)
self.update()
self.button_stop = QPushButton('Stop', self)
self.button_stop.setFixedHeight(40)
self.button_stop.setFixedWidth(170)
self.button_stop.move(200, 215)
self.button_stop.setDisabled(True)
self.button_stop.clicked.connect(self.stop_function)
self.update()
self.label = QLineEdit(self)
self.label.move(10, 170)
self.label.resize(170, 40)
self.label.setEnabled(False)
self.label.setAlignment(Qt.AlignCenter)
self.label.setStyleSheet("color: red;")
self.update()
def start_function(self):
self.button_start.setDisabled(True)
self.button_stop.setDisabled(False)
while self.running is True:
self.value += 1
self.label.setText(str(self.value))
print("Value: ", self.value)
time.sleep(1)
def stop_function(self):
self.running = False
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
mainWin.update()
sys.exit(app.exec_())
Maybe I'm a little late and you figured out yourself how to solve this problem, but for those with the same issue:
You can't set a loop in the GUI thread for this purpose, It will block the GUI thread from doing anything. Instead, You can use QTimer to schedule something to run at a later point in time. You can use it to implement something like a stopwatch.
Here is a functional minimal example:
import sys
import time
from PyQt5 import QtWidgets, Qt
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QLineEdit
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MainWindow(QMainWindow):
value = 0
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(600, 500))
self.setMaximumSize(QSize(600, 500))
self.setWindowTitle("Demo app")
QApplication.setStyle("fusion")
self.move(1000, 200)
self.button_start = QPushButton('Start', self)
self.button_start.setFixedHeight(40)
self.button_start.setFixedWidth(170)
self.button_start.move(10, 215)
self.button_start.clicked.connect(self.start_function)
self.update()
self.button_stop = QPushButton('Stop', self)
self.button_stop.setFixedHeight(40)
self.button_stop.setFixedWidth(170)
self.button_stop.move(200, 215)
self.button_stop.setDisabled(True)
self.button_stop.clicked.connect(self.stop_function)
self.update()
self.label = QLineEdit(self)
self.label.move(10, 170)
self.label.resize(170, 40)
self.label.setEnabled(False)
self.label.setAlignment(Qt.AlignCenter)
self.label.setStyleSheet("color: red;")
self.label.setText(str(self.value))
self.update()
self.workTimer = QTimer()
self.workTimer.setInterval(1000)
self.workTimer.timeout.connect(self.increase_value)
def start_function(self):
self.workTimer.start()
self.button_start.setDisabled(True)
self.button_stop.setDisabled(False)
def stop_function(self):
self.workTimer.stop()
self.button_start.setDisabled(False)
self.button_stop.setDisabled(True)
def increase_value(self):
self.value += 1
self.label.setText(str(self.value))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
mainWin.update()
sys.exit(app.exec_())
Hi in my application I have main windows and when a data comes from another thread, I need to show it in another screen for 2 seconds and then go back to previous screen. Screens has many components so I made a simple version to demonstrate my purpose.
Data comes from another thread successfully I can change the text of label. However I can not make disappear the old one and apeear the new one.
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QApplication,QMainWindow, QLabel, QWidget, QGridLayout, QVBoxLayout, QGroupBox
from PyQt5.QtGui import QTextDocument
from PyQt5 import QtCore, Qt
from PyQt5.QtGui import QIcon, QPixmap, QFont
from time import strftime
import datetime
from babel.dates import format_date, format_datetime, format_time
import sys
import worker
import time
class Form(QWidget):
def __init__(self):
super().__init__()
self.label_main = QLabel("Welcome")
self.label_uid = QLabel("Exit")
self.left = 0
self.top = 0
self._width = 480
self._height = 800
self.layout_main = QVBoxLayout()
self.layout_access = QVBoxLayout()
self.obj = worker.Worker() # no parent!
self.thread = QThread() # no parent!
self.obj.return_uid.connect(self.onCardRead)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.get_uid)
self.thread.start()
self.initUI()
def initUI(self):
self.setLayout(self.layout_main)
self.layout_main.addWidget(self.label_main)
self.setWindowTitle('Main Thread')
self.show()
def secondUI(self):
self.setLayout(self.layout_access)
self.layout_access.addWidget(self.label_uid)
self.setWindowTitle('Access Thread')
self.show()
add self.windowname.close() or just self.close() after the show()
Try it:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Worker(QtCore.QObject):
return_uid = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
def get_uid(self):
print("start")
count = 0
QtCore.QThread.msleep(1000)
while count < 10:
QtCore.QThread.msleep(200)
self.return_uid.emit(count)
count += 1
self.finished.emit()
class Form(QWidget):
def __init__(self):
super().__init__()
self.label_main = QLabel("Welcome")
self.label_uid = QLabel("Exit")
self.layout_main = QVBoxLayout()
self.layout_access = QVBoxLayout()
# self.obj = worker.Worker() # no parent!
self.obj = Worker()
self.thread = QThread()
self.obj.return_uid.connect(self.onCardRead)
self.obj.moveToThread(self.thread)
# self.obj.finished.connect(self.thread.quit)
self.obj.finished.connect(self.close)
self.thread.started.connect(self.obj.get_uid)
self.thread.start()
self.initUI()
def initUI(self):
self.setLayout(self.layout_main)
self.layout_main.addWidget(self.label_main)
self.setWindowTitle('Main Thread')
self.show()
def secondUI(self):
self.setLayout(self.layout_access)
self.layout_access.addWidget(self.label_uid)
self.setWindowTitle('Access Thread')
self.show()
def onCardRead(self, id):
self.label_main.setNum(id)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Form()
sys.exit(app.exec_())
Update
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Worker(QtCore.QObject):
return_uid = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
def get_uid(self):
print("start")
count = 0
QtCore.QThread.msleep(1000)
while count < 10:
QtCore.QThread.msleep(500)
self.return_uid.emit(count)
count += 1
self.finished.emit()
class Form(QWidget):
def __init__(self):
super().__init__()
self.label_main = QLabel("Welcome")
self.label_uid = QLabel("Exit")
self.layout_main = QVBoxLayout()
# self.layout_access = QVBoxLayout()
self.obj = Worker()
self.thread = QThread()
self.obj.return_uid.connect(self.onCardRead)
self.obj.moveToThread(self.thread)
# self.obj.finished.connect(self.close)
self.obj.finished.connect(self.secondUI) # <---
self.thread.started.connect(self.obj.get_uid)
self.thread.start()
self.initUI()
def initUI(self):
self.setLayout(self.layout_main)
self.layout_main.addWidget(self.label_main)
self.setWindowTitle('Main Thread')
self.resize(300, 100)
self.show()
def secondUI(self): # <---
self.hide()
self.windowSecond = QWidget()
self.layout_access = QVBoxLayout(self.windowSecond)
self.layout_access.addWidget(self.label_uid)
self.windowSecond.setWindowTitle('Main Screen')
self.windowSecond.resize(300, 200)
self.windowSecond.show()
def onCardRead(self, id):
self.label_main.setNum(id)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Form()
sys.exit(app.exec_())