PYQT5 crashing when calling .text() on a QLineEdit widget - python

When I run the code below my program crashes, and I believe this is to-do with the .text() called when the Line edit has something typed in it. I need to assign a variable to what is entered here.
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtWidgets
class loginScreen(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
usernameBox = QtWidgets.QLineEdit()
usernameBox.textChanged.connect(self.myfunc)
vArrangement = QtWidgets.QVBoxLayout()
vArrangement.addWidget(usernameBox)
self.setLayout(vArrangement)
self.show()
def myfunc(self):
x = usernameBox.text()
print(x)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = loginScreen()
sys.exit(app.exec_())

If you observe usernameBox it is created as a local variable so it can not be accessed by the other methods of the class, in your case there are 2 solutions:
Make a usernameBox attribute of the class.
class loginScreen(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.usernameBox = QtWidgets.QLineEdit()
self.usernameBox.textChanged.connect(self.myfunc)
vArrangement = QtWidgets.QVBoxLayout()
vArrangement.addWidget(self.usernameBox)
self.setLayout(vArrangement)
self.show()
def myfunc(self):
x = self.usernameBox.text()
print(x)
Or use sender() that obtains the object that emits the signal, in your case the QLineEdit.
class loginScreen(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
usernameBox = QtWidgets.QLineEdit()
usernameBox.textChanged.connect(self.myfunc)
vArrangement = QtWidgets.QVBoxLayout()
vArrangement.addWidget(usernameBox)
self.setLayout(vArrangement)
self.show()
def myfunc(self):
x = self.sender().text()
print(x)

Related

PyQt5 passing argument between two classes: lambda vs partial

I am trying to pass an argument between two PyQt5 classes. I used three methods:
Using lambda functions.
Wrapper function (similar to lambda function).
partial from functools module.
In the example below, I have two windows:
MainWindow has QLineEdit mw_line_edit and a QPushButton mw_open_new_dialog_button.
Dialog: has a QLineEdit line_edit and aQPushButton push_button.
When I click the button push_button, I want it to insert the content of line_edit to mw_line_edit.
Here is a minimal example:
import sys
from functools import partial
from PyQt5 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.central_widget = QtWidgets.QWidget(self)
self.setCentralWidget(self.central_widget)
self.mw_open_new_dialog_button = QtWidgets.QPushButton('Open New dialog', self)
self.mw_line_edit = QtWidgets.QLineEdit(self)
self.hlayout = QtWidgets.QHBoxLayout(self)
self.hlayout.addWidget(self.mw_open_new_dialog_button)
self.hlayout.addWidget(self.mw_line_edit)
self.central_widget.setLayout(self.hlayout)
self.mw_open_new_dialog_button.clicked.connect(self.open_new_dialog)
def open_new_dialog(self):
self.dlg = Dialog()
#self.dlg.clicked.connect(partial(self.write_something, self.dlg.line_edit.text())) # <<<<<<< This does not work
self.dlg.clicked.connect(lambda: self.write_something(self.dlg.line_edit.text())) # this works
#self.dlg.clicked.connect(self.wrapper(self.dlg.line_edit.text()))# <<<<<<<<<<This does not work
self.dlg.exec()
#QtCore.pyqtSlot()
def write_something(self, text):
self.mw_line_edit.setText(text)
def wrapper(self, text):
return lambda: self.write_something(text)
class Dialog(QtWidgets.QDialog):
clicked = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(QtWidgets.QDialog, self).__init__(parent)
self.hlayout = QtWidgets.QHBoxLayout(self)
self.line_edit = QtWidgets.QLineEdit(self)
self.push_button = QtWidgets.QPushButton('Click me', self)
self.hlayout.addWidget(self.line_edit)
self.hlayout.addWidget(self.push_button)
self.label = QtWidgets.QLabel('I am a Qlabel', self)
self.hlayout.addWidget(self.label)
self.setLayout(self.hlayout)
self.push_button.clicked.connect(self.clicked)
def write_something(self, text):
print(text)
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())
As you can see in the commented lines, only the following method works:
self.dlg.clicked.connect(lambda: self.write_something(self.dlg.line_edit.text()))
Why the other two do not work, i.e:
self.dlg.clicked.connect(partial(self.write_something, self.dlg.line_edit.text())) # <<<<<<< This does not work
self.dlg.clicked.connect(self.wrapper(self.dlg.line_edit.text()))# <<<<<<<<<<This does not work
Thanks
1) functools.partial()
What arguments are you passing to partial? You are passing the method write_something and the text of self.dlg.line_edit at the time the connection is made.
And what is the value of that text? it is an empty string, this explains the failure.
Is there any solution for this case? Yes, instead of passing the text, pass the QLineEdit, and in the method write_something get the text and set it in the other QLineEdit:
def open_new_dialog(self):
self.dlg = Dialog()
self.dlg.clicked.connect(partial(self.write_something, self.dlg.line_edit))
self.dlg.exec()
def write_something(self, le):
self.mw_line_edit.setText(le.text())
2) wrapper
It is the same problem, you are passing the empty text that you have at the moment of the connection
Is there any solution? Yes, the same solution as the previous one.
def open_new_dialog(self):
self.dlg = Dialog()
self.dlg.clicked.connect(self.wrapper(self.dlg.line_edit))
self.dlg.exec()
def write_something(self, text):
self.mw_line_edit.setText(text)
def wrapper(self, line):
return lambda: self.write_something(line.text())
Will there be a clean solution? Yes, create a signal that transports the text when you click.
from PyQt5 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
self.mw_open_new_dialog_button = QtWidgets.QPushButton('Open New dialog')
self.mw_line_edit = QtWidgets.QLineEdit()
hlayout = QtWidgets.QHBoxLayout(central_widget)
hlayout.addWidget(self.mw_open_new_dialog_button)
hlayout.addWidget(self.mw_line_edit)
self.mw_open_new_dialog_button.clicked.connect(self.open_new_dialog)
#QtCore.pyqtSlot()
def open_new_dialog(self):
self.dlg = Dialog()
self.dlg.textSignal.connect(self.mw_line_edit.setText)
self.dlg.exec()
class Dialog(QtWidgets.QDialog):
textSignal = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(QtWidgets.QDialog, self).__init__(parent)
hlayout = QtWidgets.QHBoxLayout(self)
self.line_edit = QtWidgets.QLineEdit()
self.push_button = QtWidgets.QPushButton('Click me')
hlayout.addWidget(self.line_edit)
hlayout.addWidget(self.push_button)
self.label = QtWidgets.QLabel('I am a Qlabel')
hlayout.addWidget(self.label)
self.push_button.clicked.connect(self.sendText)
#QtCore.pyqtSlot()
def sendText(self):
self.textSignal.emit(self.line_edit.text())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec())

