PyQt5: Validate LineEdit through QPushButton using signals and slots - python

Having trouble connecting a QPushButton to a QMessageBox in PyQt5 as there seems to be little documentation in comparison with PyQt4. As it stands the QmessageBox is executing before the main layout I believe this to be an issue somewhere with .self and .exec_()?
The second, and main issue however is concerning connecting the two widgets. I am looking to implement some form of validity check; i.e when both QLineEdit fields contain text then on the click of 'Submit' the form should clear the fields, however if either of the fields are left blank when the 'submit' is clicked, Id like the QMessageBox to be opened. I'm unsure how to implement this as I don't know how to connect both the textField AND the PushButton together.
from PyQt5.QtWidgets import QWidget, QLabel, QLineEdit,QSpinBox,
QDoubleSpinBox, QComboBox, QRadioButton, QPushButton, QHBoxLayout, QVBoxLayout,
QTextEdit, QGridLayout, QApplication, QMessageBox
from PyQt5.QtCore import Qt, pyqtSlot
import csv
class Buttons (QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.Widgets()
self.arrange()
self.close()
self.messageBox()
self.setWindowTitle ("test")
def Widgets(self):
self.nameLabel = QLabel("Name: ")
self.nameLineEdit = QLineEdit()
self.surnameLabel = QLabel("Surname: ")
self.surnameLineEdit = QLineEdit()
self.button1 = QPushButton("submit")
self.button2 = QPushButton("cancel")
self.button1.setMaximumSize(150,20)
self.button2.setMaximumSize(150,20)
def arrange (self):
nameField = QVBoxLayout()
nameField.addWidget(self.nameLabel)
nameField.addWidget(self.nameLineEdit)
nameField.addWidget(self.surnameLabel)
nameField.addWidget(self.surnameLineEdit)
#QHBoxLayout for Buttons:
buttonsLayout = QHBoxLayout()
buttonsLayout.addWidget(self.button1)
buttonsLayout.addWidget(self.button2)
self.button1.setSizePolicy(10,10)
self.button2.setSizePolicy(10,10)
#Creating mainLayout:
mainLayout = QVBoxLayout()
mainLayout.addLayout(nameField)
mainLayout.addLayout(buttonsLayout)
self.setLayout(mainLayout)
#pyqtSlot()
def close(self):
#close window
self.button2.clicked.connect(app.quit)
def clear(self):
pass
#Clear textFields when button is clicked:
#pyqtSlot()
def messageBox(self):
self.message = QMessageBox()
self.message.setText ("submit ERROR")
self.message.setStandardButtons(QMessageBox.Ok)
self.button1.clicked.connect(self.messageBox)
self.message.exec_()
I have tried a few different techniques none of which have worked successfully
self.connect(self.Button1, SIGNAL("clicked()"),self.clicked)
def clicked(self):
QMessageBox.about(self, "message!"(self.messageBox()))
if __name__ == "__main__":
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication (sys.argv)
window = Buttons()
window.show()
sys.exit (app.exec_())

Read over your program and follow the path of execution. Start with window = Buttons() which means the Buttons.__init__ method runs. Within this you run Buttons.messageBox which connects the messageBox slot to the clicked signal of button1. It then opens the message box (self.message.exec_()). Your __init__ method eventually finishes and then app.exec_() is called which actually shows the main GUI and starts the Qt event loop which processes things like button clicks and redraw events.
At this point, your program has already been told to show the message box and is configured to run Buttons.messageBox when you click the button. When you click the button, it shows the message box, and makes an additional connection for the button. Next time you click the button you will get 2 message boxes showing!
The solution is to separate out the signal connection from the messageBox slot. Rather than calling self.messageBox() in your __init__ method, make the signal connection for button1 there.
class Buttons (QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.Widgets()
self.arrange()
self.close()
self.button1.clicked.connect(self.messageBox)
self.setWindowTitle ("test")
#pyqtSlot()
def messageBox(self):
self.message = QMessageBox()
self.message.setText ("submit ERROR")
self.message.setStandardButtons(QMessageBox.Ok)
self.message.exec_()

Related

The program just stops running once i click the signinButton and exits with Process finished with exit code -1073740791 (0xC0000409)

from PyQt6.QtWidgets import (
QMainWindow, QApplication, QDialog, QDialogButtonBox, QLabel, QTextEdit, QPushButton, QMessageBox, QMdiArea,
QTableWidgetItem, QStackedWidget
)
from PyQt6 import uic
import sys
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi(r"C:\Users\csc\Documents\Rentour\front.ui", self)
self.show()
# define widgets
self.button = self.findChild(QPushButton, "signinButton")
self.signinButton.clicked.connect(self.OpenSignUp)
def OpenSignUp(self):
Sign_Up = Second()
widget.addWidget(Sign_Up)
widget.setCurrentIndex(widget.currentIndex()+1)
class Second(QMainWindow):
def __init__(self):
super(Second, self).__init__()
uic.loadUi(r"C:\Users\csc\Documents\Rentour\signpopup.ui", self)
# define widgets
self.button = self.findChild(QPushButton, "SubmitSignButton")
self.SubmitSignButton.clicked.connect(self.SignUpSave)
def SignUpSave(self):
email =self.EmailLine.text()
phoneno =self.PhonenoLine.text()
name =self.NameLine.text()
password = self.PasswordLine.text()
print(password)
app = QApplication(sys.argv)
mainwindow = UI()
widget = QStackedWidget()
widget.addWidget(mainwindow)
widget.show()
app.exec()
this is my code. Am trying to create a login/signup page. So when i click the signinButton, i want it to load the ui for the page which will have a bunch of line edits whose inputs im attempting to store in variables.
The ui files were made using qt designer and and i made this file from scratch. I also referred code with Hala(Youtuber). I Am trying to create a login/signup page. So when i click the signinButton, i want it to load the ui for the page which will have a bunch of line edits whose inputs im attempting to store in variables.
The problem is in your OpenSignUp function. The line widget.addWidget(Sign_Up) is not a valid command for a couple of reasons.
Also Sign_Up varable is holding a newly constructed QMainWindow, which are meant to be top level widgets and shouldn't be added to a layout.
It isn't totally clear what you are trying to achieve, but I am going to assume that you are trying to launch a secondary sign in window, In which case you want to use the open() method to launch the new window.
For example:
def OpenSignUp(self):
self.Sign_Up = Second()
self.Sign_Up.open()
Since you are using a stacked widget it is also possible that your goal is to simply switch to a different widget in the same window. In which case your Second class should probably just be a standard QWidget, and not a QMainWindow. and your stacked widget should be set as the central widget of your UI class.
So that would probably look something like this:
from PyQt6.QtWidgets import (
QMainWindow, QApplication, QDialog, QDialogButtonBox, QLabel, QTextEdit, QPushButton, QMessageBox, QMdiArea,
QTableWidgetItem, QStackedWidget
)
from PyQt6 import uic
import sys
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
# uic.loadUi(r"C:\Users\csc\Documents\Rentour\front.ui", self)
self.widget = QStackedWidget()
self.widget.addWidget(mainwindow)
self.setCentralWidget(self.widget)
self.Sign_Up = Second()
self.widget.addWidget(self.Sign_Up)
# define widgets
self.button = self.findChild(QPushButton, "signinButton")
self.signinButton.clicked.connect(self.OpenSignUp)
def OpenSignUp(self):
self.widget.setCurrentIndex(widget.currentIndex()+1)
class Second(QWidget):
def __init__(self):
super(Second, self).__init__()
uic.loadUi(r"C:\Users\csc\Documents\Rentour\signpopup.ui", self)
# define widgets
self.button = self.findChild(QPushButton, "SubmitSignButton")
self.SubmitSignButton.clicked.connect(self.SignUpSave)
def SignUpSave(self):
email =self.EmailLine.text()
phoneno =self.PhonenoLine.text()
name =self.NameLine.text()
password = self.PasswordLine.text()
print(password)
app = QApplication(sys.argv)
mainwindow = UI()
mainwindow.show()
app.exec()
Except you will need to rearrange the .ui file for UI class so that it is applied on top of the stacked widget as well.

App crashes after closing multiple QMessageBoxes

i have an odd problem with the GUI i am working on with Python using PyQt5.
The GUI has a single button that calls a class method which executes a Worker QThread.
In the Worker's run()-method I emit two consecutive signals that call the main GUIs show_messagebox()-method which displays a messagebox containing the information from the signal.
Displaying multiple QMessageBoxes works fine, but after closing the last one, the whole program including the main GUI exits/crashes. Unfortunately I don't get an error message.
Calling self.show_messagebox.emit() only once in the run()-method works as intended and doesn't close the whole program.
I assume that for some reason the program thinks that the last remaining QMessageBox (the first one that gets emitted) becomes the parent and after closing it the program is done?
Unfortunately I haven't found a solution on how to solve this issue, which is why I decided to ask.
Here is the full source code:
import sys
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QMainWindow, QWidget, QMessageBox
from PyQt5 import QtWidgets
class Worker(QObject):
finished = pyqtSignal()
show_messagebox = pyqtSignal(str, str)
def run(self):
self.show_messagebox.emit("Test1", "Test Text 1")
self.show_messagebox.emit("Test2", "Test Text 2")
self.finished.emit()
class WindowMain(QMainWindow):
def __init__(self):
super(WindowMain, self).__init__()
layout = QVBoxLayout()
self.button = QPushButton("Start")
self.messagebox = QMessageBox()
layout.addWidget(self.button)
self.button.clicked.connect(self.execute)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.show()
def execute(self):
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.show_messagebox.connect(self.show_messagebox)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
def show_messagebox(self, title, text, icon=QMessageBox.Information):
messagebox = QMessageBox()
messagebox.setWindowTitle(title)
messagebox.setText(text)
messagebox.setIcon(icon)
messagebox.exec_()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = WindowMain()
window.show()
sys.exit(app.exec_())
Thanks in advance for any help!

Cannot automatically close second window with QInputDialog

Issue in my code sample:
On "Select..." the selection dialog appears and works basically, but closing it with "Ok" or "Cancel", unfortunately leaves a second window open (title "New Popup"), which is very ugly... :/ I am searching for the way to close that automatically after the user applied the selection in the list, returning to the main window (and the data processing should still work of course).
Thanks for any help! :)
Background: Inside a basic pyqt5 window application, I created a simple list selection as a second (popup) window (separate class), where the user can select an item and the result is then processed in the main window class.
That works basically but I've got an issue with correctly closing the popup window and I was not able to find a solution since hours... Basically I tried self.close, self.destroy, self.hide etc. but nothing had an effect, probably I am missing a piece.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import QTimer
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QLineEdit, QInputDialog, QLabel, QVBoxLayout
class PopupDialog(QtWidgets.QDialog):
def __init__(self):
super(PopupDialog, self).__init__()
self.selected_item = None
layout = QtWidgets.QFormLayout()
self.setLayout(layout)
self.setWindowTitle("New Popup")
self.setMinimumWidth(400)
items = ("C", "C++", "Java", "Python")
item, ok = QInputDialog.getItem(self, "select input dialog",
"list of languages", items, 0, False)
self.selected_item = item
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setMinimumWidth(600)
self.setWindowTitle("Main Window")
self.le = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Select...")
button.clicked.connect(self.get_input)
layout = QtWidgets.QHBoxLayout()
layout.addWidget(self.le)
layout.addWidget(button)
self.setLayout(layout)
def get_input(self):
popup = PopupDialog()
popup.exec_()
print("got selection data: ", popup.selected_item)
self.le.setText(popup.selected_item)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

PyQt5 QFileDialog stops application from closing

I am trying to write a PyQt5 application that does the following:
Creates and opens a Main Window. The MainWindow has a function that opens a QFileDialog window.
Function to open QFileDialog can be triggered in two ways (1) from a file menu option called 'Open' (2) automatically, after the Main window is shown.
My problem is that I haven't found a way to get the QfileDialog to automatically open (2) that doesn't cause the application to hang when the main window is closed. Basic example of code can be found below:
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QWidget,
QHBoxLayout, QCalendarWidget, QScrollArea, QFileDialog, QAction, QFrame)
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.openAction = QAction(QIcon('/usr/share/icons/breeze/places/64/folder-open.svg'), 'Open', self)
self.openAction.triggered.connect(self.openDialog)
self.menubar = QMenuBar(self)
fileMenu = self.menubar.addMenu('&File')
fileMenu.addAction(self.openAction)
self.event_widgets = EventWidgets(self)
self.setMenuBar(self.menubar)
self.setCentralWidget(self.event_widgets)
def openDialog(self):
ics_path = QFileDialog.getOpenFileName(self, 'Open file', '/home/michael/')
class EventWidgets(QWidget):
def __init__(self, parent):
super(EventWidgets, self).__init__(parent)
self.initUI()
def initUI(self):
self.calendar = QCalendarWidget(self)
self.frame = QFrame()
self.scrollArea = QScrollArea()
self.scrollArea.setWidget(self.frame)
horizontal_box = QHBoxLayout()
horizontal_box.addWidget(self.calendar)
horizontal_box.addWidget(self.scrollArea)
self.setLayout(horizontal_box)
if __name__ == '__main__':
app = QApplication(sys.argv)
app_window = MainWindow()
app_window.showMaximized()
app_window.openDialog()
sys.exit(app.exec_())
Code has been tested on KDE Neon and Arch Linux, both have same issue.
I can get round this issue by handling the close event of the Main Window manually - i.e. adding this function to MainWindow:
def closeEvent(self, event):
sys.exit()
But I am not sure a) why this is necessary b) if it is best practice.
I tried your code on macOS Sierra and it works as it's supposed to. However I would propose a different approach to solve your problem.
What you could do is to implement the showEvent() function in your MainWindow class, which is executed whenever a widget is displayed (either using .show() or .showMaximized()) and trigger your custom slot to open the QFileDialog. When doing this you could make use of a single shot timer to trigger the slot with some minimal delay: the reason behind this is that if you simply open the dialog from within the showEvent(), you will see the dialog window but not the MainWindow below it (because the QFileDialog is blocking the UI until the user perform some action). The single shot timer adds some delay to the opening of the QFileDialog, and allows the MainWindow to be rendered behind the modal dialog. Here is a possible solution (not that I made some minor changes to your code, which you should be able to easily pick up):
import sys
from PyQt5 import QtCore
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.openAction = QtWidgets.QAction('Open', self)
self.openAction.triggered.connect(self.openDialog)
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
fileMenu.addAction(self.openAction)
self.event_widgets = EventWidgets(self)
self.setCentralWidget(self.event_widgets)
def showEvent(self, showEvent):
QtCore.QTimer.singleShot(50, self.openDialog)
#QtCore.pyqtSlot()
def openDialog(self):
ics_path = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', '/Users/daniele/')
class EventWidgets(QtWidgets.QWidget):
def __init__(self, parent):
super(EventWidgets, self).__init__(parent)
self.calendar = QtWidgets.QCalendarWidget(self)
self.frame = QtWidgets.QFrame()
self.scrollArea = QtWidgets.QScrollArea()
self.scrollArea.setWidget(self.frame)
horizontal_box = QtWidgets.QHBoxLayout()
horizontal_box.addWidget(self.calendar)
horizontal_box.addWidget(self.scrollArea)
self.setLayout(horizontal_box)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app_window = MainWindow()
app_window.showMaximized()
sys.exit(app.exec_())

