jarvisteach / appJar

Simple Tkinter GUIs in Python
http://appJar.info
Other
615 stars 67 forks source link

TTK Support #189

Open ghost opened 7 years ago

ghost commented 7 years ago

Is there any chance you'd be willing to add ttk support, or accept a PR adding ttk support? I personally like the native look a lot more, and I'd love to use ttk widgets with appJar.

List of supported widgets (http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-widget-set.html)

Basic widgets:

And their other incantations:

Others:

Nice to haves:

jarvisteach commented 7 years ago

Definitely interested in adding this.

How do you envisage it working?

Perhaps just a flag that can be set to use ttk if available? This will then import ttk and replace any existing widgets with ttk widgets, as well as make any new widgets available?

Is ttk always part of a Python installation? I seem to remember having issues importing ttk when I first started appJar...

If a user wants ttk, then the config options might need to be changed too?

ghost commented 7 years ago

I imagined using a flag, and then tk/ttk specific functions/options would check for the flag and then use the relevant options. I think the best way to set the flag would be either:

app = gui( useTtk=True )

Or:

app = gui()
app.useTtk()

I'm not sure if tk and ttk widgets can exist together, so if have to check that out.

The x86 Windows installer for python 3.6 has ttk by default, but when I get home I can check some Linux distros. Either way, I think using tk as the default and and requiring ttk to be explicitly asked for would be for the best.

Config options are the biggest issue with this. I remember bg, padx, and pady being the biggest culprits, but it won't be too hard to get the ttk widgets running properly, just some if..else statements checking for the useTtk flag.

EDIT: Fixed formatting

ghost commented 7 years ago

Fully updated and (almost) clean VMs of Ubuntu 16.04 LTS 64 bit, Fedora 25 64 bit, and FreeBSD 64 bit all properly import tkinter, and ttk in python 2 and 3 after installing the necessary packages. It looks like every modern pre-built python is linked against tkinter >= 8.5

jarvisteach commented 7 years ago

Great, thanks for investigating that!

OK, I'll add the second option you mention above, and for now simply have that import ttk - that will override any existing widgets.

I'll leave it at that for the current scheduled release (already overdue) - unless it breaks the basic setup - then in the next release we can look at updating config functions, as well as exposing any other widgets/features.

jarvisteach commented 6 years ago

This doesn't really have any effect.

Need to change instantiation to actually be ttk.Button, etc....

jarvisteach commented 6 years ago

Check here for how to set ttk styles: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-style-layer.html

And themes: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-theme-layer.html

jarvisteach commented 6 years ago

Making progress - need to work out how to subclass using a switch for ttk (for SelectableLabel, AutoCompleteEntry, ajScale)- might not be possible...

Need to go through top-level frames, etc - make sure they are all ttk - might need to have ttk a constructor parameter to achieve this.

Need to look at scrollbars - on list boxes, scroll panes, etc

mpmc commented 6 years ago

Just a small note, you may want to use "clam" as default style on Linux, "aqua" for Mac and "vista" for Windows, these are all built-in so shouldn't cause any issues, you could slap it in a try/except to ignore failed style changes.

jarvisteach commented 6 years ago

Have a solution for subclassing:

However, note that a lot of special classes rely heavily on non-ttk supported features - and can't use a ttk.Label

jarvisteach commented 6 years ago

The useTtk flag needs to be moved to the constructor - otherwise the base Frame can't be a ttk frame

jarvisteach commented 6 years ago

useTtk now available in constructor - the only benefit is that the entire GUI is in a ttk.Frame instead of Frame - gets rid of the white border.

mpmc commented 6 years ago

TextArea isn't supported by Ttk.

mark@mark-desktop2:~/dev/git/HS602/hs602util$ python3 gui.py --ttk default
Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1553, in __call__
    return self.func(*args)
  File "/usr/lib/python3.5/tkinter/__init__.py", line 599, in callit
    func(*args)
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 1852, in __processEventQueue
    func(*args, **kwargs)
  File "gui.py", line 103, in error_win
    self.addScrolledTextArea('traceback')
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 6883, in addScrolledTextArea
    text = self.__buildTextArea(title, self.getContainer(), True)
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 6849, in __buildTextArea
    text.config(highlightbackground=self.__getContainerBg())
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 3239, in __getContainerBg
    return self.getContainer()["bg"]
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1337, in cget
    return self.tk.call(self._w, 'cget', '-' + key)
