Best practices for creating a UI in qt for python? QML, QuiLoadUI, or generate through python?
-
wrote on 1 May 2019, 15:17 last edited by metalshreds 5 Feb 2019, 14:38
I'm new to QT for python, trying to avoid digging myself into a hole. I want to separate the ui as much as possible from the logic. reading the documentation I would go with QML and python route but wanted to get others opinions who have more experience. Thanks in advance!
-
wrote on 23 May 2019, 17:50 last edited by
I don't do QML...but can generally make good use of the UI designer as a starting point.
-
wrote on 4 Jun 2019, 21:10 last edited by Denni 6 Apr 2019, 21:12
@metalshreds I am still fairly new to pyqt5 but a long time software engineer and the class system of python should be all that you need to use to divorce the UI code from the middle tier or back-end logic as it would be done in any other object oriented program
I find that pyqt5 handles the GUI stuff quite nicely using code-wise implementation and that adding a designer actually adds a layer of complexity that can make the project a bit more difficult to troubleshoot later on. I say this because the code it seems to create is much more cryptic in nature while the code you can create using code-wise pyqt5 can be done fairly straight forward since you have full control of all that is being done
Still to each their own if you are more comfortable using a designer then by all means use it just keep in mind if someone else has to pick up where you left off they might have trouble understanding what you did unless they too are already familiar with that specific designer
-
wrote on 5 Jun 2019, 01:39 last edited by
Hi,
I am very new to python as well as GUI programming as well. Have you looked into converting your .ui file into a .py file with pyuic? That way you have the framework for the GUI and can add your python code to it.
-
wrote on 5 Jun 2019, 13:45 last edited by Denni 6 May 2019, 13:46
If either of you ( @PyGrrl_FL or @metalshreds ) need help in understanding how to implement your GUI using the code-wise approach as opposed to using a tool just ask. I think once you understand how easy that is to do using pyqt5 you might wonder why those designers are even being used (I know I wonder why sometimes).
-
If either of you ( @PyGrrl_FL or @metalshreds ) need help in understanding how to implement your GUI using the code-wise approach as opposed to using a tool just ask. I think once you understand how easy that is to do using pyqt5 you might wonder why those designers are even being used (I know I wonder why sometimes).
wrote on 5 Jun 2019, 13:56 last edited by@Denni Hi, I for one would love to understand GUI implementation using the code-wise approach as opposed to using Qt Designer. I am reading all I can and trying many different things but sometimes getting lost in all of the information and different tools/techniques out there.
-
wrote on 5 Jun 2019, 15:30 last edited by Denni 6 Jun 2019, 16:24
Okay well my first bit of advise is to chew on it a little bit at a time get something working save that, make a copy, and then play with it by tweaking it a little. Document each of these changes as you save them to help you remember what you added at each stage so that you can easily go back and remove or pull out the bits and pieces you might need later on for another project. To help you all get started here is a fairly basic program that gives you a bit more meat to play with than most of the standard examples I came across and used to build this one. Still this was built using what is already out there.
Note wherever you end up saving this program create an images folder there and put your two images within that folder and either name them what I did or change the names I give to match your own.
import sys import time import sqlite3 from collections import deque # The following is down-and-dirty as one should only import what one actually needs # Still when playing around with stuff this makes it a lot easier from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * ############################## SQLite3 Database Class ############################## class sqlDatabase: # This class would handle everything pertinenting to ones Database def __init__(self,filename): self.__dbaseName = filename self.__SQLDef = '' def GetCatGroups(self): # This simulates what one would get back from a database Query CatGrpRecSet = [ {'CatgryName':'All', 'GroupName':''} , {'CatgryName':'FirstCategory', 'GroupName':'Group-1'} , {'CatgryName':'FirstCategory', 'GroupName':'Group-2'} , {'CatgryName':'FirstCategory', 'GroupName':'Group-3'} , {'CatgryName':'SecndCategory', 'GroupName':'Group-4'} , {'CatgryName':'SecndCategory', 'GroupName':'Group-5'} , {'CatgryName':'SecndCategory', 'GroupName':'Group-6'} , {'CatgryName':'ThirdCategory', 'GroupName':'Group-7'} , {'CatgryName':'ThirdCategory', 'GroupName':'Group-8'} , {'CatgryName':'ThirdCategory', 'GroupName':'Group-9'} ] return CatGrpRecSet def GetItemData(self, CatGrpSlctd): # This simulates what one would get back from a database Query if CatGrpSlctd == 'All': ItmRecSet = [ {'CatgryName':'FirstCategory', 'GroupName':'Group-1', 'ItemName':'Item-01'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-1', 'ItemName':'Item-02'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-1', 'ItemName':'Item-03'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-2', 'ItemName':'Item-04'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-2', 'ItemName':'Item-05'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-2', 'ItemName':'Item-06'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-3', 'ItemName':'Item-07'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-3', 'ItemName':'Item-08'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-3', 'ItemName':'Item-09'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-4', 'ItemName':'Item-11'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-4', 'ItemName':'Item-12'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-4', 'ItemName':'Item-13'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-5', 'ItemName':'Item-14'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-5', 'ItemName':'Item-15'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-5', 'ItemName':'Item-16'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-6', 'ItemName':'Item-17'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-6', 'ItemName':'Item-18'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-6', 'ItemName':'Item-19'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-7', 'ItemName':'Item-21'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-7', 'ItemName':'Item-22'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-7', 'ItemName':'Item-23'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-8', 'ItemName':'Item-24'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-8', 'ItemName':'Item-25'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-8', 'ItemName':'Item-26'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-9', 'ItemName':'Item-27'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-9', 'ItemName':'Item-28'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-9', 'ItemName':'Item-29'} ] elif CatGrpSlctd in (['FirstCategory', 'Group-1', 'Group-2', 'Group-3']): ItmRecSet = [ {'CatgryName':'FirstCategory', 'GroupName':'Group-1', 'ItemName':'Item-01'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-1', 'ItemName':'Item-02'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-1', 'ItemName':'Item-03'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-2', 'ItemName':'Item-04'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-2', 'ItemName':'Item-05'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-2', 'ItemName':'Item-06'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-3', 'ItemName':'Item-07'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-3', 'ItemName':'Item-08'}, {'CatgryName':'FirstCategory', 'GroupName':'Group-3', 'ItemName':'Item-09'} ] elif CatGrpSlctd in (['SecndCategory', 'Group-4', 'Group-5', 'Group-6']): ItmRecSet = [ {'CatgryName':'SecndCategory', 'GroupName':'Group-4', 'ItemName':'Item-11'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-4', 'ItemName':'Item-12'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-4', 'ItemName':'Item-13'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-5', 'ItemName':'Item-14'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-5', 'ItemName':'Item-15'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-5', 'ItemName':'Item-16'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-6', 'ItemName':'Item-17'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-6', 'ItemName':'Item-18'}, {'CatgryName':'SecndCategory', 'GroupName':'Group-6', 'ItemName':'Item-19'} ] elif CatGrpSlctd in (['ThirdCategory', 'Group-7', 'Group-8', 'Group-9']): ItmRecSet = [ {'CatgryName':'ThirdCategory', 'GroupName':'Group-7', 'ItemName':'Item-21'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-7', 'ItemName':'Item-22'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-7', 'ItemName':'Item-23'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-8', 'ItemName':'Item-24'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-8', 'ItemName':'Item-25'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-8', 'ItemName':'Item-26'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-9', 'ItemName':'Item-27'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-9', 'ItemName':'Item-28'}, {'CatgryName':'ThirdCategory', 'GroupName':'Group-9', 'ItemName':'Item-29'} ] return ItmRecSet ############################## Category Groups Tree List ############################## class CatGroupTree(QTreeView): def __init__(self, parent): QTreeView.__init__(self, parent) self.CntrPane = parent self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(['Category/Group']) self.setModel(self.model) self.clicked.connect(self.__itemSingleClicked) self.setEditTriggers(QAbstractItemView.NoEditTriggers) @property def CntrPane(self): return self.__parent @CntrPane.setter def CntrPane(self, value): self.__parent = value def SetContent(self, tmpCatGrpList): self.model.setRowCount(0) TreeRoot = self.model.invisibleRootItem() SeenCat = {} QuedData = deque(tmpCatGrpList) while QuedData: Itm = QuedData.popleft() CurNode = TreeRoot CatNam = Itm['CatgryName'] GrpNam = Itm['GroupName'] if CatNam in SeenCat: CurNode = SeenCat[CatNam] CurNode.appendRow([QStandardItem(GrpNam)]) else: CurNode.appendRow([QStandardItem(CatNam)]) if len(GrpNam) > 0: CurNode = CurNode.child(CurNode.rowCount() - 1) CurNode.appendRow([QStandardItem(GrpNam)]) SeenCat[CatNam] = CurNode # Initialize Selection to First Row 'All' self.setCurrentIndex(self.model.index(0, 0)) def __itemSingleClicked(self, index): Item = self.selectedIndexes()[0] ItemVal = Item.model().itemFromIndex(index).text() self.CntrPane.RefrshTreeSelctd(ItemVal) ############################## Item Data List ############################## class ItemDsplyr(QTreeView): def __init__(self, parent): QTreeView.__init__(self, parent) self.CntrPane = parent self.model = QStandardItemModel(0, 3) self.model.setHorizontalHeaderLabels(['CatgryName', 'GroupName', 'ItemName']) self.setModel(self.model) self.clicked.connect(self.__itemSingleClicked) @property def CntrPane(self): return self.__parent @CntrPane.setter def CntrPane(self, value): self.__parent = value def SetContent(self, tmpItmList): self.model.setRowCount(0) for Item in tmpItmList: if Item['CatgryName'] != 'All': ItmNam = QStandardItem(Item['ItemName']) CatNam = QStandardItem(Item['CatgryName']) GrpNam = QStandardItem(Item['GroupName']) self.model.appendRow([CatNam, GrpNam, ItmNam]) def __itemSingleClicked(self, index): Item = self.selectedIndexes()[0] ItemVal = Item.model().itemFromIndex(index).text() self.CntrPane.RefrshListSelctd(ItemVal) class CenterPane(QWidget): def __init__(self, parent, dbase): QWidget.__init__(self) # The Parent (MainWindow) handle is being passed in just in case the CenterPane needs to communicate back up to the MainWindow # Other things can be passed in as well if needed such as a handle to the database self.MainWin = parent self.Dbase = dbase self.TreeItem = 'All' self.ListItem = '' CatGrpList = self.Dbase.GetCatGroups() ItemList = self.Dbase.GetItemData(self.TreeItem) self.CatGrpTree = CatGroupTree(self) self.CatGrpTree.SetContent(CatGrpList) self.ItemDsply = ItemDsplyr(self) self.ItemDsply.SetContent(ItemList) CntrPane = QSplitter(Qt.Horizontal, self) CntrPane.addWidget(self.CatGrpTree) CntrPane.addWidget(self.ItemDsply) CntrPane.setSizes([75,200]) hbox = QHBoxLayout(self) hbox.addWidget(CntrPane) self.setLayout(hbox) @property def MainWin(self): return self.__parent @MainWin.setter def MainWin(self, value): self.__parent = value # Local Handle to the Database @property def Dbase(self): return self.__Database @Dbase.setter def Dbase(self, value): self.__Database = value @property def TreeItem(self): return self.__treeItmSelctd @TreeItem.setter def TreeItem(self, value): self.__treeItmSelctd = value @property def ListItem(self): return self.__listItmSelctd @ListItem.setter def ListItem(self, value): self.__listItmSelctd = value def RefrshTreeSelctd(self, TreeItmSlctd): print("Tree Item Clicked:",TreeItmSlctd) if TreeItmSlctd != self.TreeItem: self.TreeItem = TreeItmSlctd #Retrieve new data and repopulate List ItmList = self.Dbase.GetItemData(self.TreeItem) self.ItemDsply.SetContent(ItmList) self.ListItem = '' def RefrshListSelctd(self, LstItmSlctd): print("List Item Clicked:",LstItmSlctd) if LstItmSlctd != self.ListItem: self.ListItem = LstItmSlctd print("******* New Item") class MenuToolBar(QDockWidget): def __init__(self, parent): QDockWidget.__init__(self) self.MainMenu = parent.menuBar() # ******* File Menu Items ******* self.FileMenu = self.MainMenu.addMenu('File') self.OpenFileAct = QAction(QIcon('images/open.ico'), 'Open File', self) self.OpenFileAct.setShortcut("Ctrl+O") self.OpenFileAct.setStatusTip('Open an Existing File') self.OpenFileAct.triggered.connect(self.__windowMenuFile) self.FileMenu.addAction(self.OpenFileAct) self.InitToolBar(parent) def InitToolBar(self, parent): # Add Menu item to the Toolbar self.mainToolBar = parent.addToolBar("Quick Access") self.mainToolBar.addAction(self.OpenFileAct) def __windowMenuFile(self): # Stub function just to give something to play with (value, choice) = QInputDialog.getText( self, "Retrieve File","Please provide a file to retrieve", QLineEdit.Normal, "FilePath/FileName.ext") if choice: print("Choice :",choice) class Window(QMainWindow): def __init__(self, DatabaseFilePath, parent=None): super(Window, self).__init__(parent) self.dbase = sqlDatabase(DatabaseFilePath) LeftCorner = 100 # Distance in from the left edge of the screen TopCorner = 100 # Distance down from the top edge of the screen WindowWidth = 800 WindowHeight = 600 MinWndowWdth = 425 WindowIcon = 'images/favicon.png' # Locate the images folder where this program is run from self.setWindowTitle('Main Window') self.setGeometry(LeftCorner, TopCorner, WindowWidth, WindowHeight) self.setWindowIcon(QIcon(WindowIcon)) self.setMinimumWidth(MinWndowWdth) # A Window is made of of these regions from top to bottom: Menu Bar, Toolbar, Center Pane, and Status Bar # A class should be created to handle each one of these as separate units 2 examples are given self.setCentralWidget(CenterPane(self, self.dbase)) # Note the Toolbar is simply an extension of the Menu system and are dealt with as one unit self.MenuToolBar = MenuToolBar(self) self.SetStatusBar() self.setStyle(QStyleFactory.create('Cleanlooks')) def SetStatusBar(self): # Basically a stub for augmentation later on StatusMsg = '' self.StatBar = self.statusBar() if len(StatusMsg) < 1: StatusMsg = 'Ready' self.StatBar.showMessage(StatusMsg) if __name__ == "__main__": # This uses/receives no command line arguements baseThread = QApplication([]) DbaseToUse = 'FullDatabasePathAndName' baseGUI = Window(DbaseToUse) #----- This defines and implements the GUI interface baseGUI.show() sys.exit(baseThread.exec_()) #----- This launches the baseThread within which the baseGUI is contained