Problems using Tile/Ttk

Issues with Tile/Ttk

MG has started this page to list some problems he's been having trying to switch to Tile/Ttk widgets in an app using Tk 8.5. All of the development I'm doing is on Windows XP with a "Classic" Windows look, and I'm hoping someone might be able to help me out with a few of the problems...

Please feel free to add things here if you're having any problems switching from classic Tk widgets to Ttk, or just with using Ttk widgets in general.

MG Another one, but not related to my inability to use ttk::style for a change ;) I've just been kind've surprised to find that you disabling ttk::treeview widgets apparently does nothing. On Win Xp, using Tcl/Tk 8.5.1, I just did:

  pack [ttk::treeview .t]
  .t insert {} end -id a -text a
  .t insert {} end -id b -text b
  .t insert {} end -id c -text c
  .t selection set a
  .t state disabled

The text for the currently selected item became blurred-looking (just the normal "sunken" look for disabled text, with the fg in white and the bg blue, I think; so visually it seems disabled). But I can still select elements (which triggers <<TreeviewSelect>>), which seems strange/wrong. Is this an oversight, or intended behaviour?

I know I can do something semi-hacky (checking the individual bindings to see which need this is less hacky) like

  foreach x [bind Treeview] {
    bind Treeview $x "if \{!\[%W instate disabled\]\} \{ [bind Treeview $x] \}"

to change all the bindings to only trigger when it's not disabled, but thought I would see what others thought first. Is this a bug that needs reporting, or just a curious feature to be aware of?

MG My latest hurdle is trying to get coloured buttons. In classic Tk, this is a case of doing:

  button .b -background red

In Tile, I've tried things along the lines of

  ttk::button .b
  1 % ttk::style configure TButton -background red
  2 % ttk::style configure TButton.Button -background red
  3 % ttk::style configure TButton.Button.label -background red

... and so on. Number 1 there has some effect, but not the desired one - I just get a red border around the outside of the button. 2 and 3 (and all kinds of variations, based on the layout elements returned by ttk::style layout TButton) have no effect.

But, if I do #1 and then switch to a different theme from my default (xpnative):

  ttk::setTheme clam

The button changes to a Clam button - and it's red! Now, someone has shown me in the past how I can hack bits out of the clamTheme.tcl file before to basically steal their button for my theme, but that's not hugely helpful - the whole reason I'm trying to switch to Tile is for properly native widgets, and (while the Clam button is a very attractive shape), it's definitely not native. (Ironically, on Windows with the Classic look, my red Tk button looks totally native, but I'd like to do this so it works cross-platform, and even on my Win XP system with the "Classic" look turned off, regular Tk buttons stick out.)

So, my question: is there a way to actually get a native looking button, in the color of my choice, no matter the theme I'm using? Or would I be better just admitting defeat here, and sticking with a regular old Tk button, and hoping noone notices that it's different from all the other buttons in my app? This has been driving me mad on and off for months, so any help someone in the know could offer would be greatly appreciated. Thanks for your time.

Martyn Smith Have you tried looking at which explains exactly what you are trying to do and how to do it. You could also look at the tcl files as part of tile which define the toolbutton style.

   button .b -style Toolbutton

Don't forget changing the TButton style changes ALL your buttons.

MG I have (it's basically my first stop when I'm having issues with Ttk, now), but I couldn't find anything there that actually worked on Windows. The problem I'm having isn't that I can't define a style to change the -background option for my button, it's that the option doesn't actually change the color of the button on Windows, just a border around it. For other themes (or other options, like -foreground) it works just as I'd expect.

For instance, taking one of the examples from that page (colors changed just to make it a bit more visually distinct):

  ttk::style map TButton \ 
        -background [list disabled green  active red] \ 
        -foreground [list disabled blue] \ 
        -relief [list {pressed !disabled} sunken] \ 

  pack [ttk::button .btn -text "Example"]
  .btn state disabled

Then I change the theme to clam:

  ttk::setTheme clam

and repeat the ttk::style map .... This is how the two different buttons look:

What I'm trying to do is get a native looking button like the first, but with a proper green background like the clam button has. There must be something, somewhere, setting the button's color to grey (well, SystemButtonFace), but the -background option is referring to a different element of the widget. (Given the difference between this and other Tk/Ttk widgets' use of -background, and of -border options, I would go so far as to say the wrong element of the widget.)

Martyn Smith I tried this out on my XPSP3 with 'crystalxp' and it appears that the xpnative uses windows built in button i.e. Not drawn buttons, TK buttons are drawn using a graphics layer, this means that xpnative, ie OS native buttons are defined by the system (mine are apparently curved bitmap style buttons), I believe that this is the same for TTK bitmap themes like smog or plastik which tile bitmaps for the buttons.

I think you will have to find another UI design/analogy for the coloured buttons or maybe design a theme based on a drawn theme using the standard 'system' colours from windows.

PT 12-May-2009: Lets work this through a bit. ttk widgets are composed of a set of visible elements bound into a widget style using a layout definition. The style elements are the bits that actually produce pixels on the screen. In the case of the Windows XP theme the elements are generally implemented using the Microsoft Visual Styles API so what you get are the various parts of a button drawn using the native Windows style engine. If you really want to change the background of a button then you must change the style of the button and define a new element to replace the one being used as the background. Elements come from element factories and there are currently 2 (in 8.6 three) sources of elements. The 'image' engine lets you define elements using images. The 'from' engine lets you load elements from other themes. The 'vsapi' engine lets you define elements using the Windows themeing API.

Given the above requirements we want to change the background of a button in the current theme. We can explore the current button:

 % ttk::style layout TButton
 Button.button -sticky nswe -children {
    Button.focus -sticky nswe -children {
        Button.padding -sticky nswe -children {
            Button.label -sticky nswe}}}

This is the xpnative version. The button element provides the border and background because that is how Windows handles it. The default theme looks a bit different:

 % ttk::style theme settings default {ttk::style layout TButton}
 Button.border -sticky nswe -border 1 -children {
     Button.focus -sticky nswe -children {
        Button.padding -sticky nswe -children {
            Button.label -sticky nswe}}}

Here we have a border element that will do the work. So we can steal it and replace the button element with a new my.border element:

 % ttk::style element create my.border from default
 % ttk::style layout My.TButton { -sticky news -border 1 -children {
    My.Button.focus -sticky news -children {
        My.Button.padding -sticky news -children {
            My.Button.label -sticky news }}}}

If we create an instance of this 'ttk::button .b -text test -style My.TButton' then we will see a flat default background label with a xp style focus ring. We need to configure this style and map the widget states to make it look like a button.

 % ttk::style configure My.TButton -background SteelBlue -relief raised
 % ttk::style map My.TButton -relief {{pressed !disabled} sunken}

Or we could copy over all the defaults from the default theme:

 % ttk::style configure My.TButton {*}[ttk::style theme settings default {ttk::style configure TButton}]
 % ttk::style map My.TButton {*}[ttk::style theme settings default {ttk::style map TButton}]

Note that what we have now doesn't look right at all. It looks like a Win95 or old Tk button. The text and the focus ring are using the correct elements but the rest of it looks crap. It will always do so if you discard the theme like this. You could create an image and fix up the borders to look a bit better, but then it would look wrong on Vista or the XP green theme or the dark grey Zune theme. Just because it is possible doesn't mean it's smart.

A further issue will arise if the end user switches themes. In that case you need to put the code for creating this style into a function and have that function be bound to the <<ThemeChanged>> virtual event.

MG Thanks for the detailed explanation, Pat. Yeah, I was trying to avoid that (basically creating a whole new button), and was hoping there was a way to actually recolor a "proper" winnative/xpnative button that I was missing somehow, but when simply using -background failed I kinda suspected I was fighting a losing battle there :) But this begs the question for me - why can't you change the color? I'm not remotely familiar with the Windows style API, but I know I've seen Microsoft apps using differently colored buttons. (That said, the vast majority of Microsoft apps I have installed on my computer atm completely ignore my Windows display settings, and (for example) use XP style buttons even though I have Windows set to the Classic (95/98) look, and it really wouldn't suprise me if they used methods not in the API to get the look they want and just leave everyone else out.) So, is there just no support in the published API for doing this, or is it just something that Ttk widgets don't make use of currently?

For the time being I'll just stick with standard Tk buttons where I'm using differently coloured buttons, and see if I can find some other way to distinguish the different ones later (maybe use -compound and include an image or something). Thanks, both of you, for your help and suggestions.

PT 13-May-2009: I thought about this some more. You can't change the xp theme background colour because it is defined as a bitmap image that is then tiled across the defined region. However we can place a tk controlled surface into the layout inside the border region so that the main body of the widget has a specified colour but the border continues to match the theme. For xpnative:

 ttk::style layout Tomato.Button {
   Tomato.Button.button -sticky nswe -children {
     Tomato.Button.focus -sticky nswe -children {
       Tomato.Button.fill -sticky nswe -children {
         Tomato.Button.padding -sticky nswe -children {
           Tomato.Button.label -sticky nswe}}}}}
 ttk::style configure Tomato.Button -width -11 -padding {1 1} -anchor center -background tomato
 pack [ttk::button .b -text Testing -style Tomato.Button] -padx 5 -pady 5

In other styles there will not necessarily be a .button element and the focus ring might be around the text rather than around the padding.

Bear in mind that this defines a style and all instances of this style will have the same colour. You still have no ability to define the background colour of a single widget without defining another style. Although you can use widget state to modify the -background eg:

  ttk::style map Tomato.Button -background {background chocolate}
  .b state background

can toggle the colour based on this modified state flag (in 8.5 there are not many such flags, 8.6 has 3 user states - user1 to user3)

MG finally gave in and just stopped using -background for this; I switched to using different coloured images to represent the current state, and used -compound instead. I prefer the old appearance on my computer, but in the long run the new look is probably going to be more consistently good-looking in other themes.