sunyata
sunyata

Reputation: 2251

Dark theme for Qt widgets?

Background

I'm building a PyQt5 application, that I'd like to have a dark theme for. Previously I've worked with Android development where there was a dark theme that I could set for a whole application

Question

Is there a dark theme built into Qt (that applies to all widgets in an application, and that is cross-platform)?

Upvotes: 48

Views: 103296

Answers (7)

Hoodlum
Hoodlum

Reputation: 1003

In Qt 6.5 (PyQt6), there is a proper built-in way to get the system light/dark theme.

If all you want is for your widgets to respect the system settings, you can set your application style to 'fusion', and let Qt do the rest. Beware that this might make stuff look horrible if you have stylesheets or are using custom colors.

with QApplication(sys.argv) as qapp:
    qapp.setApplicationName("My Awesome App")
    qapp.setStyle('fusion')

    w = MainUIWindow()
    w.show()

    sys.exit(qapp.exec())

If you want to catch the change from light to dark mode so you can update the stylesheet/custom color, you can connect to the QApplication.styleHints().colorSchemeChanged signal, which will give you information about the new Qt.ColorScheme (either Light, Dark or Unknown).

For more information, and some other suggestions on how to have a bit more control over the process, have a look at this answer and the related article on dark mode for windows 11

Upvotes: 0

yurenchen
yurenchen

Reputation: 2493

PyQtDarkTheme

pip3 install pyqtdarktheme

https://pyqtdarktheme.readthedocs.io/en/stable/how_to_use.html
https://pypi.org/project/pyqtdarktheme/


pyqtdarktheme make life esaier.

qdarktheme.setup_theme("auto")

It can follow OS dark-mode dynamically.

Upvotes: 3

tCot
tCot

Reputation: 357

I suggest you qt-material package which has beautiful dark themes. It is easy to use, just install it as Python package and add these lines:

from qt_material import apply_stylesheet

...

app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()

# setup stylesheet
apply_stylesheet(app, theme='dark_teal.xml')

Documentation is here : https://pypi.org/project/qt-material/

Upvotes: 3

Michael Herrmann
Michael Herrmann

Reputation: 5003

There's no dark theme built into Qt. But you can quite easily create one yourself with the following code:

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QPalette, QColor


app = QApplication([])
# Force the style to be the same on all OSs:
app.setStyle("Fusion")

# Now use a palette to switch to dark colors:
palette = QPalette()
palette.setColor(QPalette.Window, QColor(53, 53, 53))
palette.setColor(QPalette.WindowText, Qt.white)
palette.setColor(QPalette.Base, QColor(25, 25, 25))
palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
palette.setColor(QPalette.ToolTipBase, Qt.black)
palette.setColor(QPalette.ToolTipText, Qt.white)
palette.setColor(QPalette.Text, Qt.white)
palette.setColor(QPalette.Button, QColor(53, 53, 53))
palette.setColor(QPalette.ButtonText, Qt.white)
palette.setColor(QPalette.BrightText, Qt.red)
palette.setColor(QPalette.Link, QColor(42, 130, 218))
palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
palette.setColor(QPalette.HighlightedText, Qt.black)
app.setPalette(palette)

The nice thing about this is that it introduces no external dependencies. If you're interested what the above changes look like, I created an example PyQt5 app with a dark theme. Here is a screenshot:

Qt dark theme

Upvotes: 38

Mike R
Mike R

Reputation: 879

I was trying to apply this to my fbs based app and found the below easily allowed me to style the app by applying it to the AppContext

class AppContext(ApplicationContext):
    def run(self):
        self.main_window.show()
        return self.app.exec_()

    @cached_property
    def main_window(self):
        return MainWindow(self)

    if theme_selection == 'Dark':
        QApplication.setStyle("Fusion")
        #
        # # Now use a palette to switch to dark colors:
        dark_palette = QPalette()
        dark_palette.setColor(QPalette.Window, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.WindowText, Qt.white)
        dark_palette.setColor(QPalette.Base, QColor(35, 35, 35))
        dark_palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.ToolTipBase, QColor(25, 25, 25))
        dark_palette.setColor(QPalette.ToolTipText, Qt.white)
        dark_palette.setColor(QPalette.Text, Qt.white)
        dark_palette.setColor(QPalette.Button, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.ButtonText, Qt.white)
        dark_palette.setColor(QPalette.BrightText, Qt.red)
        dark_palette.setColor(QPalette.Link, QColor(42, 130, 218))
        dark_palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
        dark_palette.setColor(QPalette.HighlightedText, QColor(35, 35, 35))
        dark_palette.setColor(QPalette.Active, QPalette.Button, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.Disabled, QPalette.ButtonText, Qt.darkGray)
        dark_palette.setColor(QPalette.Disabled, QPalette.WindowText, Qt.darkGray)
        dark_palette.setColor(QPalette.Disabled, QPalette.Text, Qt.darkGray)
        dark_palette.setColor(QPalette.Disabled, QPalette.Light, QColor(53, 53, 53))
        QApplication.setPalette(dark_palette)
    elif theme_selection == 'Light':
        QApplication.setStyle("")
        pass
    else:
        pass

