PYQT: mainwindow closes while having second and third window open - python-3.x

I need some help in my current problem.
I have a window with three buttons. When pressing two of them ("GraphicButton" and "QuestionButton") a second and third window shall open. This works fine. The third button "ImportButton" is for populating a table widget in the mainwindow with a dataframe from excel.
When I press the "ImportButton" first everything works fine.
BUT when I press one of the other buttons first to open the other windows and THEN press the "ImportButton", the mainwindow and the other ones will close.
I could use some advice here.
Here is my code for main.py:
import sys
import pandas as pd
import os
from qtpy import QtWidgets, QtCore, QtGui
from ui.mainwindow import Ui_MainWindow
from ui.graphwindow import Ui_GraphWindow
from ui.questionwindow import Ui_QuestionWindow
app = QtWidgets.QApplication(sys.argv)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setWindowTitle("Test")
self.ui.ImportButton.clicked.connect(self.import_clicked)
self.ui.GraphicButton.clicked.connect(self.graphic_clicked)
self.ui.QuestionButton.clicked.connect(self.question_clicked)
def import_clicked(self):
self.ui.RefWidget.setSortingEnabled(False)
self.ui.LubWidget.setSortingEnabled(False)
self.ui.MixWidget.setSortingEnabled(False)
self.ui.RefWidget.setRowCount(0)
self.ui.LubWidget.setRowCount(0)
self.ui.MixWidget.setRowCount(0)
if self.ui.RefBoxC.currentIndex() == 0:
A = os.path.join(os.path.dirname(__file__),"./Datenbank/CO2.xlsx")
df = pd.read_excel(A)
self.ui.RefWidget.setColumnCount(len(df.columns))
self.ui.RefWidget.setRowCount(len(df.index))
for i in range(len(df.index)):
for j in range(len(df.columns)):
self.ui.RefWidget.setItem(i, j, QtWidgets.QTableWidgetItem(str(df.iat[i, j])))
self.ui.RefWidget.resizeColumnsToContents()
self.ui.RefWidget.resizeRowsToContents()
else:
A = os.path.join(os.path.dirname(__file__), "./Datenbank/PlatzhalterRef.xlsx")
df = pd.read_excel(A)
self.ui.RefWidget.setColumnCount(len(df.columns))
self.ui.RefWidget.setRowCount(len(df.index))
for i in range(len(df.index)):
for j in range(len(df.columns)):
self.ui.RefWidget.setItem(i, j, QtWidgets.QTableWidgetItem(str(df.iat[i, j])))
self.ui.RefWidget.resizeColumnsToContents()
self.ui.RefWidget.resizeRowsToContents()
self.ui.RefWidget.setSortingEnabled(True)
self.ui.LubWidget.setSortingEnabled(True)
self.ui.MixWidget.setSortingEnabled(True)
def graphic_clicked(self):
self.Gwindow = QtWidgets.QMainWindow()
self.ui = Ui_GraphWindow()
self.ui.setupUi(self.Gwindow)
self.Gwindow.show()
def question_clicked(self):
self.Questwindow = QtWidgets.QMainWindow()
self.ui = Ui_QuestionWindow()
self.ui.setupUi(self.Questwindow)
self.Questwindow.show()
window = MainWindow()
window.show()
sys.exit(app.exec_())

