Frame size after font change

I’ve been battling creating a Frame that uses a GridBagSizer where I change the font on the elements to be large via

self.fbig = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT).MakeBold().Scale(4.)

The window is never large enough to show all the elements. Using just the system font it appears ok.

The large font screenshot

vs system font
wxpython4.1.2a-sysfont

My sample code to create this:

import string
import random

import wx
alpha = string.ascii_uppercase


def floatstr():
    return f'{random.random() * 5:.6f}'


class MyP(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent, wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL, name="MyP")
        self.fbig = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT).MakeBold().Scale(4.)
        # self.fbig = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)
        self.gbs = wx.GridBagSizer(5, 5)
        self.gbs.SetEmptyCellSize((0, 0))

        for row in range(5):
            col = 0
            lbl = f'row {row} col {col} {alpha[row::6]}'  # make a longer label
            b = wx.Button(self, label=lbl)
            b.SetFont(self.fbig)
            self.gbs.Add(b, pos=(row, col), flag=wx.EXPAND | wx.ALIGN_LEFT)

            # two textctrl's holding floats
            for _ in range(2):
                col += 1
                atxt = wx.TextCtrl(self, value=floatstr(), style=wx.TE_READONLY)
                atxt.SetFont(self.fbig)
                sz_x, _ = atxt.GetSizeFromTextSize(atxt.GetTextExtent('9' * 8))
                atxt.SetInitialSize((sz_x, -1))
                self.gbs.Add(atxt, pos=(row, col), flag=wx.ALIGN_RIGHT | wx.EXPAND)

            # textctrl holding string
            col += 1
            stxt = f'{alpha[row + 3::6]}'
            ut = wx.TextCtrl(self, value=stxt, style=wx.BORDER_NONE | wx.TE_READONLY)
            sz_x, _ = ut.GetTextExtent(stxt)
            ut.SetMinSize((sz_x, -1))
            self.gbs.Add(ut, pos=(row, col), flag=wx.EXPAND | wx.ALIGN_LEFT)

        # per an example, tried this too but no change
        # flags = wx.SizerFlags(proportion=1).Expand().Border(wx.ALL, 10)
        # box = wx.BoxSizer()
        # box.Add(self.gbs, flags)
        # self.SetSizer(box)
        self.SetSizer(self.gbs)
        self.Layout()


class MyF(wx.Frame):
    def __init__(self, title):
        super().__init__(parent=None, title=title)

        panel = wx.Panel(self, style=wx.TAB_TRAVERSAL | wx.BORDER_DOUBLE)
        vsizer = wx.BoxSizer(wx.VERTICAL)

        myp = MyP(panel)
        vsizer.Add(myp, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)

        my_btn = wx.Button(panel, label='Press Me')
        vsizer.Add(my_btn, 0, wx.ALL | wx.CENTER, 5)

        panel.SetSizerAndFit(vsizer)
        self.SetClientSize(panel.GetSize())
        self.Layout()
        self.Show()


if __name__ == '__main__':
    app = wx.App()
    MyF(title='A Frame')
    app.MainLoop()

This happens to be on a RHEL 8 computer with gtk, python 3.9.

I’ve tried lots of different things, so clearly I’m missing something critical. Ideas?

T.

It’s the panel which is throwing you off!
You are setting the sizer for the panel and ignoring the frame, then hoping it will sort itself out, something we’ve all been guilty of. :grinning:
Try setting up a sizer for the frame as well and load the panel.
e.g.

import string
import random

import wx
alpha = string.ascii_uppercase


def floatstr():
    return f'{random.random() * 5:.6f}'


