[Python / PyQt5] Feedback op opzet

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Shinji
  • Registratie: Februari 2002
  • Nu online
Hi!

Ik ben me aan het verdiepen in Python en dan met name PyQt5. Ik heb diverse resources gebruikt (learnpyqt.com en voorbeelden van stackoverflow o.a.). En ben tot een basis opzet gekomen van hoe ik mijn applicatie er uit wil laten zien.

Echter ontbreekt het mij aan een goede mentor en sparring partner waar ik om adviezen kan vragen of de opzet een beetje handig is.

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import sys

from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QAction, QTabWidget
from PyQt5.QtGui import QPalette, QColor

class MainWindow(QMainWindow):

    pages = []

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # MainWindow settings
        self.setWindowTitle("My Awesome App")

        # create menu and tabs
        menu = self.menuBar()
        tabs = QTabWidget()

        self.define_menu(menu, tabs)

        # tab settings
        tabs.setDocumentMode(True)
        tabs.setMovable(True)
        tabs.setTabsClosable(True)

        # set tab widget
        self.setCentralWidget(tabs)

        # set events/signals
        tabs.tabCloseRequested.connect(lambda x: self.tabToggled(self.pages[x],tabs))


    def tabToggled(self, t, tabs, checked=None):
        # if tab is activated via menu checked is True, else tab needs to be removed
        if checked:
            self.pages.append(t)
            tabs.addTab(t(), t.name)
        else:
            # get dict index
            index = self.pages.index(t)

            # get corresponding tabindex
            for i in range(0,tabs.count()):
                if (t.name == tabs.widget(i).name):
                    tabindex = tabs.indexOf(tabs.widget(i))

            # remove checkmarks in menu if tab is closed on tab itself
            if t.name == 'page1':
                self.page1_action.setChecked(False)
            elif t.name == 'page2':
                self.page2_action.setChecked(False)

            # remove from dict and QTabWidget
            del self.pages[index]
            tabs.removeTab(tabindex)


    def define_menu(self, menu, tabs):
        file_menu = menu.addMenu('&File')

        # Set menu options as class variables so they can be updated from the tabs
        self.page1_action = QAction('Page&1', self)
        self.page1_action.triggered.connect(lambda x: self.tabToggled(Page1, tabs, x))
        self.page1_action.setCheckable(True)

        self.page2_action = QAction('Page&2', self)
        self.page2_action.triggered.connect(lambda x: self.tabToggled(Page2, tabs, x))
        self.page2_action.setCheckable(True)

        # Add options to menu
        file_menu.addAction(self.page1_action)
        file_menu.addAction(self.page2_action)


class Page1(QWidget):

    # Tab 1
    name = 'page1'

    def __init__(self, *args, **kwargs):
        super(Page1, self).__init__(*args, **kwargs)
        self.setAutoFillBackground(True)

        palette = self.palette()
        palette.setColor(QPalette.Window, QColor('blue'))
        self.setPalette(palette)


class Page2(QWidget):

    # Tab 2
    name = 'page2'

    def __init__(self, *args, **kwargs):
        super(Page2, self).__init__(*args, **kwargs)
        self.setAutoFillBackground(True)

        palette = self.palette()
        palette.setColor(QPalette.Window, QColor('red'))
        self.setPalette(palette)

app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec_()


Afbeeldingslocatie: https://tweakers.net/i/4YwvdlRCiB36T5wgj7Dj3QDg_EI=/full-fit-in/4000x4000/filters:no_upscale():fill(white):strip_exif()/f/image/J11xIKrn360DVkZlc2g1U1yq.png?f=user_large

In het menu zitten de twee opties met de tabs met een checkbox ervoor, als je die aanzet verschijnt de tab, als je die uit zet wordt die verwijderd. Werkt in principe zoals ik het zou verwachten.

Zaken waar ik in elk geval over twijfel:
1. Ik heb nu de menu items klasse variabelen gemaakt zodat ze geupdate kunnen worden als iemand op het kruisje op het tabblad klikt. Heel erg netjes leek me dit niet maar ik kon geen andere manier bedenken hoe ik dit anders zou kunnen oplossen.
2. Ik heb nu eigenlijk een dict met daarin de tabbladen en een QTabWidget met daarin hetzelfde. Dat lijkt me dubbelop en ik vroeg me af of dat niet eenvoudiger kon. De dict hou ik nu bij omdat ik moet weten op welke tab er is geklikt Ik ben dit nu al een tijdje aan het proberen en ik kom er telkens op dat als ik een nieuwe tab toevoeg ik werk met een QWidget (tab) maar als ik hem verwijder ik met een index van het tabblad werk. Als het afzonderlijke calls waren had ik er twee afzonderlijke functies van kunnen maken, maar vanuit het menu kan ik voor zover ik weet maar 1 call doen. Om een of andere manier lukt het mij niet om dit uniform te krijgen zodat ik maar een van de twee hoef bij te houden.

Buiten de specifieke punten, was ik benieuwd of hier enorme design flaws in zitten, dingen die handiger kunnen etc. Ik heb de code nog niet echt gechecked op conformiteit met de regels (PEP8?) etc. Het gaat me meer om de globale gedachtengang.

Doel van de applicatie is dat ik voor mezelf een soort boilerplate heb waar ik makkelijk een nieuw tabblad met een nieuwe functie in kan toevoegen. Ik maak dan een nieuwe class aan voor die tab en hoef in de basis maar een paar regels toe te voegen zodat het tabblad te gebruiken is,

Acties:
  • 0 Henk 'm!

  • Shinji
  • Registratie: Februari 2002
  • Nu online
Vanacht in ieder geval bedacht hoe ik de dict weg kon werken zodat ik geen dubbele administratie heb:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import sys

from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QAction, QTabWidget
from PyQt5.QtGui import QPalette, QColor

class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # MainWindow settings
        self.setWindowTitle("My Awesome App")

        # create menu and tabs
        menu = self.menuBar()
        tabs = QTabWidget()

        self.define_menu(menu, tabs)

        # tab settings
        tabs.setDocumentMode(True)
        tabs.setMovable(True)
        tabs.setTabsClosable(True)

        # set tab widget
        self.setCentralWidget(tabs)

        # set events/signals
        tabs.tabCloseRequested.connect(lambda x: self.tabToggled(tabs,self.getTab(tabs,x)))


    def tabToggled(self, tabs, t, checked=None):
        # if tab is activated via menu checked is True, else tab needs to be removed
        if checked:
            tab = t()
            tabs.addTab(tab, tab.name)
        else:
            # get tab index
            tabindex = self.getTabIndex(tabs,t)

            # remove checkmarks in menu if tab is closed on tab itself
            if t.name == 'page1':
                self.page1_action.setChecked(False)
            elif t.name == 'page2':
                self.page2_action.setChecked(False)

            # remove from QTabWidget
            tabs.removeTab(tabindex)


    def define_menu(self, menu, tabs):
        file_menu = menu.addMenu('&File')

        # Set menu options as class variables so they can be updated from the tabs
        self.page1_action = QAction('Page&1', self)
        self.page1_action.triggered.connect(lambda x: self.tabToggled(tabs, Page1, x))
        self.page1_action.setCheckable(True)

        self.page2_action = QAction('Page&2', self)
        self.page2_action.triggered.connect(lambda x: self.tabToggled(tabs, Page2, x))
        self.page2_action.setCheckable(True)

        # Add options to menu
        file_menu.addAction(self.page1_action)
        file_menu.addAction(self.page2_action)


    def getTabIndex(self, tabs, tab):
        for i in range(0, tabs.count()):
            # loop through all tabs and check name against name of given tab
            if (tab.name == tabs.widget(i).name):
                tabindex = tabs.indexOf(tabs.widget(i))
                print(tabindex)
                return tabindex
            else:
                # not found yet
                continue

    def getTab(self, tabs, index):
        return tabs.widget(index)

