Forum: PC-Programmierung Strukturieren von PyQt-Code


von Hendrik W. (derhendrik)


Lesenswert?

Hallo Community!

Auch ich versuche mich gerade an der Programmierung einer 
Maschinensteuerung inkl. Visualisierung mit Python bzw. PyQt.

Da dies im Rahmen einer Studienarbeit geschieht, würde ich gerne 
möglichst sauberen Code schreiben. Leider finde ich im Internet nur 
wenige umfangreiche Beispiele. (falls ihr also ein gutes Beispiel, 
ähnlich wie: https://gist.github.com/PhilReinhold/05b475549e4c37e2f95a, 
was schlussendlich so: http://i.imgur.com/uuS3YPM.jpg aussieht, kennt, 
immer her damit :) )

Als Nicht-Informatiker habe ich leider (noch?) kein gutes Gespür für die 
Strukturierung meines Codes entwickelt.

Konkret geht es bei meiner Frage um die Erzeugung von Klassen und deren 
Instanzen und wie ich dies sinnvoll gestalte.

Vorweg eine ganz banale Frage: Macht es Sinn, eine Klasse zu erstellen, 
wenn es von dieser nur eine Instanz gibt?

Gerne würde ich mehrere Tabs haben, wozu ich vorerst im mainwindow ein 
Tabwidget erstelle. Nun ähneln sich die unterschiedlichen Tabs teilweise 
(beispielsweise im Layout), unterscheiden sich aber entsprechend auch 
(beispielsweise für die Beschriftung der Buttons und Labels).

Meine Frage ist es jetzt:
Wird nun für jeden eigenen Tab eine eigene Klasse erstellt (A) oder 
sollte eine einzelne Klasse für alle Tabs erstellt werden (B)?

Hier mal Codeschnipsel der beiden Möglichkeiten:

A) Jeder Tab eine Klasse
1
class MainWindow(QtGui.QMainWindow):
2
   
3
    def __init__(self):
4
        super(MainWindow,self).__init__()
5
        self.createComponents()
6
        self.setupTabs()
7
        self.createLayout()
8
9
    def createComponents(self):
10
        self.tabwidget = QtGui.QTabWidget()
11
   
12
    def setupTabs(self):        
13
        self.tab1 = TabInhalt1(self)
14
        self.tab2 = TabInhalt2(self)
15
        self.tab3 = TabInhalt3(self)      
16
       
17
    def createLayout(self):
18
        layoutZentral = QtGui.QVBoxLayout()
19
        layoutZentral.addWidget(self.tabwidget)
20
        widgetZentral = QtGui.QWidget()
21
        widgetZentral.setLayout(layoutZentral)  
22
        self.setCentralWidget(widgetZentral)
23
24
class TabInhalt1(self):
25
    def __init__(self):
26
        super(TabInhalt1,self).__init__()
27
        (Hier alles rein, was in Tab 1 gehört)
28
        mainwindow.tabwidget.addTab(self)
29
30
class TabInhalt2(self):
31
    def __init__(self):
32
        super(TabInhalt2,self).__init__()
33
        (Hier alles rein, was in Tab 2 gehört)
34
        mainwindow.tabwidget.addTab(self)
35
36
class TabInhalt3(self):
37
    def __init__(self):
38
        super(TabInhalt2,self).__init__()
39
        (Hier alles rein, was in Tab 3 gehört)
40
        mainwindow.tabwidget.addTab(self)

B) Eine Klasse für alle Tabs(so habe ich es zurzeit gemacht)
1
class MainWindow(QtGui.QMainWindow):
2
   
3
    def __init__(self):
4
        super(MainWindow,self).__init__()
5
        self.createComponents()
6
        self.setupTabs()
7
        self.createLayout()
8
9
    def createComponents(self):
10
        self.tabwidget = QtGui.QTabWidget()
11
   
12
    def setupTabs(self):        
13
        self.tab1 = TabInhalt(self,'Seite_1')
14
        self.tab2 = TabInhalt(self,'Seite_2')
15
        self.tab3 = TabInhalt(self,'Seite_3')      
16
       
17
    def createLayout(self):
18
        layoutZentral = QtGui.QVBoxLayout()
19
        layoutZentral.addWidget(self.tabwidget)
20
        widgetZentral = QtGui.QWidget()
21
        widgetZentral.setLayout(layoutZentral)  
22
        self.setCentralWidget(widgetZentral)
23
24
class TabInhalt(QtGui.QWidget):
25
    def __init__(self,mainwindow,tabname):
26
        self.tabname = tabname
27
        super(TabInhalt,self).__init__()              
28
        self.populate_tab(self.tabname)
29
        mainwindow.tabwidget.addTab(self, self.tabname)
30
 
31
    def populate_tab(self, tabname):
32
33
        #An dieser Stelle vorerst die "gemeinsamen" Elemente erstellen, dann folgt Fallunterscheidung um welchen Tab es sich handelt
34
35
36
              
37
        #Tab 1
38
        if (self.tabname=='Seite_1'):
39
            self.labelHalloWelt =  QtGui.QLabel("Hallo Welt")
40
            self.labelHalloMom = QtGui.QLabel("Hallo Mom")
41
            self.labelHalloDad = QtGui.QLabel("Hallo Dad")  
42
           
43
            layout = QtGui.QVBoxLayout()            
44
            layout.addWidget(self.labelHalloWelt)
