Grid + GridSizer: Groesse, Ausrichtung...

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Ich habe zum Beispiel das folgende Programm unter Linux:

Code: Alles auswählen

#!/usr/bin/env python

"""
usage: ./einmaleins.py <size of table>
"""

import wx
import wx.grid
import sys
     
class MainFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title="Einmaleins", size=(640, 480))
        self.einmaleins = EinmaleinsGrid(self, int(sys.argv[1]))

        sizer = wx.GridSizer(0, 0, 0, 0)
        sizer.Add(self.einmaleins, flag=wx.ALIGN_CENTRE)
        self.SetSizerAndFit(sizer)


class EinmaleinsGrid(wx.grid.Grid):
    def __init__(self, parent, size):
        wx.grid.Grid.__init__(self, parent, -1)
        self.size = size
        self.SetTable(EinmaleinsTable(size))


class EinmaleinsTable(wx.grid.PyGridTableBase):
    
    def __init__(self, size):
        wx.grid.PyGridTableBase.__init__(self)
        self.size = size
    
    def GetNumberRows(self):
        return self.size
    
    def GetNumberCols(self):
        return self.size
        
    def GetValue(self, row, col):
        return (row+1) * (col+1)
    
    def GetColLabelValue(self, col):
        return str(col+1)

if __name__ == "__main__": 
    app = wx.PySimpleApp()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()
Erstens: Bei mir ist unter der Tabelle und rechts davon noch ein freier Streifen zu sehen. Meist ist es so, dass das Fenster gerade die Tabelle beinhaltet, aber fuer die ueberfluessigen freien Streifen werden Scrollbars eingefuegt. Kann man das verhindern?

Zweitens: Ich haette gerne, dass die Tabelle (wenn sie kleiner ist als das Fenster), im Fenster zentriert wird. Dafuer benutze ich in Zeile 18 wx.ALIGN_CENTRE. Dummerweise wird das Grid dann nicht vergroessert, sobald man das Fenster vergroessert. Benutze ich stattdessen wx.EXPAND, wird das Grid zwar automatisch vergroessert, der Inhalt steht aber immer links oben in der Ecke. Kann ich auf einfache Weise beides haben?
Benutzeravatar
DatenMetzgerX
User
Beiträge: 398
Registriert: Freitag 28. April 2006, 06:28
Wohnort: Zürich Seebach (CH)

Ein Gridsizer mit 0 rows und 0 cols?

Code: Alles auswählen

    def __init__(self):
        wx.Frame.__init__(self, None, title="Einmaleins", size=(640, 480))
        self.einmaleins = EinmaleinsGrid(self, int(sys.argv[1]))

        sizer = wx.GridSizer(1, 1, 0, 0)
        
        sizer.Add(self.einmaleins, flag=wx.ALIGN_CENTRE | wx.EXPAND)
        self.SetSizer(sizer)
        sizer.Fit(self)
        sizer.SetSizeHints(self)
Irgend wie kann ich gar kein wx mehr coden :roll:
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

DatenMetzgerX hat geschrieben:Ein Gridsizer mit 0 rows und 0 cols?
In der Doku von wxWidgets steht:
Constructor for a wxGridSizer. rows and cols determine the number of columns and rows in the sizer - if either of the parameters is zero, it will be calculated to form the total number of children in the sizer, thus making the sizer grow dynamically
Werde aber morgen ausprobieren, ob das einen Unterschied macht. Wenn ich wx.ALIGN_CENTRE | wx.EXPAND angebe, hat das centre keine Auswirkung mehr, da das Grid einfach den ganzen Platz einimmt.
Benutzeravatar
DatenMetzgerX
User
Beiträge: 398
Registriert: Freitag 28. April 2006, 06:28
Wohnort: Zürich Seebach (CH)