PyQt5, QThread: How to keep GUI responsive while running non-loop function in thread? [duplicate]

I want to access the parent class widgets in the QThread class
This line gives hangs GUI "Example().setWindowTitle("Window")"
How can I do that?
class Example(QWidget):
def __init__(self):
super().__init__()
self.myclass2 = myclass2()
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
self.show()
class myclass2(QThread):
def __init__(self, parent=None):
super(myclass2, self).__init__(parent)
def run(self):
while True:
time.sleep(.1)
print(" in thread \n")
Example().setWindowTitle("Window")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
You must understand the following expression:
Example().setWindowTitle("Window")
it is equivalent to:
obj = Example()
obj.setWindowTitle("Window")
That is, you are creating another Example object other than ex = Example(), and that object is creating another myclass2 object, and that other myclass2 object is creating another Example, and an infinite loop is clearly being created.
Another thing that in the future could cause you problems is to establish the same name for different things, although in this case it is not a problem but in future occasions it could bring you problems, the code to which I refer is:
self.myclass2 = myclass2()
For example, it is recommended that classes should start with capital letters.
Another error that is valid only in Qt is that the GUI can not be created or manipulated in a thread other than the main thread. So you can not change the title directly in the other thread but there are 2 methods:
1. QMetaObject::invokeMethod(...)
But for this we must pass the GUI through a property:
class Example(QWidget):
def __init__(self):
super().__init__()
self.myclass2 = MyClass()
self.myclass2.gui = self
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
self.show()
class MyClass(QThread):
def run(self):
while True:
time.sleep(.1)
print(" in thread \n")
QMetaObject.invokeMethod(self.gui, "setWindowTitle", Qt.QueuedConnection, Q_ARG(str, "Window"))
2. signals & slots
class Example(QWidget):
def __init__(self):
super().__init__()
self.myclass2 = MyClass()
self.myclass2.titleChanged.connect(self.setWindowTitle)
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QIcon('web.png'))
self.show()
class MyClass(QThread):
titleChanged = pyqtSignal(str)
def run(self):
while True:
time.sleep(.1)
print(" in thread \n")
self.titleChanged.emit("Window")
PLUS:
You should not modify the GUI from the thread directly but send the data through a signal:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
lay = QtWidgets.QVBoxLayout(self)
le = QtWidgets.QLineEdit()
lay.addWidget(le)
self.myclass2 = MyClass()
self.myclass2.titleChanged.connect(self.setWindowTitle)
self.myclass2.infoChanged.connect(le.setText) # <-- connect signal
self.myclass2.start()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Icon')
self.setWindowIcon(QtGui.QIcon('web.png'))
self.show()
class MyClass(QtCore.QThread):
titleChanged = QtCore.pyqtSignal(str)
infoChanged = QtCore.pyqtSignal(str) # <-- create signal
def run(self):
counter = 0
while True:
QtCore.QThread.msleep(100)
print(" in thread \n")
self.titleChanged.emit("Window")
self.infoChanged.emit("{}".format(counter)) # <-- emit signal
counter += 1
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