Disclaimer: the solution provided by this answer has not been tested and may be incomplete.
When you click either the GraphicButton or QuestionButton,
self.ui = Ui_GraphWindow()
or
self.ui = Ui_QuestionWindow()
This will modify self.ui. When the ImportButton is clicked, self.ui will refer to the other windows. This, I believe, is unintentional.
When the commands in import_clicked() are run, there may be references to undefined classes, widgets, and whatnot.
self.ui.RefWidget.setSortingEnabled(False)
I'm guessing RefWidget is not defined in Ui_QuestionWindow or Ui_GraphWindow.
To solve this, try adding
def import_clicked(self):
self.ui = Ui_MainWindow() # set self.ui to refer to a MainWindow
# ...
and see if it works. (Warning: a new MainWindow may be created and the old MainWindow be left forsaken. Again, I haven't tested this to see if it does create a new MainWindow but my intuition tells me that it does.)
EDIT: Above suggestion in strikethrough did not work for OP.
If further trouble and chaos ensues, consider having three separate member variables instead of a single self.ui.
def __init__(self, parent = None):
super().__init__(parent)
self.uiMain = Ui_MainWindow() # new member variable: self.uiMain
# ...
def import_clicked(self):
self.uiMain.RefWidget.setSortingEnabled(False)
self.uiMain.LubWidget.setSortingEnabled(False)
self.uiMain.MixWidget.setSortingEnabled(False)
# ...
def graphic_clicked(self):
self.Gwindow = QtWidgets.QMainWindow()
self.uiGraphic = Ui_GraphWindow() # new member variable: self.uiGraphic
# ...
def question_clicked(self):
self.Questwindow = QtWidgets.QMainWindow()
self.uiQuestion = Ui_QuestionWindow() # new member variable: self.uiQuestion
# ...

Related

slot to right click menu action does does not work

I have written the below code to which I finally managed to add menu but connecitn menu to a function doesnt seem to work:
import os
from PyQt5 import uic
from PyQt5 import QtWidgets
from PyQt5 import QtCore
FILE_LOCATION = os.path.dirname(os.path.realpath(__file__))
class MainDialogWindow(QtWidgets.QDialog):
def __init__(self):
super(MainDialogWindow,self).__init__()
ui_file = os.path.join(FILE_LOCATION, "example.ui")
self._ui = uic.loadUi(ui_file, self)
self.registerCallbacks()
self.initUI()
def initUI(self):
"""Initialize the UI.
"""
self.textBrowser.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
def registerCallbacks(self):
self.textBrowser.customContextMenuRequested.connect(self.context_menu)
# self.connect(self.textBrowser, QtCore.Signal('customContextMenuRequested(const QPoint &)'), self.context_menu)
def context_menu(self, pos):
menu = QtWidgets.QMenu(self)
action = menu.addAction("clear")
menu.exec_(self.mapToGlobal(pos))
action.trigered.connect(self.clear)
def clear(self):
"""Slot to claer text.
"""
print("clear")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainDialogWindow()
window.show()
window.setGeometry(500, 300, 300, 300)
sys.exit(app.exec_())
please helpp,, I want call the clear function from the right click menu
I don't seem to understand how the menu.exec_() method works, that method blocks the execution of sequential tasks until the user selects a QAction from the QMenu. In your case, for example, until when you press "clear" and the triggered signal is emitted (note: you have a typo), but at that moment there is no connection, so the clear method will not be called. The solution is to make the connection before invoking the QMenu exec_():
def context_menu(self, pos):
menu = QtWidgets.QMenu(self)
action = menu.addAction("clear")
action.triggered.connect(self.clear)
menu.exec_(self.mapToGlobal(pos))

Function is not run when connecting Button to it in PyQt4

I m new to PyQt4 and having problems when trying to connect a button to a function.
I m using PyQt 4.11.4.
Neither the clicked.connect method nor the line in the comment below seemed to work.
from PyQt4 import QtGui, QtCore
import sys
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
def setupUi(self, Window):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(200, 200)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setGeometry(0,0,100,100)
MainWindow.setCentralWidget(self.centralwidget)
self.pushButton = QtGui.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(100, 50, 55, 20))
self.pushButton.setText("Run")
self.pushButton.clicked.connect(self.run)
#OR QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL('clicked()'),self.run)
def run(self):
print("ok")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
Window().setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
First of all, by placing statements directly after the __name__ == "__main__" test you introduce global variables. For instance the MainWindow variable is global and is subsequently altered in the Window.setupUi method. This is undesirable. It's better to create a main function and call only that. For example by changing those lines into this...
def main():
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
Window().setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
you will get a run-time error and thus notice something's wrong.
By the way, it is the convention in Python to start class names with an upper case character and objects with a lower case. Therefore, at first glance I thought that MainWindow was a class. It is, however, an object and should better be renamed to mainWindow. Notice how the StackOverflow syntax highlighting colors all identifiers that start with an capital letter light blue?
So, the issue with the signal is that you create two main window objects: first by MainWindow = QtGui.QMainWindow() and then on the next line by Window().setupUi(MainWindow). On that second line you call setupUi with the self parameter set to a temporary Window object, and the Window parameter set to a QtGui.QMainWindow object (MainWindow). It's not surprising that this doesn't work, it is actually a bit surprising it does something at all.
Finally, it is strongly recommended to use layouts in Qt to arrange the widgets. This makes the calls to setGeometry() superfluous. Adding widgets to a layout also set's their parent, so you don't need to do this separately then. I think you definitely should read the docs on layout management (except the last section on writing your own layout manager).
Apply all the advice above and you get something like this...
from PyQt4 import QtGui, QtCore
import sys
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self._setupUi()
def _setupUi(self):
self.resize(200, 200)
# the central widget is called my widget to prevent clashes with
# the centralWidget() function.
self.mainWidget = QtGui.QWidget()
self.setCentralWidget(self.mainWidget)
self.mainLayout = QtGui.QVBoxLayout()
self.mainWidget.setLayout(self.mainLayout)
self.pushButton = QtGui.QPushButton()
self.pushButton.setText("Run")
self.mainLayout.addWidget(self.pushButton)
self.pushButton.clicked.connect(self.run)
def run(self):
print("ok")
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = Window()
mainWindow.setObjectName("mainWindow") # can be omitted.
mainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