class MyP(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent, wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL, name="MyP")
        self.fbig = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT).MakeBold().Scale(4.)
        # self.fbig = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)
        self.gbs = wx.GridBagSizer(5, 5)
        self.gbs.SetEmptyCellSize((0, 0))

        for row in range(5):
            col = 0
            lbl = f'row {row} col {col} {alpha[row::6]}'  # make a longer label
            b = wx.Button(self, label=lbl)
            b.SetFont(self.fbig)
            self.gbs.Add(b, pos=(row, col), flag=wx.EXPAND | wx.ALIGN_LEFT)

            # two textctrl's holding floats
            for _ in range(2):
                col += 1
                atxt = wx.TextCtrl(self, value=floatstr(), style=wx.TE_READONLY)
                atxt.SetFont(self.fbig)
                sz_x, _ = atxt.GetSizeFromTextSize(atxt.GetTextExtent('9' * 8))
                atxt.SetInitialSize((sz_x, -1))
                self.gbs.Add(atxt, pos=(row, col), flag=wx.ALIGN_RIGHT | wx.EXPAND)

            # textctrl holding string
            col += 1
            stxt = f'{alpha[row + 3::6]}'
            ut = wx.TextCtrl(self, value=stxt, style=wx.BORDER_NONE | wx.TE_READONLY)
            sz_x, _ = ut.GetTextExtent(stxt)
            ut.SetMinSize((sz_x, -1))
            self.gbs.Add(ut, pos=(row, col), flag=wx.EXPAND | wx.ALIGN_LEFT)

        self.SetSizer(self.gbs)
        self.Layout()

class MyF(wx.Frame):
    def __init__(self, title):
        super().__init__(parent=None, title=title)

        panel = wx.Panel(self, style=wx.TAB_TRAVERSAL | wx.BORDER_DOUBLE)
        fsizer = wx.BoxSizer(wx.VERTICAL)

        psizer = wx.BoxSizer(wx.VERTICAL)
        myp = MyP(panel)
        psizer.Add(myp, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)

        my_btn = wx.Button(panel, label='Press Me')
        psizer.Add(my_btn, 0, wx.ALL | wx.CENTER, 5)

        panel.SetSizer(psizer)

        fsizer.Add(panel, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)

        self.SetSizerAndFit(fsizer)
        self.Layout()
        self.Show()


if __name__ == '__main__':
    app = wx.App()
    MyF(title='A Frame')
    app.MainLoop()

1 Like

Clearly I should have asked this question a week ago when I first got stuck! Thank you! 🤦

T.

The lessons we retain, are ones we struggled with the hardest.
or more eloquently:

I hear and I forget.
I see and I remember.
I do and I understand.

  • Confucius
1 Like

So if now with that same base code, I want to Hide and Show rows of the grid and get the window to adjust properly.
Keeping track of all windows per row myself [instead of using gbs.FindItemAtPosition((row,col)) live which didn’t work once all rows hidden], I’m able to make them appear and disappear. Yay.

But:
a) If I build them all at start and show them all as before, the frame is correct. But when I start hiding them, the frame does not shrink.

b) Bigger issue, if I build them all at start but hide 2 rows in MyF().__init__ before frame’s .Show(), the newly shown ones don’t have the correct size AND the frame doesn’t resize itself to make all visible.

So, again, I’m missing something necessary.

Case b)

Code for Case b) with two calls to self.on_button_hide(None) in MyF().__init__

import string
import random

import wx
import logging

logfmt = "%(asctime)s.%(msecs)03d %(name)s: %(message)s"
datefmt = "%Y-%m-%d %H:%M:%S"

default_level = logging.DEBUG
default_fmt = logfmt
default_datefmt = datefmt

default_formatter = logging.Formatter(default_fmt, default_datefmt)
log = logging.getLogger()

alpha = string.ascii_uppercase
start_rows = 5


def floatstr():
    return f'{random.random() * 5:.6f}'


