Forum: PC-Programmierung Datenhaltung Python GUI


von Karl (Gast)


Lesenswert?

Hallo,

ich möchte ein Programm mit einer GUI mit wxPython schreiben. Ich habe 
verschiedenen Klassen definiert, in denen die Daten gespeichert werden 
sollen (diese werden dann an andere Funktionsklassen übergeben und 
berechnen das Ergebnis. Das Backend ist fertig.). Die GUI habe ich als 
wx.Notebook konzipiert. Nun frage ich mich, wie bzw. wo ich sie Daten 
der Klassen speichern soll. Die Daten sollen z.b. mit einem wx.TextCtrl 
auf einem Panel eingegeben werden und bei einem Event (Button, Change 
...) kann ich die Eingabe des TextCtrl dann einer Instanz der Klasse 
übergeben. Die Frage ist, wo "deklariere" ich diese? Quick und Dirty 
wäre natürlich das global zu machen. Alternativ könnte ich mir 
vorstellen das z.B. an den Frame oder das Panel anzuhängen. Dann muss 
man aber z.B. über GetParent() darauf zugreifen und wenn man später an 
der Struktur der GUI was ändert, dann muss man auch alles überarbeiten. 
Gibt es bessere Möglichkeiten?

von MaWin (Gast)


Lesenswert?

Code zeigen

von Heiner (Gast)


Lesenswert?

MVC ist bekannt? Die Klassendeklaration gehören ins model, der 
controller zieht die Daten aus der view, sobald das entsprechende 
Ereignis eingetreten ist, und setzt sie zu einer Instanz deiner 
Datenklasse zusammen. Dann wird sie dort entweder weiterverarbeitet oder 
geht zwecks Persistenz ins model.

Da die Geschichte nun leider schon halb fertig gestrickt ist, könnte es 
etwas fummelig werden, diese Struktur nachträglich herzustellen. Das ist 
die Folge davon, sich über solche Dinge nicht Gedanken zu machen, bevor 
auch nur eine einzige Zeile Code geschrieben ist. Keine Panik, nächstes 
mal machst du es besser.

MaWin schrieb:
> Code zeigen

Hör auf, die Leute zu nerven. Wozu soll das auf diesem 
Abstraktionsniveau gut sein?

von Karl (Gast)


Lesenswert?

