Skip to content

Commit

Permalink
Merge pull request #187 from oskarsh/beta
Browse files Browse the repository at this point in the history
v3.2.4
  • Loading branch information
l0drex committed May 12, 2023
2 parents 0cd2fb2 + 283af10 commit d91abbc
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 29 deletions.
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def main(arguments):
logger.debug(f'Using language {lang}')

# system translations
path = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
path = QLibraryInfo.path(QLibraryInfo.TranslationsPath)
translator = QTranslator(app)
if translator.load(QLocale.system(), 'qtbase', '_', path):
app.installTranslator(translator)
Expand Down
5 changes: 1 addition & 4 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ cp ./resources/yin-yang /usr/bin/
cp ./resources/Yin-Yang.desktop "$USER_HOME/.local/share/applications/Yin-Yang.desktop"
# copy icon
cp ./resources/logo.svg /usr/share/icons/hicolor/scalable/apps/yin_yang.svg
# systemd unit files
mkdir -p "$USER_HOME/.local/share/systemd/user/"
cp ./resources/yin_yang.service "$USER_HOME/.local/share/systemd/user/yin_yang.service"
cp ./resources/yin_yang.timer "$USER_HOME/.local/share/systemd/user/yin_yang.timer"
# systemd unit files will be installed by the app

cat << "EOF"
__ ___ __ __
Expand Down
4 changes: 4 additions & 0 deletions scripts/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@ rm -rf /opt/yin-yang /usr/bin/yin-yang
echo "Removing manifest"
rm -f /usr/lib/mozilla/native-messaging-hosts/yin_yang.json

echo "Removing systemd units"
rm -f "$HOME/.local/share/systemd/user/yin_yang.timer"
rm -f "$HOME/.local/share/systemd/user/yin_yang.service"

echo Yin-Yang uninstalled succesfully
echo have a nice day ...
9 changes: 5 additions & 4 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
import os
import pathlib
from abc import ABC, abstractmethod
from datetime import time
from functools import cache
from time import sleep
from typing import Union, Optional

from PySide6.QtCore import QObject
from PySide6.QtPositioning import QGeoPositionInfoSource, QGeoPositionInfo, QGeoCoordinate
from psutil import process_iter, NoSuchProcess
from datetime import time
from typing import Union, Optional

from suntime import Sun, SunTimeException
from src.plugins import get_plugins

from src.meta import Modes, Desktop, PluginKey, ConfigEvent
from src.plugins import get_plugins

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -71,6 +71,7 @@ def update_config(config_old: dict, defaults: dict):
plugin_settings: dict = defaults['plugins']
for plugin_name, plugin_config in plugin_settings.items():
update_plugin_config(config_old, plugin_config, plugin_name)

return config_new


Expand Down
180 changes: 161 additions & 19 deletions src/plugins/konsole.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import logging
import os
import re
import subprocess
from configparser import ConfigParser
from itertools import chain
from pathlib import Path
from shutil import copyfile

import psutil
from PySide6.QtDBus import QDBusConnection, QDBusMessage
Expand All @@ -18,17 +22,45 @@ class Konsole(Plugin):
This is necessary to allow live theme changes.
"""
global_path = Path('/usr/share/konsole')
config_path = Path.home() / '.config/konsolerc'

@property
def user_path(self) -> Path:
return Path.home() / '.local/share/konsole'

def __init__(self):
super().__init__()
self.theme_light = 'BlackOnWhite'
self.theme_dark = 'Breeze'
self._theme_light = 'BlackOnWhite'
self._theme_dark = 'Breeze'

@property
def theme_light(self):
return self._theme_light

@theme_light.setter
def theme_light(self, value):
self.update_profile(False, value)
self._theme_light = value

@property
def theme_dark(self):
return self._theme_dark

@theme_dark.setter
def theme_dark(self, value):
self.update_profile(True, value)
self._theme_dark = value

def set_mode(self, dark: bool) -> bool:
# run checks
if not super().set_mode(dark):
return False

profile = 'Dark' if dark else 'Light'

# update default profile, if application is started afterward
self.default_profile = profile + '.profile'

def set_theme(self, theme: str):
# Set Konsole profile for all sessions

# Get the process IDs of all running Konsole instances owned by the current user
Expand All @@ -39,47 +71,157 @@ def set_theme(self, theme: str):

# loop: console processes
for proc_id in process_ids:
set_profile(f'org.kde.konsole-{proc_id}', theme)
logger.debug(f'Changing profile in konsole session {proc_id}')
set_profile(f'org.kde.konsole-{proc_id}', profile)

set_profile('org.kde.yakuake', profile)

process_ids = [
proc.pid for proc in psutil.process_iter(['name', 'username'])
if proc.info['name'] == 'dolphin' and proc.info['username'] == os.getlogin()
]

# loop: dolphin processes
for proc_id in process_ids:
logger.debug(f'Changing profile in dolphin session {proc_id}')
set_profile(f'org.kde.dolphin-{proc_id}', profile)

set_profile('org.kde.yakuake', theme)
return True

def set_theme(self, theme: str):
# everything is done in set_mode (above)
pass

@property
def available_themes(self) -> dict:
if not self.available:
return {}

profile_paths = [
p.name.removesuffix('.profile') for p in self.user_path.iterdir()
if p.is_file() and p.suffix == '.profile'
]
themes = dict(sorted([
(p.with_suffix('').name, p)
for p in chain(self.global_path.iterdir(), self.user_path.iterdir())
if p.is_file() and p.suffix == '.colorscheme'
]))

return {profile: profile for profile in profile_paths}
themes_dict = {}
config_parser = ConfigParser()

def get_input(self, widget):
input_widgets = super().get_input(widget)
for widget in input_widgets:
widget.setToolTip(
'Select a profile. '
'Create new profiles or edit existing ones within Konsole to change the color scheme.'
)
for theme, theme_path in themes.items():
config_parser.read(theme_path)
theme_name = config_parser['General']['Description']
themes_dict[theme] = theme_name

return input_widgets
assert themes_dict != {}, 'No themes found!'
return themes_dict

@property
def available(self) -> bool:
return self.global_path.is_dir()

@property
def default_profile(self):
value = None
# cant use config parser because of weird file structure
with self.config_path.open('r') as file:
for line in file:
# Search for the pattern "DefaultProfile=*"
match = re.search(r'DefaultProfile=(.*)', line)

# If a match is found, return the content of the wildcard '*'
if match:
value = match.group(1)

if value is None:
# use the first found profile
for file in self.user_path.iterdir():
if file.suffix == '.profile':
value = file.name
break
if value is not None:
logger.warning(f'No default profile found, using {value} instead.')
else:
raise ValueError('No Konsole profile found.')

return value

@default_profile.setter
def default_profile(self, value: str):
assert value.endswith('.profile')

with self.config_path.open('r') as file:
lines = file.readlines()
for i, line in enumerate(lines):
# Search for the pattern "DefaultProfile=*"
match = re.search(r'DefaultProfile=(.*)', line)

# If a match is found, return the content of the wildcard '*'
if match:
lines[i] = f'DefaultProfile={value}\n'
break
with self.config_path.open('w') as file:
file.writelines(lines)

def update_profile(self, dark: bool, theme: str):
if not self.available or theme == '':
# theme is empty string on super init
return

# update the color scheme setting in either dark or light profile
logger.debug('Updating konsole profile')

file_path = self.user_path / ('Dark.profile' if dark else 'Light.profile')
if not file_path.exists():
self.create_profiles()

profile_config = ConfigParser()
profile_config.optionxform = str
profile_config.read(file_path)
profile_config['Appearance']['ColorScheme'] = theme
with open(file_path, 'w') as file:
profile_config.write(file)

def create_profiles(self):
logger.debug('Creating new profiles for live-switching between light and dark themes.')
# copy default profile to create theme profiles
light_profile = self.user_path / 'Light.profile'
dark_profile = self.user_path / 'Dark.profile'
# TODO there is a parent profile section in the profile file, maybe we can use that (in a later version)?
copyfile(self.user_path / self.default_profile, light_profile)
copyfile(self.user_path / self.default_profile, dark_profile)

# Change name in file
profile_config = ConfigParser()
profile_config.optionxform = str

profile_config.read(light_profile)
profile_config['General']['Name'] = light_profile.stem

with open(light_profile, 'w') as file:
profile_config.write(file)

profile_config.read(dark_profile)
profile_config['General']['Name'] = dark_profile.stem

with open(dark_profile, 'w') as file:
profile_config.write(file)


def set_profile(service: str, profile: str):
# connect to the session bus
connection = QDBusConnection.sessionBus()

# maybe it's possible with pyside6 dbus packages, but this was simpler and worked
sessions = subprocess.check_output(f'qdbus {service} | grep "Sessions/"', shell=True)
try:
sessions = subprocess.check_output(f'qdbus {service} | grep "Sessions/"', shell=True)
except subprocess.CalledProcessError:
# happens when dolphins konsole is not opened
logger.debug(f'No Konsole sessions available in service {service}, skipping')
return
sessions = sessions.decode('utf-8').removesuffix('\n').split('\n')

# loop: process sessions
for session in sessions:
logger.debug(f'Changing profile of session {session} to {profile}')
# set profile
message = QDBusMessage.createMethodCall(
service,
Expand Down
6 changes: 5 additions & 1 deletion src/yin_yang.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

"""
title: yin_yang
description: yin_yang provides an easy way to toggle between light and dark
Expand All @@ -13,6 +12,8 @@
import time
from threading import Thread

from src.plugins.notify import Notification
from src.plugins.sound import Sound
from src.daemon_handler import update_times
from src.meta import PluginKey
from src.config import config, plugins
Expand All @@ -39,6 +40,9 @@ def set_mode(dark: bool, force=False):
logger.info(f'Switching to {"dark" if dark else "light"} mode.')
for p in plugins:
if config.get_plugin_key(p.name, PluginKey.ENABLED):
if force and (isinstance(p, Sound) or isinstance(p, Notification)):
# skip sound and notify on apply settings
continue
try:
logger.info(f'Changing theme in plugin {p.name}')
p_thread = Thread(target=p.set_mode, args=[dark], name=p.name)
Expand Down

0 comments on commit d91abbc

Please sign in to comment.