PYQT 4: How can i call a GUI Function in another class? - python

I will make 2 examples to explain it
I created a Button that when i click it, it shows a message in the TextEdit Widget (in this case it shows "A string").
If i do that in the same class of the GUI, i have no problems:
from PyQt4.QtGui import *
import sys
class Aplicacion(QWidget):
def __init__(self, parent=None):
super(Aplicacion, self).__init__()
vbox = QVBoxLayout(self)
self.textedit = QTextEdit('')
self.button = QPushButton("Do anything")
#Layouts
vbox.addWidget(self.textedit)
vbox.addWidget(self.button)
#Connections
self.button.clicked.connect(self.aFunction)
def aFunction(self):
self.textedit.append("A string")
app = QApplication(sys.argv)
test = Aplicacion()
test.show()
app.exec_()
It works fine: http://puu.sh/kpEHC.png
But when i am trying to do the same in another class or function i get this error:
from PyQt4.QtGui import *
import sys
def appendAnything(self):
Aplicacion().textedit.append("test") # HERE IS THE ERROR
class Aplicacion(QWidget):
def __init__(self, parent=None):
super(Aplicacion, self).__init__()
vbox = QVBoxLayout(self)
self.textedit = QTextEdit('')
self.button = QPushButton("Do anything")
#Layouts
vbox.addWidget(self.textedit)
vbox.addWidget(self.button)
#Connections
self.button.clicked.connect(appendAnything)
def aFunction(self):
self.textedit.append("A string")
app = QApplication(sys.argv)
test = Aplicacion()
test.show()
app.exec_()
Error:
Aplicacion().textedit.append("test") # HERE IS THE ERROR
RuntimeError: wrapped C/C++ object of type QTextEdit has been deleted
Image: http://puu.sh/kpETO.png
Thanks and sorry for my english

This happens because you are creating a new Aplicacion instances inside the function scope and it gets destroyed as soon as your code goes back to the main loop since you don't keep any reference of this new instance. Also I don't understand why you are creating a new Aplicacion instance: I think you should probably pass a reference of your main widget to the function. An easy (non thread safe) way to do that is to use partial:
from PyQt4.QtGui import *
from functools import partial
import sys
def appendAnything(self, app):
app.textedit.append("test")
class Aplicacion(QWidget):
def __init__(self, parent=None):
super(Aplicacion, self).__init__()
vbox = QVBoxLayout(self)
self.textedit = QTextEdit('')
self.button = QPushButton("Do anything")
#Layouts
vbox.addWidget(self.textedit)
vbox.addWidget(self.button)
#Connections
self.button.clicked.connect(partial(appendAnything, app=self))
def aFunction(self):
self.textedit.append("A string")
if __name__ == '__main__':
app = QApplication(sys.argv)
test = Aplicacion()
test.show()
app.exec_()

Related

Writing a custom QPushButton class in Python

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.

Unable to acces a "def" in a sub window - "MainWindow object has no attribute 'updateTime' "

