• Re: tkinter ttk.Treeview: changing background colour of single item whe

    From Thomas Passin@21:1/5 to John O'Hagan on Sat Feb 4 11:25:09 2023
    I haven't worked specifically with a Treeview, but I think you need to
    detect the onClick event (or an onSelect event if there is one) and have
    that trigger the flashing. Otherwise the selection probably overwrites
    styling that you added. That's what I do for classic Tk buttons to make
    them flash when selected. (I only flash once, though; I don't keep them flashing).

    On 2/4/2023 5:42 AM, John O'Hagan wrote:
    Hi list

    I'm using a ttk Treeview to display a hierarchical data structure. When
    an error condition arises in a node, I want the corresponding item in
    the Treeview to flash its background color.

    Using a tag to flash the item background works, except when the item is selected, when the tag has no effect. I want the item to keep flashing
    even when selected.

    The code below is a minimal example of the issue. It displays a
    Treeview containing two items with the first one flashing. If you
    select it, you don't see the flashing anymore.

    from tkinter import *
    from tkinter.ttk import *

    root = Tk()
    t = Treeview(root)

    t.insert('', 0, iid='item1', text='item1')
    t.insert('', 1, text='item2')
    t.tag_configure('flashtag', background='red')
    t.pack()

    def flash():
    tags = t.item('item1', 'tags')
    t.item('item1', tags='' if tags else 'flashtag')
    t.after(500, flash)

    flash()
    mainloop()

    Other than tags, the only other way I've found to dynamically change
    Treeview backgrounds is using styles, in particular Style.map to
    configure the background of a selected item, but they apply to all
    items in the Treeview and would flash all selected items, not just the
    one I want to keep flashing.

    Is there another way to do what I want?

    Thanks

    --

    John


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From stefalem@21:1/5 to All on Mon Feb 6 10:22:39 2023
    Is there another way to do what I want?

    Try this:

    from tkinter import *
    from tkinter.ttk import *

    root = Tk()
    t = Treeview(root)

    t.insert('', 0, iid='item1', text='item1')
    t.insert('', 1, text='item2')
    t.tag_configure('flashtag', background='red')
    t.pack()

    def flash():
    tags = t.item('item1', 'tags')
    t.item('item1', tags='' if tags else 'flashtag')
    t.after(500, flash)
    itemselected = t.selection()
    for x in itemselected:
    if (x == 'item1'):
    t.selection_remove(t.get_children())

    flash()

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From stefalem@21:1/5 to All on Mon Feb 6 10:26:35 2023
    Is there another way to do what I want?

    try this:

    from tkinter import *
    from tkinter.ttk import *

    root = Tk()
    t = Treeview(root)

    t.insert('', 0, iid='item1', text='item1')
    t.insert('', 1, text='item2')
    t.tag_configure('flashtag', background='red')
    t.pack()

    def flash():
    tags = t.item('item1', 'tags')
    t.item('item1', tags='' if tags else 'flashtag')
    t.after(500, flash)
    itemselected = t.selection()
    for x in itemselected:
    if (x == 'item1'):
    t.selection_remove(t.get_children())

    flash()
    mainloop()

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From stefalem@21:1/5 to All on Mon Feb 6 10:19:50 2023
    Il giorno sabato 4 febbraio 2023 alle 11:43:29 UTC+1 John O'Hagan ha scritto: ...

    Is there another way to do what I want?

    from tkinter import *
    from tkinter.ttk import *

    root = Tk()
    t = Treeview(root)

    t.insert('', 0, iid='item1', text='item1')
    t.insert('', 1, text='item2')
    t.tag_configure('flashtag', background='red')
    t.pack()

    def flash():
    tags = t.item('item1', 'tags')
    t.item('item1', tags='' if tags else 'flashtag')
    t.after(500, flash)
    itemselected = t.selection()
    for x in itemselected:
    if (x == 'item1'):
    t.selection_remove(t.get_children())

    flash()


    Thanks

    --

    John

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From stefalem@21:1/5 to All on Mon Feb 6 22:14:36 2023
    I apologize for the 3 messages sent, I could not access the usual news
    server and with Google Groups I messed up :)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John O'Hagan@21:1/5 to stefalem on Sun Feb 12 22:10:20 2023
    On Mon, 2023-02-06 at 10:19 -0800, stefalem wrote:
    Il giorno sabato 4 febbraio 2023 alle 11:43:29 UTC+1 John O'Hagan ha
    scritto:
    ...

    Is there another way to do what I want? 

    from tkinter import *
    from tkinter.ttk import *

    root = Tk()
    t = Treeview(root)

    t.insert('', 0, iid='item1', text='item1')
    t.insert('', 1, text='item2')
    t.tag_configure('flashtag', background='red')
    t.pack()
      
    def flash():
     tags = t.item('item1', 'tags')
     t.item('item1', tags='' if tags else 'flashtag')
     t.after(500, flash)
     itemselected = t.selection()
     for x in itemselected:
      if (x == 'item1'):
       t.selection_remove(t.get_children())

    flash()

    Thank you for your reply. Unfortunately that's not quite what I'm
    after, because it unselects the flashing item.

    My goal was to be able to change the colour of an individual item
    regardless of whether it is selected or not. To do that, it is
    necessary to be able to change the colour of an individual selected
    item, without changing the selection or changing the colour of other
    selected items. It seems this isn't possible.

    Thanks

    John

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From stefalem@21:1/5 to All on Sun Feb 12 12:31:44 2023
    Il 12/02/23 12:10, John O'Hagan ha scritto:

    My goal was to be able to change the colour of an individual item
    regardless of whether it is selected or not. To do that, it is
    necessary to be able to change the colour of an individual selected
    item, without changing the selection or changing the colour of other selected items. It seems this isn't possible.

    ok sorry. As another alternative I had thought of this:

    from tkinter import *
    from tkinter.ttk import *

    root = Tk()
    t = Treeview(root)

    t.insert('', 0, iid='item1', text='item1')
    t.insert('', 1, text='item2')
    t.tag_configure('flashtag', background='red')
    t.pack()
    style = Style()
    styleDefault = (style.map("Treeview"))

    def flash():
    tags = t.item('item1', 'tags')
    t.item('item1', tags='' if tags else 'flashtag')
    t.after(500, flash)
    itemselected = t.selection()
    for x in itemselected:
    if (x == 'item1'):
    style.configure('Treeview', selectbackground='red')
    style.map('Treeview', background=[('disabled', 'white')], foreground=[('disabled', 'red')])
    else:
    style.map('Treeview', background=[('selected', styleDefault['background'][1][1])],
    foreground=[('selected', styleDefault['background'][0][1])])
    flash()
    mainloop()

    Maybe it can be useful for other situations.

    Bye bye

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Thomas Passin@21:1/5 to John O'Hagan on Sun Feb 12 08:59:03 2023
    On 2/12/2023 6:10 AM, John O'Hagan wrote:
    On Mon, 2023-02-06 at 10:19 -0800, stefalem wrote:
    Il giorno sabato 4 febbraio 2023 alle 11:43:29 UTC+1 John O'Hagan ha
    scritto:
    ...

    Is there another way to do what I want?

    from tkinter import *
    from tkinter.ttk import *

    root = Tk()
    t = Treeview(root)

    t.insert('', 0, iid='item1', text='item1')
    t.insert('', 1, text='item2')
    t.tag_configure('flashtag', background='red')
    t.pack()

    def flash():
     tags = t.item('item1', 'tags')
     t.item('item1', tags='' if tags else 'flashtag')
     t.after(500, flash)
     itemselected = t.selection()
     for x in itemselected:
      if (x == 'item1'):
       t.selection_remove(t.get_children())

    flash()

    Thank you for your reply. Unfortunately that's not quite what I'm
    after, because it unselects the flashing item.

    My goal was to be able to change the colour of an individual item
    regardless of whether it is selected or not. To do that, it is
    necessary to be able to change the colour of an individual selected
    item, without changing the selection or changing the colour of other
    selected items. It seems this isn't possible.

    I haven't worked with ttk objects or Treeviews, but judging from old
    style objects, I think you have to re-apply your color and flashing when
    the item becomes selected and possibly again when it becomes unselected.

    Depending on exactly what effect you want, you may also need to apply
    color and flashing when the mouse moves over the item and again when it
    leaves. When I make changes in e.g. color, I like to save the previous value(s) in the object itself. That way I can easily restore say a
    background color without having to work out what it used to be, which
    may be some color that Tk applies based on the desktop theme and who
    know what else.

    Here's an example (simplified) for changing color on mouse hover and
    leave events:

    BG_KEY = 'bg' if platform.lower().startswith('win') \
    else 'activebackground' # Different for Linux!

    def on_enter(event):
    w = event.widget
    w.old_bg = w.cget('bg')
    w[BG_KEY] = BUTTON_HOVER # Color you have chosen

    def on_leave(event):
    w = event.widget
    _bg = w.old_bg
    w[BG_KEY] = _bg

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John O'Hagan@21:1/5 to Thomas Passin on Mon Feb 13 16:42:03 2023
    On Sun, 2023-02-12 at 08:59 -0500, Thomas Passin wrote:

    [...]

    On 2/12/2023 6:10 AM, John O'Hagan wrote:

    [...]


    My goal was to be able to change the colour of an individual item regardless of whether it is selected or not. To do that, it is
    necessary to be able to change the colour of an individual selected
    item, without changing the selection or changing the colour of
    other
    selected items. It seems this isn't possible.

    I haven't worked with ttk objects or Treeviews, but judging from old
    style objects, I think you have to re-apply your color and flashing
    when
    the item becomes selected and possibly again when it becomes
    unselected.

    Depending on exactly what effect you want, you may also need to apply
    color and flashing when the mouse moves over the item and again when
    it
    leaves.  When I make changes in e.g. color, I like to save the
    previous
    value(s) in the object itself.  That way I can easily restore say a background color without having to work out what it used to be, which
    may be some color that Tk applies based on the desktop theme and who
    know what else.

    Here's an example (simplified) for changing color on mouse hover and
    leave events:

    BG_KEY = 'bg' if platform.lower().startswith('win') \
              else 'activebackground'  # Different for Linux!

    def on_enter(event):
         w = event.widget
         w.old_bg = w.cget('bg')
         w[BG_KEY] = BUTTON_HOVER  # Color you have chosen

    def on_leave(event):
         w = event.widget
         _bg = w.old_bg
         w[BG_KEY] = _bg



    Thank you again for your reply, Thomas. I didn't know tkinter
    configuration keys have different names on different platforms, that
    seems unneccesary! I also like the idea of storing the original
    settings on the object itself.

    However, because of the nature of ttk widgets, I think I'm stuck with
    my original issue. Unlike classic tkinter widgets, a lot of the ttk configuration options are not available on individual widgets, but must
    be done through styles or tags. On Treeviews (and possibly other ttk
    widgets), the colours for selected items can only be set using
    style.map (AFAIK).  The colours for individual items can (only?) be set
    using tags, but this is evidently overridden when the item is
    selected by the selection colour dictated by the style. Treeview tags
    do not have a 'selectbackground' option like many tkinter widgets do.
    (Fonts _can_ be set this way, so I could flash, say, the font size, but
    that's ugly!)

    As as aside, IMO ttk looks great (and Treeview is really useful) but
    this approach to configuration is a double-edged sword. On one hand
    it's super-easy to change the whole look, but on the other, it can be
    overly restrictive if you want to control the look of individual
    widgets. 

    I might try to put forward a feature request to add a
    'selectbackground' option to ttk tags to see if there's any interest.

    Thanks

    --

    John

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