Python Forum

Full Version: Is there a better way of laying out my GUI?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I have created a simple GUI using PyQt5. However, I can't seem to find a a good way to arange my widgets. I tried groups but with my current code, I would have groups inside of groups, which starts to get confusing - it also doesn't give me that much control over where I place my widgets.
This is what I have so far:
import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class Settings():
    SCENE_SIZE_X = 1200
    SCENE_SIZE_Y = 900
    GRID_COUNT_X = 4
    GRID_COUNT_Y = 5
    GRID_BOX_WIDTH = 10
    GRID_BOX_HEIGHT = 10

class Grid(QtWidgets.QGraphicsScene):

    def __init__(self):
        super().__init__()

        self.lines = []
        self.initUI()

    def initUI(self):
        self.draw_grid()
        self.set_opacity(1.0)

    def draw_grid(self):
        width = Settings.GRID_COUNT_X * Settings.GRID_BOX_WIDTH
        height = Settings.GRID_COUNT_Y * Settings.GRID_BOX_HEIGHT
        self.setSceneRect(0, 0, width, height)
        self.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)

        pen = QPen(QColor(255,0,255), 1, Qt.SolidLine)

        for x in range(0,Settings.GRID_COUNT_X+1):
            xc = x * Settings.GRID_BOX_WIDTH
            self.lines.append(self.addLine(xc,0,xc,height,pen))

        for y in range(0,Settings.GRID_COUNT_Y+1):
            yc = y * Settings.GRID_BOX_HEIGHT
            self.lines.append(self.addLine(0,yc,width,yc,pen))

    def set_opacity(self,opacity):
        for line in self.lines:
            line.setOpacity(opacity)

class ShowGrid(QtWidgets.QWidget):
    
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        self.createGridLayout()
        
        gridLayout = QHBoxLayout()
        gridLayout.addWidget(self.horizontalGroupBox)
        self.setLayout(gridLayout)

    def createGridLayout(self):
        self.horizontalGroupBox = QGroupBox("Board")

        layout = QGridLayout()
        layout.addWidget(QtWidgets.QGraphicsView(Grid()))
        self.horizontalGroupBox.setLayout(layout)

class ShowDice(QtWidgets.QWidget):
    
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        self.createGridLayout()
        
        gridLayout = QVBoxLayout()
        gridLayout.addWidget(self.horizontalGroupBox)
        self.setLayout(gridLayout)

    def createGridLayout(self):
        self.horizontalGroupBox = QGroupBox("Roll Dice")

        layout = QGridLayout()
        layout.setColumnStretch(0, 1)
        layout.addWidget(QPushButton('Roll'),0,1)
        self.horizontalGroupBox.setLayout(layout)

class App(QtWidgets.QDialog):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 100, Settings.SCENE_SIZE_X, Settings.SCENE_SIZE_Y)

        self.createGridLayout()
        
        gridLayout = QVBoxLayout()
        gridLayout.addWidget(self.horizontalGroupBox)
        self.setLayout(gridLayout)

    def createGridLayout(self):
        self.horizontalGroupBox = QGroupBox("")

        layout = QGridLayout()
        layout.addWidget(ShowGrid(),0,1)
        layout.addWidget(ShowDice(),1,0)
        self.horizontalGroupBox.setLayout(layout)    
        

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle("Fusion")
    ex = App()
    ex.show()
    #scene.resize(Settings.SCENE_SIZE_X,Settings.SCENE_SIZE_Y)
    sys.exit(app.exec_())
And if it is run then you get this:
[attachment=627]
which looks messy because it has groups in groups and isn't arranged neatly. What is the best way to arrange these widgets (and others in the future) so I have lots of control over where they are placed?

Thanks,
Dream

edit: corrections

This is my new code, slightly improved:
import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class Settings():
    SCENE_SIZE_X = 1200
    SCENE_SIZE_Y = 900
    GRID_COUNT_X = 4
    GRID_COUNT_Y = 5
    GRID_BOX_WIDTH = 100
    GRID_BOX_HEIGHT = 100

