My question is tough to explain but I am trying my best. Please help me in this regard.
I designed a gui in QtDesigner and converted .ui files into .py e.g. main_window.py. Now in order to avoid changing in main_window.py I created another class for listeners.
class Main():
window = None
app = None
def __init__(self):
self.launch()
self.attach_listener()
self.execute()
''' Launch GUI '''
def launch(self):
self.app = QtGui.QApplication(sys.argv)
self.window = Ui_MainWindow()
self.window.show()
''' Execute Window '''
def execute(self):
sys.exit(self.app.exec_())
''' Attach Listeners '''
def attach_listener(self):
self.window.add_button.clicked.connect(self.add_listener)
self.window.delete_button.clicked.connect(self.delete_listener)
self.window.update_button.clicked.connect(self.update_listener)
self.window.connect(self.window.combo_box, QtCore.SIGNAL('activated(QString)'), self.logout_listener)
I have another child_window.py with same structure, but I can't open that window from this one because of QApplication. I searched for the answer but couldn't apply on my code. Those answers were applicable when class was extending from QtGui.QMainWindow or QtGui.QWidget, but my scenario is different.
You are mixing up the Ui_MainWindow object with the actual window object (QMainWindow, QDialog,QWidget etc.) self.window = Ui_MainWindow() doesn't do anything because the class you are attaching it to is not a Window. You need to create a window and apply the Ui_MainWindow to it.
Apparently you can make this work, but it doesn't look pretty. You need to access your widgets via findChild. The only benefit I can see is that you don't run pyside-uic after changing a form in the designer, and that's pretty easy.
The easier way
When you use pyuic / pyside-uic it converts the .ui files to .py files. You shouldn't edit the .py's as they will be overwritten the next time you use QtDesigner. You need to create a window class and apply the UI class to it.
Setting up a new form
Some of these module names might seem a bit awkward, but I've settled on them because in the past I've had trouble with name clashes between modules and classes; caused by me not understanding how Qt Designer was going to handle it's naming.
If you look at the file that pyside-uic created, the top of it contains the correct class and method names that you need to use in your mainWinClass.py
mainWinui.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'mainWinui.ui'
#
# Created: Sun Feb 7 14:22:09 2016
# by: pyside-uic 0.2.15 running on PySide 1.2.4
#
# WARNING! All changes made in this file will be lost!
from PySide import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
Create a new mainwinClass.py and copy the correct imports and class names to it, plus a bit of boilerplate to load the .ui.
It looks like this:
mainWinClass.py
from mainWinui import Ui_MainWindow
from PySide import QtGui
class MainWin(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setup_signals()
# Is this is the same as your Listeners ??
def setup_signals(self):
# Signal for updating the lineedits from the grid
self.ui.tabWidget.currentChanged.connect(self.onTabChanged)
# Connect the "Add Staff" button to the addStaffMember method
self.ui.btnAddStaff.clicked.connect(self.addStaffMember)
Then use another file to launch the app itself, and to maintain some non GUI aspects of the app, like updaters or global logging. I've seen code where all the child windows are instantiated in here to, but I don't (normally) do it that way. I keep them in the main form instead. It depends on how you intend to design the app.
appname.py
from PySide import QtGui, QtCore
from mainwinClass import MainWin
import sys
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainWin = MainWin()
mainWin.show()
sys.exit(app.exec_())
# Nothing else _needed_ in here
Now for any child windows follow the same again.
Modal forms
In Qt Designer create a new 'Dialog with buttons bottom'. Add widgets as desired and save as dialogAddStaffui.ui.
Run
pyside-uic -o dialogAddStaffui.py dialogAddStaffui.ui.
Create a new, empty text document called dialogAddStaffClass.py and using dialogAddStaffui.ui as a reference for class names etc. edit dialogAddStaffClass.py to look like this:
dialogAddStaffClass
from dialogAddStaffui import Ui_DialogAddStaff
from PySide import QtCore, QtGui
class DialogAddStaff(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.ui = Ui_DialogAddStaff()
self.ui.setupUi(self)
# Your own init stuff
Those two imports are the only ones needed here. If you are trying to copy this, realise that in the Qt Designer I have
windowModality = ApplicationModal and the form was a "Dialog with Buttons Bottom"
For these simple forms, they have an accept method that checks the validity of data that the user has entered and closes with self.done(1). If you want to see how the validation and close is handled:
dialogAddStaffClass
def validate(self):
retval = True
if not self.ui.editLname.text():
retval = False
QtGui.QMessageBox.information(self, 'Invalid Last name',
"Last Name must not be blank")
self.ui.editLname.setFocus()
return retval
def accept(self):
if self.validate():
self.done(1)
With these dialog forms Qt has automatically set the OK button to fire accept. I just overrode that method.
If you want communication between parent and child you can either set a property on the child that references the parent, or read the the properties of the child after it closes, but before it's variable gets garbage collected. There can be issues with creating circular references so be careful.
As the new form is modal, the user can't interact with the Main Form until they have closed the Child Form, and the function that launches the chil d window will halt until the child window is closed, therefore it's ok to use a local variable to hold the child class.
The 'Add Staff' button is connected to the addStaffMember function.
mainWinClass.py
from dialogAddStaffClass import DialogAddStaff
def addStaffMember(self):
addStaffForm = DialogAddStaff()
res = addStaffForm.exec_() # exec_ waits, show would continue
# Function waits here for the Modal form to close.
if res: # child was closed with OK and validated correctly
print(addStaffForm.ui.editLname.text())
# Saveing other Staff data happens here
Because the child form is run with exec_, the main form waits untill the child form closes before continuing. When the function exits the addStaffForm variable is garbage collected, so there is no longer any reference to the attributes of the child form. (and probably no form ...)
If you want to open a Long lived form, you would instantiate it somewhere more long lasting.
Non Modal forms
Here is an example of a SisterForm. It is was created in the designer from the 'Main Window' type (It has it's own menu's and Status bar etc.). If you don't need these frills, use a Dialog form but set it's windowModality to NonModal.
sisterWinui.ui
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'sisterWinui.ui'
#
# Created: Mon Feb 8 12:05:37 2016
# by: pyside-uic 0.2.15 running on PySide 1.2.4
#
# WARNING! All changes made in this file will be lost!
from PySide import QtCore, QtGui
class Ui_SisterWin(object):
def setupUi(self, SisterWin):
SisterWin.setObjectName("SisterWin")
Run uic
pyside-uic -o sisterWinui.py sisterWinui.ui
Create a file sisterwinClass.py - set up it's import and init
sisterwinClass.py
from sisterWinui import Ui_SisterWin
from PySide import QtCore, QtGui
class SisterWin(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.ui = Ui_SisterWin()
self.ui.setupUi(self)
# Your custom stuff after this
In Qt Designer, add a button or whatever to your main form to launch sisterForm. Then make some edits to the mainwinClass. Make a variable to hold it in a long lived place
mainwinClass
from sisterwinClass import SisterWin
# no other new imports needed, just the stuff you had before
class MainWin(QtGui.QMainWindow):
def __init__(self, parent=None):
# These three lines were already there
QtGui.QMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Add a long lived attribute to hold the class instance
self.sisterWin = None
# Next line was already there
self.setup_signals()
def setup_signals(self):
# Connect button to openSisterWin
self.ui.btnSisterwin.clicked.connect(self.openSisterWin)
# You probably have other connects after this
# This toggles the window
def openSisterWin(self):
if not self.sisterWin:
self.sisterWin = SisterWin()
if self.sisterWin.isVisible():
print('Hiding')
self.sisterWin.hide()
# hide or close, it's your choice
# self.sisterWin.close()
else:
print('Showing')
self.sisterWin.show()
I hope that covers what you were looking for now ? If are trying to find out how to hide the main window , look here Happy hacking :-)