You can use Qsettings to save a preference for which mode like this and restore on start.

if settings.contains("theme_selection"):
    # there is the key in QSettings
    print('Checking for theme preference in config')
    theme_selection = settings.value('theme_selection')
    print('Found theme_selection in config:' + theme_selection)
else:
    if not is_mac():
        print('theme_selection not found in config. Using default Darkmode')
        settings.setValue('theme_selection', 'Dark')
        theme_selection = settings.value('theme_selection')
    elif is_mac():
        print('theme_selection not found in config. Using default Lightmode')
        settings.setValue('theme_selection', 'Light')
        theme_selection = settings.value('theme_selection')
    pass

Looks amazing could not comment on Michael Herrmann's post to say thanks but did upvote it.

Before: How it looked before dark pallete mode enabled

After: With Pallete mode enabled

Middle part is xterm.js so that is why its still white for now as its not a QT styled thing.

Upvotes: 9

Alex Huszagh
Alex Huszagh

Reputation: 14634

No, but you may use my fairly comprehensive stylesheets that should look excellent on most platforms (it's inspired by KDE's Breeze Theme, which is a dark theme that is quite elegant). This was (hard) forked from the excellent QDarkStylesheet, which I felt had theme issues in numerous areas, so I modified it extensively for my own needs and added a light theme.

Simple Use

A sample of the theme is here. To use it in PyQt5, simply add the following lines to a project:

import sys
from PyQt5.QtCore import QFile, QTextStream
from PyQt5.QtWidgets import QApplication
import breeze_resources

app = QApplication(sys.argv)
file = QFile(":/dark.qss")
file.open(QFile.ReadOnly | QFile.Text)
stream = QTextStream(file)
app.setStyleSheet(stream.readAll())

enter image description here

Dynamic Stylesheet Toggling

In response to a comment, the easiest way to adjust the stylesheet to use either the light or the dark stylesheet dynamically is to wrap it in a function. You may then use the function as a slot to a Qt signal (warning: I primarily develop using C++, so there may be small errors in my code for the signal/slot mechanism).

def toggle_stylesheet(path):
    '''
    Toggle the stylesheet to use the desired path in the Qt resource
    system (prefixed by `:/`) or generically (a path to a file on
    system).

    :path:      A full path to a resource or file on system
    '''

    # get the QApplication instance,  or crash if not set
    app = QApplication.instance()
    if app is None:
        raise RuntimeError("No Qt Application found.")

    file = QFile(path)
    file.open(QFile.ReadOnly | QFile.Text)
    stream = QTextStream(file)
    app.setStyleSheet(stream.readAll())

Now we can add generic application logic that can use this function in a signal/slot mechanism (using a lambda as a convenient wrapper, if needed, to provide the path to the stylesheet toggler):

# add logic for setting up application
app = QApplication(sys.argv)
# more logic for creating top-level widgets, application logic ...

parent = ...
light_btn = QPushButton("Toggle light.", parent)
light_btn.clicked.connect(lambda: toggle_stylesheet(":/light.qss"))

dark_btn = QPushButton("Toggle dark.", parent)
dark_btn.clicked.connect(lambda: toggle_stylesheet(":/dark.qss"))

# add to the layout, do other stuff
# ...

# end the Qt application
sys.exit(app.exec_())

This allows users to dynamically change the theme of an application developed with PyQt5 (or using analogous logic in C++, Qt5) to either a light or dark theme.

Disclaimer: Obviously I am the maintainer.

Upvotes: 76

Dmitry Sazonov
Dmitry Sazonov

Reputation: 9014

Founded in my bookmarks. I don't remember the original source.

QApplication::setStyle(QStyleFactory::create("Fusion"));
QPalette p;
p = qApp->palette();
p.setColor(QPalette::Window, QColor(53,53,53));
p.setColor(QPalette::Button, QColor(53,53,53));
p.setColor(QPalette::Highlight, QColor(142,45,197));
p.setColor(QPalette::ButtonText, QColor(255,255,255));
qApp->setPalette(p);

P.S. it may be adjusted with QSS if necessary.

Upvotes: 3

Related Questions