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_())
Related
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()
This is entire my code:
import sys
from PySide2.QtCore import Qt
from PySide2.QtWidgets import (
QApplication,
QHBoxLayout,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
v = QVBoxLayout()
h = QHBoxLayout()
for a in range(10):
button = QPushButton(str(a))
button.clicked.connect(lambda checked, a=a: self.button_clicked(a)) # error here
h.addWidget(button)
v.addLayout(h)
self.label = QLabel("")
v.addWidget(self.label)
w = QWidget()
w.setLayout(v)
self.setCentralWidget(w)
def button_clicked(self, n):
self.label.setText(str(n))
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
When I run this code, I get a window like this:
Below the buttons, there is a QLabel, and I want when I click on any button, the button's label will refer to this QLabel, but I get a bunch of confusing errors in the terminal. What's wrong with my code, help me, thanks.
The clicked signal is overload so it accepts 2 signatures where it can send a bool or not. The default signature depends on the library, in this case it seems that PySide2 by default does not send the "checked" parameter, unlike PyQt5 that does.
The solution is to indicate the signature:
button.clicked[bool].connect(lambda checked, a=a: self.button_clicked(a))
I have a simple pyside2 application which looks kinda like this:
import sys
from PySide2.QtWidgets import QApplication, QDialog, QPushButton, QFileDialog, QVBoxLayout
from PySide2 import QtGui
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.setWindowTitle("My Form")
self.setWindowIcon(QtGui.QIcon("myicon.png"))
layout = QVBoxLayout()
self.button = QPushButton("Open dialog")
self.button.clicked.connect(self.browse)
layout.addWidget(self.button)
self.setLayout(layout)
def browse(self):
qfd = QFileDialog()
qfd.setWindowIcon(QtGui.QIcon("myicon.png"))
filename, _ = qfd.getOpenFileName(None, "Load Data", ".", "*.txt")
if __name__ == '__main__':
# Create the Qt Application
app = QApplication(sys.argv)
# Create and show the form
form = Form()
form.show()
# Run the main Qt loop
sys.exit(app.exec_())
I want to setup the same icon for the QFileDialog as the main window icon but for some reason it does not work. Is there a way to set it like I'm trying above? Thanks for ideas, pointers and help in advance! (I'm using Ubuntu 20.04)
The getOpenFileName method is a static method that creates an internal QFileDialog other than "qfd" so the icon is not applied. One possible option is not to use getOpenFileName but to create the logic only using the QFileDialog class, and another solution is to access the QFileDialog object created inside getOpenFileName using the characteristic that is a toplevel:
import sys
from PySide2 import QtCore, QtGui, QtWidgets
class Form(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.setWindowTitle("My Form")
self.setWindowIcon(QtGui.QIcon("myicon.png"))
layout = QtWidgets.QVBoxLayout(self)
self.button = QtWidgets.QPushButton("Open dialog")
self.button.clicked.connect(self.browse)
layout.addWidget(self.button)
def browse(self):
QtCore.QTimer.singleShot(0, self.handle_timeout)
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
None,
"Load Data",
".",
"*.txt",
options=QtWidgets.QFileDialog.DontUseNativeDialog,
)
def handle_timeout(self):
for w in QtWidgets.QApplication.topLevelWidgets():
if isinstance(w, QtWidgets.QFileDialog):
w.setWindowIcon(QtGui.QIcon("myicon.png"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
I want to write a notepad in python with PyQt5 for persian language.
how can I align the thext in QPlainTextEdit to right?
this is my code:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPlainTextEdit
from PyQt5.QtCore import Qt, QLocale
class TextBox(QPlainTextEdit):
def persian(self):
self.setFixedSize(640, 480)
self.setLayoutDirection(Qt.RightToLeft)
self.setLocale(QLocale(QLocale.Persian, QLocale.Iran))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.GUI()
def GUI(self):
self.setWindowTitle("My title")
self.setFixedSize(640, 480)
self.text = TextBox(self)
self.text.persian()
app = QApplication([])
window = MainWindow()
window.show()
app.exec_()
You can use a QTextEdit instead of QPlainTextEdit and use setAlignment(Qt.AlignRight), e.g.
from PyQt5.QtWidgets import QTextEdit
from PyQt5.QtCore import Qt
class TextBox(QTextEdit):
def persian(self):
self.setFixedSize(640, 480)
self.setLayoutDirection(Qt.RightToLeft)
self.setLocale(QLocale(QLocale.Persian, QLocale.Iran))
# set text alignment to AlignRight
self.setAlignment(Qt.AlignRight)
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_())