ah ok, in dem Fall würde ich ein FlexGridSizer verwenden. Bei dem Kannst du angeben in welche Richtung die Widgets angepasst werden sollen. (sizer.AddGrowableCol(nr) und sizer.AddGrowableRow(nr). Je nach dem expandiert dann die Row nr odr Col nr. [/img]
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Prinzipiell ist es schon OK, dass expand horizontal UND vertikal funktioniert, darum geht es mir nicht.

Ich braeuchte ein bedingtes expand, sodass die Tabelle maximal soweit expandiert wird, dass gerade alles zu sehen ist, und darueberhinaus soll dass centre greifen. Oder andersrum ein centre, dass der Tabelle sagt, dass sie sich beim vergroessern des Fensters resizen darf... (OK, das koennte man auch irgendwie per Hand hinbekommen, aber gibt es noch was einfacheres?)

Mmh, gerade sehe ich SetMaxSize/GetMaxSize. Vlt. hilft mir das weiter, muss ich heute abend mal schauen.

Oh, und interessanterweise denkt mein Windowmanager, dass wxPython-Anwendungen die Fenstergroesse 0x0 haben... :o
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Rebecca hat geschrieben:Mmh, gerade sehe ich SetMaxSize/GetMaxSize. Vlt. hilft mir das weiter
Nicht wirklich. Prinzipiell waere das MaxSize nicht schlecht, aber erstens wird mit wx.ALIGN_CENTRE|wx.EXPAND das centre anscheinend ignoriert, auch wenn wg. der MaxSize das Grid nicht mehr das ganze Fenster ausfuellt. Zweitens will ich MaxSize ja nicht per Hand festsetzen...

Uebehaupt ist die MinSize defaultmaessig auf (-1, -1), aber ich kann das Fenster nicht kleiner machen als wie die Tabelle anfangs dargestellt wird. Wer ist dafuer nun wieder verantwortlich? Irgendwie steige ich da noch nicht so ganz durch...

GUIs koennen ja so frustrierend sein... :evil:
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Schau dir mal in der Demo das Beispiel zu `wx.GridBagSizer` an. Damit kannst bestimmten ob Links und/oder rechts noch "Platz" sein sol.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Habs gerade gestestet und mit wx.ALIGN_CENTRE|wx.EXPAND geht es doch nicht :? Hmm, vielleicht einen ganz anderen Ansatz versuchen?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Rebecca hat geschrieben:Kann ich auf einfache Weise beides haben?
Hi Rebecca!

Nein, es gibt keine einfache Möglichkeit!

Denn, ``wx.ALIGN_CENTRE`` ``wx.EXPAND`` schließen sich gegenseitig aus. Du kannst etwas in einem Behälter zentrieren oder den kompletten Behälter ausfüllen lassen. Beides kann doch nicht funktionieren -- oder zumindest keinen sichtbaren Unterschied bringen. :? Etwas füllt den ganzen Behälter aus -- wie willst du erkennen, ob es zentriert ist?

Das ``wx.grid`` ist **intern** in ein ``wx.ScrolledWindow`` eingebettet. Vielleicht kannst du irgendwie darauf zugreifen und die virtuelle Größe des ``wx.ScrolledWindow`` erfragen. Dann hättest du eine Entscheidungsgrundlage zum Umschalten der verschiedenen Ansichten. Wenn das ``wx.ScrolledWindow`` kleiner ist als dein ``GetClientSize`` deines Frames (oder besser deines im Frame eingebetteten, noch nicht existierenden, Panels), dann kannst du es im Panel zentrieren. Wenn nicht, dann füllst du einfach den kompletten Kontainer aus.

So wie es aussieht, kannst du mit ``GetVirtualSize()`` die interne Größe des Grids herausfinden. 8)
Edit: Oder doch nicht... :roll:

Auf Basis dieses Wissens, kannst du dich selber um das Layout deines Grids kümmern.

Lösung:
1. Lege dein Grid auf ein Panel.
2. Verwende keinen Sizer im Panel.
3. Binde das Event WX_SIZE an eine Methode, in der du dich selbst um die Position und Größe deines Grids kümmerst.

lg
Gerold
:-)

PS: Mit ``SetSizeHints`` kannst du die Mindestgröße deines Grids festlegen. Wenn du diese damit auf einen kleinen Wert zurück setzt, solltest du keine Probleme haben, dein Grid zu verkleinern.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

...und hier eine komplizierte Lösung. :-)

