Execute a function after closing a window (not a child window)? - python

I have in my application a button which after being clicked opens up another window (which is a separate python file).
I want to execute a function after this second window has been closed. Is there a way I can capture that window's 'closed' signal or something like that?
Here is my code: (the main window)
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton
import second_dialog # the second window I am importing
from sys import exit as sysExit
class Marker(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(350, 250)
self.openbtn = QPushButton('open')
self.openbtn.clicked.connect(self.open_dialog)
self.label_1 = QtWidgets.QLabel()
self.label_1.setGeometry(QtCore.QRect(10, 20, 150, 31))
self.label_1.setObjectName("label_1")
self.label_1.setText("HEy")
HBox = QHBoxLayout()
HBox.addWidget(self.openbtn)
HBox.addWidget(self.label_1)
HBox.addStretch(1)
VBox = QVBoxLayout()
VBox.addLayout(HBox)
VBox.addStretch(1)
self.setLayout(VBox)
def open_dialog(self):
self.dialog = QtWidgets.QWidget()
self.box = second_dialog.Marker()
self.box.show()
def do_something(self):
self.label_1.setText("Closed")
def paintEvent(self, event):
p = QPainter(self)
p.fillRect(self.rect(), QColor(128, 128, 128, 128))
if __name__ == "__main__":
MainEventThred = QApplication([])
MainApp = Marker()
MainApp.show()
MainEventThred.exec()
Here is the code for the second window:
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton
import first_window
from sys import exit as sysExit
class Marker(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(350, 250)
self.openbtn = QPushButton('close')
self.openbtn.clicked.connect(self.Close)
HBox = QHBoxLayout()
HBox.addWidget(self.openbtn)
HBox.addStretch(1)
VBox = QVBoxLayout()
VBox.addLayout(HBox)
VBox.addStretch(1)
self.setLayout(VBox)
def Close(self):
TEST_draggable.Marker.do_something(first_window.Marker)
self.close()
def paintEvent(self, event):
p = QPainter(self)
p.fillRect(self.rect(), QColor(128, 128, 128, 128))
if __name__ == "__main__":
MainEventThred = QApplication([])
MainApp = Marker()
MainApp.show()
MainEventThred.exec()
The code does not run and throws this error:
Traceback (most recent call last):
File "some/path/second_dialog.py", line 28, in Close
TEST_draggable.Marker.do_something(first_window.Marker)
File "some/path/first_window.py", line 39, in do_something
self.label_1.setText("clicked")
AttributeError: type object 'Marker' has no attribute 'label_1'
[1] 7552 abort (core dumped) /usr/local/bin/python3
How can I fix this? I've looked up a lot of forums suspecting circular imports as the culprit but I'm not sure. Please help.

The problem has nothing to do with imports, but with the fact that you're trying to run an instance method on a class.
The "culprit" is here:
TEST_draggable.Marker.do_something(first_window.Marker)
first_window.Marker will be used for the self argument in do_something, but since it is the class, it has no label_1 attribute (only the instances of that class have that attribute). I suggest you to do some research on how classes and instances work in general, and how to deal with them in Python.
The easiest solution is to create your own signal for the second window and connect that signal on the function that will "do_something". Instead of connecting to a new function, then, we subclass the closeEvent() and send the signal from there, so that we can capture the close even when the user clicks the dedicated button on the title bar. Note that if you want to change the behavior of close you can just override it and then call the base implementation (super().close()) instead of using another function name (which should not have a capitalized name, by the way, as they should only be used for classes and constants).
This is the second class; be aware that using identical names for different classes is a very bad idea.
from PyQt5.QtCore import pyqtSignal
class Marker(QWidget):
closed = pyqtSignal()
def __init__(self):
QWidget.__init__(self)
self.resize(350, 250)
self.openbtn = QPushButton('close')
self.openbtn.clicked.connect(self.close)
# ...
def closeEvent(self, event):
self.closed.emit()
Then, in the first:
class Marker(QWidget):
# ...
def open_dialog(self):
self.dialog = QtWidgets.QWidget()
self.box = second_dialog.Marker()
self.box.closed.connect(self.do_something)
self.box.show()

Related

A blank window after overriding PyQt5.QWidget.__init__()

I am learning to write PyQt5 codes.When I tried to code a subclass extented from PyQt5.QWidget and to override its constructor function, I found that the new code didn't work.(A blank window without anything you coded would show up.)It seems that the program stopped after "super().init()".Even I think the constructor don't run.But when I put the lines except "super()._init()" in a overridden function "PyQt5.Qwidegt.show()", it runs well.I am wondering why.It is quite pluzzing for a green hand.
The codes are as followed:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout,
QPushButton
class VBoxLayoutWindow(QWidget):
def __int__(self):
super().__init__()
self.resize(300, 300)
self.setWindowTitle("VBox Layout")
layout = QVBoxLayout()
button1 = QPushButton("Button1")
button2 = QPushButton("Button2")
button3 = QPushButton("Button3")
layout.addWidget(button1)
layout.addWidget(button2)
layout.addWidget(button3)
layout.addStretch(2)
self.setLayout(layout)
app = QApplication(sys.argv)
w = VBoxLayoutWindow()
w.show()
app.exec_()

PyQt5-How can i send inputs received from the user via the UI to the function [duplicate]

hello world i am trying to get a QLineEdit to work as a user Input witch they are suppose to type in a song name. after the song name is entered i am wanting that song to start playing after the click the play button everything is working fine other then the part where they can type in what ever song they want in that folder. the problem is im not sure on how to make the QlineEdit word and update everytime someone thing is entered into the text box here is my code hopefully someone can help me out Thanks in advance!
import sys
import webbrowser
import random
import time
import os
import subprocess
from PyQt4.QtCore import QSize, QTimer, SIGNAL
from PyQt4.QtGui import QApplication,QScrollBar,QLineEdit , QDialog , QFormLayout ,QGraphicsRectItem , QMainWindow, QPushButton, QWidget, QIcon, QLabel, QPainter, QPixmap, QMessageBox, QAction, QKeySequence, QFont, QFontMetrics, QMovie
from PyQt4 import QtGui
import vlc
#----|Imports End|----#
class UIWindow(QWidget):
def __init__(self, parent=None):
super(UIWindow, self).__init__(parent)
self.resize(QSize(400, 450))
self.Play = QPushButton('Play', self)
self.Play.resize(100,40)
self.Play.move(45, 100)#
self.Pause = QPushButton('Pause', self)
self.Pause.resize(100,40)
self.Pause.move(260, 100)#
self.Tbox = QLineEdit('Song name',self)
self.Tbox.resize(400,25)
self.Tbox.move(0,50)
self.Play.clicked.connect(self.PlayB)
self.Pause.clicked.connect(self.PauseB)
self.Flask = vlc.MediaPlayer("C:\Users\Matt\Music\\"+str(self.Tbox.text())+".mp3")
def PlayB(self):
self.Flask.play()
def PauseB(self):
self.Flask.stop()
class MainWindow(QMainWindow,):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setGeometry(745 ,350 , 400, 450)
self.setFixedSize(400, 450)
self.startUIWindow()
def startUIWindow(self):
self.Window = UIWindow(self)
self.setWindowTitle("HELP ME!")
self.setCentralWidget(self.Window)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
sys.exit(app.exec_())
You can easily get text with QLineEdit.text() method.
Or same way set text with QLineEdit.setText() method
If you want to connect it to QTextEdit You can connect it with .textChanged signal which is emited from QTextEdit everytime text changes.
The same way how you use .clicked signal you can use this one as:
QTextEdit.textChanged.connect(your_method_to_put_text_somewhere_else)

PyQt5 has error 'MainWindow' has no attribute 'exec_'

I'm new to using PyQT for a class project and I'm having some issues with it.
I want to launch MainWindow and to have 2 buttons to launch 2 separate dialog boxes (Dialog and subdialog); however I am getting the following error:
sys.exit(mainwindowa.exec_())
AttributeError: 'MainWindow' object has no attribute 'exec_'
Here is my code. I've tried playing around with how I write the exec_ function like writing it in the MainWindow class but I get the same errors and I'm pretty confuse on what to do
from PyQt5.QtWidgets import (QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
QVBoxLayout, QMainWindow)
import sys
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("My App")
layout = QVBoxLayout()
self.mainwindowapp = QPushButton('Dialog')
self.subwindowapp = QPushButton('CustomDialog')
layout.addWidget(self.mainwindowapp)
layout.addWidget(self.subwindowapp)
self.setLayout(layout)
self.show()
self.mainwindowapp.clicked.connect(self.button_clicked)
def button_clicked(self):
run = Dialog()
run()
class Dialog(QDialog):
def __init__(self):
super(Dialog, self).__init__()
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.formGroupBox)
mainLayout.addWidget(buttonBox)
self.setLayout(mainLayout)
class CustomDialog(QDialog):
def __init__(self):
super().__init__(CustomDialog, self)
self.layout = QVBoxLayout()
self.layout.addWidget(QLabel('e'))
self.setLayout(self.layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwindowa = MainWindow()
sys.exit(mainwindowa.exec_())
You are drawing an incorrect conclusion, you think that if the QDialog class has an exec_() method, so will other widgets like QMainWindow and that is not correct. QDialog creates an internal eventloop that other widgets do not, so in your case use the show() method and use the exec_() method of the QApplication:
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwindowa = MainWindow()
mainwindowa.show()
sys.exit(app.exec_())

How to keep PyQt5 responsive when calling gnuplot?

I am trying to create plots with a Python GUI and gnuplot.
I am generating the code in Python and send it to gnuplot.
This basically works with piping data to gnuplot, but:
Disadvantages:
the Python program is blocked until you close gnuplot
you have to load/start gnuplot again and again everytime you're making a plot which seems to take annoying extra time (on slow computers)
My questions:
how to keep the Python program responsive?
is there a way to start gnuplot once and keep it running?
how to just update the gnuplot terminal if there is a new plot?
Thank you for hints and links.
Here is my code:
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPlainTextEdit, QPushButton
import subprocess
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,400,200)
self.myTextEdit = QPlainTextEdit()
self.myTextEdit.setPlainText("plot sin(x)")
self.button = QPushButton('Plot code',self)
self.button.clicked.connect(self.on_button_click)
vbox = QVBoxLayout(self)
vbox.addWidget(self.myTextEdit)
vbox.addWidget(self.button)
self.setLayout(vbox)
#pyqtSlot()
def on_button_click(self):
gnuplot_str = self.myTextEdit.document().toPlainText() + "\n"
gnuplot_path = r'C:\Programs\gnuplot\bin\gnuplot.exe'
plot = subprocess.Popen([gnuplot_path,'-p'],stdin=subprocess.PIPE)
plot.communicate(gnuplot_str.encode())
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Instead of using subprocess you must use QProcess which is friendly to the Qt event loop as I show below:
import sys
from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPlainTextEdit, QPushButton
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,400,200)
self.myTextEdit = QPlainTextEdit()
self.myTextEdit.setPlainText("plot sin(x)")
self.button = QPushButton('Plot code',self)
self.button.clicked.connect(self.on_button_click)
vbox = QVBoxLayout(self)
vbox.addWidget(self.myTextEdit)
vbox.addWidget(self.button)
gnuplot_path = r'C:\Programs\gnuplot\bin\gnuplot.exe'
self.process = QProcess(self)
self.process.start(gnuplot_path, ["-p"])
#pyqtSlot()
def on_button_click(self):
gnuplot_str = self.myTextEdit.document().toPlainText() + "\n"
self.process.write(gnuplot_str.encode())
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())

