On 3/9/23 04:06, Alan Gauld wrote:


Thank you for the feedback, I appreciate the comments.

To add a little extra, there is actually a reason I lean toward overuse of .config() for a lot of things even though they could be sent to the constructor (other than that w["attribute"]= doesn't work in a lambda).

It comes from a use case I needed to solve in the shortcoming of tk with frames not being scrollable, requiring a canvas.

When dynamically generating a series of widgets (i.e. loading a custom json/xml layout config file), getting the scrollable area right is a bit of a chore with a catch-22 situation.

With an inner frame in a canvas, pack_propogate(tk.True) is the default on the frame, so that the children dictate the size, but in doing so it prevents the frame from expanding to the full width of the area it's given within the UI layout.

If you turn pack_propogate to tk.False, it breaks the ability for the canvas to return bbox(tk.ALL) (which is kind of rough anyway as the tcl docs state "The return value may overestimate the actual bounding box by a few pixels."). So you end up with 1,1 and no way of knowing what size scrollable area to set. Trying to obtain this value from a source outside of the canvas requires knowing what you are placing the canvas in each time, and was creating a similar catch-22 as the outer widget doesn't know what it wants it's size to be without knowing what the inner widget wants.. Switching to False, grabbing bbox, then back to True of course causes an unsightly flicker and disrupts the smoothness of the user experience.

So my solution is to create a widget instantiator which does a few things, mainly adding something I call a "ghost".

it's something like this:

instantiator(qlass, master, config_args=None, pack_args=None, init_args=None, ghost=False):
  if not init_args:
    init_args = {}
  object = qlass(master=master, **init_args)
  if hasattr(master, "ghost"):
    object.ghost = qlass(master=master.ghost, **init_args)
  elif ghost:
    object.ghost = qlass(master=tk.Frame(), **init_args)

When i pass it a canvas and say ghost=True, the canvas gets a .ghost duplicate, which is attached to an arbitrary frame that I never pack and stays invisible.

Subsequent widgets created to the canvas then see that their parent, starting with the canvas, have a ghost, and in return get a ghost of themselves attached to their parents ghost.

This allows you to get an accurate bbox size from the unseen ghost canvas that mirrors the visible version.

Keeping the init_args down to only what is necessary helps in consistency, and the subsequent config_args and pack_args I have in their respective dicts.

This also allows me to create a quality of life pack function I call ppack()

def ppack(self):
  self.pack(**self.pack_args)
  if hasattr(self, "ghost"):
    self.ghost.pack(**self.ghost.pack_args)
  return self

That allows each primary widget to manage and track their own set of configurations.

Of course I could strip a lot of that crap out if I find a better and smooth way of obtaining those bbox numbers, but I didn't see any quick solutions in glancing through the tcl/tk source so I went with what works for now.







--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to