TkinterEP / ttkthemes

A group of themes for the ttk extenstions for Tkinter
GNU General Public License v3.0
365 stars 47 forks source link

Allow using an existing root window #5

Closed Akuli closed 7 years ago

Akuli commented 7 years ago

First of all, these themes are awesome. Thanks!

Here's my problem: I want to use ttkthemes in my project, but I want it to be an optional plugin so people aren't forced to install it in order to use my project. My plugin API doesn't support customizing the creation of the root window (for obvious reasons), so I ended up with this mess:

import functools

import ttkthemes

def setup():
    # yes, this is needed
    ttkthemes.ThemedStyle()

    # ttkthemes wants me to create a ttkthemes.ThemedTk instead of a sane
    # tkinter.Tk, but i don't like that at all so i need these weird poops
    root = code that returns a root window
    root.img_support = False
    for theme in sorted(ttkthemes.ThemedTk.get_themes(root)):
        callback = functools.partial(ttkthemes.ThemedTk.set_theme, root, theme)
        # here's code that adds a menu item that runs callback()
    del root.img_support

I think there should be a documented way to list and set themes with an existing root window. Currently all ttkthemes stuff in my code uses undocumented implementation details.

RedFantom commented 7 years ago

The line with ttkthemes.ThemedStyle() is indeed required, as it calls upon the tk attribute (which is an internal Tcl-interpreter for a Tk instance, which Tkinter automatically assigns to the most recently created Tk object if no alternative is provided in the kwargs). The current line you're using for that is not at all readable though, as a lot of stuff is happening under the hood that's not shown. It can also easily be broken if the code is not called in the order you expect, causing very weird behaviour that's hard to debug.

Moving on through your code: what you're doing is not how Python, nor this library, is meant to be used. You're modifying a normal Tk instance into something that has to resemble a ThemedTk instance by calling functions of the ThemedTk class on it. This is something that I cannot support if I ever decide to change stuff, as it's really fragile (PyCharm will give you a notification for a reason, if you don't use it, try it 😉 ). Trust me, I've tried that stuff too once and it was not a pleasent experience.

Something like this would suit your needs, I think:

import tkinter as tk
from ttkthemes import ThemedStyle

def setup():
    root = tk.Tk() # Or your own custom class for that matter, or whatever 
                   # way you want to get your Tk instance
    style = ThemedStyle(root)
    style.set_theme("plastik")

That's the way the API is intended to work, and as it has been tested to work. The other option is a custom class that inherits from two other classes:

import tkinter as tk
from ttkthemes import ThemedTk

class MyThemedCustomTk(NormalCustomTk, ThemedTk):
    # And add the right stuff, but an __init__ call to ThemedTk is still mandatory!

If, even after these two options, you would like to bypass all of the ttkthemes python code, you can (though I will not guarantee its compatibility with future versions) by doing what ThemedTk essentially does in its __init__ function: calling upon a tkinter.Tk.tk Tcl interpreter.

Edit: Admittedly, I haven't had the time to document the use of ThemedStyle yet. For that, I do apologize.

Akuli commented 7 years ago

I know how the tk attribute works, and I have a comment about the code being :hankey:. Anyway, I didn't know that ThemedStyle also has get_themes and set_theme methods. Thanks!