Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add XFCE support #39

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This package allows to detect if the user is using Dark Mode on:
- [macOS 10.14+](https://support.apple.com/en-us/HT208976)
- [Windows 10 1607+](https://blogs.windows.com/windowsexperience/2016/08/08/windows-10-tip-personalize-your-pc-by-enabling-the-dark-theme/)
- Linux with [a dark GTK theme](https://www.gnome-look.org/browse/cat/135/ord/rating/?tag=dark).
- Linux with [a dark XFCE theme](https://www.xfce-look.org/browse?tag=dark).

The main application of this package is to detect the Dark mode from your GUI Python application (Tkinter/wx/pyqt/qt for python (pyside)/...) and apply the needed adjustments to your interface. Darkdetect is particularly useful if your GUI library **does not** provide a public API for this detection (I am looking at you, Qt). In addition, this package does not depend on other modules or packages that are not already included in standard Python distributions.

Expand All @@ -25,6 +26,8 @@ False
```
It's that easy.

The theme can be either 'Dark' or 'Light'. If the library cannot detect a theme unambiguously, it returns 'Unknown' so that the application can choose an appropriate default behavior. Unknown may also be returned if not using a GTK or XFCE desktop. When the theme is Unknown, both isDark() and isLight() will return False.

You can create a dark mode switch listener daemon thread with `darkdetect.listener` and pass a callback function. The function will be called with string "Dark" or "Light" when the OS switches the dark mode setting.

``` python
Expand Down Expand Up @@ -61,3 +64,4 @@ pip install darkdetect[macos-listener]
- On macOS, detection of the dark menu bar and dock option (available from macOS 10.10) is not supported.
- [Details](https://stackoverflow.com/questions/25207077/how-to-detect-if-os-x-is-in-dark-mode) on the detection method used on macOS.
- [Details](https://askubuntu.com/questions/1261366/detecting-dark-mode#comment2132694_1261366) on the experimental detection method used on Linux.

15 changes: 13 additions & 2 deletions darkdetect/__main__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
#-----------------------------------------------------------------------------
# Copyright (C) 2019 Alberto Sottile
# Copyright (C) 2023 Alberto Sottile, Michael Harvey
#
# Distributed under the terms of the 3-clause BSD License.
#
# Usage: python -m darkdetect [watch]
#-----------------------------------------------------------------------------

import darkdetect
import sys

def print_theme(theme):
print('Current theme: {}'.format(theme))

print_theme(darkdetect.theme())

# demonstrate use of listener
if len(sys.argv) == 2 and sys.argv[1] == 'watch':
darkdetect.listener(print_theme)

print('Current theme: {}'.format(darkdetect.theme()))
94 changes: 70 additions & 24 deletions darkdetect/_linux_detect.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,91 @@
#-----------------------------------------------------------------------------
# Copyright (C) 2019 Alberto Sottile, Eric Larson
# Copyright (C) 2023 Alberto Sottile, Eric Larson, Michael Harvey
#
# Distributed under the terms of the 3-clause BSD License.
#-----------------------------------------------------------------------------

import subprocess

def _run(args):
out = subprocess.run(args, capture_output=True)
return out.stdout.decode()


def _desktop():
"""
Get simplified desktop identifier. There are several variations of the gnome
desktop string. If desktop is not recognized, return empty string.

:return: GNOME, MATE, KDE, XFCE, Unity, LXDE
"""
import os
desktop = ''
if 'XDG_CURRENT_DESKTOP' in os.environ:
desktop = os.environ['XDG_CURRENT_DESKTOP']

# https://askubuntu.com/questions/72549/how-to-determine-which-window-manager-and-desktop-environment-is-running/227669#227669
if 'GNOME' in desktop:
desktop = 'GNOME'
elif 'Cinnamon' in desktop:
desktop = 'GNOME'

return desktop


def theme():
"""
:return: 'Dark' or 'Light', or 'Unknown' if the theme cannot be unambiguously identified.
"""
theme_name = ''
try:
#Using the freedesktop specifications for checking dark mode
out = subprocess.run(
['gsettings', 'get', 'org.gnome.desktop.interface', 'color-scheme'],
capture_output=True)
stdout = out.stdout.decode()
#If not found then trying older gtk-theme method
if len(stdout)<1:
out = subprocess.run(
['gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-theme'],
capture_output=True)
stdout = out.stdout.decode()
desktop = _desktop()

if desktop == 'GNOME':
#Using the freedesktop specifications for checking dark mode
stdout = _run(['gsettings', 'get', 'org.gnome.desktop.interface', 'color-scheme'])
#If not found then trying older gtk-theme method
if len(stdout)<1:
stdout = _run(['gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-theme'])
# we have a string, now remove start and end quote added by gsettings
theme_name = stdout.lower().strip()[1:-1]
elif desktop == 'XFCE':
theme_name = _run(['xfconf-query', '-c', 'xsettings', '-p', '/Net/ThemeName'])
except Exception:
return 'Light'
# we have a string, now remove start and end quote
theme = stdout.lower().strip()[1:-1]
if '-dark' in theme.lower():
return 'Unknown'

if '-dark' in theme_name.lower():
return 'Dark'
else:
elif theme_name:
return 'Light'
else:
return 'Unknown'


def isDark():
return theme() == 'Dark'


def isLight():
return theme() == 'Light'


# def listener(callback: typing.Callable[[str], None]) -> None:
def listener(callback):
with subprocess.Popen(
('gsettings', 'monitor', 'org.gnome.desktop.interface', 'gtk-theme'),
stdout=subprocess.PIPE,
universal_newlines=True,
) as p:
for line in p.stdout:
callback('Dark' if '-dark' in line.strip().removeprefix("gtk-theme: '").removesuffix("'").lower() else 'Light')
desktop = _desktop()

if desktop == 'GNOME':
with subprocess.Popen(
('gsettings', 'monitor', 'org.gnome.desktop.interface', 'gtk-theme'),
stdout=subprocess.PIPE,
universal_newlines=True,
) as p:
for line in p.stdout:
callback('Dark' if '-dark' in line.strip().removeprefix("gtk-theme: '").removesuffix("'").lower() else 'Light')
elif desktop == 'XFCE':
with subprocess.Popen(
('xfconf-query', '-m', '-c', 'xsettings', '-p-', '/Net/ThemeName'),
stdout=subprocess.PIPE,
universal_newlines=True,
):
callback(theme())