Deleting widgets from pyqt

class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.tabs()
def home(self):
df = QtGui.QPushButton('hello', self)
df.show()
def series(self):
df = QtGui.QCheckBox('hello', self)
df.show()
def tabs(self):
btn_home = QtGui.QPushButton(QtGui.QIcon('home.png'), 'Home', self)
btn_home.clicked.connect(self.home)
btn_series = QtGui.QPushButton(QtGui.QIcon('series.png'),'Series', self)
btn_series.clicked.connect(self.series)
self.show()
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
if __name__ == '__main__': run()
I wanted to delete the widgets shown from home module when i click series button and delete widgets from series module when i click home button.
So far whats happening is when i click series button he previous widgets from home module are still there.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.widget =QWidget()
self.layout = QHBoxLayout()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
self.tabs()
def home(self):
self.clear()
self.df1 = QPushButton('hello')
self.layout.addWidget(self.df1)
def series(self):
self.clear()
self.df2 = QCheckBox('hello')
self.layout.addWidget(self.df2)
def tabs(self):
self.btn_home = QPushButton(QIcon('home.png'), 'Home')
self.btn_home.clicked.connect(self.home)
self.layout.addWidget(self.btn_home)
self.btn_series = QPushButton(QIcon('series.png'),'Series')
self.btn_series.clicked.connect(self.series)
self.layout.addWidget(self.btn_series)
self.show()
def clear(self):
item = self.layout.itemAt(2)
if item != None :
widget = item.widget()
if widget != None:
self.layout.removeWidget(widget)
widget.deleteLater()
def run():
app = QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
if __name__ == '__main__': run()
My version is
self.main_canvas.children().remove(cogmapui)
cogmapui.deleteLater()
I checked it by putting a print("Deleted") in the cogmapui's __del__ function and, yes, it gets called.

How do i open a class from within another class by selecting an item on ToolBar?? Python/PyQt

