5

I've trying modifing the ttk.Combobox widget fonts using the traditional way

text_font = ('Courier New', '10')
mycombobox = Combobox(font = text_font)
mycombobox.pack()

but the font doesn't really change...

I also tring using ttk.Style but again nothing happens...

text_font = ('Courier New', '10')
ttk_style = ttk.Style()
ttk_style.configure('App.TCombobox', font=text_font)

mycombobox = Combobox(style = "App.TCombobox")
mycombobox.pack()

Is there a way to control the fonts? I want to change both the Entry and the ListBox fonts

Ivan Kolesnikov
  • 1,787
  • 1
  • 29
  • 45
NirMH
  • 4,769
  • 3
  • 44
  • 69
  • A similar question was previously [asked in 2015](https://stackoverflow.com/questions/28938758/combobox-fontsize-in-tkinter) and answered there. – Sun Bear Sep 21 '18 at 17:47

1 Answers1

8

It's really strange behavior, because it's works well on my side:

try:
    import tkinter as tk
    import tkinter.ttk as ttk
except ImportError:
    import Tkinter as tk
    import ttk

import random
import string


def insert_something_to_combobox(box):
    box['values'] = [gen_key() for _ in range(10)]


def gen_key(size=6, chars=string.ascii_uppercase + string.digits):
    # just to generate some random stuff
    return ''.join(random.choice(chars) for _ in range(size))


root = tk.Tk()
text_font = ('Courier New', '10')
main_frame = tk.Frame(root, bg='gray')                  # main frame
combo_box = ttk.Combobox(main_frame, font=text_font)    # apply font to combobox
entry_box = ttk.Entry(main_frame, font=text_font)       # apply font to entry
root.option_add('*TCombobox*Listbox.font', text_font)   # apply font to combobox list
combo_box.pack()
entry_box.pack()
main_frame.pack()

insert_something_to_combobox(combo_box)

root.mainloop()

comboboxentry

It's also possible to specify a font for a particular combobox, since we can rely on ttk::combobox::PopdownWindow function:

...
class CustomBox(ttk.Combobox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bind('<Map>', self._handle_popdown_font)

    def _handle_popdown_font(self, *args):
        popdown = self.tk.eval('ttk::combobox::PopdownWindow %s' % self)
        self.tk.call('%s.f.l' % popdown, 'configure', '-font', self['font'])
...
root = tk.Tk()
text_font = ('Courier New', '10')
main_frame = tk.Frame(root, bg='gray')                  # main frame
combo_box = CustomBox(main_frame, font=text_font)       # apply font to combobox
entry_box = ttk.Entry(main_frame, font=text_font)       # apply font to entry
...
root.mainloop()

However, this CustomBox lacks functionality, because popdown's font is configured once combobox widget is mapped, hence any later configuration of the font won't configure this option for the popdown.

Let's try to override default configuration method:

class CustomBox(ttk.Combobox):
    def __init__(self, *args, **kwargs):
        #   initialisation of the combobox entry
        super().__init__(*args, **kwargs)
        #   "initialisation" of the combobox popdown
        self._handle_popdown_font()

    def _handle_popdown_font(self):
        """ Handle popdown font
        Note: https://github.com/nomad-software/tcltk/blob/master/dist/library/ttk/combobox.tcl#L270
        """
        #   grab (create a new one or get existing) popdown
        popdown = self.tk.eval('ttk::combobox::PopdownWindow %s' % self)
        #   configure popdown font
        self.tk.call('%s.f.l' % popdown, 'configure', '-font', self['font'])

    def configure(self, cnf=None, **kw):
        """Configure resources of a widget. Overridden!

        The values for resources are specified as keyword
        arguments. To get an overview about
        the allowed keyword arguments call the method keys.
        """

        #   default configure behavior
        self._configure('configure', cnf, kw)
        #   if font was configured - configure font for popdown as well
        if 'font' in kw or 'font' in cnf:
            self._handle_popdown_font()

    #   keep overridden shortcut
    config = configure

This class will produce a more responsive instance of the combobox.

CommonSense
  • 4,232
  • 2
  • 14
  • 38
  • 2
    Your code is working indeed - the main difference is the `root.option_add('*TCombobox*Listbox.font', text_font)` statements that manipulate the listbox font for ALL combobox in my application. Again this works, but breaks other GUI elements in my application. – NirMH Mar 29 '17 at 08:05
  • 1
    @NirMH Yeah, I know. So your question was especially about listbox fonts. However, [in accordance to ttk.combobox documentation](http://wiki.tcl.tk/37973) combobox leverages the pre-ttk Listbox for its dropdown element and as such the 'option' command is currently required to set the listbox options. So I think that's cant be done with python to change only specified combolistbox, but they're show a way with tcl! Alas, I failed to `eval` and `call` this commands with `tk`. – CommonSense Mar 29 '17 at 08:54
  • @NirMH, anyway [here](http://stackoverflow.com/a/15463496/6634373) and [there](http://stackoverflow.com/a/28940421/6634373) people claim that `option_add` affects only widgets created after `option_add` invoked. – CommonSense Mar 29 '17 at 11:08
  • 1
    Is it only me or this must be a major *huh?* moment for people getting into `Tkinter`. It all looked so promising until that point. – z33k Jan 31 '18 at 19:19
  • 1
    @o'rety, I'v slightly updated my answer with solution for the specific combobox. Is it still a major _huh_? – CommonSense Feb 01 '18 at 10:41
  • @CommonSense: I'm being torn apart by irreconcilable emotions. On the one hand I checked and it works and that's great and all. On the other it does involve running some mystic tcl code from Python and this recursive call...In the future, I don't know, could you just not do this? To us, beginners, recursive looks diabolical. – z33k Feb 01 '18 at 20:51
  • BTW, @CommonSense, do you by any chance know why the workaround with calling `option_add` on a `root` object ceases to work as soon as one tries to move that call to any place other than the function where `root` is instantiated? I tried calling it from within an arbitrary non-immediate child widget by accessing `root` with `winfo_toplevel` and from within a `root`'s immediate child (passing down its reference in the child's constructor) - both with ill result. Do you know *why*? – z33k Feb 01 '18 at 21:00
  • @o'rety, 1) Why you think that this call is recursive? Such mystic tcl code runs once widget is mapped. Also, remember - `tkinter` just a wrapper to `tcl`/`tk`, therefore any "pythonic" call is translated to diabolical tcl. 2) Unfortunately, I don't know the workaround for your problem, because it's hard to tell what caused it. Consider asking a new question with reproducible example! – CommonSense Feb 02 '18 at 09:17
  • 1
    @CommonSense: I don't want to cause this comment section to be deleted and moved to chat so let me just say that it's no longer recursive, cause you corrected it in the meantime (there was a call to `combo_box` instead `self` on the line 8). As to my question I'm pretty sure it wasn't caused by any peculiarities in my code and was sure it's a common occurrence. Now, getting back on topic - your solution works, can't it be incorporated in the official Tkinter code? Changing font on `ttk.Combobox` would just work and there would be no need for future users to seek help on SO. – z33k Feb 02 '18 at 15:02