_tkinter.TclError: unknown option "-bg"
mark@mark-desktop2:~/dev/git/HS602/hs602util$ python3 gui.py --ttk default
Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1553, in __call__
    return self.func(*args)
  File "/usr/lib/python3.5/tkinter/__init__.py", line 599, in callit
    func(*args)
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 1852, in __processEventQueue
    func(*args, **kwargs)
  File "gui.py", line 103, in error_win
    self.addTextArea('traceback')
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 6871, in addTextArea
    text = self.__buildTextArea(title, self.getContainer())
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 6849, in __buildTextArea
    text.config(highlightbackground=self.__getContainerBg())
  File "/home/mark/.local/lib/python3.5/site-packages/appJar/appjar.py", line 3239, in __getContainerBg
    return self.getContainer()["bg"]
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1337, in cget
    return self.tk.call(self._w, 'cget', '-' + key)
_tkinter.TclError: unknown option "-bg"
mpmc commented 6 years ago

ValidationEntry using ttk errors :crying_cat_face: .

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 748, in callit
    func(*args)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 2058, in __processEventQueue
    func(*args, **kwargs)
  File "gui2.py", line 197, in result
    return self.manual()
  File "gui2.py", line 238, in manual
    self.addValidationEntry('addr', 0, 1)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 7370, in addValidationEntry
    ent = self.__buildValidationEntry(title, self.getContainer(), secret)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 7376, in __buildValidationEntry
    vFrame.config(background=self.__getContainerBg())
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 3422, in __getContainerBg
    return self.getContainer()["bg"]
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1486, in cget
    return self.tk.call(self._w, 'cget', '-' + key)
_tkinter.TclError: unknown option "-bg"

I like the use of validation alerting, but wouldn't it be easier (and less code?) to replace "setEntryValid", "setEntryInvalid", and "setEntryWaitingValidaiton" with a single function? That just flashes the entry (once) & inserts some text indicating the error - disappearing on click/text entry?

The use of the icons next to the entries is a nice touch but it kinda looks odd when you have your form neatly aligned and some are just standard entries. This could be solved with an addOptional(?)Entry.

jarvisteach commented 6 years ago

@mpmc - did a piece of work to get validation entries (sort of) working under ttk. Had to work out ttk styles, but at least now it doesn't crash. The border doesn't work on my mac - need to test on other platforms.

You're right - the validation entry could use lots of work. I will look at refactoring the set functions as part of the general add/set/get refactoring I'm currently doing, and will see what improvements can be made to its look & feel...

mpmc commented 6 years ago

@jarvisteach cool, I shall give it a go once I've redone this controller class & report any issues!

jarvisteach commented 6 years ago

Should include support for additional ttk themes: https://github.com/RedFantom/ttkthemes

cowsay652 commented 6 years ago

@jarvisteach I've been trying out ttk on the next release appJar branch, and have found some issues. The first one I encountered was when I used.setCheckbox("title", ticked=True), it would give me AttributeError: 'Checkbutton' object has no attribute 'select'. The second issue that I found was that when I used .zoomImage(), it would give me _tkinter.TclError: unknown option "-bg". I understand that ttk is still in beta, but I thought that it would be good if I let you know of these issues :)

jarvisteach commented 6 years ago

These should both be resolved now, and do please keep them coming!

cowsay652 commented 6 years ago

@jarvisteach Here some more issues (yay!). This time, it's with .addMessage(). I get _tkinter.TclError: unknown option "-bg". Also, Internationalisation looks like it breaks when using ttk, as I get appJar:WARNING [Line 285->1139/changeLanguage]: Invalid config section: <Widget name here> for all the widgets that I have used.

jarvisteach commented 6 years ago