class Grid(QtWidgets.QGraphicsScene):

    def __init__(self):
        super().__init__()

        self.lines = []
        self.scrolled = 0
        self.initUI()

    def initUI(self):
        self.draw_grid(Settings.GRID_COUNT_X, Settings.GRID_COUNT_Y, Settings.GRID_BOX_WIDTH, Settings.GRID_BOX_HEIGHT)
        self.set_opacity(1.0)

    def draw_grid(self, x_count, y_count, x_size, y_size):
        width = x_count * x_size
        height = y_count * y_size
        self.setSceneRect(0, 0, width, height)
        self.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)

        pen = QPen(QColor(0,0,0), 1, Qt.SolidLine)

        for x in range(0,x_count+1):
            xc = x * x_size
            self.lines.append(self.addLine(xc,0,xc,height,pen))
 
        for y in range(0,y_count+1):
            yc = y * y_size
            self.lines.append(self.addLine(0,yc,width,yc,pen))

    def set_opacity(self,opacity):
        for line in self.lines:
            line.setOpacity(opacity)

    def delete_grid(self):
        for line in self.lines:
            self.removeItem(line)
        del self.lines[:]

    def wheelEvent(self,event):
        self.scrolled += event.delta()/90
        if(self.scrolled > -30.0):
            self.delete_grid()
            self.draw_grid(Settings.GRID_COUNT_X, Settings.GRID_COUNT_Y, Settings.GRID_BOX_WIDTH + self.scrolled, Settings.GRID_BOX_HEIGHT + self.scrolled)
        else:
            self.scrolled = -30.0
            
class App(QtWidgets.QDialog):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 100, Settings.SCENE_SIZE_X, Settings.SCENE_SIZE_Y)

        layout = QVBoxLayout()
        layout.addWidget(QtWidgets.QGraphicsView(Grid()))
        layout.addWidget(QtWidgets.QPushButton("Hello!"))
        self.setLayout(layout)
        


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle("Fusion")
    ex = App()
    ex.show()
    sys.exit(app.exec_())
It still runs, I just don't know how to position these elements.
Sorry that you've had no reply's, i'm not sure if there is anyone around that uses pyqt on a regular basis.
The boxlayouts seem to be the equivalent of wxpython's boxsizers, so you are probably on the right track.
In what way are the widgets not positioned to how you would like them to be ?
You can align your two group boxes into a vertical layout, as it seems you don't need to use a grid at the moment. I suggest you begin by making the layout you want in Qt Designer, then convert the ui file to python and have a look at the output. It can usually be stripped down a lot from there, but at least you will get an idea of how you can organize your widgets.
Okay first off if you do this:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
You do not need this:

from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
And the latter does not work well (if at all) within pyqt5

Next you want to display it like a Window then you need to use one as follows:

class Window(QMainWindow):
    def __init__(self, DatabaseFile, parent=None):
        super(Window, self).__init__(parent)

        self.setCentralWidget(CenterPane(self))

class CenterPane(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self)

        self.Panel1 = CreatePanel1()
        self.Panel2 = CreatePanel2()

        CenterPane = QSplitter(Qt.Horizontal, self)
        CenterPane.addWidget(self.Panel1)
        CenterPane.addWidget(self.Panel2)
        CenterPane.setSizes([100,200])

        hbox = QHBoxLayout(self)
        hbox.addWidget(CenterPane)

        self.setLayout(hbox)
Note: I am fairly new to pyqt5 so there might be a better way to implement the above but so far this has worked for me and perhaps you can glean something form it that will help you. Also the above is an extremely small snippet just to show you how to layout the center pane of a window as I found that a bit of an issue initially myself
Can you post a sketch of how you would like your UI to look when it's "finished"? We can then show you how we would go about laying it out, and you can pick which you like :)