i have this WorkerSignals class which used to connect signals with the Qthread class worker, SaveToExcel() is a function that i used to run in Qthread.
class WorkerSignals(QObject):
finished = pyqtSignal()
error = pyqtSignal(tuple)
result = pyqtSignal(object)
progress = pyqtSignal(int)
class Worker(QThread):
def __init__(self,query,filename,choices,fileExtension,iterativeornot):
super(Worker,self).__init__()
self.signals = WorkerSignals()
self.query =query
self.filename = filename
self.choices = choices
self.fileExtension = fileExtension
self.iterativeornot =iterativeornot
#pyqtSlot()
def run(self):
try:
SaveToExcel(self.query,self.filename,self.choices,self.fileExtension,self.iterativeornot)
except:
self.signals.result.emit(1)
finally:
self.signals.finished.emit()
this is the class that i used to create the Qwidget that has ui
class AnotherWindow(QWidget):
def __init__(self,windowname):
super().__init__()
self.layout = QVBoxLayout()
self.label = QLabel()
self.setWindowTitle(windowname)
self.setWindowIcon(QIcon(os.path.join(basedir,'./images/import.png')))
self.setFixedSize(460,440)
self.layout.addWidget(self.label)
# Query
self.textinput = QPlainTextEdit()
self.layout.addWidget(self.textinput)
self.qhboxlayout1 = QHBoxLayout()
self.IterativeRadiobtn1 = QRadioButton('All Locations')
self.IterativeRadiobtn2 = QRadioButton('Current Locations')
self.IterativeRadiobtn2.setChecked(True)
self.qhboxlayout1.addWidget(self.IterativeRadiobtn1)
self.qhboxlayout1.addWidget(self.IterativeRadiobtn2)
self.layout.addLayout(self.qhboxlayout1)
# Check boxes
self.c1 = QCheckBox("sc",self)
self.c2 = QCheckBox("ad",self)
self.c3 = QCheckBox("sr",self)
self.c4 = QCheckBox("fc",self)
self.hboxlayoutchoices = QHBoxLayout()
#adding checkboxes to layout
self.checkboxlist = [self.c1,self.c2,self.c3,self.c4]
for cbox in self.checkboxlist:
self.hboxlayoutchoices.addWidget(cbox)
self.layout.addLayout(self.hboxlayoutchoices)
# filename
self.filename = QLineEdit()
self.layout.addWidget(self.filename)
# Combo box to show the filetype which need to be saved
self.extensions = QComboBox()
self.combodict = {'Excel 97-2003 Workbook (*.xls)':'xls','CSV UTF-8 (Comma delimited) (*.csv)':'csv'}
self.extensions.addItems(self.combodict)
self.layout.addWidget(self.extensions)
# import button
self.exportBtn = QPushButton('Import')
self.layout.addWidget(self.exportBtn)
#import function when button clicked
self.exportBtn.clicked.connect(self.IMPORT)
#setting layout
self.setLayout(self.layout)
def RadioButtonCheck(self):
if self.IterativeRadiobtn1.isChecked():
return True
if self.IterativeRadiobtn2.isChecked():
return False
def IMPORT(self):
self.cboxlist = []
for cbox in self.checkboxlist:
if cbox.isChecked():
self.cboxlist.append(cbox.text())
self.textinput.setReadOnly(True)
self.filename.setReadOnly(True)
self.exportBtn.setDisabled(True)
self.saveFilename = self.filename.text()
self.text = self.textinput.toPlainText()
self.inputextension = self.extensions.currentText()
self.getvalue = self.combodict.get(self.inputextension)
self.truorfalse = self.RadioButtonCheck()
# self.queryThread = threading.Thread(target=SaveToExcel,args=(self.text,self.saveFilename,self.cboxlist,self.getvalue,self.truorfalse))
# self.queryThread.start()
self.worker = Worker(self.text,self.saveFilename,self.cboxlist,self.getvalue,self.truorfalse)
self.worktherad = QThread()
self.worker.moveToThread(self.worktherad)
self.worktherad.started.connect(self.worker.run)
self.worktherad.finished.connect(self.complete)
self.worktherad.start()
def complete(self):
self.msg = QMessageBox()
self.msg.setWindowTitle("Status")
self.msg.setText("Import Done")
self.msg.exec()
self.textinput.setReadOnly(False)
self.filename.setReadOnly(False)
self.exportBtn.setDisabled(False)
self.exportBtn.setText("Import Again")
but when i click the import button the function won't run and just do nothing, I don't have a good knowledge about Qthreading but when i use the python default threading the function will run and import the datas. Still i don't have good clear idea about how to implent the Qthreading for the SaveToExcel function.
self.worker = Worker(self.text,self.saveFilename,self.cboxlist,self.getvalue,self.truorfalse)
in this line you should probably pass the parent field and you should accept the parent field in Worker __init__ method and pass it in super call
(so the thread will automatically destroyed once it's parent object is deleted)
and the Worker class is already a QThread you do not need to create another QThread and move it..
you should just run the self.worker by self.worker.start()
and don't forget to connect those Worker signals to valid pyqtSlot and if possible then connect those before starting the self.worker thread
Updated Code Snippet
self.worker = Worker(parent, self.text,self.saveFilename,self.cboxlist,self.getvalue,self.truorfalse) # Accept parent in __init__ method of Worker
self.worktherad.finished.connect(self.complete)
self.worktherad.start()
And also make complete function a pyqtSlot by adding decorator QtCore.pyqtSlot()
I have a Qgroupbox which contains Qcombobox with Qlabels, I want to select a value from Qcombobox and display the value as Qlabel. I have the complete code, even I do print value before and after within function every thing works as it should, Only display setText wont set text to Qlabel and update it.
Current screen
What I want
I've corrected signal code, when Qgroupbox in it Qcombobox appears or value would be changed, self.activation.connect(......) would emit an int of the index. to ensure that would work I print it-value inside the def setdatastrength(self, index), see figure below indeed it works, then argument would be passed to function self.concreteproperty.display_condata(it) would be called and do a print of value inside def display_condata(self, value) to make sure about value passing, as shown figure below, it does work. This line code self.con_strength_value.setText(fmt.format(L_Display))
wont assign value to Qlabel.
The script
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class secondtabmaterial(QtWidgets.QWidget):
def __init__(self, parent=None):
super(secondtabmaterial, self).__init__(parent)
self.concretewidgetinfo = ConcreteStrengthInFo()
Concrete_Group = QtWidgets.QGroupBox(self)
Concrete_Group.setTitle("&Concrete")
Concrete_Group.setLayout(self.concretewidgetinfo.grid)
class ConcreteStrengthComboBox(QtWidgets.QComboBox):
def __init__(self, parent = None):
super(ConcreteStrengthComboBox, self).__init__(parent)
self.addItems(["C12/15","C16/20","C20/25","C25/30","C30/37","C35/45"
,"C40/50","C45/55","C50/60","C55/67","C60/75","C70/85",
"C80/95","C90/105"])
self.setFont(QtGui.QFont("Helvetica", 10, QtGui.QFont.Normal, italic=False))
self.compressive_strength = ["12","16","20","25","30","35","40",
"45","50","55","60","70","80","90"]
class ConcreteProperty(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ConcreteProperty, self).__init__(parent)
self.setFont(QtGui.QFont("Helvetica", 10, QtGui.QFont.Normal, italic=False))
concretestrength_lay = QtWidgets.QHBoxLayout(self)
fctd = "\nfcd\n\nfctd\n\nEc"
con_strength = QtWidgets.QLabel(fctd)
self.con_strength_value = QtWidgets.QLabel(" ")
concretestrength_lay.addWidget(con_strength)
concretestrength_lay.addWidget(self.con_strength_value, alignment=QtCore.Qt.AlignRight)
self.setLayout(concretestrength_lay)
#QtCore.pyqtSlot(int)
def display_condata(self, value):
try:
L_Display = str(value)
print("-------- After ------")
print(L_Display, type(L_Display))
fmt = "{}mm"
self.con_strength_value.setText(fmt.format(L_Display))
except ValueError:
print("Error")
class ConcreteStrengthInFo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ConcreteStrengthInFo, self).__init__(parent)
self.concreteproperty = ConcreteProperty()
self.concretestrengthbox = ConcreteStrengthComboBox()
self.concretestrengthbox.activated.connect(self.setdatastrength)
hbox = QtWidgets.QHBoxLayout()
concrete_strength = QtWidgets.QLabel("Concrete strength: ")
hbox.addWidget(concrete_strength)
hbox.addWidget(self.concretestrengthbox)
self.grid = QtWidgets.QGridLayout()
self.grid.addLayout(hbox, 0, 0)
self.grid.addWidget(self.concreteproperty, 1, 0)
#QtCore.pyqtSlot(int)
def setdatastrength(self, index):
it = self.concretestrengthbox.compressive_strength[index]
self.concreteproperty.display_condata(it)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = secondtabmaterial()
w.show()
sys.exit(app.exec_())
Above code is corrected and final. Now it works as it should.
I think the issue is that your receiving slot doesn't match any of the available .activated signals.
self.activated.connect(self.setdatastrength)
#QtCore.pyqtSlot()
def setdatastrength(self):
index = self.currentIndex()
it = self.compressive_strength[index]
print(it)
self.concreteproperty.display_condata(it)
The QComboBox.activated signal emits either an int of the index, or a str of the selected value. See documentation.
You've attached it to setdatastrength which accepts doesn't accept any parameters (aside from self, from the object) — this means it doesn't match the signature of either available signal, and won't be called. If you update the definition to add the index value, and accept a single int it should work.
self.activated.connect(self.setdatastrength)
#QtCore.pyqtSlot(int) # add the target type for this slot.
def setdatastrength(self, index):
it = self.compressive_strength[index]
print(it)
self.concreteproperty.display_condata(it)
After the update — the above looks now to be fixed, although you don't need the additional index = self.currentIndex() in setdatastrength it's not doing any harm.
Looking at your code, I think the label is being updated. The issue actually is that you can't see the label at all. Looking at the init for ConcreteProperty
class ConcreteProperty(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ConcreteProperty, self).__init__(parent)
self.setFont(QtGui.QFont("Helvetica", 10, QtGui.QFont.Normal, italic=False))
self.concretestrength_lay = QtWidgets.QHBoxLayout()
fctd = "\nfcd\n\nfctd\n\nEc"
con_strength = QtWidgets.QLabel(fctd)
self.con_strength_value = QtWidgets.QLabel(" ")
self.concretestrength_lay.addWidget(con_strength)
self.concretestrength_lay.addWidget(self.con_strength_value, alignment=QtCore.Qt.AlignLeft)
The reason the changes are not appearing is that you create two ConcreteProperty objects, one in ConcreteStrengthInfo and one in ConcreteStrengthComboBox. Updates to the combo box trigger an update of the ConcreteProperty attached to the combobox, not the other one (they are separate objects). The visible ConcreteProperty is unaffected.
To make this work, you need to move the signal attachment + the slot out of the combo box object. The following is a replacement for the two parts —
class ConcreteStrengthComboBox(QtWidgets.QComboBox):
def __init__(self, parent = None):
super(ConcreteStrengthComboBox, self).__init__(parent)
self.addItems(["C12/15","C16/20","C20/25","C25/30","C30/37","C35/45","C40/50","C45/55",
"C50/60","C55/67","C60/75","C70/85","C80/95","C90/105"])
self.setFont(QtGui.QFont("Helvetica", 10, QtGui.QFont.Normal, italic=False))
self.compressive_strength = ["12","16","20","25","30","35","40","45","50","55",
"60","70","80","90"]
class ConcreteStrengthInFo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ConcreteStrengthInFo, self).__init__(parent)
hbox = QtWidgets.QHBoxLayout()
concrete_strength = QtWidgets.QLabel("Concrete strength: ")
hbox.addWidget(concrete_strength)
self.concreteproperty = ConcreteProperty()
self.concretestrengthbox = ConcreteStrengthComboBox()
hbox.addWidget(self.concretestrengthbox)
self.concretestrengthbox.activated.connect(self.setdatastrength)
self.vlay = QtWidgets.QVBoxLayout()
self.vlay.addLayout(hbox)
self.vlay.addLayout(self.concreteproperty.concretestrength_lay)
#QtCore.pyqtSlot(int)
def setdatastrength(self, index):
it = self.concretestrengthbox.compressive_strength[index]
print(it)
self.concreteproperty.display_condata(it)
This works for me locally.
On menu I can trigger:
def on_git_update(self):
update_widget = UpdateView()
self.gui.setCentralWidget(update_widget)
updateGit = UpdateGit()
updateGit.progress.connect(update_widget.on_progress)
updateGit.start()
then I have:
class UpdateView(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
vbox = QVBoxLayout()
self.pbar = QProgressBar()
vbox.addWidget(self.pbar)
vbox.addStretch(1)
self.setLayout(vbox)
def on_progress(self, value):
self.pbar.setValue(int(value * 100))
class UpdateGit(QThread):
progress = pyqtSignal(float)
def __del__(self):
self.wait()
def run(self):
for i in range(10):
self.progress.emit(i / 10)
sleep(.5)
The app freezes during the processing, afaik it should work as it is in a thread using signals.
Also, it works as expected with the app updating every step when I run it in debug mode via pycharm.
How is my thread set up incorrectly?
A variable created in a function only exists until the function exists, and this is what happens with updateGit, in the case of update_widget when it is set as centralwidget it has a greater scope since Qt handles it. The solution is to extend the scope of the thread by making it a member of the class.
def on_git_update(self):
update_widget = UpdateView()
self.gui.setCentralWidget(update_widget)
self.updateGit = UpdateGit()
self.updateGit.progress.connect(update_widget.on_progress)
self.updateGit.start()
I've built an User Interface using QtDesigner and then converted the .ui to .py. The User Interface has different comboBox and textBox from which I want to read the values once the Run button is clicked. Run a function and then populate other text boxes of the user interface once the calculations are completed. However when I change the value of the comboBox and click the button the script still reads the initial value.
I did a simple GUI with a comboBox with two items and a textBox. I'm trying to read the the comboBox text and based on the selected item set the text of the textBox.
Here is the code I'm using to run the GUI and read the value:
from PyQt4 import QtGui
from pyQt4 import QtCore
import sys
import GUI
class MyThread(QtCore.QThread):
updated = QtCore.pyqtSignal(str)
def run(self):
self.gui = Window()
name = self.gui.gui_Name.currentText()
print (name)
if name == 'Cristina':
country = 'Italy'
else:
country = 'Other'
self.updated.emit(str(1))
class Window(QtGui.QMainWindow, GUI.Home):
def __init__(self,parent = None):
super(Window,self).__init__(parent)
self.setupUi(self)
self._thread = MyThread(self)
self._thread.updated.connect(self.updateText)
self.update()
self.
self.pushButton.clicked.connect(self._thread.start)
def updateText(self,text):
self.Country.setText(str(country))
Any thoughts?
Thanks
If the code that you implement in the run is the one that I think you are abusing the threads since with the currentTextChanged signal it would be enough as I show below:
class Window(QtGui.QMainWindow, GUI.Home):
def __init__(self,parent = None):
super(Window,self).__init__(parent)
self.setupUi(self)
self.gui_Name.currentTextChanged.connect(self.onCurrentTextChanged)
def onCurrentTextChanged(self, text):
if if name == 'Cristina':
country = 'Italy'
else:
country = 'Other'
self.Country.setText(str(country))
On the other hand if the real code is a time-consuming task then the use of the threads is adequate. If the task takes as reference the value of the QComboBox at the moment of pressing the button then it establishes that value as property of the thread, in your case you are creating a new GUI in another thread instead of using the existing GUI:
class MyThread(QtCore.QThread):
updated = QtCore.pyqtSignal(str)
def run(self):
name = self.currentText
print(name)
if name == 'Cristina':
country = 'Italy'
else:
country = 'Other'
self.updated.emit(country)
class Window(QtGui.QMainWindow, GUI.Home):
def __init__(self,parent = None):
super(Window,self).__init__(parent)
self.setupUi(self)
self._thread = MyThread(self)
self._thread.updated.connect(self.Country.setText)
self.pushButton.clicked.connect(self.start_thread)
def start_thread(self):
self._thread.currentText = self.gui_Name.currentText()
self._thread.start()
I'm trying to update QLineEdit() with setText.
However, that is made through a signal thread class (A)
that is able to send every time signal to a widget class (B) [deputy to change the text].
let see an example:
classB(QWidget):
def __init__(self, parent = None):
super(classB, self).__init__(parent)
self.lineLayout = QGridLayout()
self.textbox = QLineEdit("")
self.lineLayout.addWidget(self.textbox, 0, 0)
self.setLayout(self.lineLayout)
def setInt(self,intVal):
# The shell displays this value perfectly
print(intVal)
# it does not display the change in QWidget that lives in MainWidget
self.textbox.setText("val: " + str(intVal))
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
While the class A's code:
classA(QThread):
sendVal = QtCore.pyqtSignal(int)
def __init__(self,serial):
QThread.__init__(self)
def do_stuff(self):
cont = 0
while True:
self.sendVal(cont)
cont += 1
So we connect the signal:
class MainWidget(QtWidgets.QWidget):
getClassA = classA()
getClassB = classB()
getClassA.sendVal.connect(getClassB.setInt)
I have observed the following behaviors:
The int signal is perfectly received within the [setInt] function of class B;
The real problem is that everything that happens inside the setInt function stays in setInt. I can not even change a hypothetical class variable (In def init of classB)