Internationalisation looks to be a slightly bigger issue than just ttk - it's broken across the board, as result of the architecture change.

This bit no longer works: kind = vars(gui)[section] Need to replace that with a lookup in the enum, using the section name - might cause issues due to the capitalisation.

Also, the automated testing should have picked this up - need to actually run some assertions that text has been changed post language switch...

Will raise this/move this to another issue.

cowsay652 commented 6 years ago

@jarvisteach I've found that when using label frames and .setBg(), the background of the label frame doesn't change, with (as far as I've tested) only images and labels using the correct background colour. Also, regarding internationalisation, I get a warning that says that the Notebook widget is not supported (should this move to #71 ?) and this obviously leads to the notebook tabs not being translated.

mpmc commented 6 years ago

@jarvisteach

Do you plan on making ttk widgets the default btw? Once they're all working that is.

cowsay652 commented 6 years ago

@jarvisteach Here are a couple of things that I found that aren't currently being themed by ttk: Spinboxes and Toolbars.

jarvisteach commented 6 years ago

Hey @The-Sleepy-Penguin I don't think there is a ttk spinbox - http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-widget-set.html

What theming are you trying to achieve with it?

cowsay652 commented 6 years ago

@jarvisteach Oops. My mistake - I didn't realise that there wasn't a ttk spinbox (I was just looking for widgets that would let the user make a selection). Perhaps the Optionbox should be replaced by the Combobox for ttk? And another thing - Radiobuttons in ttk don't respond to .setBg (along with Labelframes).

Edit: After a quick search I found this for the ttk spinbox. It looks like it uses the buttons from the ttk scrollbar to change the selection.

cowsay652 commented 6 years ago

@jarvisteach I've read the next_release docs and found how to change the background of most things, but the label in the LabelFrame doesn't change colour when I do: program.ttkStyle.configure("TLabel", background="white"), and I can't find the name for the checkbox layout, as I've tried to use TCheck, TCheckbox and TCheckBox but all return Layout xxx not found.

Also, I noticed a typo in the docs - with the code examples, you put background= as backgroun= :P

jarvisteach commented 6 years ago

Hi @The-Sleepy-Penguin could you give me some sample code to test out...

cowsay652 commented 6 years ago

@jarvisteach Here is some code that demonstrates the issue with Label Frames and Checkboxes:

from appJar import gui

with gui("appJar", useTtk=True) as app:

    app.ttkStyle.configure("TFrame", background="blue")
    app.ttkStyle.configure("TLabel", background="blue")
    app.ttkStyle.configure("TCheckBox", background="blue")
    app.ttkStyle.configure("TCheck", background="blue")
    app.setBg("blue")

    with app.labelFrame("Text"):
        app.setLabelFrameStyle("Text", "TFrame")
        app.addCheckBox("Option here...")
        app.setCheckBoxStyle("Option here...", "TCheckBox")

    app.addCheckBox("Another option...")
    app.setCheckBoxStyle("Another option...", "TCheck")

And here is the result:

capture

Hope this helps :)

jarvisteach commented 6 years ago

OK, had a little play, try these:

These set the default styles for the widgets, so you should't need to call .set XXX Style()

I've also discovered this:

ttk styles inherit from one another, so setting background on . will be inherited by all widgets.

I'm going to use this in appJar...

cowsay652 commented 6 years ago

@jarvisteach Thanks! Those worked a charm.

cowsay652 commented 6 years ago

Okay... just tried this with message boxes and sub windows - app.ttkStyle.configure(".", background="blue") doesn't seem to work. I was also wondering if the message box was themed by ttk, because it doesn't use the regular font used by ttk themes, and when I use .setMessageBg() instead, the background colour changes.

capture 2

When using label frames and third party ttk themes, unless I use app.ttkStyle.configure("TFrame", background="blue") and app.setLabelFrameStyle("title", "TFrame"), it too won't respond to app.ttkStyle.configure(".", background="blue").

jarvisteach commented 6 years ago
mpmc commented 6 years ago

