The following minimal code crashes in the QThread's run for loop. This works when removing the widget object.
import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Widget(QLabel):
def __init__(self):
super(Widget, self).__init__()
self.setText("hello")
self.show()
class Worker(QThread):
def __init__(self):
super(Worker, self).__init__()
def run(self):
for i in range(1,2):
label = QLabel()
label.setText(str(i))
label.show()
time.sleep(1)
label.close()
app = QApplication(sys.argv)
widget = Widget()
worker = Worker()
worker.start()
sys.exit(app.exec_())
From qt5 documentation:
http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
You can't create widgets in worker thread.
Apparently time.sleep is causing the segfault. You can use QThread.sleep (self.sleep in a thread) instead.
Cheers!
Related
With PyQT5 and QtDesigner I used to make window app with threading like that:
class MainUIClass(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self,parent=None):
super().__init__()
self.setupUi(self)
#Create threads
self.thread_1 = QThread()
# Create worker object
self.worker_1 = Worker_1(self)
# Move workers to thread
self.worker_1.moveToThread(self.thread_1)
# Connect signals and slots
self.thread_1.started.connect(self.worker_1.run)
self.thread_1.start(QThread.Priority.NormalPriority)
where Worker_1 is like:
class Worker_1(QObject):
def __init__(self,mainWin,parent = None ):
super(CoreWorker, self).__init__(parent)
#super().__init__()
self.mainWin = mainWin
def run(self):
while True:
pass
# do something
and main
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainUIClass()
win.show()
sys.exit(app.exec_())
Similarly for thread 2,3,...
Now I want try to make something similar with PySide but for design I want to use qml (in QtCreator) instead of using Ui_MainWindow from QT designer. Maybe the main difference (problem) is not in converting from PyQT to Pyside but from converting from qt designer to to qml.
Is there any help please?
i've encountered a very weird behavior which led to a seg fault when working on my app.
can someone please explain why it crashes ?
system:
ubuntu 18.04
PyQt 5.9.2
python 3.6
conditions:
have a QApplication based class
create QQmlApplicationEngine (even if its not used)
import- from PyQt5.QtQuick import QQuickImageProvider (even if its not used)
have a signal/slot that send/recieve a list type and send a [None]
emit the signal only after the engine is instantiated
** it doesnt matter if the slot has a #pyqtslot decorator or not
** if i change the signal signature to object instead of list everything is fine
** if you remove even one of the conditions you wont get the seg fault.
example:
import sys
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtQuick import QQuickImageProvider # if you comment out it wont crash
APP = None
class SomeCrashingClass(QObject):
sig = pyqtSignal(list)
def __init__(self):
QObject.__init__(self)
self.sig.connect(self.my_func)
self.sig.emit([None]) # if you send a [] it wont crash
def my_func(self):
print(f"-------------everything is fineeeeeeeeeeeeeeeeeeee-----------------")
class CrashingApp(QApplication):
def __init__(self):
super(CrashingApp, self).__init__(sys.argv)
self._engine = QQmlApplicationEngine() # if you comment out it wont crash
bla = SomeCrashingClass()
def run(self):
print(f"---------------------running the app ------------------")
exit_status = self.exec_()
print(f"----------exit status {exit_status}")
def run():
global APP
APP = CrashingApp()
sys.exit(APP.run())
if __name__ == "__main__":
run()
EDIT:
changing sig = pyqtSignal(object) solves the issue
I'm using Qt Designer for design GUI to use in python, after designing my desired UI in Qt Designer, convert it to python code and then I changed generated code to do some action in my python code, but if I changed the UI with Qt Designer and convert it to python code again, I lost my previous changes on my code.
how can I solve the problem?
can we Spreading a Class Over Multiple Files in python to write code in other files?
To avoid having these problems it is advisable not to modify this file but to create a new file where we implement a class that uses that design.
For example, suppose you have used the MainWindow template in the design.ui file, then convert it to Ui_Design.py like to the following structure:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
[...]
def retranslateUi(self, MainWindow):
[...]
Then we will create a new file that we will call logic.py where we will create the file that handles the logic and that uses the previous design:
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
So even if you modify the design and generate the file again .py you will not have to modify the file of the logic.
To generalize the idea we must have the following rules but for this the logic class must have the following structure:
class Logic(PyQtClass, DesignClass):
def __init__(self, *args, **kwargs):
PyQtClass.__init__(self, *args, **kwargs)
self.setupUi(self)
PyQtClass: This class depends on the design chosen.
Template PyQtClass
─────────────────────────────────────────────
Main Window QMainWindow
Widget QWidget
Dialog with Buttons Bottom QDialog
Dialog with Buttons Right QDialog
Dialog with Without Buttons QDialog
DesignClass: The name of the class that appears in your design.
The advantage of this implementation is that you can implement all the logic since it is a widget, for example we will implement the solution closing pyqt messageBox with closeevent of the parent window :
class Logic(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
def closeEvent(self, event):
answer = QtWidgets.QMessageBox.question(
self,
'Are you sure you want to quit ?',
'Task is in progress !',
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No)
if answer == QtWidgets.QMessageBox.Yes:
event.accept()
else:
event.ignore()
The easiest way is to use the *.ui file directly in the python code, you don't need convert to *.py file every time you change the ui.
you can use this pseudo code in your project.
# imports
from PyQt5 import uic
# load ui file
baseUIClass, baseUIWidget = uic.loadUiType("MainGui.ui")
# use loaded ui file in the logic class
class Logic(baseUIWidget, baseUIClass):
def __init__(self, parent=None):
super(Logic, self).__init__(parent)
self.setupUi(self)
.
.
.
.
def main():
app = QtWidgets.QApplication(sys.argv)
ui = Logic(None)
ui.showMaximized()
sys.exit(app.exec_())
I set the printInWidget clicked method for the btnprint button in widget1. But I want to replace that command in MainWindow with command printInMain by code: self.wid.btnprint.clicked.connect(self.printInMain). I don't know why it does both commands in MainWindow. Please help me make the command printInWidget not execute when running MainWidow.
Sorry everyone (English is not my native language and I only approached pyqt5 for a few months by teaching myself).
when I click button "printText"
my code:
mainwindow.py
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QTextEdit, QGridLayout, QVBoxLayout, QFileDialog,QHBoxLayout,QSpacerItem,QSizePolicy
from PyQt5 import uic, QtCore
import sys
from widget1 import *
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("MainWindow.ui", self)
self.wid = widget1()
self.verticalLayout.addWidget(self.wid)
self.wid.btnprint.clicked.connect(self.printInMain)
# I want to override method of button printText
def printInMain(self):
self.wid.labelB.setText('New text (method in Main)')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = UI()
window.show()
app.exec_()
widget1.py
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow
from PyQt5.QtGui import QIcon
from PyQt5 import uic
class widget1(QWidget):
def __init__(self):
super().__init__()
uic.loadUi('widget1.ui',self)
self.btnprint.clicked.connect(self.printInwidget)
def printInwidget(self):
self.labelA.setText('Hello World (Method in Widget)')
if __name__ == '__main__':
app = QApplication(sys.argv)
Main = QMainWindow()
widget = widget1()
widget.show()
sys.exit(app.exec_())
You need to disconnect. If you want to use the initial behaviour again you need to connect the signal to the slot again. I would suggest creating a reconnect() function that will do that for you if you decide to do the whole thing many times.
Here is an example.
More info can be found in the official QObject documentation (look for bool QObject::disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)).
Also try to provide full code or at least a minimal working example. What you have posted right now cannot work due to missing dependencies and a UI file.
I have constructed a GUI in QtChooser and the code for it is written in PyQt5. I have a total of 7 tabs in the GUI, each of which are defined in the QMainWindow class of my code. These definitions contain the codes for each TextEdit, LineEdit, PushButtons, RadioButtons, etc.
However, in one the the tabs, I want to embed an external terminal which will open when a particular PushButton is clicked within that tab. I was able to open the Urxvt terminal when the RadioButton is toggled. The issue I'm facing now is to open the terminal specifically in the area of the TextEdit. This is how the original GUI (built in the QtDesigner looks like. I need the terminal to open in the TextEdit below the Output label. But, this is how the terminal opens in the GUI when the code is run
This is a part of the updated code:
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QMessageBox, QAction
from PyQt5.QtCore import QDate, QTime, QDateTime, Qt
import sys
import platform
import os
import subprocess
import time
import re
import textwrap
class EmbTerminal(QtWidgets.QWidget):
def __init__(self, parent=None):
super(EmbTerminal, self).__init__()
self.process = QtCore.QProcess(self)
self.terminal = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.terminal)
# Works also with urxvt:
self.process.start('urxvt',['-embed', str(int(self.winId())), '-bg', '#000000', '-fg', '#ffffff'])
self.setFixedSize(539, 308)
class Ui_Dialog(QtWidgets.QMainWindow):
def __init__(self):
super(Ui_Dialog, self).__init__()
#Load GUI from QT5 Designer
uic.loadUi("S1_mainwindow.ui", self)
def openTerminalCheckBox (self):
if self.openTerminalRMachineRadioButton.isChecked():
status = True
self.commandLineRemoteCommandRMachineLineEdit.setDisabled(True)
self.commandlineRemoteCommandRMachineLabel.setDisabled(True)
self.executeRemoteCommandRMachinePushButton.setDisabled(True)
self.remoteMachineOutputLabel.setText("Terminal")
self.outputRMachineTextEdit = QtWidgets.QTabWidget()
self.gridLayout_6.addWidget(self.outputRMachineTextEdit)
self.outputRMachineTextEdit.addTab(EmbTerminal(), "EmbTerminal")
else:
status = False
app = QtWidgets.QApplication(sys.argv) # Create an instance of QtWidgets.QApplication
window = Ui_Dialog()
main = mainWindow()
main.show() # Create an instance of our class
app.exec_()
I need to open the terminal specifically in the QTextEdit which is already been defined in that tab. Do you guys have any suggestions/input?