Dark mode with wxpython on Windows

Good morning
I’m trying to set dark mode with wx on windows
But the dark mode does not apply to all items
Is there something I forgot to do?

image

hello, which instructions did you use?

Marco

This is the example

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        super(MyFrame, self).__init__(parent, title=title, size=(400, 300))
        self.panel = wx.Panel(self)

        # Set dark theme colors
        dark_bg_color = "#252525"  # Dark background color
        dark_fg_color = "#FFFFFF"  # Dark foreground color

        # Set dark theme for the frame and panel
        self.SetBackgroundColour(dark_bg_color)
        self.panel.SetBackgroundColour(dark_bg_color)
        
        # Creating a read-only multi-line edit box
        self.text_ctrl = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_READONLY)
        
        # Creating buttons
        self.button1 = wx.Button(self.panel, label='Button 1')
        self.button2 = wx.Button(self.panel, label='Button 2')
        self.button3 = wx.Button(self.panel, label='Button 3')
        self.button4 = wx.Button(self.panel, label='Button 4')
        
        # Creating an edit box for path and browse button
        self.path_text_ctrl = wx.TextCtrl(self.panel)
        self.browse_button = wx.Button(self.panel, label='Browse')
        
        # Setting up the layout using a sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.text_ctrl, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
        
        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add(self.button1, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        button_sizer.Add(self.button2, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        button_sizer.Add(self.button3, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        button_sizer.Add(self.button4, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        
        path_sizer = wx.BoxSizer(wx.HORIZONTAL)
        path_sizer.Add(self.path_text_ctrl, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        path_sizer.Add(self.browse_button, flag=wx.ALL, border=5)
        
        sizer.Add(button_sizer, flag=wx.EXPAND)
        sizer.Add(path_sizer, flag=wx.EXPAND | wx.ALL, border=10)
        
        self.panel.SetSizer(sizer)
        
        # Binding events
        self.browse_button.Bind(wx.EVT_BUTTON, self.on_browse)
        
    def on_browse(self, event):
        dialog = wx.FileDialog(self, "Choose a file", style=wx.FD_OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            path = dialog.GetPath()
            self.path_text_ctrl.SetValue(path)
        dialog.Destroy()

if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame(None, "Window with wxPython")
    frame.Show()
    app.MainLoop()

Are there solutions to use dark mode with wxpython?

You should check on wxwidgets mailing lists about this. There was discussion in the last months about accessing OS API for dark mode. Using setbackground/foreground directly in wxpython code is tedious and probably has limitations. I recently found win32mica (only win11): it states to work also with wxpython but haven’t tried yet. If someone has experience with this or other approaches please share

Marco

Looks like MSWEnableDarkMode is in wxWindows 3.3.0, I can’t confirm it works… just looking at github
as far I know we’re on wxWidgets’ 3.2.0 release tag

I am using wxpython==4.1.1 version
I don’t know if dark mode is disabled
And how can something like this be disabled at a time when all programs and systems tend to support dark mode

I would love to see something more general about support of normal vs. dark mode, but what I have found is that if I set colors directly, I may run into problems where widgets or text become invisible in one mode or the other, because foregrounds and backgrounds have the same or close colors. If instead, I use something like this:

<widget>.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))

and use the colors defined here: wx.SystemColour — wxPython Phoenix 4.2.1 documentation then this works much better. Further, at least on MacOS, if the system transitions between dark/light my windows all change nicely.

@MesterPerfect - I’m not clear what you are trying to do. Are you trying to make your wxPython application respond to changing the operating system to a dark theme? Or are you not changing the OS theme, but just trying to make your application simulate a dark theme on its own?

I only run on linux and, if I change the desktop to use a dark theme, nearly all wxPython controls display in the correct dark theme colours without my having to make any code changes. The only exceptions I have noticed are controls for which I have already explicit set specific colours, plus the StyledTextCtrl.

I’m trying to get my app to emulate a dark theme on its own
As for the mode switching that happens to you on Linux, it doesn’t happen on Windows

I don’t think this will work but I will try

@RichardT I’m trying to get my app to emulate a dark theme on its own
As for the mode switching that happens to you on Linux, it doesn’t happen on Windows

Thanks for the clarification. As far as I can see the only option would be to explicitly set the colours for all the controls. Perhaps you could call GetChildren() recursively. However, I have come across some controls for which GetChildren() returns an empty list, so it won’t work in all cases.

I modified the code you posted earlier:

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        super(MyFrame, self).__init__(parent, title=title, size=(400, 300))
        self.panel = wx.Panel(self)

        # Set dark theme colors
        self.dark_bg_color = "#252525"  # Dark background color
        self.dark_fg_color = "#FFFFFF"  # Dark foreground color

        # Set dark theme for the frame and panel
        # self.SetBackgroundColour(dark_bg_color)
        # self.panel.SetBackgroundColour(dark_bg_color)

        # Creating a read-only multi-line edit box
        self.text_ctrl = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_READONLY)

        # Creating buttons
        self.button1 = wx.Button(self.panel, label='Button 1')
        self.button2 = wx.Button(self.panel, label='Button 2')
        self.button3 = wx.Button(self.panel, label='Button 3')
        self.button4 = wx.Button(self.panel, label='Button 4')

        # Creating an edit box for path and browse button
        self.path_text_ctrl = wx.TextCtrl(self.panel)
        self.browse_button = wx.Button(self.panel, label='Browse')

        # Setting up the layout using a sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.text_ctrl, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add(self.button1, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        button_sizer.Add(self.button2, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        button_sizer.Add(self.button3, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        button_sizer.Add(self.button4, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)

        path_sizer = wx.BoxSizer(wx.HORIZONTAL)
        path_sizer.Add(self.path_text_ctrl, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        path_sizer.Add(self.browse_button, flag=wx.ALL, border=5)

        sizer.Add(button_sizer, flag=wx.EXPAND)
        sizer.Add(path_sizer, flag=wx.EXPAND | wx.ALL, border=10)

        self.panel.SetSizer(sizer)

        self.set_dark_mode(self)

        # Binding events
        self.browse_button.Bind(wx.EVT_BUTTON, self.on_browse)


    def set_dark_mode(self, control):
        for child in control.GetChildren():
            child.SetForegroundColour(self.dark_fg_color)
            child.SetBackgroundColour(self.dark_bg_color)
            self.set_dark_mode(child)


    def on_browse(self, event):
        dialog = wx.FileDialog(self, "Choose a file", style=wx.FD_OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            path = dialog.GetPath()
            self.path_text_ctrl.SetValue(path)
        dialog.Destroy()


if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame(None, "Window with wxPython")
    frame.Show()
    app.MainLoop()

When I ran it using Python 3.10.6 + wxPython 4.2.1 gtk3 (phoenix) wxWidgets 3.2.2.1 on Linux Mint 21.1, I got the following display:

Screenshot at 2023-06-09 22-06-32

I don’t know if it would work the same on Windows. Also, this example only contains a few simple controls, more complex controls are unlikely to be affected. In addition when you click the ‘Browse’ button it displays a file selection dialog whose colours won’t be affected.

1 Like

@RichardT This is what I got on Windows
image

I am using python3.9.9 and wxpython4.1.1 on Windows 10

I was afraid of that. Things like scrollbars and other sub-components (like the pop-up menu of a combobox) won’t be accessible to GetChildren() - I’m not sure that it’s possible to access them at all from wxPython?

It’s really annoying, I’m starting to get tired of this library and I’m thinking of moving to PyQt

1 Like

If you google MSWEnableDarkMode, you can see the current progress or look here
Or here

Since I’m running embedded, I have a bit more control, but it’s not perfect, menus aren’t themed

themed
I did a bunch of theming work in MFC, what a pain, so the guys on the wxWindows team are doing a great job
It wont be long

Blockquote[quote=“wxdan, post:6, topic:36530, full:true”]
Looks like MSWEnableDarkMode is in wxWindows 3.3.0, I can’t confirm it works… just looking at github
as far I know we’re on wxWidgets’ 3.2.0 release tag
[/quote]

Yes, MSWEnableDarkMode and wxDarkModeSettings were added in the current 3.3 unstable development branch. The wxPython 4.2.1 release is using the wxWidgets 3.2.2.1 release.

1 Like

Looks like we’re going to have to wait a long time for the dark mode to be supported well and reliably

as @RichardT said: High Contrast in System Settings :rofl:

image