I've got a MainWindow where I call a subwindow (pop-up) from a pushbutton and I'm unable to access def updateTime(self) and it gives me an attribute error:
AttributeError: 'MainWindow' object has no attribute 'updateTime'
If I take out the MainWindow section it works fine so I really don't understand what the problem is. Any help would be greatly appreciated.
from PyQt4 import QtGui, QtCore
from PyQt4 import *
from PyQt4.QtCore import *
import sys
CurrentTime = 0
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.central = QtGui.QWidget(self)
self.setCentralWidget(self.central)
self.hboxPB = QtGui.QHBoxLayout()
self.vboxPB = QtGui.QVBoxLayout()
self.MyButton = QtGui.QPushButton(self.central)
self.MyButton.setText("Push Me")
self.hboxPB.addWidget(self.MyButton)
self.vboxPB.addLayout(self.hboxPB)
self.MyButton.resize(90,90)
self.MyButton.clicked.connect(lambda: widgetWindow.start(self))
class widgetWindow(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self,parent)
super(widgetWindow, self).__init__()
widgetWindow.start(self)
def start(self):
window = QtGui.QMainWindow(self)
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
CentralWidget = QtGui.QWidget()
self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
CentralWidgetLayout = QtGui.QHBoxLayout()
VBox = QtGui.QVBoxLayout()
CentralWidgetLayout.addWidget(self.timeSlider)
VBox.addLayout(CentralWidgetLayout)
CentralWidget.setLayout(VBox)
window.setCentralWidget(CentralWidget)
window.show()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateTime)
self.timer.start(1000)
def updateTime(self):
global CurrentTime
CurrentTime = CurrentTime + 1
self.timeSlider.setValue(CurrentTime)
def main():
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()
win.resize(800,450)
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Your issue starts with the line
self.MyButton.clicked.connect(lambda: widgetWindow.start(self))
Let's de-construct what this is doing.
You have a button, self.MyButton which exists within an instance of the class MainWindow (you instantiate the class in the main() function)
You are connecting to the clicked signal of the button.
The function you connect to this signal is a lambda function which calls a function in the class widgetWindow. Note that this is distinct from calling a method of an instance of a class. You are not instantiating a class here (you are not creating an object). You are saying, use the method definition start in the class widgetWindow but make it act on the object self where self is an instance of the class MainWindow.
Hopefully you are now starting to see the problem with what you did. Rather than creating an instance of your widgetWindow class, you tried to use a method from widgetWindow as if it was a method of MainWindow. I would suggest reading more on object oriented programming in Python to get your head around this if you are having trouble still (especially if you are unclear as to the distinction between a class and an object)
The solution is thus to create an instance of widgetWindow (rather than directly accessing the methods of the class), and connect your button to a method of that instance. I've modified your code to do this, which is posted below. I've commented the sections I've changed. If you have questions about what I've done, let me know.
from PyQt4 import QtGui, QtCore
from PyQt4 import *
from PyQt4.QtCore import *
import sys
CurrentTime = 0
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.central = QtGui.QWidget(self)
self.setCentralWidget(self.central)
self.hboxPB = QtGui.QHBoxLayout()
self.vboxPB = QtGui.QVBoxLayout()
self.MyButton = QtGui.QPushButton(self.central)
self.MyButton.setText("Push Me")
self.hboxPB.addWidget(self.MyButton)
self.vboxPB.addLayout(self.hboxPB)
self.MyButton.resize(90,90)
# instantiate the widgetWindow (pass this window as the parent)
self.widgetwindow = widgetWindow(self)
# connect the button to the start method of this instance we created above
self.MyButton.clicked.connect(self.widgetwindow.start)
# no need to subclass QWidget here. This is just a wrapper class to hold your main window
class widgetWindow(object):
def __init__(self, parent = None):
# Store the parent for use later
self.parent = parent
def start(self):
# change the window parent to be the parent we stored in the __init__ method
window = QtGui.QMainWindow(self.parent)
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
# Add a signal to stop the timer when the window is destroyed
window.destroyed.connect(self.stopTimer)
CentralWidget = QtGui.QWidget()
# Change the parent to be the window we just created
self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal, window)
CentralWidgetLayout = QtGui.QHBoxLayout()
VBox = QtGui.QVBoxLayout()
CentralWidgetLayout.addWidget(self.timeSlider)
VBox.addLayout(CentralWidgetLayout)
CentralWidget.setLayout(VBox)
window.setCentralWidget(CentralWidget)
window.show()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateTime)
self.timer.start(1000)
def updateTime(self):
global CurrentTime
CurrentTime = CurrentTime + 1
self.timeSlider.setValue(CurrentTime)
# Method to stop the timer once the window is destroyed
def stopTimer(self):
self.timer.stop()
def main():
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()
win.resize(800,450)
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Try this:
from PyQt4 import QtGui, QtCore
from PyQt4 import *
from PyQt4.QtCore import *
import sys
CurrentTime = 0
class widgetWindow(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self,parent)
super(widgetWindow, self).__init__()
widgetWindow.start(self)
def start(self):
window = QtGui.QMainWindow(self)
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
CentralWidget = QtGui.QWidget()
self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
CentralWidgetLayout = QtGui.QHBoxLayout()
VBox = QtGui.QVBoxLayout()
CentralWidgetLayout.addWidget(self.timeSlider)
VBox.addLayout(CentralWidgetLayout)
CentralWidget.setLayout(VBox)
window.setCentralWidget(CentralWidget)
window.show()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateTime)
self.timer.start(1000)
def updateTime(self):
global CurrentTime
CurrentTime = CurrentTime + 1
self.timeSlider.setValue(CurrentTime)
class MainWindow(QtGui.QMainWindow,widgetWindow):#here add subclass
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.central = QtGui.QWidget(self)
self.setCentralWidget(self.central)
self.hboxPB = QtGui.QHBoxLayout()
self.vboxPB = QtGui.QVBoxLayout()
self.MyButton = QtGui.QPushButton(self.central)
self.MyButton.setText("Push Me")
self.hboxPB.addWidget(self.MyButton)
self.vboxPB.addLayout(self.hboxPB)
self.MyButton.resize(90,90)
self.MyButton.clicked.connect(lambda: widgetWindow.start(self))
def main():
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.show()
win.resize(800,450)
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You had the wrong namespace. You must make sure that you use the proper namespace or the interpreter does not know the proper place to look for the class.