@jarvisteach Is the non-ttk theme your own btw? It looks totally different to what I see elsewhere. The optionbox looks completely different too.

vs image

cowsay652 commented 6 years ago

@jarvisteach I've found that .addIconButton() and .addImageButton() produce errors only when ttk is set to True, and align is set to None. eg:

from appJar import gui

with gui(useTtk=True) as app:
    app.addImageButton("button2", None, "Capture 2.PNG", align=None) # Uncomment this
    app.addIconButton("button", None, "md-play", align=None) # Or this

Produces this for .addImageButton():

2017-12-28 00:15:51,892 appJar:ERROR [Line 5->1522/__exit__]: ContextManager failed: unknown option "-justify"
Traceback (most recent call last):
  File "C:/Users/Penguin/Desktop/icon buttons.py", line 5, in <module>
    app.addImageButton("button2", None, "Capture 2.PNG", align=None)
  File "C:/Users/Penguin/Desktop\appJar\appjar.py", line 6823, in addImageButton
    self.setButtonImage(title, imgFile, align)
  File "C:/Users/Penguin/Desktop\appJar\appjar.py", line 6844, in setButtonImage
    but.config(image=image, compound=TOP, text="", justify=LEFT)
  File "C:\Program Files\Python35\lib\tkinter\__init__.py", line 1338, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Program Files\Python35\lib\tkinter\__init__.py", line 1329, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: unknown option "-justify"

and this for .addIconButton():

2017-12-28 00:15:43,907 appJar:ERROR [Line 4->1522/__exit__]: ContextManager failed: unknown option "-justify"
Traceback (most recent call last):
  File "C:/Users/Penguin/Desktop/icon buttons.py", line 4, in <module>
    app.addIconButton("button", None, "md-play", align=None)
  File "C:/Users/Penguin/Desktop\appJar\appjar.py", line 6829, in addIconButton
    return self.addImageButton(title, func, icon, row, column, colspan, rowspan, align)
  File "C:/Users/Penguin/Desktop\appJar\appjar.py", line 6823, in addImageButton
    self.setButtonImage(title, imgFile, align)
  File "C:/Users/Penguin/Desktop\appJar\appjar.py", line 6844, in setButtonImage
    but.config(image=image, compound=TOP, text="", justify=LEFT)
  File "C:\Program Files\Python35\lib\tkinter\__init__.py", line 1338, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Program Files\Python35\lib\tkinter\__init__.py", line 1329, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: unknown option "-justify"

If ttk is set to False or the align parameter is filled, the error will not occur.

jarvisteach commented 6 years ago

@The-Sleepy-Penguin - try setting align="none" instead of None

cowsay652 commented 6 years ago

@jarvisteach Thanks, that worked.

cowsay652 commented 6 years ago

@jarvisteach When using padding within a frame, the padding doesn't take the colour of the background:

from appJar import gui

with gui("appJar", useTtk=True) as app:
    app.setPadding(10, 10)
    app.ttkStyle.configure(".", background="white")
    app.addButton("Button", func=None)
    app.addButton("Button2", func=None)

    with app.frame("frame"):
        app.setPadding(10, 10)
        app.ttkStyle.configure(".", background="white")
        app.setBg("white")
        app.addButton("Button3", func=None)
        app.addButton("Button4", func=None)

And so results in this: padding

jarvisteach commented 6 years ago

Hi @The-Sleepy-Penguin - I don't know what causes this, it could be a ttk specific thing, where extra style needs to be set (maybe for the padding), or something else. I'll try to look into it in the next few days...

jarvisteach commented 6 years ago

@The-Sleepy-Penguin - OK resolved issue with frames - still can't get dynamic inheritance to work properly, so Frame is now dynamic, and should work with both ttk and regular styling.

cowsay652 commented 6 years ago

@jarvisteach Great! I can confirm that it works :)

adtzlr commented 6 years ago

@jarvisteach Would it be possible to integrate a themed Dropdown Widget (like OptionBox)? As @TheSleepyPenguin mentioned that the TTK ComboBox could replace the AppJar OptionBox?