45
            layout.addWidget(self.labelHalloMom)
46
            layout.addWidget(self.labelHalloDad)            
47
           
48
            self.setLayout(layout)
49
       
50
        #Tab 2  
51
        elif (self.tabname =='Seite_2'):
52
            return #(Hier habe ich mal den Code für die Leserlichkeit abgekürzt)
53
 
54
        #Tab 3  
55
        elif (self.tabname =='Seite_3'):
56
            return #(Hier habe ich mal den Code für die Leserlichkeit abgekürzt)

Ich hoffe, dass ich es irgendwie verständlich machen konnte, wo mein 
Problem liegt. Würde mich über eine Rückmeldung freuen! Falls ihr 
irgendwelche Guidelines zur Strukturierung von PyQt-Code kennt, freue 
ich mich natürlich auch darüber.

Grüße und Danke!

Hendrik

von Noch einer (Gast)


Lesenswert?

Deine Objekte sind ja Stellvertreter für Gegenstände in der realen Welt. 
Einfacher Grundsatz: wenn du für jeden Gegenstand ein Objekt anlegst, 
wird es am übersichtlichsten. So ein Tab als realen Gegenstand 
bezeichnen, ist vielleicht etwas schräg. Anfangs wurde 1:1 ein realer 
Schreibtisch mit Ablagen und Papierkorb nach gebaut. Dann hat es sich 
immer weiter von einem realen Schreibtisch entfernt.

Das Problem liegt eher in den Details. Im laufe der Zeit bekommst du 
immer mehr Abhängigkeiten, verlierst den Überblick, an welchen Stellen 
eine Änderung neue Fehler verursacht.

Die eigentliche Frage ist: wie lässt sich vermeiden, dass Tab1 auf die 
Interna von Tab2 zugreifen muss? In der Praxis ist die entscheidende 
Frage: welche Lösung kommt mit weniger Public-Methoden aus.

von Karl Käfer (Gast)


Lesenswert?

Hallo Hendrik,

Hendrik W. schrieb:
> Auch ich versuche mich gerade an der Programmierung einer
> Maschinensteuerung inkl. Visualisierung mit Python bzw. PyQt.
> [...]
> Vorweg eine ganz banale Frage: Macht es Sinn, eine Klasse zu erstellen,
> wenn es von dieser nur eine Instanz gibt?

Ja.

> Gerne würde ich mehrere Tabs haben, wozu ich vorerst im mainwindow ein
> Tabwidget erstelle. Nun ähneln sich die unterschiedlichen Tabs teilweise
> (beispielsweise im Layout), unterscheiden sich aber entsprechend auch
> (beispielsweise für die Beschriftung der Buttons und Labels).
>
> Meine Frage ist es jetzt:
> Wird nun für jeden eigenen Tab eine eigene Klasse erstellt (A) oder
> sollte eine einzelne Klasse für alle Tabs erstellt werden (B)?

Ich persönlich würde zu (C) tendieren: eine BaseTab-Klasse für alle 
Tabs, die von QWidget erbt, und dann für jeden Tab eine eigene Klasse, 
die von Deiner BaseTab-Klasse erbt.
1
class BaseTab(QtGui.QWidget): ...
2
class TabEins(BaseTab): ...
3
class TabZwei(BaseTab): ...
4
5
class MainWin(QtGui.QMainWindow):
6
  ...
7
  def createComponents(self):
8
    self.tabwidget = QtGui.QTabWidget()
9
    self.tabwidget.addTab(TabEins(), "TabEins")
10
    self.tabwidget.addTab(TabZwei(), "TabZwei")

Vorteil: Du kannst die Tabs über eine gemeinsame Schnittstelle 
ansprechen, die Du in BaseTab implementierst, um Deine Tabs damit über 
Ereignisse wie beispielsweise das Eintreffen neuer Daten zu informieren.

> Ich hoffe, dass ich es irgendwie verständlich machen konnte, wo mein
> Problem liegt. Würde mich über eine Rückmeldung freuen! Falls ihr
> irgendwelche Guidelines zur Strukturierung von PyQt-Code kennt, freue
> ich mich natürlich auch darüber.

Naja, hier geht es weniger um Python- oder Qt-Code, sondern um das 
Design der Architektur von objektorientierter Software, insofern wirst 
Du fündig, wenn Du nach "OO Design" und "Design Patterns" suchst. 
Speziell für Python kenne ich da leider nicht sehr viel, aber die 
Erfahrungen und Erkenntnisse aus anderen Programmiersprachen wie Java 
oder C++ lassen sich meist leicht auf Python übertragen.

HTH,
Karl

von Hendrik W. (derhendrik)


Lesenswert?

Hallo Karl!

Erstmal vielen Dank für deine Hilfe, zu diesem Ansatz mit einer 
Basisklasse wurde mir auch im Pythonforum geraten. (Hier mal mein 
Beitrag, hoffe mal verlinken von anderen Foren ist in Ordnung: 
http://www.python-forum.de/viewtopic.php?f=24&t=36815)

Mir kam es am Anfang halt etwas komisch vor, eine Klasse für nur eine 
Instanz zu erstellen. Meine Vermutung war, dass man Klassen erstellt, um 
sich gewisse Gemeinsamkeiten der Instanzen (Attribute und Methoden) zu 
Nutze zu machen.

Vielen Dank nochmal!

Hendrik

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.