• modifying notebook tabs and styles in general

    From aapost@21:1/5 to All on Mon Dec 26 22:21:41 2022
    Curious if there might be any advanced creative ways to dig a little
    deeper and improve on what I have so far without modifying tcl/tk itself.

    I am implementing a close button on Notebook tabs (only found one
    example elsewhere that helped a bit) and am pretty close to what I want
    (to a workable extent..) but not quite fully how I want it.

    I am using python/tkinter 3.11.1, tcl/tk 8.6.13, debian testing for my
    main project -- BUT I rewrote the example in tcl as well, so both
    versions are below. Asside from a few language differences due to my not knowing tcl (variable scope/naming/type stuff), it is functionally the
    same. (I may need to address memory clean up, but haven't
    researched/focussed on that yet)

    A few comments before the code.

    ttk::style element create seems interesting at first, but not as useful
    or interesting as it could be once you dig in...

    You can create a predefined image with it, that has a padding/border,
    and a unique element name which you can grab later in code, but it can't inherit any options as far as I can tell from other elements.. which
    makes it static and not useful for what I want, I can't dynamically use
    it or not use it like I can a label image with an image option.

    You can also duplicate an element and create a unique name using "from",
    but it can't inherit from multiple things (to make a compound like a
    label text+image), and you can't take away element options from it to
    make.. a more basic widget.. so the new creation is just a copy with a
    new element name, and all it's options overlap with the original.. so a .padding2 is the same thing as a .padding when modifying a layout, and
    the setting of those are hard coded in the widget design.

    The "tab" on notebook and it's properties are not very accessible in
    code. with notebook 'add', you add a frame or widget for the main area,
    along with label-like options for the tab itself, but it's all a bit
    obfuscated away from the tangible object. (it would be nice if it had
    more direct
    customization, maybe something similar to -labelwidget from LabelFrame,
    though I haven't dug in to that widget yet)

    So as far as what I have been able to do.
    I can tell the tabs have the same options as a label when you do a
    notebook add I can see when looking at the layout for TNotebook.Tab, it
    has a .label nested at the end.

    I can look at the element options for label, and look at what other
    elements are available, and see that it seems to be the combination of a
    .text and a .image element (different than the 'image' created via
    element create etype image, because this premade .image HAS options and
    is the element (i believe) that a label uses)

    So I can swap out
    ('TNotebook.label', {'side': 'top','sticky': ''}),

    for:
    ('TNotebook.text', {'side': 'top','sticky': ''}),
    ('TNotebook.image', {'side': 'top','sticky': ''}),

    and the label options given to the notebook 'add' flow through to each
    of those corresponding elements options as if they were a label.
    You lose the 'compound' option. but that doesn't matter in my case.

    What I would like, is some way to add a little bit of invisible padding
    between the .text and the .image (preferably dynamic), something like a
    2nd padding set to {2 0 0 0}, but I assume that probably isn't possible.

    I have tried a number of things, .border is already used/set somewhere
    outside the TNotebook.Tab stack, and I don't really see anything else
    that I might be able to manupulate that I am not doing already. (It also
    leave behind static empty padding containers when the image isn't used.)

    Anyway, below is the example, first tcl, then python:

    ####################
    #TCL
    ####################

    package require Tk

    global _clicked_tab
    wm geometry . 400x400

    proc on_x_press {widget x y} {
    global _clicked_tab
    set elem [$widget identify $x $y]

    # or custom element name
    if {$elem == "image"} {
    set index [$widget index @$x,$y]
    $widget tab $index -image ::img::x1_img
    $widget state pressed
    set _clicked_tab $index
    return break
    }
    }

    proc on_x_release {widget x y} {
    global _clicked_tab
    if {![$widget instate pressed]} {
    return
    }

    set elem [$widget identify $x $y]
    set index [$widget index @$x,$y]

    #or custom element name
    if {$elem != "image" || $index == "" || $index != $_clicked_tab} {
    $widget state !pressed
    $widget tab $_clicked_tab -image { ::img::x_img active ::img::x2_img }
    set _clicked_tab None
    return
    }

    if {$_clicked_tab == $index} {
    $widget forget $index
    event generate $widget <<NotebookTabClosed>>
    }

    $widget state !pressed
    set _clicked_tab None
    }

    image create photo ::img::x_img -format GIF -data {
    R0lGODdhEAAQAIAAAAAAANnZ2SwAAAAAEAAQAAACJIyPaaDK+1wMoMpJ7akYPt54
    YCiS20hy6OU165p1bwdt5nIrBQA7
    }
    image create photo ::img::x1_img -format GIF -data {
    R0lGODdhEAAQAKEAAAAAAP8AANnZ2QAAACwAAAAAEAAQAAACKZSPaaDKFkITEUWZ
    bq5HK+81VziNk0WKI6ZiKcTBrPzF1IwezynsPFIAADs=
    }
    image create photo ::img::x2_img -format GIF -data {
    R0lGODdhEAAQAKEAAAAAADiGLtnZ2QAAACwAAAAAEAAQAAACKZSPaaDKFkITEUWZ
    bq5HK+81VziNk0WKI6ZiKcTBrPzF1IwezynsPFIAADs=
    }

    ttk::style layout TNbimgtabs {TNbimgtabs.client -sticky nswe}
    ttk::style layout TNbimgtabs.Tab {
    TNbimgtabs.tab -sticky nswe -children {
    TNbimgtabs.padding -side top -sticky nswe -children {
    TNbimgtabs.focus -side top -sticky nswe -children {
    TNbimgtabs.text -side left sticky
    TNbimgtabs.image -side left sticky
    }
    }
    }
    }
    ttk::style configure TNbimgtabs.Tab -padding {4 2} -background #c3c3c3 ttk::style map TNbimgtabs.Tab -background { selected #d9d9d9 }

    ttk::notebook .nb -style TNbimgtabs
    pack .nb -expand true -fill both

    for {set i 0} {$i<4} {incr i} {
    .nb add [ttk::frame .nb.f$i] \
    -text "Tab $i" \
    -image { ::img::x_img active ::img::x2_img }
    #-compound right
    }

    set _clicked_tab None

    bind .nb <ButtonPress-1> {on_x_press %W %x %y}
    bind .nb <ButtonRelease-1> {on_x_release %W %x %y}



    ####################
    #PYTHON
    ####################

    import tkinter as tk
    import tkinter.ttk as ttk

    root = tk.Tk()
    root.geometry("400x400")

    #these may need to be tied to a widget or root Tk() class,
    #they are self.x_img in my code, as I was having issues
    #getting them to show up if they were defined by themselves
    x_img = tk.PhotoImage(
    name="x_img", master=root,

    data="R0lGODdhEAAQAIAAAAAAANnZ2SwAAAAAEAAQAAACJIyPaaDK+1wMoMpJ7akYPt54YCiS20hy6OU165p1bwdt5nIrBQA7"
    )
    x1_img = tk.PhotoImage(
    name="x1_img", master=root,

    data="R0lGODdhEAAQAKEAAAAAAP8AANnZ2QAAACwAAAAAEAAQAAACKZSPaaDKFkITEUWZbq5HK+81VziNk0WKI6ZiKcTBrPzF1IwezynsPFIAADs="
    )
    x2_img = tk.PhotoImage(
    name="x2_img", master=root,

    data="R0lGODdhEAAQAKEAAAAAADiGLtnZ2QAAACwAAAAAEAAQAAACKZSPaaDKFkITEUWZbq5HK+81VziNk0WKI6ZiKcTBrPzF1IwezynsPFIAADs="
    )
    style = ttk.Style()
    #these commented things don't work
    #style.element_create("otherimg", "image", "x_img",
    # ("active", "!disabled", "x_img"),
    # border=6, sticky='') #style.element_create("imagewpadding", "from", "default", "image") #style.element_create("otherpadding", "from", "default", "padding") #style.layout("ImageWPadding", [
    # ("ImageWPadding.imagewpadding", {
    # 'side': 'left',
    # 'sticky': '',
    # 'children': [
    # ('TNbimgtabs.imagewpadding',
    {'side': 'left', 'sticky': ''}),
    # ]
    # })
    # ])
    style.layout("TNbimgtabs", [("TNbimgtabs.client", {"sticky": "nswe"})]) style.layout("TNbimgtabs.Tab", [
    ('TNbimgtabs.tab', {
    'sticky': 'nswe',
    'children': [
    ('TNbimgtabs.padding', {
    'side': 'top',
    'sticky': 'nswe',
    'children': [
    ('TNbimgtabs.focus', {
    'side': 'top',
    'sticky': 'nswe',
    'children': [
    #('TNbimgtabs.label',
    {'side': 'top','sticky': ''}),
    ('TNbimgtabs.text',
    {'side': 'left', 'sticky': ''}),
    ('TNbimgtabs.image',
    {'side': 'left', 'sticky': ''}),
    #('TNbimgtabs.border', {
    # 'side': 'left',
    # 'sticky': '',
    # 'children': [
    #
    ('TNbimgtabs.imagewpadding', {'side': 'left', 'sticky': ''}),
    # ]
    #})
    ]
    })
    ]
    })
    ]
    })
    ]
    )
    #these are the defaults that TNotebook have on my debian testing /
    python 3.11 system
    #they don't carry over when defining a new layout above, and you have to
    figure out
    #what they are by running .lookup() and digging in source code,
    -tabmargins shown in
    #altTheme.tcl does not appear implemented in python style.configure('TNbimgtabs.Tab', padding=(4, 2), background="#c3c3c3") style.map('TNbimgtabs.Tab', background=[('selected', '#d9d9d9')])

    def on_x_press(widget, event):
    element = widget.identify(event.x, event.y)

    # or custom element name
    if "image" in element:
    index = widget.index("@%d,%d" % (event.x, event.y))
    widget.tab(index, image="x1_img")
    # self.tab(index, padding=50)
    widget.state(['pressed'])
    widget._clicked_tab = index
    return "break"

    def on_x_release(widget, event):
    if not widget.instate(['pressed']):
    return
    element = widget.identify(event.x, event.y)

    try:
    index = widget.index("@%d,%d" % (event.x, event.y))
    except:
    index = None
    #print("no index")

    #or custom element name
    if ("image" not in element) or index == None or index != widget._clicked_tab:
    widget.state(['!pressed'])
    widget.tab(widget._clicked_tab, image=["x_img", ("active",
    "!disabled"), "x2_img", ])
    widget._clicked_tab = None
    return

    if widget._clicked_tab == index:
    widget.forget(index)
    widget.event_generate("<<NotebookTabClosed>>")

    widget.state(["!pressed"])
    widget._clicked_tab = None

    nb = ttk.Notebook(style='TNbimgtabs')
    nb.pack(expand=True, fill=tk.BOTH)
    nb.tabs = []
    for x in range(4):
    nb.tabs.append(ttk.Frame(master=nb))
    nb.add(nb.tabs[-1], text="Tab " + str(x), image=["x_img", ("active", "!disabled"), "x2_img",])

    nb.bind("<ButtonPress-1>", lambda event, widget=nb: on_x_press(widget,
    event))
    nb.bind("<ButtonRelease-1>", lambda event, widget=nb:
    on_x_release(widget, event))

    root.mainloop()

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From saitology9@21:1/5 to aapost on Tue Dec 27 13:23:45 2022
    On 12/26/2022 10:21 PM, aapost wrote:
    Curious if there might be any advanced creative ways to dig a little
    deeper and improve on what I have so far without modifying tcl/tk itself.

    I am implementing a close button on Notebook tabs (only found one
    example elsewhere that helped a bit) and am pretty close to what I want
    (to a workable extent..) but not quite fully how I want it.

    I am using python/tkinter 3.11.1, tcl/tk 8.6.13, debian testing for my
    main project -- BUT I rewrote the example in tcl as well, so both
    versions are below. Asside from a few language differences due to my not knowing tcl (variable scope/naming/type stuff), it is functionally the
    same. (I may need to address memory clean up, but haven't
    researched/focussed on that yet)

    A few comments before the code.

    ttk::style element create seems interesting at first, but not as useful
    or interesting as it could be once you dig in...


    I believe Ttk styling is generally believed to be a "black art" sort of
    thing in that a) it is less flexible than and supports fewer options
    than the traditional widgets, and b) it is hard to modify styles to
    achieve the look you want.

    So you might be better off with a non-ttk notebook.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dieter Lonken@21:1/5 to All on Tue Dec 27 15:26:37 2022
    saitology9 schrieb am Dienstag, 27. Dezember 2022 um 19:23:49 UTC+1:
    On 12/26/2022 10:21 PM, aapost wrote:
    Curious if there might be any advanced creative ways to dig a little
    deeper and improve on what I have so far without modifying tcl/tk itself.

    I am implementing a close button on Notebook tabs (only found one
    example elsewhere that helped a bit) and am pretty close to what I want
    (to a workable extent..) but not quite fully how I want it.

    I am using python/tkinter 3.11.1, tcl/tk 8.6.13, debian testing for my
    main project -- BUT I rewrote the example in tcl as well, so both
    versions are below. Asside from a few language differences due to my not knowing tcl (variable scope/naming/type stuff), it is functionally the same. (I may need to address memory clean up, but haven't researched/focussed on that yet)

    A few comments before the code.

    ttk::style element create seems interesting at first, but not as useful
    or interesting as it could be once you dig in...

    I believe Ttk styling is generally believed to be a "black art" sort of
    thing in that a) it is less flexible than and supports fewer options
    than the traditional widgets, and b) it is hard to modify styles to
    achieve the look you want.

    So you might be better off with a non-ttk notebook.

    Have a look at scrollutil::plainnotebook Command
    by
    Csaba Nemethi
    or built your own plainnotebook in combination with modified radiobuttons

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From aapost@21:1/5 to Dieter Lonken on Tue Dec 27 23:49:59 2022
    On 12/27/22 18:26, Dieter Lonken wrote:
    saitology9 schrieb am Dienstag, 27. Dezember 2022 um 19:23:49 UTC+1:
    On 12/26/2022 10:21 PM, aapost wrote:
    Curious if there might be any advanced creative ways to dig a little
    deeper and improve on what I have so far without modifying tcl/tk itself. >>>
    I am implementing a close button on Notebook tabs (only found one
    example elsewhere that helped a bit) and am pretty close to what I want
    (to a workable extent..) but not quite fully how I want it.

    I am using python/tkinter 3.11.1, tcl/tk 8.6.13, debian testing for my
    main project -- BUT I rewrote the example in tcl as well, so both
    versions are below. Asside from a few language differences due to my not >>> knowing tcl (variable scope/naming/type stuff), it is functionally the
    same. (I may need to address memory clean up, but haven't
    researched/focussed on that yet)

    A few comments before the code.

    ttk::style element create seems interesting at first, but not as useful
    or interesting as it could be once you dig in...

    I believe Ttk styling is generally believed to be a "black art" sort of
    thing in that a) it is less flexible than and supports fewer options
    than the traditional widgets, and b) it is hard to modify styles to
    achieve the look you want.

    So you might be better off with a non-ttk notebook.

    Have a look at scrollutil::plainnotebook Command
    by
    Csaba Nemethi
    or built your own plainnotebook in combination with modified radiobuttons


    Thank you for the response! Definitely some added functionality on those notebooks that I will borrow from, and nice to see some more advanced approaches on what is possible.

    They still use the option-less 'image' element create etype for the
    close button itself though, which forces it on all tabs (I can mix the
    tab types within a single notebook with my approach).

    Thinking about it more, for now I am going with an empty .border element
    inside my tab layout between:

    ('TNbimgtabs.text', {'side': 'left', 'sticky': ''}),
    ('TNbimgtabs.border', {'side': 'left', 'sticky': ''}),
    ('TNbimgtabs.image', {'side': 'left', 'sticky': ''}),

    borderwidth set at 1 gives me the 2 pixel separator in between text and
    image that I wanted (as a 2x2 box), and I will just have to live with
    the empty 2x2 box of extra padding on the right side for tabs that I opt
    to not use a close button on.

    Until I perhaps decide to redesign it all from scratch with padding that
    looks good in all scenarios.

    Thanks again.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From aapost@21:1/5 to aapost on Wed Dec 28 15:32:00 2022
    On 12/26/22 22:21, aapost wrote:
    nb.tabs = []just noting the removal of a naming conflict bug in my python rather
    than let it stagnate, nb.tabs should be nb.tabs_list = []
    also might not really even be necessary any more as
    nb.tabs()
    nb.index()
    nb.nametowidget()
    widget.winfo_children()
    all get me to the info and objects I need to track/access.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)