Qt trough PySide: QTextEdit doesn't get focus on window creation if there is a button in the window

I'm writing some code using Python 3.4, Qt 4.8.6 trough PySide 1.2.1.
I'm trying to get a custom Text Editor to work by using a QTextEdit and some QPushButton's, problem is: if there are QPushButtons in the Editor, QTextEdit doesn't get focused at window initialization, even if a explicit call to QTextEdit.setFocus() is made. If the buttons aren't included, all Just Works (TM), here is some code:
Trouble Code
#!/usr/bin/env python3
import sys
from PySide.QtGui import *
from PySide.QtCore import *
app = QApplication(sys.argv)
class MyEditor(QHBoxLayout):
def __init__(self):
super(MyEditor, self).__init__()
self.add_buttons()
self.add_editor()
def add_buttons(self):
self.buttons_layout = QVBoxLayout()
self.addLayout(self.buttons_layout)
self.b1 = QPushButton('1')
self.b2 = QPushButton('2')
for b in (self.b1, self.b2):
self.buttons_layout.addWidget(b)
def add_editor(self):
self.editor = QTextEdit()
self.addWidget(self.editor)
self.editor.setFocus()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.editor = MyEditor()
self.setLayout(self.editor)
self.show()
window = Window()
app.exec_()
sys.exit()
But if I don't include the Buttons (by commenting out the call to self.add_buttons), the QTextEdit gets focused just fine:
#!/usr/bin/env python3
import sys
from PySide.QtGui import *
from PySide.QtCore import *
app = QApplication(sys.argv)
class MyEditor(QHBoxLayout):
def __init__(self):
super(MyEditor, self).__init__()
# self.add_buttons() <- Now focus works, but no buttons :(
self.add_editor()
def add_buttons(self):
self.buttons_layout = QVBoxLayout()
self.addLayout(self.buttons_layout)
self.b1 = QPushButton('1')
self.b2 = QPushButton('2')
for b in (self.b1, self.b2):
self.buttons_layout.addWidget(b)
def add_editor(self):
self.editor = QTextEdit()
self.addWidget(self.editor)
self.editor.setFocus()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.editor = MyEditor()
self.setLayout(self.editor)
self.show()
window = Window()
app.exec_()
sys.exit()
I deeply researched the PySide docs, google and etc. but no answer could be found, any ideas?
Thanks in advance.
PS.: Sorry about any mistakes in my English, I'm not a native of this language.
Delaying the call to setFocus on the QTextEdit to after setLayout on QWidget has finished solves the problem. So I guess that setLayout changes the focus and one has to manually reset it afterwards. It might have to do with the tab order (see first answer in Set QLineEdit focus in Qt).
Here is your example with buttons and focus on the editor widget:
from PySide.QtGui import *
class MyEditor(QHBoxLayout):
def __init__(self):
super(MyEditor, self).__init__()
self.add_buttons()
self.add_editor()
def add_buttons(self):
self.buttons_layout = QVBoxLayout()
self.addLayout(self.buttons_layout)
self.b1 = QPushButton('1')
self.b2 = QPushButton('2')
for b in (self.b1, self.b2):
self.buttons_layout.addWidget(b)
def add_editor(self):
self.editor = QTextEdit()
self.addWidget(self.editor)
def set_focus(self):
self.editor.setFocus()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.editor = MyEditor()
self.setLayout(self.editor)
self.editor.set_focus()
self.show()
app = QApplication([])
window = Window()
app.exec_()

