Wie Bilder problemlos in BoxSizer darstellen?

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Benutzeravatar
STiGMaTa_ch
User
Beiträge: 32
Registriert: Sonntag 14. Mai 2006, 22:58
Wohnort: Rueti ZH, Schweiz

Hallo zusammen.

Ich bin hier langsam am verzweifeln. Seit zwei Tagen komme ich nun mit meinem Problem einfach nicht weiter und brauche ein wenig Input von euch :(

Folgendes: Ich möchte aus einer Liste ein Spiel auswählen und beim linksklicken des entsprechenden Spieles sollen mir im unteren Teil des Fensters die ersten 8 passenden Screenshots dargestellt werden.

Soweit so Gut. Halbwegs funktioniert das auch, jedoch habe ich folgende Probleme damit:

- Die Bilder werden erst richtig dargestellt, wenn ich das gesammt Fenster 1x Resize, dann minimiere und dann wieder öffne.

- Klicke ich zuerst auf ein Game mit 8 Screenshots, danach auf ein Game mit z.B. 3 Shots dann sieht man die (alten) 5 Shots noch, wie lösche ich die?

Hier mal ein Screenshot wie es aussehen sollte (Erreicht indem ich zuerst auf Larry 6 - VGA geklickt hatte, danach ein wenig das gesammte Fenster resized habe und dann noch kurz das Fenster minimiert und wieder hervorgeholt habe)

Bild

Und so sieht es aus, wenn ich mein Programm frisch gestartet und dann sogleich auf das Larry 6 - VGA Icon geklickt habe:

Bild

Ich habe Momentan echt keinen Plan woran das liegen könnte. Habe ich vielleicht die Sizer Falsch angewandt? Der Aufbau des ganzen ist folgendermassen:

Links und Rechts wird mittels (wx.SplitterWindow) unterteilt. Den Rechten Teil unterteile ich dann mittels (wx.FlexGridSizer) nochmals in eine obere und untere Hälfte. (FlexGrid habe ich gewählt, weil dadurch der untere Teil immer eine fixe Grösse behält.) im unteren Tel habe ich dann ein ScrolledWindow eingesetzt, welches wiederum einen BoxSizer enthält. Und in eben diesen BoxSizer möchte ich meine Bilder laden...

Hier mal die relevantesten Codestellen:

Code: Alles auswählen

        [...]
        ##########
        # Splitt Window into Left and Right with Sash
        self.SplitterLeftRight = wx.SplitterWindow(self, -1, style=wx.SP_BORDER)
        self.SplitterLeftRight.SetMinimumPaneSize(100)
        # Splitt Window end
        ##########

        ##########
        # Left Window (Tree List)
        self.FrodoBoxSorter = wx.TreeCtrl(self.SplitterLeftRight)
        [...]

        ##########
        # Right Window (Notebook and Preview Window)
        self.RightPanel = wx.Panel(self.SplitterLeftRight)
        RightUpDownSizer = wx.FlexGridSizer(2, 1, 0, 0)
        RightUpDownSizer.AddGrowableRow(0)
        RightUpDownSizer.AddGrowableCol(0)
        self.RightPanel.SetSizer(RightUpDownSizer)
        # Right Window end
        ##########

        ##########
        # Upper Right Window (Notebooks)
        self.FrodoBoxNotebook = wx.Notebook(self.RightPanel, -1, style=wx.NB_TOP)
        RightUpDownSizer.Add(self.FrodoBoxNotebook, 1, wx.EXPAND, 0)
        
        # Icon List
        [... Code welcher die Icon Liste kreiert ...]
        # Icon List end

        # Report List
        [... Code welcher die Report Liste erstellt ...]
        # Report List end

        # Upper Right Window (Notebooks) end
        ##########
        [...]
        ##########
        # Lower Right Window (Preview)
        #self.unten = wx.Panel(self.RightPanel)
        self.unten = wx.ScrolledWindow(self.RightPanel, -1, style=wx.TAB_TRAVERSAL)
        self.unten.SetMinSize((480, 105))
        self.unten.SetScrollRate(1, 100)
        self.PicSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.unten.SetSizer(self.PicSizer)
        RightUpDownSizer.Add(self.unten,1,wx.EXPAND,0)

        # Lower Right Window (Preview)
        ##########
        [... Nachfolgender Code wird bei Klick auf entsprechendes Game ausgefuehrt ...]
    def OnGameSelected(self, evt):
        self.PicSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.unten.SetSizer(self.PicSizer)

        capturePath = conf.FrodoConfigContent.get('frodobox-dirs', 'capturedir')
        item = evt.GetItem()
        gameName = item.GetText()
        shortName = conf.FrodoProfiles.get(gameName, 'shortname')

        globber = capturePath+conf.OsSeparator+shortName+"_[1-8].*"
        files=glob.glob(globber)
        files.sort()

        for name in files:
            img = wx.Image(name, wx.BITMAP_TYPE_ANY)
            scaled = img.Scale(120, 75)
            bitmap = wx.BitmapFromImage(scaled)
            bild = wx.StaticBitmap(self.unten, -1, bitmap)
            self.unten.Freeze()
            self.PicSizer.Add(bild,1,wx.EXPAND,0) 
            self.unten.SetSizerAndFit(self.PicSizer)
            self.unten.Thaw()
        [...]
Würde mich über jede Art von Tipp freuen. Danke für eure Zeit!
STiGMaTa
EDV-Systeme verarbeiten, womit sie gefüttert werden. Kommt Mist rein, kommt Mist raus. (André Kostolany)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hi STiGMaTa!

Ich bin noch nicht so fit mit wxPython, dass ich das ohne ausführbarem Testcode hin bekomme. Aber hier ein paar Kleinigkeiten, die mir aufgefallen sind.

Du verwendest wx.ScrolledWindow, ohne, dass du auf die wirklich benötigte Größe eingehst.

Sollte es nicht so laufen, dass der Sizer befüllt wird, dann dem ScrolledWindow zugeordnet wird, und danach mit FitVirtual() auf die korrekte Größe gebracht wird?

SetSizer() oder SetSizerAndFit() macht nur einmal Sinn. Es bringt nicht wirklich etwas, wenn ein Sizer mehrmals dem selben Objekt zugeordnet wird. (Zeile 73)

Die Anweisung self.PicSizer.Add(bild,1,wx.EXPAND,0) stört mich ein wenig. Wie wäre es mit self.PicSizer.Add(bild, 0,wx.EXPAND)?
Es geht mir nämlich darum, dass es keine Bezugsbreite gibt, zu welcher ausgedehnt werden kann. Im Gegenteil. Die virtuelle Größe des ScrolledWindow sollte sich eigentlich an den Platzbedarf des PicSizers anpassen. (Zeile 72)

Sehe ich das richtig, dass sich die Bilder, rechts unten, ständig ändern? Wenn ja, dann musst du irgendwo eine Liste mit den Bildobjekten führen, dass du sie, bevor die nächsten Bilder geladen werden, vom Sizer entfernen und danach zerstören (destroy) kannst. Es ist wichtig, dass die Bilder vorher vom Sizer entfernt werden.

Ich habe hier mal einen Codeausschnitt aus dem Programm das ich derzeit schreibe. Darin wird zuerst geprüft, ob es bereits ein Objekt gibt, das in einem Panel angezeigt werden soll oder nicht. Wenn ja, dann wird zuerst das Objekt aus dem Sizer entfernt, dann erst wird das Objekt gelöscht.
Danach wird geprüft, welches Objekt in einer Box angezeigt werden soll. Es wird das neue Objekt erstellt, mit Daten befüllt, dem Sizer hinzugefügt und ruckelfrei angezeigt.

Noch eine paar Erklärungen zum Code:
- self.detail_area = Variable, die das anzuzeigende Panel-Objekt enthält.
- self.vbox_body = wx.BoxSizer der das anzuzeigende Panel-Objekt zugewiesen bekommt und von dem ein bereits vorhandenes Panel-Objekt vorher entfernt werden muss.
- DetailPanelXXX = Das sind Klassen, die von einem wx.Panel abgeleitet wurden. Davon gibt es mehrere.
- self.init_detailpanel_XXX = Prozeduren, die das entsprechende Detailpanel mit Daten füllt. (ist nicht wichtig)
- self.Layout() = Damit wird die Neuberechnung der Sizer angestoßen.
- self.Freeze() und self.Thaw() = Optischen Seitenaufbau stoppen und wieder starten.

Code: Alles auswählen

    #----------------------------------------------------------------------
    def show_detail_area(self, event = None):
        """
        Prüft was für ein Eventtype ausgewählt wurde und zeigt je nach 
        Auswahl den dafür vorgesehenen Detailbereich an.
        """

        self.Freeze() # Um das Flackern zu vermeiden
        try:
            # Alten Detailbereich aus Sizer entfernen
            if self.detail_area:
                self.vbox_body.Detach(self.detail_area)
                self.detail_area.Destroy()
                
            # Je nach Eventtype den dafür zuständigen Detailbereich laden
            if self._current_eventtype == "daily":
                # Eventtype -- daily
                self.detail_area = DetailPanelDaily(self)
                self.init_detailpanel_daily()
            elif self._current_eventtype == "weekly":
                # Eventtype -- weekly
                self.detail_area = DetailPanelWeekly(self)
                self.init_detailpanel_weekly()
            elif self._current_eventtype == "monthly":
                # Eventtype -- monthly
                self.detail_area = DetailPanelMonthly(self)
                self.init_detailpanel_monthly()
            elif self._current_eventtype == "once":
                # Eventtype -- once
                self.detail_area = DetailPanelOnce(self)
                self.init_detailpanel_once()
            elif self._current_eventtype == "at_systemstart":
                # Eventtype -- once
                self.detail_area = DetailPanelAtSystemstart(self)
                self.init_detailpanel_at_systemstart()
            elif self._current_eventtype == "at_login":
                # Eventtype -- at_login
                self.detail_area = DetailPanelAtLogin(self)
                self.init_detailpanel_at_login()
            elif self._current_eventtype == "on_idle":
                # Eventtype -- on_idle
                self.detail_area = DetailPanelOnIdle(self)
                self.init_detailpanel_on_idle()
            else:
                # sonstiges
                self.detail_area = None
                
            # Detailbereich mit Sizer verbinden
            if self.detail_area:
                self.vbox_body.Add(self.detail_area, 0, wx.EXPAND | wx.ALL, 5)
            
            # Hauptsizer neu berechnen
            self.Layout()
        
        finally:
            self.Thaw()
        
        if event:
            event.Skip()
Weiters würde ich, das muss aber nicht sein, zuerst alle Bilder hinzufügen und erst danach aktualisieren, also mit Layout() das oberste Window-Objekt, also wahrscheinlich dein Frame, aktualisieren.

Mehr fällt mir im Moment nicht ein.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
STiGMaTa_ch
User
Beiträge: 32
Registriert: Sonntag 14. Mai 2006, 22:58
Wohnort: Rueti ZH, Schweiz

Guten Morgen Gerold
gerold hat geschrieben:Ich bin noch nicht so fit mit wxPython, dass ich das ohne ausführbarem Testcode hin bekomme.
Habe dir mal den aktuellen Stand des Codes (inkl. aller benötigten Files für eine Demo) als Zip File hochgeladen. ~250kb

http://mitglied.lycos.de/stiggichaos/fr ... odoBox.zip
#edit Sehe gerade, dass der direkte Link nicht funktioniert. Daher hier ein Link zum Verzeichnis. Dann sollte man das Zip so runterladen können.
http://mitglied.lycos.de/stiggichaos/frodobox/
gerold hat geschrieben:Aber hier ein paar Kleinigkeiten, die mir aufgefallen sind.

Du verwendest wx.ScrolledWindow, ohne, dass du auf die wirklich benötigte Größe eingehst.
Erm ich dachte, da kümmert sich das ScrolledWindow selber drumm...

Meinst du damit, dass ich vorher mittels wxWindow::SetVirtualSize die virtuelle Grösse setzen muss (also bei 8 Bildern inkl. 5Pixel Abstand zueinander ~1000Pixel)??
gerold hat geschrieben:SetSizer() oder SetSizerAndFit() macht nur einmal Sinn. Es bringt nicht wirklich etwas, wenn ein Sizer mehrmals dem selben Objekt zugeordnet wird. (Zeile 73)
Ja, sorry. Ich habe soviel rumgebastelt und gewurstelt. Das was du da an Code siehst ist einfach der letzte gespeicherte Stand. Unoptimiert :(
gerold hat geschrieben:Sehe ich das richtig, dass sich die Bilder, rechts unten, ständig ändern?
Korrekt. Jedesmal wenn ein anderes Game ausgewählt wird erscheinen da unten seine vorhandenen (maximal 8 ) Screenshots.
gerold hat geschrieben:Wenn ja, dann musst du irgendwo eine Liste mit den Bildobjekten führen, dass du sie, bevor die nächsten Bilder geladen werden, vom Sizer entfernen und danach zerstören (destroy) kannst. Es ist wichtig, dass die Bilder vorher vom Sizer entfernt werden.
Du meinst jetzt aber keine Image List oder? Die kann ich bei normalen Bildern doch gar nicht benutzen (dachte ich zumindest)...

bez. des löschens: Im Moment ist mir klar, dass der Code die Bilder nicht löscht sondern lediglich weitere Bilder in den Sizer hängt. Das löschen Problem ist etwas zweitrangig geworden, da ich zuerst das korrekte darstellen lösen wollte. Erst wenn das klappt wollte ich mich dem löschen hingeben :-)


gerold hat geschrieben:Ich habe hier mal einen Codeausschnitt aus dem Programm das ich
Danke für das Beispiel. Ich werde mir das mal in Ruhe ansehen und versuchen meinen Code entsprechend anzupassen.
gerold hat geschrieben:Weiters würde ich, das muss aber nicht sein, zuerst alle Bilder hinzufügen und erst danach aktualisieren, also mit Layout() das oberste Window-Objekt, also wahrscheinlich dein Frame, aktualisieren.

Mehr fällt mir im Moment nicht ein.
Hey, ich bin schon mega froh, dass du dir überhaupt die Zeit genommen hast! Ich werde alle deine Punkte in ruhe durchgehen und mich nochmals melden...

Allerdings erst Montag wieder, da ich Heute bis Sonntag kurzfristig zur Grossmutter muss. Aber mit dem Laptop im Koffer und deinem ausgedruckten Input komme ich sicher weiter...

Lieber Gruss
STiGMaTa
EDV-Systeme verarbeiten, womit sie gefüttert werden. Kommt Mist rein, kommt Mist raus. (André Kostolany)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Hi STiGMaTa!

Ich habe mir deinen Code angesehen. Eines deiner Probleme ist gelöst, wenn du mit self.unten.FitInside() das ScrolledWindow anpasst. (Zeile 9)

Code: Alles auswählen

        self.unten.Freeze()
        for name in files:
            img = wx.Image(name, wx.BITMAP_TYPE_ANY)
            scaled = img.Scale(120, 75)
            bitmap = wx.BitmapFromImage(scaled)
            bild = wx.StaticBitmap(self.unten, -1, bitmap)
            self.PicSizer.Add(bild,0,wx.EXPAND) 
            self.unten.SetSizer(self.PicSizer)
        self.unten.FitInside()
        self.unten.Thaw()
Das Problem, dass du vorher die vorhandenen Bilder wieder löschen musst, bevor du neue hinzufügst, habe ich bereits weiter oben geklärt.

Mit "Liste mit den Bildobjekten" meine ich kein wx.ImageList sondern eine einfache Python-Liste mit den Referenzen zu den Bildern.

Code: Alles auswählen

bilder = []
bild = wx.StaticBitmap(self.unten, -1, bitmap)
bilder.append(bild)
So hast du eine Liste, die du dann durchlaufen kannst um die vorhandenen Bilder wieder zu löschen.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
STiGMaTa_ch
User
Beiträge: 32
Registriert: Sonntag 14. Mai 2006, 22:58
Wohnort: Rueti ZH, Schweiz

Hallo Gerold
gerold hat geschrieben:Hi STiGMaTa!

Ich habe mir deinen Code angesehen. Eines deiner Probleme ist gelöst, wenn du mit self.unten.FitInside() das ScrolledWindow anpasst.

[...]

Das Problem, dass du vorher die vorhandenen Bilder wieder löschen musst, bevor du neue hinzufügst, habe ich bereits weiter oben geklärt.
Vielen Herzlichen Dank für deinen Input und die Zeit, welche du dir genommen hast! Das hat bestens funktioniert. Die Bilderliste wird nun korrekt dargestellt und auch das löschen der Bilder funktioniert einwandfrei.

Lieber Gruss
STiGMaTa
EDV-Systeme verarbeiten, womit sie gefüttert werden. Kommt Mist rein, kommt Mist raus. (André Kostolany)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

STiGMaTa_ch hat geschrieben:Das hat bestens funktioniert. Die Bilderliste wird nun korrekt dargestellt und auch das löschen der Bilder funktioniert einwandfrei.
Das freut mich. :-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten