-
-
Notifications
You must be signed in to change notification settings - Fork 52
Contributing Plugins
If you want to join the growing list of contributors this is the place to start. Maybe you are missing some Plugins or catched a bug you would like to resolve on your own.
If you haven't done this already, set up your project:
- clone the repository
- create a virtualenv with python (optional)
- make sure you have all requirements installed
- since v3.1.0, you need systemd and the systemd-python package
There are two ways to change the theme in an application:
- Developing a script that changes config files or runs a command (a plugin in Yin & Yang)
- Develop an extension for the application where you want to change the theme, and trigger it by communicating with Yin & Yang
Plugins provide a way to directly change the theme by either running a command or changing a config file.
Each plugin is a class in src/plugins
and extends the class Plugin
in _plugin.py
.
There are some subclasses of the Plugin
class that you should use if suitable:
-
PluginCommandline
: A plugin which switches the theme through a terminal command:
class MyPlugin(PluginCommandline):
def __init__(self):
super().__init__(['terminal-command', '--set', '{theme}'])
self.theme_light = 'light theme'
self.theme_dark = 'dark theme'
⚠️ Note that by default, the availability is termined by callingcommand[0] --help
. If that call fails, the application assumes that the plugin is not available and hides it from the GUI. You can override this behaviour if needed.
-
PluginDesktopDependent
: A plugin that behaves differently on different desktops:
class MyPlugin(PluginDesktopDependent):
def __init__(self, desktop: Desktop):
match desktop:
case Desktop.KDE:
super().__init__(_Kde())
case Desktop.GNOME:
super().__init__(_Gnome())
case _:
super().__init__(None)
class _Kde(Plugin):
pass # write a plugin for KDE here
class _Gnome(Plugin):
pass # write a plugin for Gnome here
-
ExternalPlugin
: Used for communicating with other scripts, for example browser plugins. Does nothing except present itself in the UI and provides a config.
If none of the above subclasses seem suitable, subclass the Plugin
class and write your code to set any theme by overwriting the method set_theme()
.
Make sure that all preconditions are correct, e.g.:
if not (self.available and self.enabled):
return
if not theme:
raise ValueError(f'Theme \"{theme}\" is invalid')
For changing config files, there are some helper methods in _plugin.py
:
-
inplace_change()
, which replaces a given line by a new one -
get_stuff_in_dir()
, which returns all files or directories in a path
For JSON files, I recommend the json module python provides, for config files similar to INI you should use the config parser module.
For dbus calls, you should use the qt dbus module (the KDE version of the wallpaper plugin can help you get started).
You also need to create an instance of your plugin in /src/plugins/__init__.py
. Provide desktop
if your plugin depends on the desktop.
Now update the version in the default property of the config class. Finally, make sure your plugin works and all unit tests run successfully.
By default, each plugin has its own section in the plugins window, with one simple text input to set the desired theme.
If you want to provide a combo box instead, you can simply implement the property method available_themes
. Any other widget can be used by overriding either
-
get_widget()
if you want to edit the whole widget -
get_input()
if you only want to use different inputs for the light and dark theme. Seewallpaper.py
for example, it replaces thelineEdits
withbuttons
.
Automatic tests check that your plugin has the expected behavior. However, to test that your set_theme()
method works, you have to do the following:
- Enable your plugin (via UI or the config file stored in
~/.config/yin-yang/yin-yang.json
). - Change the
False
intests/test_plugins.py: test_set_theme_works()
toTrue
. Make sure you do not commit this change later. - Run
tests/test_plugins.py
.
If you need to update the theme from an external application, you can do the following:
- Write a new yin-yang plugin and subclass
ExternalPlugin
. - Call
communicate.py
as a process from your application. - Write your application name into stdin of that process.
- Read the response from stdout. It should be a JSON object with the following data:
{
"enabled": true,
"dark_mode": true,
"scheduled": true,
"themes": ["light_theme", "dark_theme"],
"times": [1615881600, 1615924800]
}
-
enabled
is true if your plugin is enabled. If not, the response only containsenabled
anddark_mode
-
dark_mode
is true if the system is currently using a dark theme -
scheduled
is true if the theme changes automatically. If not, the response does not contain the times section. -
themes
is a list of the preferred themes as strings. -
times
is a list of the times when the theme changes. These are Unix times in seconds since the epoch, and always "surround" the current time. This enables your external application to calculate the preferred theme directly and compare it todark_mode
if you want.For example, the times provided above would be the times when called on
2021-03-16 13:31:05
.
To test if communicate.py
gives the desired output,
go to the directory where you cloned this repo and open a terminal.
The following is an example output for Firefox:
$ python
> import communicate.py
> communicate.send_config('firefox')
{'enabled': True, 'dark_mode': True, 'scheduled': True, 'themes': ['firefox-compact-light@mozilla.org', 'firefox-compact-dark@mozilla.org'], 'times': [1620756000, 1620795600]}