Got some problem with PyQT and QThread.
When using method from another module in QThread run(), i can't get values from it.
How can i get them?
from PyQt4 import QtCore, QtGui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.initAnotherThread()
self.setFixedSize(640, 360)
self.setWindowTitle('Window')
self.logText = QtGui.QTextEdit(self)
self.logText.setReadOnly(True)
self.logText.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.logText.resize(460, 330)
self.clickButton = QtGui.QPushButton('Click Button', self)
self.clickButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.clickButton.setGeometry(470, 50, 160, 35)
self.connect(self.clickButton, QtCore.SIGNAL('clicked()'), self.onButtonClick)
def initAnotherThread(self):
self.updaterThread = anotherThread(self)
def onButtonClick(self):
self.logText.clear()
self.updaterThread.start()
class anotherThread(QtCore.QThread):
def __init__(self, mw):
super(anotherThread, self).__init__(mw)
self.mw = mw
def run(self):
print "Another thread started"
AnotherModule.anotherMethod()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
In AnotherModule i have next:
anotherMethod():
x=0
while x<100:
x+=1
What should i do to get x value from AnotherModule?
Related
I’m using PyQt5 and I need to have my main window detect when a different window closes. I read here Emit a signal from another class to main class that creating a signal class to serve as an intermediary should work. However I haven’t gotten my example to work.
In my example, clicking the button opens a QWidget window. When the QWidget is closed, the main window is supposed to change from a blue background to a red background. However, the main window remains blue using the script below.
What am I doing wrong?
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtCore import QObject, pyqtSignal
import os, sys
class MySignal(QObject):
signal = pyqtSignal()
class MyMainWindow(QMainWindow):
def __init__(self):
super().__init__()
#Make mainwindow
self.setGeometry(100,100,300,200)
self.setStyleSheet('background-color: blue')
# Make widget and button objects and set connection
self.widget = MyWidget()
self.btn = QPushButton(self)
self.btn.setText('Click')
self.btn.move(175, 150)
self.btn.setStyleSheet('background-color: white')
self.btn.clicked.connect(self.widget.showWidget)
# Make signal object and set connection
self.mySignal = MySignal()
self.mySignal.signal.connect(self.changeToRed)
# Let's start
self.show()
def changeToRed(self):
self.setStyleSheet('background-color: red')
def closeEvent(self, event):
os._exit(0)
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(500, 100, 200, 200)
self.sig = MySignal()
def showWidget(self):
self.show()
def closeEvent(self, event):
self.sig.signal.emit()
self.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyMainWindow()
app.exec()```
The reason your code fails is that the connected signal object in MyMainWindow is not the same as the one you create in MyWidget, so the signal is never emitted. Here is a modified solution using signals in the normal way:
from PyQt5.QtWidgets import QPushButton, QMainWindow, QWidget, QApplication
from PyQt5.QtCore import QObject, pyqtSignal
import os, sys
class MyMainWindow(QMainWindow):
def __init__(self):
super().__init__()
#Make mainwindow
self.setGeometry(100,100,300,200)
self.setStyleSheet('background-color: blue')
# Make widget and button objects and set connection
self.widget = MyWidget()
self.btn = QPushButton(self)
self.btn.setText('Click')
self.btn.move(175, 150)
self.btn.setStyleSheet('background-color: white')
self.btn.clicked.connect(self.widget.showWidget)
self.widget.sig.connect(self.changeToRed)
# Let's start
self.show()
def changeToRed(self):
self.setStyleSheet('background-color: red')
class MyWidget(QWidget):
sig = pyqtSignal()
def __init__(self):
super().__init__()
self.setGeometry(500, 100, 200, 200)
def showWidget(self):
self.show()
def closeEvent(self, event):
self.sig.emit()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyMainWindow()
app.exec()
All you need is to add the connection between the close event and the function that turn your main screen red:
self.widget.closeEvent = self.changeToRed
this line should be in your main Window class
change your changeToRed function so it will except the event too:
def changeToRed(self, e):
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_())
Can someone me explane how should like my code, or what I'm doing wrong ?
I want to use button 'btn_run' to run 'view_splash' function. But somethink going wrong, but 'view_splash' won't start. It show me no errors.
import sys
from PyQt4 import QtGui, QtCore
import time
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(500, 150, 500, 600)
self.setWindowTitle('Test GUI')
self.threadclass = AThread()
self.connect(self.threadclass, QtCore.SIGNAL("view_splash()"), self.view_splash)
self.home()
def home(self):
btn_run = QtGui.QPushButton("Run", self)
self.threadclass = AThread()
btn_run.clicked.connect(self.threadclass.start)
btn_run.resize(120, 40)
btn_run.move(190, 540)
self.show()
def view_splash(self):
print('test')
label = QLabel("<font color=red size=10<b>" + "SPLASH" + "</b></font>")
label.setWindowFlags(Qt.SplashScreen | Qt.WindowStaysOnTopHint)
label.show()
QtCore.QTimer.singleShot(5000, label.close)
class AThread(QtCore.QThread):
def __init__(self):
super(AThread, self).__init__()
def run(self):
print(1)
print(2)
time.sleep(5)
print(3)
print(4)
self.emit(QtCore.SIGNAL("view_splash()"))
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
You need to create and connect the signals differently.
class AThread(QtCore.QThread):
view_splash = QtCore.pyqtSignal()
def run(self):
...
self.view_splash.emit()
class Window(QtGui.QMainWindow):
def __init__(self):
...
self.threadclass.view_splash.connect(self.view_splash)
I've recently started learning PyQt on my own and I've come in some trouble trying to write a custom class that inherits from QPushButton so I can adjust its attributes. I'm trying to pass a text as an argument whenever I initialize an object of this class. I am pretty sure there's something wrong with my init but I haven't found it yet.
Here is the code:
import sys
from PySide import QtGui, QtCore
class mainb(QtGui.QPushButton):
def __init__(Text,self, parent = None):
super().__init__(parent)
self.setupbt(Text)
def setupbt(self):
self.setFlat(True)
self.setText(Text)
self.setGeometry(200,100, 60, 35)
self.move(300,300)
print('chegu aqui')
self.setToolTip('Isso é muito maneiro <b>Artur</b>')
self.show()
class mainwindow(QtGui.QWidget):
def __init__(self , parent = None):
super().__init__()
self.setupgui()
def setupgui(self):
self.setToolTip('Oi <i>QWidget</i> widget')
self.resize(800,600)
self.setWindowTitle('Janela do Artur')
af = mainb("Bom dia",self)
self.show()
"""
btn = QtGui.QPushButton('Botão',self)
btn.clicked.connect(QtCore.QCoreApplication.instance().quit)
btn.resize(btn.sizeHint())
btn.move(300, 50)
"""
def main():
app = QtGui.QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are using super in wrong way, super must get a instance and another thing your first arg is Text, that's wrong that should be self. I fixed some more and the below code should work for you
import sys
from PySide import QtGui, QtCore
class mainb(QtGui.QPushButton):
def __init__(self, Text, parent = None):
super(mainb, self).__init__()
self.setupbt(Text)
def setupbt(self, Text):
self.setFlat(True)
self.setText(Text)
self.setGeometry(200,100, 60, 35)
self.move(300,300)
print('chegu aqui')
self.setToolTip('Isso muito maneiro <b>Artur</b>')
self.show()
class mainwindow(QtGui.QWidget):
def __init__(self , parent = None):
super(mainwindow, self).__init__()
self.setupgui()
def setupgui(self):
self.setToolTip('Oi <i>QWidget</i> widget')
self.resize(800,600)
self.setWindowTitle('Janela do Artur')
newLayout = QtGui.QHBoxLayout()
af = mainb("Bom dia",self)
newLayout.addWidget(af)
self.setLayout(newLayout)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = mainwindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Your def setupbt(self) does not seem to take the text as argument. Try def setupbt(self, Text): instead.