pyqt5 "AttributeError: type object 'Window' has no attribute label" but it does?

I am trying to create a UI for a text game.
This works fine, however I wanted to create my own simple print function to print to the parts of the UI (in the example the QLabel) this works, if the function is in the UI file but when I move the functions to another file, I get
"AttributeError: type object 'Window' has no attribute 'label'"
even though my IDE says Window.label exists before running it.
Is this some quirk or QT? or am I making a mistake?
UI.py
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Window(QWidget):
def __init__(self):
super().__init__()
self.show()
import game
self.label = QLabel("Text")
self.label.setAlignment(Qt.AlignCenter | Qt.AlignTop)
self.output = QTextEdit()
self.output.setReadOnly(True)
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(self.label,0,0,1,10)
grid.addWidget(self.output,1,0,10,10)
self.setLayout(grid)
game.Game.test()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Window()
sys.exit(app.exec_())
Game.py
from UI import Window
class Game():
def print_UI(self,*Args, **Kwargs):
Window.setup.output.insertPlainText(*Args, **Kwargs)
def print_label(self,*Args, **Kwargs):
Window.label.setText(*Args, **Kwargs)
def test():
Game.print_label("HI")
You can pass window object as a parameter to the constructor of Game instead of importing game in Window class and importing Window again in game as that was leaving the label member uninitialized
UI.py
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import game
class Window(QWidget):
def __init__(self):
super().__init__()
self.show()
self.label = QLabel("Text")
self.label.setAlignment(Qt.AlignCenter | Qt.AlignTop)
self.output = QTextEdit()
self.output.setReadOnly(True)
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(self.label,0,0,1,10)
grid.addWidget(self.output,1,0,10,10)
self.setLayout(grid)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Window()
game.Game(ex).test()
sys.exit(app.exec_())
game.py
class Game():
def __init__(self,window):
self.Window = window
def print_UI(self,*Args, **Kwargs):
self.Window.setup.output.insertPlainText(*Args, **Kwargs)
def print_label(self,*Args, **Kwargs):
self.Window.label.setText(*Args, **Kwargs)
def test(self):
self.print_label("HI")
I believe the issue is that you're trying to do object-oriented programming, but not actually doing object-oriented programming. For instance, you're trying to use your Window class within Game.py's Game class by just importing it and calling its functions. Instead, what you've got to do is use inheritance. Namely, your Game.py's Game class should inherit from your UI.py's Window class.
Change your Game.py to the following:
from UI import Window
class Game(Window): # Game is inheriting from Window
def __init__(self):
Window.__init__(self) # Game is inheriting Window class attributes & functions
def print_UI(self,*Args, **Kwargs):
self.setup.output.insertPlainText(*Args, **Kwargs) # self refers to Game as well as Window class
def print_label(self,*Args, **Kwargs):
self.label.setText(*Args, **Kwargs) # self refers to Game as well as Window class
def test():
Game.print_label("HI")
Instead of using Window.__init__(self), you can check out an explanation of the super() function.

Categories