1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
@author: 
5
"""
6
7
import wx
8
import wx.dataview as dv
9
import os
10
import sys
11
from scr.entry import *
12
13
14
_ = wx.GetTranslation
15
16
17
def getNextImageID(count):
18
    imID = 0
19
    while True:
20
        yield imID
21
        imID += 1
22
        if imID == count:
23
            imID = 0
24
class MyDisplaySettings():
25
    def __init__(self):
26
        '''Settings for various Displays
27
        
28
        ToolSize - Symboly of ToolBar
29
        FrameSize - Main Window
30
        BookSize - Symbols of Toolbook
31
        '''
32
33
        #get horizontal resulution of smalest Display
34
        MinHorSize = 10000 
35
        Displays = (wx.Display(i) for i in range(wx.Display.GetCount()))
36
        Sizes = [Display.GetGeometry().GetSize() for Display in Displays]
37
        for size in Sizes:
38
            if size[0] < MinHorSize:
39
                MinHorSize = size[0]
40
        #MinHorSize = 1000
41
        if MinHorSize < 1024:
42
            self.ToolSize = (24,24)
43
            self.FrameSize = (800,600)
44
            self.BookSize = (36,36)
45
        else:
46
            self.ToolSize = (48,48)
47
            self.FrameSize = (1440,900)
48
            self.BookSize = (72,72)
49
        self.ProjectParameterColumn = int((self.FrameSize[0]-self.BookSize[0])/4)
50
51
class MyApp(wx.App):
52
    locale = None
53
54
    def OnInit(self):
55
        print('Running wxPython ' + wx.version())
56
        # Set Current directory to the one containing this file
57
        os.chdir(os.path.dirname(os.path.abspath(__file__)))
58
        self.SetAppName('Helical_pile_GUI')
59
        self.InitLanguage()
60
        # Create the main window
61
        myFrame = MyFrame()
62
        self.SetTopWindow(myFrame)
63
        myFrame.Show()
64
        return True
65
66
67
    def InitLanguage(self):
68
        '''Load language settings'''
69
        #https://wiki.wxpython.org/How%20to%20use%20the%20Internationalization%20-%20i18n%20%28Phoenix%29?action=AttachFile&do=view&target=source.zip
70
        langsAvail = {
71
            "System default" : wx.LANGUAGE_DEFAULT,
72
            "English"        : wx.LANGUAGE_ENGLISH,
73
            "German"         : wx.LANGUAGE_GERMAN,
74
        }
75
        lang = wx.LANGUAGE_DEFAULT
76
        wx.Locale.AddCatalogLookupPathPrefix("locale")
77
        self.locale = wx.Locale()
78
        if not self.locale.Init(lang):
79
            wx.LogWarning("This language is not supported by the system.")
80
        if not self.locale.AddCatalog("GUI"):
81
            wx.LogError("Couldn't find/load the 'GUI' catalog for locale '" + self.locale.GetCanonicalName() + "'.")
82
            
83
class MyFrame(wx.Frame):
84
    def __init__(self):
85
        '''Main Frame with Toolbar, Statusbar and Toolbook''' 
86
        DS = MyDisplaySettings() 
87
        super().__init__(None, title=_('GUI'), size = DS.FrameSize)
88
        self.CreateStatusBar()
89
        self.MakeToolBar()
90
        panel = wx.Panel(self)
91
        notebook = MyToolbook(panel)
92
        sizer = wx.BoxSizer(wx.VERTICAL)
93
        sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
94
        panel.SetSizer(sizer)
95
        self.Layout()      
96
        self.Show()
97
    def MakeToolBar(self):
98
        '''Init Toolbar with Buttons'''
99
        DS = MyDisplaySettings()
100
        myToolBar = self.CreateToolBar(wx.TB_TEXT)
101
        # New Button to start new Project
102
        new_bmp =  wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_TOOLBAR, DS.ToolSize)
103
        self.ButtonNew = myToolBar.AddTool(10, "New", new_bmp, wx.NullBitmap, wx.ITEM_NORMAL, "New", "Long help for 'New'", None)
104
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id=10)
105
        self.Bind(wx.EVT_TOOL_RCLICKED, self.OnToolClick, id=10)
106
        #print(self.ButtonNew.GetId())
107
        #Open Button
108
        open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, DS.ToolSize)
109
        
110
    def OnToolClick(self,event):
111
        print('Click')
112
        
113
class MyMainPanel(wx.Panel):
114
    def __init__(self, parent):
115
        super().__init__(parent)
116
        myToolbook = MyToolbook(self)
117
        
118
class MyToolbook(wx.Toolbook):
119
    def __init__(self, parent):
120
        super().__init__(parent, style = wx.BK_LEFT)
121
        self.DS = MyDisplaySettings()
122
        
123
        # make an image list using ./img folder
124
        il = wx.ImageList(self.DS.BookSize[0], self.DS.BookSize[1])
125
        bmp = self.scale_bitmap(wx.Bitmap('./img/prj.png', wx.BITMAP_TYPE_PNG))
126
        il.Add(bmp)
127
        bmp = self.scale_bitmap(wx.Bitmap('./img/soil.png', wx.BITMAP_TYPE_PNG))
128
        il.Add(bmp)
129
        bmp = self.scale_bitmap(wx.Bitmap('./img/strat.png', wx.BITMAP_TYPE_PNG))
130
        il.Add(bmp)
131
        bmp = self.scale_bitmap(wx.Bitmap('./img/geom.png', wx.BITMAP_TYPE_PNG))
132
        il.Add(bmp)
133
        self.AssignImageList(il)
134
        
135
        panel = MyToolbookPanelMaster(self)
136
        PanelProject = MyToolbookProject(self)
137
        self.AddPage(PanelProject,_('Project'), imageId=0)
138
        self.AddPage(panel,_('Soil'), imageId=1)
139
        self.AddPage(panel,_('Layering'), imageId=2)
140
        self.AddPage(panel,_('Geometry'), imageId=3)
141
        
142
    
143
    def scale_bitmap(self, bitmap):
144
        image = bitmap.ConvertToImage()
145
        image = image.Scale(self.DS.BookSize[0], self.DS.BookSize[1], wx.IMAGE_QUALITY_HIGH)
146
        result = wx.Bitmap(image)
147
        return result
148
        
149
class MyToolbookPanelMaster(wx.Panel):
150
    def __init__(self, parent):
151
        super().__init__(parent)
152
        self.SetBackgroundColour(wx.WHITE)
153
        
154
class MyToolbookProject(MyToolbookPanelMaster):
155
    def __init__(self, parent):
156
        super().__init__(parent)
157
        DS = MyDisplaySettings()
158
        BS = wx.BoxSizer(wx.VERTICAL)
159
        
160
        
161
        self.listctrl = listctrl = dv.DataViewListCtrl(self, style = dv.DV_ROW_LINES | dv.DV_VERT_RULES | dv.DV_MULTIPLE | dv.DV_HORIZ_RULES | dv.DV_VARIABLE_LINE_HEIGHT)
162
        
163
164
        listctrl.AppendTextColumn(_("Parameter"), width =  DS.ProjectParameterColumn) #flags=dv.DATAVIEW_COL_RESIZABLE
165
        listctrl.AppendTextColumn(_("Value"), width =  DS.ProjectParameterColumn*2, mode=dv.DATAVIEW_CELL_EDITABLE)
166
        
167
        self.data = data = Project()
168
        data.Name = "Spannendes Projekt"
169
        data.Location = "an einem geheimen Ort"
170
        data.Description = "Eine spannende Konstruktion zur zur Ausführung toller Sachen"
171
        
172
        self.datalist = [[_('Name'), data.Name],
173
                         [_('Location'), data.Location],
174
                         [_('Description'), data.Description]]
175
        
176
        for Item in self.datalist:
177
            listctrl.AppendItem(Item)
178
        
179
        self.Bind(dv.EVT_DATAVIEW_ITEM_EDITING_DONE, self.edit_done, listctrl)
180
        BS.Add(listctrl, 1, wx.EXPAND, 20) #wx.EXPAND wx.ALL wx.SHAPED
181
        self.SetSizer(BS)
182
        
183
    def edit_done(self,event):
184
        print(self.listctrl.GetSelectedRow()][1])
185
        #Hier die Daten aus der listctrl und an Project übergeben
186
        #aber wo soll man Project speichern um es später z.b. auf 
187
        #Toolbook-Page Geometry zu verwenden?
188
        
189
if __name__ == '__main__':
190
    app = MyApp()
191
    app.MainLoop()

von Karl (Gast)


Lesenswert?

Ich hänge bei
1
class MyToolbookProject
 bzw. dort bei
1
def edit_done(self,event):

Die Klasse Project ist dabei das Trivialste und hat nur die 3 Member. 
Aber andere Klassen sind da etwas komplizierter gestrickt.

von Karl (Gast)


Lesenswert?

Später soll natürlich auch ein Speicher-Button in die Toolbar, womit 
dann die ganzen Daten der Objekte (also Project, Soil ...) auf der 
Festplatte gespeichert werden können.

von MaWin (Gast)


Lesenswert?

Karl schrieb:
> Alternativ könnte ich mir
> vorstellen das z.B. an den Frame oder das Panel anzuhängen. Dann muss
> man aber z.B. über GetParent() darauf zugreifen

Nicht unbedingt.

Klassen, die Zugriff auf die Datenhaltung benötigen können dies auf 
mehrere Arten bekommen:
- Wie du schon sagtest indirekt über die GUI-Hierarchie.
- Man kann bei der Erstellung der Klassen bereits eine Referenz auf die 
Datenhaltung mitgeben.
- Jede Klasse kann ihren Parent fragen, ob dieser eine Referenz auf die 
Daten hat. Wenn nicht, wird die Anfrage an den Parent weitergereicht. 
Bis man irgendwann oben angekommen ist, bei einer Klasse, die die 
Anfrage befriedigen kann. Das ist im Prinzip das umgekehrte Design des 
vorherigen Punkts. Bei Änderung der Daten oder Ablagestruktur ändert 
sich nichts an den weiterreichenden Schichten.
- Global. Ja, kann man machen. Wenn man absolut ausschließen kann, dass 
man die Hierarchie mehrfach instanziieren können muss. Aber wäre jetzt 
auch nicht meine erste Lösung.

von Karl (Gast)


Lesenswert?

Heiner schrieb:
> MVC ist bekannt?

War mir nicht bekannt, ich kannte nur Entity-control-boundary.

Heiner schrieb:
> Da die Geschichte nun leider schon halb fertig gestrickt ist, könnte es
> etwas fummelig werden, diese Struktur nachträglich herzustellen.

Ist noch weit weg von halb fertig. Ich denke es lässt sich relativ 
einfach noch ergänzen.

Heiner schrieb:
> MaWin schrieb:
>> Code zeigen
>
> Hör auf, die Leute zu nerven. Wozu soll das auf diesem
> Abstraktionsniveau gut sein?

Ich hatte beim erstellen schon überlegt, den Code mitzugeben, dann aber 
mich restmal für die "Abstraktion" entschieden. Ich kann MaWins wünsch 
nachvollziehen.
Ich werde eine Controllerklasse schreiben und die als Referenz mitgeben. 
Auf das implementieren eines "Beobachter" werde ich aber wohl 
verzichten. Prinzipiell wäre es sicher Toll, die Software am Reißbrett 
zu entwerfen und dann umzusetzen, abe ich bin kein Informatiker und das 
Gesamtprojekt ist doch ehr überschaubar.

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.