I am trying to create a simple GUI using PyQt5. I am running my code in windows 10 from Spyder(Anaconda latest version, python 3.7). Here is my code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 button test'
self.left = 50
self.top = 50
self.width = 720
self.height = 800
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
button = QPushButton('PyQt5 button', self)
button.setToolTip('This is an example button')
button.move(100,70)
button.clicked.connect(self.on_click)
self.show()
#pyqtSlot()
def on_click(self):
print('PyQt5 button click')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
app.exec_()
A window pop up. Now if I close the GUI by clicking on closing button(top right corner of the GUI),Spyder IP console does not return to normal condition. It freezes. What should I use in code to solve the issue?
Thanks,
Moni
Closing the window doesn't appear to close the QApplication object. To do this, override the closeEvent function of the main window by adding these two lines after the on_Click definition
def closeEvent(self,event):
QApplication.quit()
This will close the window and the QApplication object as well and control should return to the console
You can try and look at the answer in the following thread:
Can't Kill PyQT Window after closing it. Which requires me to restart the kernal
Seems to have resemblance to your problem and a solution.
Related
So, basically I have a main window, with a bunch of buttons, which open different windows. On startup, I would like to open up all of them. The problem I am facing is, they are opening up minimised for some reason. I cannot figure out why?
class mainwinActions(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
...
self.open_all_win()
def open_all_win(self):
self.open_exp()
self.open_lick()
self.open_door()
def open_exp(self):
self.expwin = fooWin()
self.expwin.show()
# The same for self.open_lick/self.open_door
Also, I have a button attached to the function.
self.openAllButton.clicked.connect(lambda: self.open_all_win())
And the button does NOT have the same problem. It opens up the windows normally (non-minimised/windowed).
What I tried:
class fooWin(QtWidgets.QWidget, Ui_fooWin):
def __init__(self):
super().__init__()
self.setupUi(self)
...
if self.windowState() == QtCore.Qt.WindowState.WindowMinimized:
# Window is minimised. Restore it.
self.setWindowState(QtCore.Qt.WindowState.WindowActive)
I also tried replacing self.open_all_win() with
self.openAllButton.click()
Edit: Reproducible code
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget
import sys
class AnotherWindow(QWidget):
def __init__(self):
super().__init__()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.button = QPushButton("Push for Window")
self.button.clicked.connect(self.show_new_window)
self.setCentralWidget(self.button)
self.show_new_window()
def show_new_window(self):
self.w = AnotherWindow()
self.w.show()
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()
Edit2: Just to clarify, the problem is only on startup. When I run the code, I want all the windows up without clicking on any button. The windows should be visible when I run the file.
QtCore.QT_VERSION_STR = 6.2.3
OS:MacOS Monterey and Windows 10
I'm struggling with making the transition from Tkinter to the more object-oriented approach of PyQt. My particular question focuses on combining file dialogs with buttons. In the example below, having lines of code I admittedly don't completely understand, I use a file dialog to load a time series, stored as a .csv file, into a dataframe and then plot it:
import sys
import pandas as pd
from matplotlib import pyplot as plt
from PyQt5.QtWidgets import QApplication, QWidget, QFileDialog,
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'Series Plotter'
self.left = 10
self.top = 10
self.width = 640
self.height = 480
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.openFileNameDialog()
self.show()
def openFileNameDialog(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
fileName, _ = QFileDialog.getOpenFileName(self,"Choose File", "","csv (*.csv)",
options=options)
df=pd.read_csv(fileName)
df.plot()
plt.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
This works insofar as it automatically launches a file dialog and plots the chosen time series. However, I'd prefer to do the following:
Have an "open file" button in my window to launch the file dialog.
Have a "plot series" button in my window to plot the selected series. I imagine this button would need to be disabled until the series is actually chosen.
Despite looking at various tutorials, I'm struggling to find the pythonic way of doing this, a fact I attribute to my limited knowledge of the objected-oriented approach.
You can try to create a main window and add button there, and connect button click event to a function where you initialize the class you have created, something like this:
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(300,300)
frame =QtWidgets.QFrame()
self.setCentralWidget(frame)
layout = QtWidgets.QHBoxLayout()
frame.setLayout(layout)
self.fileOpenButton = QtWidgets.QPushButton('Click to open File Dialog',self)
layout.addWidget(self.fileOpenButton)
self.fileOpenButton.clicked.connect(self.buttonClicked)
def buttonClicked(self):
wig = App()
def main():
app = QApplication([])
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The output:
After clicking the button:
After selecting a file:
I am currently trying to troubleshoot an issue I am running into with a Qt application I am developing in Python. I only have a simple Main Window and a modal QDialog setup to collect user input after triggering a menu action on the Main Window. The QDialog is behaving as expected, since I've been able to confirm the test print is executed upon the QDialog returning Accepted, but the application will completely crash, without an error message, following the print statement. The same behavior occurs when QDialog returns Rejected. To clarify, the Main Window is shown for a couple seconds and the application crashes with no error messages. I would expect the function to return focus to the Main Window (with it still being open to operate on), after receiving either result from the Process function below.
I have also tried making the QDialog modeless (using show) and the accept/reject functions from the QDialog seem to return back to the main window as expected, but calling the function that brings up the QDialog again crashes the application. I am using pyqt 5.9 for this project, but I have used an essentially identical setup to the code below in a number of other projects in pyqt 5.6 without this happening. I am trying to figure out if there are any known issues with pyqt 5.9 that could be contributing to this issue, or if I have an error in my code somewhere that is causing this crash to occur. I was considering rolling back to 5.6 to see if that would get rid of the issue, but I feel like there may be something I am misunderstanding that is causing this to occur.
I am running the code out of Anaconda prompt (Anaconda 4.8.3, Python 3.7) on Windows 10, since Qt applications still have issues being run repeatedly within Spyder. I am using the pyqt that comes with Anaconda and haven't done any additional pip installs of pyqt.
Main Window Code
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout, QHBoxLayout, QVBoxLayout, QAction, QLabel
from PyQt5.QtWidgets import QDialog, QListWidget, QListWidgetItem, QAbstractItemView, QPushButton, QLineEdit, QSpacerItem, QSizePolicy
class FirstWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.title = 'Window 1'
self.left = 350
self.top = 150
self.width = 800
self.height = 500
self.setWindowTitle(self.title)
self.setGeometry(self.left,self.top,self.width,self.height)
widget = QWidget()
self.setCentralWidget(widget)
grid = QGridLayout()
widget.setLayout(grid)
mainMenu = self.menuBar()
mainMenu.setNativeMenuBar(False)
subMenu = mainMenu.addMenu('File')
modifyDB = QAction('Test',self)
subMenu.addAction(modifyDB)
modifyDB.triggered.connect(self.Process)
self.statusBar().showMessage('Ready')
def Process(self):
dialog = DialogWin()
if dialog.exec_() == QDialog.Accepted:
print('test passed')
if __name__ == '__main__':
if not QApplication.instance():
app = QApplication(sys.argv)
else:
app = QApplication.instance()
mainWin = FirstWindow()
mainWin.show()
app.exec_()
Dialog Code
class DialogWin(QDialog):
def __init__(self,parent=None):
super(DialogWin,self).__init__(parent)
self.title = 'Database Table Name Selection'
self.left = 350
self.top = 150
self.width = 300
self.height = 300
self.setWindowTitle(self.title)
self.setGeometry(self.left,self.top,self.width,self.height)
vbox = QVBoxLayout()
spacer = QSpacerItem(10,10,QSizePolicy.Expanding,QSizePolicy.Expanding)
self.list_exp = QLabel('Select Existing Table to Overwrite')
vbox.addWidget(self.list_exp)
self.table_list = QListWidget()
self.table_list.setSelectionMode(QAbstractItemView.SingleSelection)
vbox.addWidget(self.table_list)
hbox = QHBoxLayout()
hbox.addItem(spacer)
self.option_exp = QLabel('<span style="font-size:8pt; font-weight:600; color:#aa0000;">OR</span>')
hbox.addWidget(self.option_exp)
hbox.addItem(spacer)
vbox.addLayout(hbox)
self.new_name = QLineEdit(placeholderText='Enter New Source Name')
vbox.addWidget(self.new_name)
hbox = QHBoxLayout()
self.okButton = QPushButton('OK')
hbox.addWidget(self.okButton)
self.okButton.clicked.connect(self.accept)
self.cancelButton = QPushButton('Cancel')
hbox.addWidget(self.cancelButton)
self.cancelButton.clicked.connect(self.reject)
vbox.addLayout(hbox)
self.setLayout(vbox)
item = QListWidgetItem('No Tables Available...')
item.setFlags(item.flags() ^ Qt.ItemIsSelectable)
self.table_list.addItem(item)
Any input on this issue would be tremendously helpful. I've spent a lot of hours trying to understand how this setup could be working for me in one application and not in another.
Explanation:
The problem is that you are using the same QSpacerItem 2 times, and when the QDialog is closed as it is a local variable it will be deleted, and Qt will also eliminate the internal objects, and in that case the QSpacerItem will have a double elimination that causes the "Segmentation fault".
Solution:
You must create 2 QSpacerItem:
# ...
hbox = QHBoxLayout()
hbox.addItem(spacer)
self.option_exp = QLabel('OR')
hbox.addWidget(self.option_exp)
hbox.addItem(QSpacerItem(10,10,QSizePolicy.Expanding,QSizePolicy.Expanding))
vbox.addLayout(hbox)
# ...
Another alternative is not to use QSpacerItem but to set a stretch factor:
# ...
hbox = QHBoxLayout()
hbox.addStretch()
self.option_exp = QLabel('OR')
hbox.addWidget(self.option_exp)
hbox.addStretch()
vbox.addLayout(hbox)
# ...
Or don't use QHBoxLayout and set the QLabel directly to the QVBoxLayout by setting the alignment:
# ...
self.table_list = QListWidget()
self.table_list.setSelectionMode(QAbstractItemView.SingleSelection)
vbox.addWidget(self.table_list)
self.option_exp = QLabel('OR')
vbox.addWidget(self.option_exp, alignment=Qt.AlignHCenter)
self.new_name = QLineEdit(placeholderText='Enter New Source Name')
vbox.addWidget(self.new_name)
# ...
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_())
I have created two different pyqt windows, and within one of them, by pressing a button, it should bring up another smaller window. While my code does pretty much exactly what I just dais it should do, there is a problem with the way the smaller popup window is displayed.
This is my code for displaying the windows and the button functionality:
from PyQt4 import QtGui
from EnterprisePassport import Ui_StudentEnterprisePassport
from Session_tracker import Ui_Session_tracker
class StudentEnterprisePassport(Ui_StudentEnterprisePassport):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.sessionTracker_btn.clicked.connect(self.handleButton)
self.window2 = None
def handleButton(self):
if self.window2 is None:
self.window2 = Session_tracker(self)
self.window2.show()
class Session_tracker(Ui_Session_tracker):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = StudentEnterprisePassport()
window.show()
sys.exit(app.exec_())
I can still use the functions within the window, but I can't move it, or close it, and there is no title bar. Have I done something wrong within my code for the popup window to appear like this?
Edit:
Original Session tracker window: Original window
Popup session tracker window: Popup window
In order to show the other widget in it's own window, it has to be a QMainWindow or a QDialog.
One option, if you don't want to convert your existing Session_tracker to a QDialog, is to just wrap it in a QDialog
def handleButton(self):
if self.window2 is None:
self.window2 = QtGui.QDialog(self)
lay = QtGui.QVBoxLayout()
self.window2.setLayout(lay)
self.session_tracker = Session_tracker(self.window2)
lay.addWidget(self.session_tracker)
self.window2.show()