ttk.Treeview: Farbe für einzelne Zeilen geht nicht wie erwartet

Fragen zu Tkinter.
Antworten
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

Ich stehe gerade mal fest auf dem Schlauch. Nach allem, was ich in Lehrbuch, tkinter-Dokumentation und per Suchmaschine finden kann, müsste das folgende Beispielprogramm hessische und bayerische Städte in der Treeview-Komponente farblich kennzeichnen. Es denkt aber gar nicht daran.

Das Eingabefeld unten zeigt die entsprechenden Städte richtig mit Bundesland an (Doppelklick oder Enter-Taste). Daraus schließe ich, dass das Vergeben der tags richtig funktioniert, es hat nur nicht die Wirkung, für die tag_configure eigentlich sorgen sollte.

Code: Alles auswählen

#!/usr/bin/env python3
# treeview_forum.py

"""Treeview-Komponente erforschen"""

import tkinter as tk
from tkinter import ttk

CITIES = [
    "1,Frankfurt,HE",
    "2,Augsburg,BY",
    "3,Kiel,SH",
    "4,Flensburg,SH",
    "5,Stuttgart,BW"
    ]


class TreeFrame(ttk.Frame):

    column_defs = {
        "#0": {"label": "IDN", "width": 40, "anchor": tk.E},
        "Name": {"label": "Name", "minwidth": 200, "stretch": True},
        "Country": {"label": "CC", "width": 80}
        }
    default_width = 100
    default_minwidth = 10
    default_anchor = tk.W

    def __init__(self, parent, export_city, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)
        self.export_namecolumn = export_city
        self.treeview = ttk.Treeview(self, 
                                     columns=["Name", "Country"],
                                     selectmode='browse')
        for (name, definition) in self.column_defs.items():
            label = definition.get('label', '')
            anchor = definition.get('anchor', self.default_anchor)
            minwidth = definition.get('minwidth', self.default_minwidth)
            width = definition.get('width', self.default_width)
            stretch = definition.get('stretch', False)
            self.treeview.heading(name, text=label, anchor=anchor)
            self.treeview.column(name, anchor=anchor, minwidth=minwidth,
                                 width=width, stretch=stretch)
        self.treeview.tag_configure('hessen', background='lightgreen')
        self.treeview.tag_configure('bayern', background='darkgray', 
                                    foreground='white')
        self.treeview.bind('<<TreeviewOpen>>', self.on_open_record)
        self.scrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL,
                                       command=self.treeview.yview)
        self.treeview.configure(yscrollcommand=self.scrollbar.set)
        self.treeview.grid(row=0, column=0, sticky='NSEW')
        self.scrollbar.grid(row=0, column=1, sticky='NSW')
        
    def on_open_record(self, *args):
        selected_item = self.treeview.selection()[0]
        selected_name = self.treeview.set(selected_item, 0)
        in_hessen = (" (Hessen)" 
                     if self.treeview.tag_has('hessen', selected_item) else "")
        in_bayern = (" (Bayern)" 
                     if self.treeview.tag_has('bayern', selected_item) else "")
        msg = "{0}{1}{2}".format(selected_name, in_hessen, in_bayern)
        self.export_namecolumn(msg)
        
    def populate(self, rows):
        """Clear the treeview & write the supplied data rows to it."""
        for row in self.treeview.get_children():
            self.treeview.delete(row)
        for rec in rows:
            idstr = str(rec['ID'])
            values = [rec['Name'], rec['CC']]
            if rec['CC'] == 'HE':
                cc = 'hessen'
            elif rec['CC'] == 'BY':
                cc = 'bayern'
            else:
                cc = ''
            self.treeview.insert('', 'end', iid=idstr, text=idstr, 
                                 values=values, tags=(cc,))
        if rows:
            self.treeview.focus_set()
            first = self.treeview.identify_row(0)
            self.treeview.selection_set(first)
            self.treeview.focus(first)


class RecordFrame(ttk.Frame):
    
    def __init__(self, parent):
        super().__init__(parent)
        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        ttk.Label(self, text="Ausgewählt:").grid(row=0, column=0)
        self.entryvar = tk.StringVar()
        ttk.Entry(self, textvariable=self.entryvar).grid(row=1, column=0,
                                                         sticky='EW')
        
    def set(self, value):
        self.entryvar.set(value)
        
    def get(self):
        return self.entryvar.get()
        
class Application(tk.Tk):
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.geometry("360x500")
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)
        self.recordlist = TreeFrame(self, self.show_city)
        self.recordlist.grid(row=0, padx=10, pady=10, sticky="NSEW")
        self.dataframe = RecordFrame(self)
        self.dataframe.grid(row=1, padx=10, pady=10, sticky="NSEW")
        self.recordlist.populate(self.get_all_cities(CITIES))
        
    def get_all_cities(self, citynames):
        fieldnames = ("ID", "Name", "CC")
        allcities = [dict(zip(fieldnames, row.split(','))) for row in citynames]
        return allcities
    
    def show_city(self, city):
        self.dataframe.set(city)
        
    