populating combo box with folders on disk using QFileSystemModel

Hi I have written this basic code trying to populate folders underneath the /Users/ directory, but I don't know what I am missing its not populating.
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class MyWindow(QtGui.QWidget):
"""docstring for MyWindow"""
def __init__(self, parent=None):
super(MyWindow, self).__init__()
self.setup()
def setup(self):
fsm = QtGui.QFileSystemModel()
fsm.setRootPath("/Users/")
layout = QtGui.QVBoxLayout()
combo = QtGui.QComboBox()
combo.setModel(fsm)
layout.addWidget(combo)
self.setLayout(layout)
def main():
app = QtGui.QApplication(sys.argv)
win = MyWindow()
win.show()
win.raise_()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I am getting a / in the comobobox instead of the whole list of folders under /Users/ directory.
I think its better to use QFileSystemModel instead of using os.listdir interms of efficiency and will update the view if somebody updates folder or adds folder in the /Users/ directory !
Remember that QFileSystemModel is a hierarchical model, so you need to let the QComboBox know which QModelIndex represents the children you want to display. You do that with QComboBox.setRootModelIndex()
QFileSystemModel.setRootPath() conveniently returns the QModelIndex of the path you set.
So a small change is all you need (tested on Windows) -
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class MyWindow(QtGui.QWidget):
"""docstring for MyWindow"""
def __init__(self, parent=None):
super(MyWindow, self).__init__()
self.setup()
def setup(self):
fsm = QtGui.QFileSystemModel()
index = fsm.setRootPath("/Users/")
layout = QtGui.QVBoxLayout()
combo = QtGui.QComboBox()
combo.setModel(fsm)
combo.setRootModelIndex(index)
layout.addWidget(combo)
self.setLayout(layout)
def main():
app = QtGui.QApplication(sys.argv)
win = MyWindow()
win.show()
win.raise_()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

QMainWindow flashes and disappears when called from another QMainWindow