Wahrscheinlich gibt es eine einfachere Lösung um die virtuelle Größe des Grids heraus zu finden. Diese ist mich leider nicht angesprungen. :-(

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import wx
import wx.grid

wx.SetDefaultPyEncoding("iso-8859-1")


class DataTable(wx.grid.PyGridTableBase):
   
    def __init__(self, size):
        wx.grid.PyGridTableBase.__init__(self)
        self.size = size
   
    def GetNumberRows(self):
        return self.size
   
    def GetNumberCols(self):
        return self.size
       
    def GetValue(self, row, col):
        return (row+1) * (col+1)
   
    def GetColLabelValue(self, col):
        return str(col+1)


class GridPanel(wx.Panel):
    
    def __init__(self, parent, einmaleins_max):
        
        wx.Panel.__init__(self, parent)
        self.Bind(wx.EVT_SIZE, self.on_self_size)
        
        self.SetBackgroundColour("green")
        
        grid = wx.grid.Grid(self, style = wx.WANTS_CHARS | wx.BORDER)
        grid.SetTable(DataTable(einmaleins_max))
        self.grid = grid
        
        for col in xrange(grid.GetNumberCols()):
            grid.SetColSize(col, 30)
        
        grid.SetColLabelSize(20)
        grid.SetRowLabelSize(30)
    

    def on_self_size(self, event):
        
        BUFFER = 9
        
        grid = self.grid
        
        cols = grid.GetNumberCols()
        rows = grid.GetNumberRows()
        
        sb_h = wx.ScrollBar(self, style = wx.SB_HORIZONTAL)
        scrollbar_height = sb_h.GetSizeTuple()[1]
        sb_h.Destroy()
        
        sb_v = wx.ScrollBar(self, style = wx.SB_VERTICAL)
        scrollbar_width = sb_v.GetSizeTuple()[0]
        sb_v.Destroy()
        
        virtual_width = 0.0
        for col in xrange(cols):
            virtual_width += grid.GetColSize(col)
        virtual_width += grid.GetRowLabelSize()
        virtual_width += (scrollbar_width + BUFFER)
        
        virtual_height = 0.0
        for row in xrange(rows):
            virtual_height += grid.GetRowSize(row)
        virtual_height += grid.GetColLabelSize()
        virtual_height += (scrollbar_height + BUFFER)
        
        panel_width, panel_height = self.GetClientSizeTuple()
        if panel_width > virtual_width:
            new_width = virtual_width
            new_x = (panel_width - virtual_width) / 2
        else:
            new_width = panel_width
            new_x = 0
        if panel_height > virtual_height:
            new_height = virtual_height
            new_y = (panel_height - virtual_height) / 2
        else:
            new_height = panel_height
            new_y = 0
        grid.SetSize(wx.Size(new_width, new_height))
        grid.SetPosition(wx.Point(new_x, new_y))
        
        event.Skip()


class MainFrame(wx.Frame):
    
    def __init__(self, parent = None, id = -1, title = "Einmaleins"):
        
        wx.Frame.__init__(self, parent, id, title, size = wx.Size(420, 300))
        
        panel = wx.Panel(self)
        
        vbox = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(vbox)
        
        gridpanel = GridPanel(panel, 10)
        vbox.Add(gridpanel, 1, wx.EXPAND | wx.ALL, 8)


if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MainFrame()
    frame.Center()
    frame.Show()
    app.MainLoop()
lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Cool! Das sieht schon ziemlich so aus, wie ich mir das vorstelle.

Was mich allerdings immer noch stoert, sind die freien Streifen rechts und unten im Grid. Obwohl der Inhalt der Tabelle eigentlich komplett sichtbar ist, muessen deswegen Scrollbars dargestellt werden.

Naja, ich werde mir erstmal deinen Code zu Gemuete fuehren. Vielen Dank!
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

gerold hat geschrieben:

Code: Alles auswählen

for col in xrange(grid.GetNumberCols()):
    grid.SetColSize(col, 30)
grid.SetDefaultColSize(30) ? :lol:

Es gibt auch noch GetVirtualSize(). Ich habe mal folgende Ausgabe in on_self_size eingefuegt:

Code: Alles auswählen

print virtual_width, virtual_height, grid.GetVirtualSize() + (scrollbar_width, scrollbar_height)
und ich erhalte

Code: Alles auswählen

rbreu@zam285:wx> ./tabelle_gerold.py
355.0 305.0 (98, 48)
355.0 305.0 (355, 282)
355.0 305.0 (355, 305)
355.0 305.0 (355, 305)
355.0 305.0 (355, 305)
355.0 305.0 (355, 305)
355.0 305.0 (318, 305)
355.0 305.0 (213, 231)
355.0 305.0 (355, 242)
Mmh...
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Rebecca hat geschrieben:

Code: Alles auswählen

print virtual_width, virtual_height, grid.GetVirtualSize() + (scrollbar_width, scrollbar_height)
Hi Rebecca!

``GetVirtualSize`` gibt dir leider nur die **aktuelle**, **sichtbare** Innengröße des Grids zurück. Also nichts was dir beim Herausfinden des benötigten Platzes nützlich sein kann.

Vielleicht gibt es hier jemanden, der die Tabelle ein bischen besser kennt und einen kürzeren Weg weiß, den virtuell benötigten Platz herauszufinden. Ich habe die Tabelle noch nie produktiv eingesetzt und mich deshalb auch noch nicht so ganz damit befasst. :roll:

Du musst dein Programm unbedingt auch unter Windows testen, denn es gibt wahrscheinlich Unterschiede beim Zeitpunkt des Ein- und Ausblendens der Scrollbars.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten