Git Product home page Git Product logo

darkdetect's People

Contributors

albertosottile avatar cboy343 avatar doctordalek1963 avatar larsoner avatar pierreraybaut avatar raghavdhingra24 avatar sayyid5416 avatar transparentlc avatar zwimer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

darkdetect's Issues

Linux (GNOME)

Nice project! Do you have any plans to support GNOME (or KDE) as well?

False positive

Hi,

I get a false positive on my macOS.
image
As you see, the theme turn to light (auto), but the script still tell me, that it should be dark.
To clarify, I get 'dark' even when I directly run defaults read -g AppleInterfaceStyle.

This only happens, when the theme changes while the system is locked.

best regards

Listener for dark mode switch?

Hi. I am using your very useful library to know whether dark mode has been switched on on a specific target system.

I was wondering if you plan to implement kind of listener. The problem I am currently having: if the OS switches the dark mode while the application is running, the application does not get notified about it. Only code being executed on-runtime, after the switch, will be able to fetch the newest value of the mode.

I hope, my explanation was clear enough. Otherwise, kindly let me know.

Best regards,

christian

Detect dark mode on Windows 10

Apparently Windows 10 has dark mode too. I wrote this if you would consider extending darkdetect functionality to Windows.

import platform

if platform.system() == 'Windows':
    import winreg


def is_windows_using_dark_theme():
    value = 1 # default to light theme

    try:
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize') as key:
            value, value_type = winreg.QueryValueEx(key, 'AppsUseLightTheme')
    except OSError as e:
        pass
    
    return value > 0

distutils.version LooseVersion is deprecated

Since setuptools https://setuptools.pypa.io/en/latest/history.html#v59-6-0 it emits a deprecation warning.

from distutils.version import LooseVersion as V
if V(platform.mac_ver()[0]) < V("10.14"):

This means that dependees that turn warnings into error will fail (for example in CI).

    if V(platform.mac_ver()[0]) < V("10.14"):
../../miniconda3-intel/envs/napari/lib/python3.9/site-packages/setuptools/_distutils/version.py:53: in __init__
    warnings.warn(
E   DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.

This can be worked around with a warning filter , for example:

[tool.pytest.ini_options]
filterwarnings = [
  "ignore:.*distutils Version classes are deprecated::darkdetect",  
]

It looks like you may not want dependencies, so I'm not if and how you wish to fix it.

[REQUEST] Watch for real-time theme changes

If the theme is changed from light to dark while the app is running, the app will stay in light mode while the rest of the system changes to dark mode. If you watch for theme changes, you can adapt your Qt app to dark mode just like a native app.

Module produces ValueError on Windows 8.1

The module incorrectly assumes that the output from platform.release() can be translated into int. This will cause an error on import in Windows 8.1.

Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
  File "C:\Users\David\AppData\Local\Programs\Python\Python310\lib\site-packages\darkdetect\__init__.py", line 19, in <module>
    elif sys.platform == "win32" and int(platform.release()) >= 10:
ValueError: invalid literal for int() with base 10: '8.1'

Cheers.

SIGSEGV (Address boundary error) on Apple Silicon M1, Big Sur 11.5.2

This is related to:
#11
I reported crashes on Big Sur, but it seems my issue wasn't related to Big Sur per se, but to architecture (arm64, Apple Silicon M1). In fact the version detection added in 0.4 doesn't seem to be needed in my environment (Big Sur 11.5.2, python 3.9.6/7)

I've narrowed down the fault to the mismatch in args between:

objc.objc_msgSend.argtypes = [void_p, void_p]

and
key = msg(NSString, n("stringWithUTF8String:"), _utf8('AppleInterfaceStyle'))
appearanceNS = msg(stdUserDef, n('stringForKey:'), void_p(key))

I believe this is due to arch changes, based on:
https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code

As a simple test, add at line 41: (after C is defined)

NSString = C('NSString')
objc.objc_msgSend(NSString, n("stringWithUTF8String:"), _utf8("reason"))

with: objc.objc_msgSend.argtypes = [void_p, void_p] this crashes.
with: objc.objc_msgSend.argtypes = [void_p, void_p, void_p] this runs.

As such, I was able to patch this locally by changing the number of argtypes and args for all msg. I can do a PR, but it will for sure need some extra testing/review.

Edit: I have a more elegant fix, where just the .argtypes is changed just for the 3 arg msg lines.

Implement `listener` for macOS

A listener function for macOS is currently missing. As stated in the README, calling this function on this platform raises NotImplementedError.

EDIT:

Implementation requirements

The listener function must:

  • accept a callback function (Callable[[str], None]) as its only argument
  • call the passed callback function, with the new theme name as argument, whenever there is a theme update on the system
  • be able to run from the main thread
  • be able to run from a background thread (see the example in the README)
  • not continuously poll the system (e.g. run darkdetect.theme() in a loop) as that wastes resources

The listener function should, if possible:

  • do not depend on external C extensions that have to be compiled (we would like to have pure Python wheels)
  • do not depend on packages not included in the Python standard library
    • if additional dependencies from PyPI are required, those must be only included in a setuptools extra (e.g. pip install darkdetect[macos-listener])

Related: #14

macOS listener support on apps where `sys.executable` is not a python interpreter

Right now the macOS listener invokes a subprocess with args (sys.executable, "-c", cmd); this assumes that sys.executable is a python interpreter or at least exposes -c. That is not necessarily the case when running from something like a pyinstaller build, which bundles the interpreter and other files into a binary executable which does not expose -c.

This is undocumented, though that is fixed by: #32

Right now the listener will silently fail; it would be nice to change this. One possible solution is to check that Path(sys.executable).name.startsWith("python") and fail with an exception; though then python interpreter hardlinks to different names might not work; this is why the problem is non-trivial. There would need to be some way to determine if sys.executable is a python interpreter to fail more loudly; or we could maybe wait on the subprocess for a few moments to see if it dies immediately? How exactly would need to be decided.

EDIT:

A hacky fix for pyinstaller specifically is to prefix cmd with from multiprocessing.resource_tracker import main;; as pyinstaller supports -c for any command prefixed by that string.

Crash on macOS Big Sur 11.4

I get the following error when using darkdetect from the terminal:

>>> darkdetect.isDark()
2021-06-08 19:36:55.831 Python[4299:381204] +[NSUserDefaults standardUserDefaults]: unrecognized selector sent to class 0x7fff8011ea10
2021-06-08 19:36:55.835 Python[4299:381204] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSUserDefaults standardUserDefaults]: unrecognized selector sent to class 0x7fff8011ea10'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff2055287b __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007fff2028ad92 objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff205d52e5 __CFExceptionProem + 0
	3   CoreFoundation                      0x00007fff204ba90b ___forwarding___ + 1448
	4   CoreFoundation                      0x00007fff204ba2d8 _CF_forwarding_prep_0 + 120
	5   libffi.dylib                        0x00007fff2d9ba8f5 ffi_call_unix64 + 85
	6   ???                                 0x00007ffeee3d67e0 0x0 + 140732895422432
)
libc++abi: terminating with uncaught exception of type NSException
zsh: abort      python3

This happens when using any method, i.e., isDark, isLight, or theme. For information, I'm running macOS Big Sur 11.4 (20F71).

Listener class PR acceptable?

Would a PR containing a listener class be acceptable? This would not have to remove old functionality, it could be purely additive.

Why?

  1. In my opinion, the most important reason, is allowing guaranteed cleanup. Consider a listener which uses subprocess.POpen. If a user were to force quit (for example a SIGQUIT on linux) the process, or a native component of a GUI library were to segfault, python may not clean up the child process it was listening to. Proving an API that promises to do so would be greatly appreciated. For example, a user could add a segfault handler to kill child subprocesses before os._exit(1). Right now I'm using a somewhat reimplemented version of darkdetect due to the lack of this functionality.
  2. Right now, the listeners are uninterruptible. For some listeners, like the Windows listener, this may make sense, but for others that poll processes, it is avoidable as these processes can be killed. In this case, NotImpelementedError()s are acceptable.
  3. Allowing subclassing will allow editing of functionality

Possible API + Example

class BaseListener:

  def __init__(self, callback):
      self.set_callback(callback)

  def set_callback(self, callback):
      self._callback = callback

  def listen(self):
     raise NotImpelementedError()

  def stop(self):
     raise NotImpelementedError()