This fairly minimal code creates a systray item with three right click options. One is an instance of QDialog, another QMainWindow, and also Quit. It's for a systray-driven app that will have some qdialogs and also a qmainwindow containing a table widget (or, is it possible to create a table into a qdialog?). I've been stuck on this for a week or so, read a lot of related materials and do not understand how to resolve it.
From the systray icon menu, clicking on QDialog, the dialog opens, waits for user action, and clicking the Ok or Cancel buttons will print which one was clicked. It would seem this works because QDialog has its own exec_(). Great so far.
However, clicking on the QMainWindow menu option, the main window dialog appears briefly and disappears with no chance for user input, of course. Maybe instead, the qmainwindow dialog would need to rely on the QApplication's exec_() where the object would instead be initialized just before app.exec_() perhaps? If that would work, I'm not clear on how def qmainwindow() would retrieve the information back from the user.
Hopefully a simple matter for someone who knows, a few changes, bingo.
Current environment: Windows 7 or XP, Python 2.7, Pyside
If you run this, there will be a blank place-holder in the systray that is clickable (right click), or you can also give it an actual image in place of 'sample.png'.
#!python
from PySide import QtGui, QtCore
from PySide.QtGui import QApplication, QDialog, QMainWindow
def qdialog():
qdialog_class_obj = TestClassQDialog()
qdialog_class_obj.show()
qdialog_class_obj.exec_() # wait for user
print "qdialog_user_action: ", qdialog_class_obj.qdialog_user_action
def qmainwindow():
qmainwindow_class_obj = TestClassQMainWindow()
qmainwindow_class_obj.show()
#qmainwindow_class_obj.exec_() # 'TestClassQMainWindow' object has no attribute 'exec_'
class TestClassQDialog(QDialog):
def __init__(self, parent=None):
super(TestClassQDialog, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("accepted()"), self.button_ok)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("rejected()"), self.button_cancel)
def button_ok(self):
self.qdialog_user_action = 'ok'
self.hide()
def button_cancel(self):
self.qdialog_user_action = 'cancel'
self.hide()
class TestClassQMainWindow(QMainWindow):
def __init__(self, parent=None):
super(TestClassQMainWindow, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("accepted()"), self.button_ok)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL("rejected()"), self.button_cancel)
def button_ok(self):
self.hide()
def button_cancel(self):
self.hide()
class SysTrayIcon(QMainWindow):
def __init__(self, parent=None):
super(SysTrayIcon, self).__init__(parent)
self.qdialog_action = QtGui.QAction("QDialog", self, triggered=qdialog)
self.qmainwindow_action = QtGui.QAction("QMainWindow", self, triggered=qmainwindow)
self.quit_action = QtGui.QAction("Quit", self, triggered=QtGui.qApp.quit)
self.createSystrayIcon()
self.systrayIcon.show()
def createSystrayIcon(self):
self.systrayIconMenu = QtGui.QMenu(self)
self.systrayIconMenu.addAction(self.qdialog_action)
self.systrayIconMenu.addAction(self.qmainwindow_action)
self.systrayIconMenu.addSeparator()
self.systrayIconMenu.addAction(self.quit_action)
self.systrayIcon = QtGui.QSystemTrayIcon(self)
self.systrayIcon.setContextMenu(self.systrayIconMenu)
self.systrayIcon.setIcon(QtGui.QIcon('sample.png')) # point to a valid image if you want.
self.systrayIcon.setVisible(True)
if __name__ == '__main__':
app = QtGui.QApplication([])
systrayicon = SysTrayIcon()
app.exec_()
I've got it working. What I did was move your external method qmainwindow inside of your
SysTrayIcon and created the class parameter self.qmainwindow_class_obj = TestClassQMainWindow(). I've attached the working code below. Also, you're using the old style signal slot method, I take it you're coming from old school PyQt. The new method if very nice, clean and pythonic. I've also put the new style methods in the below code. Another thing I would do is move your qdialog method inside the SysTrayIcon class. I don't really understand why you have it outside the class but maybe I'm missing something. Hope this helps.
#!python
from PySide import QtGui, QtCore
from PySide.QtGui import QApplication, QDialog, QMainWindow
def qdialog():
qdialog_class_obj = TestClassQDialog()
qdialog_class_obj.show()
qdialog_class_obj.exec_() # wait for user
print "qdialog_user_action: ", qdialog_class_obj.qdialog_user_action
class TestClassQDialog(QDialog):
def __init__(self, parent=None):
super(TestClassQDialog, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel)
self.ok_cancel.accepted.connect(self.button_ok)
self.ok_cancel.rejected.connect(self.button_cancel)
def button_ok(self):
self.qdialog_user_action = 'ok'
self.hide()
def button_cancel(self):
self.qdialog_user_action = 'cancel'
self.hide()
class TestClassQMainWindow(QMainWindow):
def __init__(self, parent=None):
super(TestClassQMainWindow, self).__init__(parent)
self.ok_cancel = QtGui.QDialogButtonBox(self)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.ok_cancel.accepted.connect(self.button_ok)
self.ok_cancel.rejected.connect(self.button_cancel)
def button_ok(self):
self.hide()
def button_cancel(self):
self.hide()
class SysTrayIcon(QMainWindow):
def __init__(self, parent=None):
super(SysTrayIcon, self).__init__(parent)
self.qmainwindow_class_obj = TestClassQMainWindow()
self.qdialog_action = QtGui.QAction("QDialog", self, triggered=qdialog)
self.qmainwindow_action = QtGui.QAction("QMainWindow", self, triggered=self.qmainwindow)
self.quit_action = QtGui.QAction("Quit", self, triggered=QtGui.qApp.quit)
self.createSystrayIcon()
self.systrayIcon.show()
def createSystrayIcon(self):
self.systrayIconMenu = QtGui.QMenu(self)
self.systrayIconMenu.addAction(self.qdialog_action)
self.systrayIconMenu.addAction(self.qmainwindow_action)
self.systrayIconMenu.addSeparator()
self.systrayIconMenu.addAction(self.quit_action)
self.systrayIcon = QtGui.QSystemTrayIcon(self)
self.systrayIcon.setContextMenu(self.systrayIconMenu)
self.systrayIcon.setIcon(QtGui.QIcon('linux.jpeg')) # point to a valid image if you want.
self.systrayIcon.setVisible(True)
def qmainwindow(self):
self.qmainwindow_class_obj.show()
if __name__ == '__main__':
app = QtGui.QApplication([])
systrayicon = SysTrayIcon()
app.exec_()

