PySimpleGUI icon indicating copy to clipboard operation
PySimpleGUI copied to clipboard

[ Enhancement] Table: setting selected_row_colors programmatically

Open juergsch opened this issue 3 years ago • 9 comments

Type of Issue (Enhancement, Error, Bug, Question)

Enhancement


Operating System

Windows

PySimpleGUI Port (tkinter, Qt, Wx, Web)

tkinter


Versions

Version information can be obtained by calling sg.main_get_debug_data() Or you can print each version shown in ()

Python version (sg.sys.version)

'3.10.2 (tags/v3.10.2:a58ebcc, Jan 17 2022, 14:12:15) [MSC v.1929 64 bit (AMD64)]'

PySimpleGUI Version (sg.__version__)

'4.60.1'

GUI Version (tkinter (sg.tclversion_detailed), PySide2, WxPython, Remi)

'8.6.12'


Your Experience In Months or Years (optional)

Years Python programming experience

Years Programming experience overall

Have used another Python GUI Framework? (tkinter, Qt, etc) (yes/no is fine)

Anything else you think would be helpful?


Troubleshooting

These items may solve your problem. Please check those you've done by changing - [ ] to - [X]

  • [ x] Searched main docs for your problem www.PySimpleGUI.org
  • [x ] Looked for Demo Programs that are similar to your goal Demos.PySimpleGUI.org
  • [ ] If not tkinter - looked for Demo Programs for specific port
  • [ ] For non tkinter - Looked at readme for your specific port if not PySimpleGUI (Qt, WX, Remi)
  • [ ] Run your program outside of your debugger (from a command line)
  • [x ] Searched through Issues (open and closed) to see if already reported Issues.PySimpleGUI.org
  • [x ] Tried using the PySimpleGUI.py file on GitHub. Your problem may have already been fixed but not released

Detailed Description

To be able to set the color of the selected table row programmatically would be very helpful for my current app. Background: The table shows a list of parameter sets for a material processing machine. If the user changes a parameter below the table related to the parameter set, the selected row should be colored to indicate that the parameter set is outdated. I cannot recalculate as the user changes parameters. This would block the user interface for a few seconds. And therefore the user should press a button for recalculation after his changes.

Code To Duplicate

Screenshot, Sketch, or Drawing


Watcha Makin?

Thanks for your great project. It saved me a lot of time so far!

juergsch avatar Jun 10 '22 13:06 juergsch

I'll get you some code this weekend that will do this. There should be enough information in the element for you to change the selected row colors by using some of the member variables to directly change the tkinter style. I think it should work out OK. I'll know more tomorrow once I spend some time with it.

PySimpleGUI avatar Jun 10 '22 21:06 PySimpleGUI

You will need to get version 4.60.0.40 from GitHub (the latest posted PySimpleGUI.py file)

This is a modified Table Demo Program that includes:

  • A button to change the select colors
  • Two functions that implement the capability:
    • change_select_row_color
    • _fixed_map

Your code should call the change_select_row_color function to change the colors of the selection.

#!/usr/bin/env python
import PySimpleGUI as sg
import random
import string

"""
    Basic use of the Table Element
    
    Copyright 2022 PySimpleGUI
"""

def _fixed_map(style, style_name, option, highlight_colors=(None, None)):
    default_map = [elm for elm in style.map("Treeview", query_opt=option) if '!' not in elm[0] and 'selected' not in elm[0]]
    custom_map = [elm for elm in style.map(style_name, query_opt=option) if '!' not in elm[0] and 'selected' not in elm[0]]
    if option == 'background':
        custom_map.append(('selected', highlight_colors[1] if highlight_colors[1] is not None else sg.ALTERNATE_TABLE_AND_TREE_SELECTED_ROW_COLORS[1]))
    elif option == 'foreground':
        custom_map.append(('selected', highlight_colors[0] if highlight_colors[0] is not None else sg.ALTERNATE_TABLE_AND_TREE_SELECTED_ROW_COLORS[0]))

    new_map = custom_map + default_map
    return new_map

def change_select_row_color(table_element:sg.Table, background_color=None, foreground_color=None):
    if background_color is not None:
        table_element.ttk_style.map(table_element.table_ttk_style_name, background=_fixed_map(table_element.ttk_style, table_element.table_ttk_style_name, 'background', (foreground_color, background_color)))
    if foreground_color is not None:
        table_element.ttk_style.map(table_element.table_ttk_style_name, foreground=_fixed_map(table_element.ttk_style, table_element.table_ttk_style_name, 'foreground', (foreground_color, background_color)))

# ------ Some functions to help generate data for the table ------
def word():
    return ''.join(random.choice(string.ascii_lowercase) for i in range(10))
def number(max_val=1000):
    return random.randint(0, max_val)

def make_table(num_rows, num_cols):
    data = [[j for j in range(num_cols)] for i in range(num_rows)]
    data[0] = [word() for __ in range(num_cols)]
    for i in range(1, num_rows):
        data[i] = [word(), *[number() for i in range(num_cols - 1)]]
    return data

# ------ Make the Table Data ------
data = make_table(num_rows=15, num_cols=6)
headings = [str(data[0][x])+' ..' for x in range(len(data[0]))]

# ------ Window Layout ------
layout = [[sg.Table(values=data[1:][:], headings=headings, max_col_width=25,
                    auto_size_columns=True,
                    # cols_justification=('left','center','right','c', 'l', 'bad'),       # Added on GitHub only as of June 2022
                    display_row_numbers=True,
                    justification='center',
                    num_rows=20,
                    alternating_row_color='lightblue',
                    key='-TABLE-',
                    selected_row_colors='red on yellow',
                    enable_events=True,
                    expand_x=False,
                    expand_y=True,
                    vertical_scroll_only=False,
                    enable_click_events=True,           # Comment out to not enable header and other clicks
                    tooltip='This is a table')],
          [sg.Button('Read'), sg.Button('Double'), sg.Button('Change Colors'), sg.Button('Select Blue on Green')],
          [sg.Text('Read = read which rows are selected')],
          [sg.Text('Double = double the amount of data in the table')],
          [sg.Text('Change Colors = Changes the colors of rows 8 and 9'), sg.Sizegrip()]]

# ------ Create Window ------
window = sg.Window('The Table Element', layout, resizable=True)

# ------ Event Loop ------
while True:
    event, values = window.read()
    print(event, values)
    if event == sg.WIN_CLOSED:
        break
    if event == 'Double':
        for i in range(1,len(data)):
            data.append(data[i])
        window['-TABLE-'].update(values=data[1:][:])
    elif event == 'Change Colors':
        window['-TABLE-'].update(row_colors=((8, 'white', 'red'), (9, 'green')))
    elif event == 'Select Blue on Green':
        change_select_row_color(window['-TABLE-'], background_color='green', foreground_color='blue')
window.close()


PySimpleGUI avatar Jun 11 '22 13:06 PySimpleGUI

Here is the sample code running. After clicking the "Select Blue on Green", the selection colors become blue text on a green background. python_ddH8ztCWz2

PySimpleGUI avatar Jun 11 '22 13:06 PySimpleGUI

Dear Mike

This is fantastic! I was hoping to get an answer or hint after some time but I'd never thought that I'd get a solution ready to use so quickly. That really helps a lot. Thank you very much!

Jürg

juergsch avatar Jun 11 '22 16:06 juergsch

You're quite welcome!

image

I take it that it worked OK.

Let's leave it open as an enhancement.

PySimpleGUI avatar Jun 11 '22 18:06 PySimpleGUI

YES, it worked perfectly fine. Thanks again.

juergsch avatar Jun 11 '22 19:06 juergsch

image

Nice! And you're very welcome again. Thank you for the "thanks"! image

PySimpleGUI avatar Jun 11 '22 19:06 PySimpleGUI

Hi I would be glad if there is a way to do the same for the Tree element but instead of changing the color of the selected row, I want to change the color of every single row programmatically, An example of what I mean is shown below

Below GUI representation program taken from https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Tree_Element.py image

This is actually pretty similar to issue referenced here https://github.com/PySimpleGUI/PySimpleGUI/issues/1026 but i am concerned about the Tree element - something like row_numbers method to tree data element kind of

Use Case: to change the color of each row based on the values in the respective row in another column @jason990420 @PySimpleGUI your inputs are much appreciated

Darksoulz15 avatar Sep 13 '23 17:09 Darksoulz15

There's no such function for Tree element, so I define a function to do the job.

from random import choice
import PySimpleGUI as sg


def set_row_colors(self, row_colors=None):
    if row_colors is not None:  # individual row colors
        self.RowColors = row_colors
        for row_def in self.RowColors:
            iid = self.KeyToID[row_def[0]]
            if len(row_def) == 2:  # only background is specified
                self.TKTreeview.tag_configure(iid, background=row_def[1])
            else:
                self.TKTreeview.tag_configure(iid, background=row_def[2], foreground=row_def[1])


sg.theme("DarkBlue")
sg.set_options(font=('Courier New', 12))

# Add tree data
treedata = sg.TreeData()
for i in range(20):
    parent = choice(list(treedata.tree_dict.keys()))
    treedata.insert(parent, i, f'Node {i:0>2d}', values=[f'Data {i:0>2d}'])

# Get keys for all nodes, except root of tree
keys = list(treedata.tree_dict.keys())
keys.remove('')

layout = [
    [sg.Tree(data=treedata, headings=['Data', ], auto_size_columns=False,
        num_rows=10, col0_width=30, col_widths=[10], justification='center',
        show_expanded=True, enable_events=True, col0_heading='Node', key='-TREE-')],
    [sg.Push(), sg.Button('Change Colors')],
]

window = sg.Window('Tree Element', layout, finalize=True)
tree = window['-TREE-']

# Define the tag for each node
for key in range(20):
    iid = tree.KeyToID[key]
    tree.widget.item(iid, tags=iid)

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED:
        break

    elif event == "Change Colors":
        # format of item in row_colors: (key_of_node, foreground_color, background_color) or (key_of_node, background_color)
        set_row_colors(window['-TREE-'], row_colors=((0, 'white', 'red'), (1, 'white', 'blue'), (2, 'white', 'black'), (3, 'grey')))

window.close()

image

jason990420 avatar Sep 15 '23 18:09 jason990420