class MyP(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent, wx.ID_ANY, pos=wx.DefaultPosition,
                         style=wx.TAB_TRAVERSAL, name="MyP")
        self.fbig = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT).MakeBold().Scale(4.)
        # self.fbig = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)
        panel = self
        self.gbs = wx.GridBagSizer(5, 5)
        self.gbs.SetEmptyCellSize((0, 0))

        # position based list of lists of all windows in a row
        self.wins = []
        for row in range(start_rows):
            rowwins = []
            col = 0
            lbl = f'row {row} col {col} {alpha[row::6]}'  # make a longer label
            b = wx.Button(panel, label=lbl)
            b.SetFont(self.fbig)
            self.gbs.Add(b, pos=(row, col), flag=wx.EXPAND | wx.ALIGN_LEFT)
            rowwins.append(b)

            # two textctrl's holding floats
            for _ in range(2):
                col += 1
                atxt = wx.TextCtrl(panel, value=floatstr(), style=wx.TE_READONLY)
                atxt.SetFont(self.fbig)
                sz_x, _ = atxt.GetSizeFromTextSize(atxt.GetTextExtent('9' * 8))
                atxt.SetInitialSize((sz_x, -1))
                self.gbs.Add(atxt, pos=(row, col), flag=wx.ALIGN_RIGHT | wx.EXPAND)
                rowwins.append(atxt)

            # textctrl holding string
            col += 1
            stxt = f'{alpha[row + 3::6]}'
            ut = wx.TextCtrl(panel, value=stxt, style=wx.BORDER_NONE | wx.TE_READONLY)
            sz_x, _ = ut.GetTextExtent(stxt)
            ut.SetMinSize((sz_x, -1))
            self.gbs.Add(ut, pos=(row, col), flag=wx.EXPAND | wx.ALIGN_LEFT)
            rowwins.append(ut)

            self.wins.append(rowwins)

        # all row indices
        self.rows = set(range(self.gbs.GetRows()))
        # which of those rows are currently shown
        self.shown = self.rows.copy()
        self.SetSizer(self.gbs)
        self.Layout()

    def show_row(self, row: int, show=True):
        """change visibility of entire row of gbs items"""

        for win in self.wins[row]:
            win.Show(show)
        if show:
            self.shown.add(row)
        else:
            if row in self.shown:
                self.shown.remove(row)

        # from the docs wx.Sizer.Show()
        # To make a sizer item disappear or reappear, use Show followed by Layout .
        self.Layout()
        # just to log result
        self.rows_shown()

    def rows_shown(self) -> tuple[list[int], list[int]]:
        """return two lists with row indices for rows shown and rows hidden"""
        shown = list(self.shown)
        hidden = list(self.rows - self.shown)
        log.warning(f'rows_shown:  {shown=} {hidden=}')
        return shown, hidden


class MyF(wx.Frame):
    def __init__(self, title):
        super().__init__(parent=None, title=title)

        panel = wx.Panel(self, style=wx.TAB_TRAVERSAL | wx.BORDER_DOUBLE)
        fsizer = wx.BoxSizer(wx.VERTICAL)

        psizer = wx.BoxSizer(wx.VERTICAL)
        self.myp = MyP(panel)
        psizer.Add(self.myp, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)

        show_btn = wx.Button(panel, label='Show Row')
        psizer.Add(show_btn, 0, wx.ALL | wx.CENTER, 5)
        show_btn.Bind(wx.EVT_BUTTON, self.on_button_show)

        hide_btn = wx.Button(panel, label='Hide Row')
        psizer.Add(hide_btn, 0, wx.ALL | wx.CENTER, 5)
        hide_btn.Bind(wx.EVT_BUTTON, self.on_button_hide)
        panel.SetSizer(psizer)

        fsizer.Add(panel, proportion=1, flag=wx.ALL | wx.EXPAND, border=5)

        self.SetSizerAndFit(fsizer)

        # Case b) hide two random rows. Comment out for Case a)
        self.on_button_hide(None)
        self.on_button_hide(None)

        self.Layout()
        self.Show()

    def on_button_show(self, evt):
        """show random hidden row"""
        _, hidden = self.myp.rows_shown()
        if hidden:
            r = random.choice(hidden)
            log.warning(f'\nbutton show: {hidden=} now show row {r}')
            self.myp.show_row(r, True)
        else:
            log.warning('\nbutton_show: no hidden rows')

    def on_button_hide(self, evt):
        """hide random shown row"""
        shown, _ = self.myp.rows_shown()
        if shown:
            r = random.choice(shown)
            log.warning(f'\nbutton hide: {shown=} now hide row {r}')
            self.myp.show_row(r, False)
        else:
            log.warning('\nbutton hide: no shown rows')


if __name__ == '__main__':
    app = wx.App()
    MyF(title='A Frame')
    app.MainLoop()

Thoughts?

T.

You will get closer to what you want if you ask the Frame to recheck itself.

in on_button_show and on_button_hide add:

        self.Layout()
        self.Fit()

It probably won’t get it 100% the first time but will thereafter and I can’t recall how to resolve that at the moment.