root = Application()
root.mainloop()

Wo liegt mein Fehler? Ersetzen der Farbnamen durch RGB-Werte habe ich ausprobiert, das hat nichts geändert. Ich habe auch versuchsweise die Aufrufe von treeview.tag_configure(...) an das Ende der Methode populate(...) verlagert, so dass die Daten vorher in den treeview kamen. Auch kein Unterschied.

Python 3.7.3, Windows 10. Mit ArchLinux und LXQt als Desktop gleiches Ergebnis.
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

Ergänzung: Debian 9, Python 3.5.3, Gnome: Das Programm tut, was es soll, einschließlich Farbkennzeichnung der Bayern und Hessen. Der Verdacht richtet sich also auf Änderungen in Python, tkinter oder tcl/tk. Aber auch die offizielle Python-Dokumentation, Version 3.7.3, für ttk.Treeview erwähnt nichts dergleichen. Weiß jemand mehr dazu?

Wenn mich meine Erinnerung nicht täuscht, hat die Farbkennzeichnung mittels tags auch noch unter einer etwas früheren Version von Python 3.7 funktioniert. Aber das kann ich nicht mehr nachprüfen.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi bb1898

Ich verstehe dein Problem nicht ganz. Hier kann ich dein Skript erfolgreich starten. Die 5 Länder Regionen werden korrekt angezeigt. Frankfurt in hellgrün, Augsburg in hellgrau die restlichen Landregionen alle mit weissem Hintergrund. Nur die selektierte Landregion wird durch eine dunkelblaue Hintergrundfarbe abgedeckt. Was ist jetzt genau dein Problem. Kannst du es etwas genauer beschreiben.

N.B. Dein Land ist nicht mein Land. Somit kenne ich die unterschiedlichen Farben der Landregionen nicht.

Gruss wuf :-)
Take it easy Mates!
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

Offensichtlich funktioniert das Programm bei Dir genau, wie es soll. Die Farben der Bundesländer sind nach persönlicher Vorliebe gewählt und nur Bayern und Hessen sollen gekennzeichnet werden. Bei mir werden die Namen unter Python 3.7 zwar alle richtig angezeigt, aber die Hintergrundfarbe ist überall weiß, auch bei Frankfurt und Augsburg.

Ergänzung, heute ausprobiert: mit einer Font-Angabe statt einer Hintergrundfarbe (also z.B. "self.treeview.tag_configure('hessen', font='Times, 10, bold')") klappt die Kennzeichnung auch mit neuestem Python. Das Problem scheint also nur bei Farbangaben zu bestehen. Aber warum?

Welche Python-Version hast Du benutzt und welches Betriebssystem?

Übrigens habe ich mir inzwischen auch die aktuelle tcl/tk-Dokumentation angesehen und auch da steht tag_configure mit Farb-Optionen drin.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi bb1898

Habe dein Skript ohne Probleme ausprobiert mit:

a) Python 3.5 / Ubuntu 16.04
b) Python 3.6 / Ubuntu 18.04

Gruss wuf :-)
Take it easy Mates!
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

Danke sehr, das stützt meine Erinnerung, dass die Farbkennzeichnung von Treeview-Zeilen noch bis vor kurzem genau so ging. Ich habe inzwischen zumindest für 3.5.3 und 3.7.3 auch die tcl/tk-Version einschließlich Patchlevel ermittelt: 8.6.6 für die frühere, 8.6.9 für die spätere Python-Version. Letzteres scheint nach wie vor der aktuelle Stand zu sein, der offiziellen Dokumentation nach zu urteilen.

Mir sieht das mehr und mehr nach einem Bug in tkinter oder evtl. in tcl/tk aus, in jedem Fall recht frisch eingeschleppt. Ich habe jetzt das Problem in der Tkinter-Mailingliste beschrieben, auf Englisch kann man immer auf mehr Resonanz hoffen, so mühsam das ist.
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

Die Tkinter-Mailingliste erwies sich als gute Idee, ich bekam sehr schnell den Hinweis auf diesen tcl/tk-Bug und die zugehörige Diskussion:
http://core.tcl.tk/tk/tktview?name=509cafafae
Im neuesten, zuoberst angezeigten Beitrag findet sich sogar ein funktionierender Workaround für Tkinter; außerdem fand ich die Diskussion lehrreich.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi bb1898
Besten Dank für deine Nachforschung und Mitteilung, welche einem die stundenlange Sucherei nach einem Update erspart.
Gruss wuf :-)
Take it easy Mates!
Antworten