PyQt5 how to destroy last widget when clicked 'OK' button on QMessageBox created by a button on that widget

I have a QMainWindow that have a button, when clicked this button, another widget pops up. This widget have a button, when clicked this button an alert message pops up. When I press 'OK' on that message button, only QMessageBox is closing, small widget is still open. I want to close that widget when I press 'OK' on that message button. I couldn't figure out how can I do this. Here is my code;
from PyQt5.QtWidgets import (QMessageBox,QApplication, QWidget, QToolTip, QPushButton,
QDesktopWidget, QMainWindow, QAction, qApp, QToolBar, QVBoxLayout,
QComboBox,QLabel,QLineEdit,QGridLayout,QMenuBar,QMenu,QStatusBar,
QTextEdit,QDialog,QFrame,QProgressBar,QHBoxLayout
)
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtGui import QIcon,QFont,QPixmap,QPalette,QWindow
from PyQt5.QtCore import QCoreApplication, Qt,QBasicTimer, QTimer,QPoint
import PyQt5.QtWidgets,PyQt5.QtCore
import time,random,subprocess,sys,json
class cssden(QMainWindow):
def __init__(self):
super().__init__()
self.mwidget = QMainWindow(self)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setFixedSize(600,500)
#Other widget button
self.owidget = QPushButton(self)
self.owidget.clicked.connect(self.second_widget)
self.show()
#other widget
#I want to destroy this widget when I press 'OK' on the QMessageBox
def second_widget(self):
self.w_window = QWidget()
self.w_window.setGeometry(650,300,600,300)
self.w_window.setStyleSheet("background-color: lightblue")
self.w_button = QPushButton(self.w_window)
self.w_button.setText("Alert")
self.w_button.clicked.connect(self.alert)
self.w_window.show()
#alert from second widget
def alert(self):
QMessageBox.about(self.w_window,"Alert","Alert message")
app = QApplication(sys.argv)
app.setStyleSheet("QMainWindow{background-color: rgb(30,30,30);border: 1px solid black}")
ex = cssden()
sys.exit(app.exec_())
I tried to connect them ('OK' button of QMessageBox and last widget) but I couldn't do it very well and I'm really confused.
I figured out the solution by myself, just make a QMessageBox(no inherit), then find the result and check if the result is something you want, close() the widget.
self.result1 = QMessageBox(QMessageBox.Information,"Alert","Alert message",QMessageBox.Ok)
self.result1.setGeometry(500,500,500,500)
self.result1.show()
result = self.result1.exec_()
if result == 1024:
self.w_window.close()
I used 'OK' button so when I printed result the value was 1024, so before you check which button clicked (if you use yes|no buttons) print the result first then find the value, then do your job.

Categories

Resources