class Page1(QWidget):

    # Tab 1
    name = 'page1'

    def __init__(self, *args, **kwargs):
        super(Page1, self).__init__(*args, **kwargs)
        self.setAutoFillBackground(True)

        palette = self.palette()
        palette.setColor(QPalette.Window, QColor('blue'))
        self.setPalette(palette)

class Page2(QWidget):

    # Tab 2
    name = 'page2'

    def __init__(self, *args, **kwargs):
        super(Page2, self).__init__(*args, **kwargs)
        self.setAutoFillBackground(True)

        palette = self.palette()
        palette.setColor(QPalette.Window, QColor('red'))
        self.setPalette(palette)

app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec_()


Dus graag deze versie in ogenschouw nemen ;)

Acties:
  • 0 Henk 'm!

  • jomas
  • Registratie: Mei 2011
  • Laatst online: 17-09 10:57
Met "Qt Designer" kan je heel eenvoudig je grafische interface ontwerpen. Als het goed is zit dit ook in PyQt5.
Je kan dan ook heel eenvoudig de signals van de interface via "def on_xxx_yyy" verbinden. xxx=elementnaam, yyy=signal.

Acties:
  • 0 Henk 'm!

  • Shinji
  • Registratie: Februari 2002
  • Nu online
jomas schreef op vrijdag 5 juni 2020 @ 18:34:
Met "Qt Designer" kan je heel eenvoudig je grafische interface ontwerpen. Als het goed is zit dit ook in PyQt5.
Je kan dan ook heel eenvoudig de signals van de interface via "def on_xxx_yyy" verbinden. xxx=elementnaam, yyy=signal.
Ik ken Qt Designer inderdaad, alleen gaat het mij niet om hoe ik zo eenvoudig mogelijk een GUI in elkaar zet maar of ik begrijp wat ik doe en tips voor wat er handiger kan qua opzet. Ik kan me voorstellen dat je bijvoorbeeld de koppeling vanuit tabblad sluiten naar vinkje weghalen in het menu zo eenvoudig in elkaar klikt in Designer en is het meer handig voor de positionering van de elementen etc. Maar al zou het wel kunnen, het was mijn doel niet ;)

Acties:
  • 0 Henk 'm!

  • Immutable
  • Registratie: April 2019
  • Laatst online: 16-09 20:06
Shinji schreef op vrijdag 5 juni 2020 @ 19:09:
[...]


Ik ken Qt Designer inderdaad, alleen gaat het mij niet om hoe ik zo eenvoudig mogelijk een GUI in elkaar zet maar of ik begrijp wat ik doe en tips voor wat er handiger kan qua opzet. Ik kan me voorstellen dat je bijvoorbeeld de koppeling vanuit tabblad sluiten naar vinkje weghalen in het menu zo eenvoudig in elkaar klikt in Designer en is het meer handig voor de positionering van de elementen etc. Maar al zou het wel kunnen, het was mijn doel niet ;)
Je vraag is een beetje onduidelijk maar ik gooi gewoon een dartpijltje. Ben je bekend met MVC patroon? Model View Controller?

Model bevat de mutabele data.
View bevat de beschrijving hoe je GUI eruit ziet voor de gebruiker.
Controller bevat de business logica.

Het is een hele oude patroon, maar prima hier toe te passen in Python met QT5. Het is zelfs nog handiger als je bijvoorbeeld QTquick gaat gebruiken(omdat QML dan alleen je view laag mag zijn en via slots en signals communiceert met de controller en model), en mooie interfaces gaat bouwen zoals Tesla in hun auto heeft. (Ze gebruiken namelijk QTquick voor hun fancy interfaces).

Anyway.. .terug naar MVC. Als je hier niet bekend mee bent is er betreft Python met QT5 echt heel veel te vinden via een simpele google search. Tik in "PyQt5 MVC" https://lmgtfy.com/?q=PyQt5+MVC

(p.s. disclaimer: Ik ben geen programmeur, dus er zijn vast slimmere mensen met betere antwoorden)

[ Voor 12% gewijzigd door Immutable op 06-06-2020 12:40 ]