In _linux_detect.py:

class Listener(BaseListener):
  def __init__(self, callback):
    super().__init__(callback)
    self._proc = None
  
  def listen(self):
    self._proc = subprocess.Popen(
        ('gsettings', 'monitor', 'org.gnome.desktop.interface', 'gtk-theme'),
        stdout=subprocess.PIPE,
        universal_newlines=True,
    ) 
    with self._proc:
        for line in p.stdout:
            self._callback('Dark' if '-dark' in line.strip().removeprefix("gtk-theme: '").removesuffix("'").lower() else 'Light')

  def stop(self):
    if self._proc is not None:
      self._proc.kill()
      self._proc.wait()
      self._proc = None

# For those who want to use the old API        
def listener(callback): 
  Listener(callback).listen()

This would allow users to ensure that cleanup has happened properly, interrupt a listener, or subclass to add / edit functionality. For listeners that subclass, really the important bit is having the subprocess saved into something that users can kill if the need should arise, but this class struct has other benefits as mentioned above.

Possible Reasons to Subclass

These are other possible benefits of allowing subclassing:

  1. A user wants to use an alternative listener for a specific OS. Perhaps their linux distro doesn't use gnome; but they still want the macOS and Windows listeners to work as is.
  2. A user wants to edit the windows listener so that interrupts are possible, they can override the listen function and perhaps poll once per second if they desire
  3. They want to easily swap out the callback function for some reason; they can use the base classes' set_callback, or have it set via a subclass method
  4. A user may want to subclass Listener and their own class as part of a mixin design pattern or some other pattern.

In the examples above, darkdetect still only provides an API that users can use, so unlike in #29 we don't need to do threading stuff. We simply provide a listener object for the user to use; and to support the existing API we simply just have the standard listener(callback) function invoke Listener(callback).listen().

If I were to make a PR to add such functionality for each supported OS, would it be considered or rejected?

correct dark detection for gtk3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk


style_context = Gtk.Window().get_style_context()
vals = [0, 0]
for idx, color in enumerate((
        style_context.get_background_color(
            Gtk.StateFlags.NORMAL
        ),
        style_context.get_color(
            Gtk.StateFlags.NORMAL
        )
)):
    for channel in ('red', 'green', 'blue', ):
        vals[idx] += getattr(color, channel)
print(f'Is dark theme: {vals[0] < vals[1]}')

[RFC] macOS code choices

Hi, first of all thanks for sharing the code.


I'm curious, was there a specific reason why you didn't choose this simpler way of detecting dark mode?

# querying for dark mode

try:
    get_status = 'defaults read -g AppleInterfaceStyle'
    import subprocess
    status = subprocess.check_output(
        get_status.split(),
        stderr = subprocess.STDOUT
    ).decode()
    status = status.replace('\n', '')
except subprocess.CalledProcessError as e:
    return False
return True if status.lower() == 'dark' else False

ModuleNotFoundError: No module named 'darkdetect' in Python 3.8

I'm currently using python 3.8.7
I installed the package via:
pip install darkdetect
And it installs fine, but when I import it I get:
ModuleNotFoundError: No module named 'darkdetect'

I guess it has something to do with your setup file only containing python versions up til 3.7?
Could you please update it for newer versions of python?
Thanks in advance

Issue: darkdetect listener cannot work under sudo

Hi. Recently I found that darkdetect listener cannot work under sudo mode. This is how I reproduce:

Environment: macOS 13.5.1 Apple Silicon, Python 3.11

Create a new venv, and install darkdetect by pip install "darkdetect[macos-listener]"

Test sample.py as follows:

import threading
import darkdetect


def printCurrentTheme(theme):
    print(f'System theme is {theme}')


if __name__ == '__main__':
    thread = threading.Thread(
        target=darkdetect.listener, args=(printCurrentTheme,), daemon=True
    )

    thread.start()
    thread.join()

When running with python sample.py, the theme is printed correctly when changing system theme in the settings manually.

However, if running with sudo python sample.py, the callback never called when switching system theme.

There are some cases that I want to launch my application as sudo, and found that darkdetect failed to behave as expected.

Is this a bug related to darkdetect, or related to macos-listener implementation under the hood? Will darkdetect fix this in the future? Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.