Add different text to a window

I have a button and a text label. Each time the button is pressed, i would like text placed from a line edit to be placed onto the window. So far I can only get one text to draw onto the window, even if I create another textlabel. Ive tried seeting a click count determining how many times a user has clicked a button but this doesnt work either. Heres what I have so far, any suggestions?
import sys
import os
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtGui import QApplication
class Window(QMainWindow):
def __init__(self, *args):
QMainWindow.__init__(self, *args)
self.centralWidget = QWidget(self)
self.setCentralWidget(self.centralWidget)
self.setGeometry(450,100,350,680)
self.btn1 = QPushButton("Enter", self.centralWidget)
self.btn1.setGeometry(10,50,150, 20)
self.btn1.clicked.connect(self.enter)
self.edit = QtGui.QLineEdit(self)
self.edit.setGeometry(10, 10, 150, 20)
self.label = QtGui.QLabel(self)
self.label.setGeometry(240, 170,150, 20)
def enter(self):
self.label.setText(self.edit.text())
def main(args):
global app
app = App(args)
app.exec_()
class App(QApplication):
def __init__(self, *args):
QApplication.__init__(self, *args)
self.main = Window()
self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
if __name__ == "__main__":
main(sys.argv)
There are a few problems with your example code, the main one being that you are trying to manually arrange the widgets, rather than using a layout.
It's hard to tell from your question exactly what you expect the output to be, so I've assumed you want the text from line-edit to be appended to the label, so that you end up with a series of lines.
Here's a simplified version of your example that hopefully does what you want:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.btn1 = QtGui.QPushButton("Enter", self)
self.btn1.clicked.connect(self.enter)
self.edit = QtGui.QLineEdit(self)
self.label = QtGui.QLabel(self)
self.label.setAlignment(
QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
layout.addWidget(self.edit)
layout.addWidget(self.btn1)
layout.addWidget(self.label)
self.setCentralWidget(widget)
def enter(self):
text = self.edit.text()
if text:
self.label.setText('%s\n%s' % (self.label.text(), text))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(450, 100, 350, 680)
window.show()
sys.exit(app.exec_())

PySide (Qt) signal not reaching my slot

I have this simplified code that doesn't work and I can't understand why... I expect MySlot.slt() to be called every time i press a key in my QTextEdit but it doesn't! Could you please have a look?
#!/usr/bin/env python2
import sys
from PySide import QtGui, QtCore
class MySlot(object):
def __init__(self, qte):
qte.textChanged.connect(self.soc)
def slt(self):
print("got signal")
class MainWid(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWid, self).__init__(parent)
self.initgui()
def initgui(self):
lay = QtGui.QVBoxLayout()
txt = QtGui.QTextEdit(self)
MySoc(txt)
lay.addWidget(txt)
self.setLayout(lay)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
wid = MainWid()
sys.exit(app.exec_())
if __name__=="__main__":
main()
Your MySoc object in initgui has local scope and is therefore destroyed at the end of initgui.
Assign the object to a variable:
...
self.soc = MySoc(txt);
...
and you will see the "got signal" output each time you press a key.

Categories

Resources