How to display a custom dialog using PyQt and QtDesigner to design the custom dialog?

I have designed 2 widgets - one is the main application widget and a custom widget which would allow me to set the preferences in my main application. They are named - main and child.
Now, I can't get the child widget to show when I click the button in the main application.
Tried to learn from the Rapid GUI programming using Python and Qt book, but the example given there is for a hand coded form and not designed using QtDesigner. I am getting confused. Kindly help.
My code so far is this-
import serial, sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from main import Ui_Form # main.py and child.py are the ui
from child import Ui_Form as Child_Form # files generated using pyuic4
class Main(QMainWindow):
def __init__(self, parent = None):
super(Main, self).__init__(parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
self.connect(self.ui.btnLaunch, SIGNAL("clicked()"), self.show)
def show(self):
dialog = QDialog()
dialog.ui = Child_Form()
dialog.ui.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
myapp = Main()
myapp.show()
sys.exit(app.exec_())
However, nothing happens when I try to launch this program.
Instead of this code:
self.connect(self.ui.btnLaunch, SIGNAL("clicked()"), self.show)
def show(self):
dialog = QDialog()
dialog.ui = Child_Form()
dialog.ui.setupUi(self)
try with this, it should work:
self.connect(self.ui.btnLaunch, SIGNAL("clicked()"), self.showDialog)
def showDialog(self):
dialog = QDialog()
dialog.ui = Child_Form()
dialog.ui.setupUi(self)
dialog.show()

Resources