I am trying to run class AddTQuestions from a def in class AddTest but it wont work!! It opens the window AddTQuestions for a split-second then closes it straight away?!
The code is shown here:
import sys
from PyQt4 import QtCore, QtGui
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
RunClassAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
RunClassAction.triggered.connect(self.run)
self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(RunClassAction)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Why Wont this Woooorkkkkk')
self.show()
def run(self):
AddQuestion = AddTQuestions()
AddQuestion.show()
class AddTQuestions(QtGui.QMainWindow):
def __init__(self, parent=None):
super(AddTQuestions, self).__init__(parent)
self.welldone = QtGui.QLabel('WellDone')
self.button = QtGui.QPushButton('Press Me')
layout = QtGui.QVBoxLayout()
layout.addWidget(self.welldone)
layout.addWidget(self.button)
self.setLayout(layout)
print("hello")
if __name__ == '__main__':
app = QtGui.QApplication([])
window = Example()
window.show()
app.exec_()
The object get's garbage collected, since you don't hold any reference to it when the function ends.
add them as class variables like this and the window stays open.
self.AddQuestion = AddTQuestions()
self.AddQuestion.show()

How to set focus on a widget at app startup?

I'm trying to set the focus on QLineEdit widget at app startup but for some reasons it fails. Calling the method which includes the QLineEdit_object.setFocus() and is bound to a button click, works perfectly. However on startup, it seems like it doesn't execute at all when set to initialize after widget creation.
Using PySide with Python.
# coding=utf-8
import sys
import PySide.QtGui as QG
import PySide.QtCore as QC
class GG(QG.QMainWindow):
def __init__(self):
super(GG, self).__init__()
self.move(0,0)
self.resize(400,300)
self.setWindowTitle('Demo')
self.tabw = QG.QTabWidget()
self.tab1 = Tab1()
self.tab2 = Tab2()
self.tabw.addTab(self.tab1, 'Tab1')
self.tabw.addTab(self.tab2, 'Tab2')
hbox = QG.QHBoxLayout()
hbox.addWidget(self.tabw)
self.setCentralWidget(self.tabw)
self.setLayout(hbox)
self.show()
class Tab1(QG.QWidget):
def __init__(self):
super(Tab1, self).__init__()
self.btns()
self.inputt()
self.layoutz()
self.inp.setFocus() # doesn't set the focus on startup ?
self.show()
def inputt(self):
self.inp = QG.QLineEdit('', self)
def btns(self):
self.btn1 = QG.QPushButton('Button1', self)
self.btn1.clicked.connect(self.focusit) # works just fine
def layoutz(self):
vbox = QG.QVBoxLayout()
vbox.addWidget(self.btn1)
vbox.addStretch(1)
vbox.addWidget(self.inp)
self.setLayout(vbox)
def focusit(self):
self.inp.setFocus() # works just fine
class Tab2(Tab1):
def __init__(self):
super(Tab2, self).__init__()
def main():
app = QG.QApplication(sys.argv)
a = GG()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Well, after some playing I came up with this solution:
import sys
import PySide.QtGui as QG
import PySide.QtCore as QC
class GG(QG.QMainWindow):
def __init__(self):
super(GG, self).__init__()
self.move(0,0)
self.resize(400,300)
self.setWindowTitle('Demo')
self.tabw = QG.QTabWidget()
self.tab1 = Tab1()
self.tab2 = Tab2()
self.tabw.addTab(self.tab1, 'Tab1')
self.tabw.addTab(self.tab2, 'Tab2')
hbox = QG.QHBoxLayout()
hbox.addWidget(self.tabw)
self.setCentralWidget(self.tabw)
self.setLayout(hbox)
self.tab2.inp.setFocus() # setting focus right here
self.tab1.inp.setFocus() # and here; notice the order
self.show()
class Tab1(QG.QWidget):
def __init__(self):
super(Tab1, self).__init__()
self.btns()
self.inputt()
self.layoutz()
self.show()
def inputt(self):
self.inp = QG.QLineEdit('', self)
def btns(self):
self.btn1 = QG.QPushButton('Button1', self)
self.btn1.clicked.connect(self.focusit)
def layoutz(self):
vbox = QG.QVBoxLayout()
vbox.addWidget(self.btn1)
vbox.addStretch(1)
vbox.addWidget(self.inp)
self.setLayout(vbox)
def focusit(self):
self.inp.setFocus()
class Tab2(Tab1):
def __init__(self):
super(Tab2, self).__init__()
def main():
app = QG.QApplication(sys.argv)
a = GG()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories