Compare commits

..

No commits in common. "master" and "meconopsis" have entirely different histories.

15 changed files with 283 additions and 1068 deletions

View file

@ -27,31 +27,33 @@
# Import Theme
from libqtile import hook
from libqtile.log_utils import logger
from kuro.utils import load_config_class
import traceback
try:
from kuro.theme import Kuro
Theme = Kuro()
except ImportError as e:
logger.error(traceback.format_exc())
logger.error("Could not load Kuro Theme. Trying to load BaseTheme. Error: {}".format(e))
try:
from kuro.base import BaseTheme as Kuro
Theme = Kuro()
except ImportError as e:
Kuro = None
logger.error(traceback.format_exc())
raise ImportError("Could not load theme Config or BaseTheme! Error: {}".format(e))
# Import theme configuration
Config = load_config_class()
if Config is None:
try:
from kuro.config import Config
except ImportError as e:
logger.error("Could not load Kuro Config. Trying to load BaseConfig. Error: {}".format(e))
try:
from kuro.base import BaseConfig as Config
except ImportError as e:
Config = None
raise ImportError("Could not load theme Config or BaseConfig! Error: {}".format(e))
try:
logger.warning("Initializing theme...")
logger.warning(f"Using config variables for '{Config.get('config_name', '????')}'")
# Initialize the Theme
Theme.initialize()
logger.warning("Initialize done")
@ -68,7 +70,6 @@ try:
hook.subscribe.focus_change(Theme.callback_focus_change)
hook.subscribe.float_change(Theme.callback_float_change)
hook.subscribe.group_window_add(Theme.callback_group_window_add)
hook.subscribe.group_window_remove(Theme.callback_group_window_remove)
hook.subscribe.client_new(Theme.callback_client_new)
hook.subscribe.client_managed(Theme.callback_client_managed)
hook.subscribe.client_killed(Theme.callback_client_killed)
@ -92,10 +93,6 @@ try:
groups = Theme.groups
layouts = Theme.layouts
widget_defaults = Theme.widget_defaults
if Config.get("use_fake_screens", False):
logger.warning(f"This host uses fake screens!")
fake_screens = Theme.screens
else:
screens = Theme.screens
dgroups_key_binder = Theme.dgroups_key_binder
dgroups_app_rules = Theme.dgroups_app_rules
@ -113,13 +110,10 @@ try:
except Exception as e:
Theme = None
Config = None
logger.error(traceback.format_exc())
raise AttributeError("Could not configure theme! Error: {}".format(e))
def main(qtile):
Config.initialize(qtile)
# set logging level
if Config.get('debug', False):
if Config.get('verbose', False):

View file

@ -1,5 +1,3 @@
import time
from libqtile import layout as libqtile_layout, layout, bar, widget
from libqtile.lazy import lazy
from libqtile.config import Key, Group, Screen, Drag, Click, Match
@ -9,21 +7,13 @@ from libqtile.log_utils import logger
class BaseConfig:
config_name = "KuroBase"
@classmethod
def get(cls, key, default):
if hasattr(cls, key):
return getattr(cls, key)
#return cls.__dict__[key]
return cls.__dict__[key]
else:
return default
@classmethod
def initialize(cls, qtile):
# Can do extra initialization based on qtile instance here
pass
class BaseTheme:
# Changing variables initialized by function
@ -55,7 +45,7 @@ class BaseTheme:
auto_fullscreen = True
focus_on_window_activation = "smart"
extensions = []
reconfigure_screens = False
reconfigure_screens = True
# XXX: Gasp! We're lying here. In fact, nobody really uses or cares about this
# string besides java UI toolkits; you can see several discussions on the
@ -70,9 +60,6 @@ class BaseTheme:
# 'export _JAVA_AWT_WM_NONREPARENTING=1'
wmname = "LG3D"
def __init__(self):
self.startup_time = time.time()
def initialize(self):
logger.info("Initializing widget defaults...")
self.widget_defaults = self.init_widget_defaults()
@ -202,9 +189,6 @@ class BaseTheme:
def callback_group_window_add(self, *args, **kwargs):
pass
def callback_group_window_remove(self, *args, **kwargs):
pass
def callback_client_new(self, *args, **kwargs):
pass

View file

@ -1,23 +1,12 @@
from kuro.base import BaseConfig
from libqtile.log_utils import logger
from libqtile.lazy import lazy
# Config variables used in the main configuration
class Config(BaseConfig):
config_name = "KuroGeneral"
# Show debug bar and messages
debug = False
verbose = False
# General variables
homedir = "/home/kevin"
use_fake_screens = False
# Screen configs (empty = autoconfig, otherwise list of Screen() kwarg dicts)
screen_kwargs = []
# Colors
foreground = "#ffffff"
background = "#000000"
@ -26,92 +15,35 @@ class Config(BaseConfig):
inactive_dark = "#333333"
# Predefined commands
cmd_brightness_up = "sudo /usr/bin/xbacklight -inc 10"
cmd_brightness_down = "sudo /usr/bin/xbacklight -dec 10"
cmd_screenshot = "/home/kevin/bin/screenshot.sh"
cmd_alt_screenshot = "/home/kevin/bin/screenshot.sh"
app_terminal = "ghostty"
web_browser = "firefox"
file_manager = "thunar"
app_launcher = "wofi --show drun,run"
lock_command = "bash /home/kevin/bin/lock.sh"
cliphistory_command = "/home/kevin/bin/cliphistory.sh"
# Default Applications
app_terminal = "terminator"
app_launcher = "wofi --show run,drun"
app_launcer_x11 = "/home/kevin/bin/dmenu_wal.sh"
file_manager = "thunar"
visualizer_app = "glava"
web_browser = "firefox"
network_config = None
# Group definitions
groups = [
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': '', 'layout': 'floating', 'options': {'border_focus': '#990000', 'border_normal': '#440000'}}
]
# Extra keybind definitions
extra_keys = [
# Display modes
{'modifiers': ['mod'], 'key': "Prior", 'action': lazy.spawn("bash /home/kevin/.screenlayout/3scrns_144_rrot.sh")},
{'modifiers': ['mod'], 'key': "Next", 'action': lazy.spawn("bash /home/kevin/.screenlayout/3scrns_60_rrot.sh")},
{'modifiers': ['mod'], 'key': "Home", 'action': lazy.spawn("bash /home/kevin/bin/monitor_day.sh")},
{'modifiers': ['mod'], 'key': "End", 'action': lazy.spawn("bash /home/kevin/bin/monitor_night.sh")},
{'modifiers': ['mod'], 'key': "Insert", 'action': lazy.spawn("bash /home/kevin/bin/monitor_gamenight.sh")},
# Backlight keys
{'modifiers': [], 'key': "XF86MonBrightnessUp", 'action': lazy.spawn("sudo /usr/bin/xbacklight -inc 10")},
{'modifiers': [], 'key': "XF86MonBrightnessDown", 'action': lazy.spawn("sudo /usr/bin/xbacklight -dec 10")},
]
# Extra floating window rules
extra_float_rules = [
# Wine Origin game launcher
{'title': 'origin.exe', 'wm_class': 'Wine'},
# Homebank popups
{'title': 'Add transaction', 'wm_class': 'homebank'},
{'title': 'Edit transaction', 'wm_class': 'homebank'},
{'title': 'Inherit transaction', 'wm_class': 'homebank'},
{'title': 'Multiple edit transactions', 'wm_class': 'homebank'},
{'title': 'Transaction splits', 'wm_class': 'homebank'},
]
# Location
loc_latitude = 52.357603
loc_longitude = 6.663761
loc_timezone = "Europe/Amsterdam"
# Autostart applications
apps_autostart_group = [
{'group': "", 'command': ["firefox"]},
{'group': "", 'command': ["terminator"]},
{'group': "", 'command': ["ghostty", "--gtk-single-instance=true", "--quit-after-last-window-close=false", "--initial-window=true"]},
{'group': "", 'command': ["/usr/bin/rambox"]},
{'group': "", 'command': ["thunar"]},
{'group': "", 'command': ["thunderbird"]},
{'group': "", 'command': ["spotify"]},
]
apps_autostart = {
'common': [
apps_autostart = [
# ["ulauncher", "--hide-window", "--no-window-shadow"], # App launcher background daemon
["mako"], # Notification daemon
["kanshi"], # Display hotplug
["wl-paste", "--watch", "cliphist", "store"], # Clipboard manager
["/usr/lib/kdeconnectd"], # KDE Connect daemon
["kdeconnect-indicator"], # KDE Connect tray
["vorta"], # Vorta backup scheduler
],
'x11': [
["dunst"], # Notification daemon
["picom", "-b"], # Compositor
["xfce4-clipman"], # Clipboard manager
["xiccd"], # Color profile manager
],
'wayland': [
["mako"], # Notification daemon
["wl-paste", "--watch", "cliphist", "store"], # Clipboard manager
["kanshi"], # Display hotplugging
]
}
# Keyboard commands
cmd_media_play = "playerctl -i kdeconnect play-pause"
@ -121,16 +53,19 @@ class Config(BaseConfig):
cmd_media_volume_up = "pamixer -d 2"
# Display mode commands
cmd_monitor_mode_3s144 = "bash /home/kevin/.screenlayout/3scrns_144_rrot.sh"
cmd_monitor_mode_3s60 = "bash /home/kevin/.screenlayout/3scrns_60_rrot.sh"
cmd_monitor_mode_day = "bash /home/kevin/bin/monitor_day.sh"
cmd_monitor_mode_night = "bash /home/kevin/bin/monitor_night.sh"
cmd_monitor_mode_alt = "bash /home/kevin/bin/monitor_gamenight.sh"
cmd_reconfigure_screens = "kanshictl reload"
cmd_wal = ["wallust", "run"]
# Commands
x11_wallpaper_config_command = "/home/kevin/bin/wal-nitrogen-noupdate" # TODO: Remove
wallpaper_config_command = "/bin/true"
# Images
desktop_bg = "/home/kevin/Pictures/wallpapers/desktop.png"
desktop_bg_folder = "/home/kevin/Pictures/wallpapers/desktop_rotation/day"
desktop_bg_night_folder = "/home/kevin/Pictures/wallpapers/desktop_rotation/night"
desktop_bg_folder = "/home/kevin/Pictures/wallpapers/desktop_rotation"
# desktop_bg_override = "/home/kevin/Pictures/safe_wallpaper.jpg"
applauncher_image = "/home/kevin/.config/qtile/kuro/resources/arch.png"
custom_layout_icon_paths = ['/home/kevin/.config/qtile/kuro/resources/layout_icons/']
@ -151,7 +86,6 @@ class Config(BaseConfig):
# Sizes
width_border = 1
margin_layout = 8
margin_layout_single = 0
width_spacer = 1
padding_spacer = 4
grow_amount = 5
@ -165,8 +99,7 @@ class Config(BaseConfig):
# Bar variables
bar_background = background
bar_rgba_opacity = "00"
bar_rgba_opacity_hover = "AA"
bar_rgba_opacity = "AA"
bar_opacity = 1.0
bar_hover_opacity = 1
@ -182,7 +115,6 @@ class Config(BaseConfig):
colour_groupbox_border_focus = foreground
colour_groupbox_icon_active = foreground
colour_groupbox_icon_inactive = inactive_light
colour_groupbox_highlight = highlight
# Tasklist variables
tasklist_border = foreground
@ -210,10 +142,10 @@ class Config(BaseConfig):
battery_update_delay = 5
# Network variables
wifi_interface = "wifi0"
wifi_interface = "wlp3s0"
wifi_theme_path = "/home/kevin/.config/qtile/kuro/resources/wifi"
wifi_update_interval = 5
wired_interface = "eth0"
wired_interface = "enp4s0"
# GPU variables
gpu_theme_path = "/home/kevin/.config/qtile/kuro/resources/gpu"
@ -222,7 +154,9 @@ class Config(BaseConfig):
volume_font = "Noto Sans"
volume_fontsize = 11
volume_theme_path = "/home/kevin/.config/qtile/kuro/resources/volume"
volume_pulse_sinks = []
volume_pulse_sink = "alsa_output.usb-CSCTEK_USB_Audio_and_HID_A34004801402-00.analog-stereo"
volume_pulse_sink2 = None
volume_is_bluetooth_icon = False
volume_update_interval = 0.2
@ -259,18 +193,5 @@ class Config(BaseConfig):
# Show battery widget
show_battery_widget = False
# Show media widget
show_media_widget = True
# Comma-separated list of ignored players in the media widget
media_ignore_players = "kdeconnect"
@classmethod
def initialize(cls, qtile):
# Can do extra initialization based on qtile instance here
super(Config, cls).initialize(qtile=qtile)
# Replace some apps if launched in X11 mode
if qtile.core.name == "x11":
logger.warning("Launched in X11 mode, overriding some apps in Config to xorg-variants.")
cls.app_launcher = cls.app_launcer_x11

View file

@ -1,45 +0,0 @@
from kuro.config import Config as GeneralConfig
class Config(GeneralConfig):
"""
Kuro QTile configuration overrides for Aria
"""
config_name = "Aria"
# Default Applications
app_terminal = "terminator"
app_launcher = "/home/kevin/bin/dmenu_wal.sh"
cmd_brightness_up = "true"
cmd_brightness_down = "true"
cmd_screenshot = "xfce4-screenshooter -r -c -d 1"
cmd_alt_screenshot = "xfce4-screenshooter -w -c -d 0"
lock_command = "bash /home/kevin/bin/lock.sh"
cliphistory_command = "true"
# Autostart applications
apps_autostart_group = [
{'group': "", 'command': ["firefox"]},
{'group': "", 'command': ["terminator"]},
{'group': "", 'command': ["/usr/bin/rambox"]},
{'group': "", 'command': ["thunar"]},
{'group': "", 'command': ["thunderbird"]},
{'group': "", 'command': ["spotify"]},
]
# Thermal indicator variables
thermal_sensor = "Package id 0"
thermal_chip = "coretemp-isa-0000"
# Network variables
wifi_interface = "wifi0"
wired_interface = "enp7s0"
# Volume widget variables
volume_pulse_sinks = [
"alsa_output.pci-0000_00_1f.3.analog-stereo",
]
# Screen organization
laptop_screen_nvidia = "eDP-1-1"
laptop_screen_intel = "eDP1"

View file

@ -1,22 +0,0 @@
from kuro.config import Config as GeneralConfig
class Config(GeneralConfig):
"""
Kuro QTile configuration overrides for Meconopsis
"""
config_name = "Meconopsis"
# Thermal indicator variables
thermal_sensor = "Package id 0"
thermal_chip = "coretemp-isa-0000"
# Network variables
wifi_interface = "wlp3s0"
wired_interface = "enp4s0"
# Volume widget variables
volume_pulse_sinks = [
# Analog jack
"alsa_output.usb-CSCTEK_USB_Audio_and_HID_A34004801402-00.analog-stereo",
]

View file

@ -1,136 +0,0 @@
from kuro.config import Config as GeneralConfig
import os
from libqtile.log_utils import logger
class Config(GeneralConfig):
"""
Kuro QTile configuration overrides for Work DBO laptop
"""
config_name = "DBO Power"
homedir = "/home/albek00"
#modifier = "mod1" # Non-existing F13 key used by AutoHotKey script
use_fake_screens = True
fake_screen_count = 2
# Default Applications
app_terminal = "terminator"
app_launcher = "/home/albek00/bin/dmenu_wal.sh"
app_launcer_x11 = "/home/albek00/bin/dmenu_wal.sh"
cmd_brightness_up = "true"
cmd_brightness_down = "true"
cmd_screenshot = "xfce4-screenshooter -r -c -d 1"
cmd_alt_screenshot = "xfce4-screenshooter -w -c -d 0"
lock_command = "true"
cliphistory_command = "true"
cmd_wal = ["wal", "-n", "-s", "-t", "-i"]
# Screen configs (empty = autoconfig, otherwise list of Screen() kwarg dicts)
screen_kwargs = [
{'x': 0, 'y': 0, 'width': int(os.getenv("QTILE_WIDTH", "3840"))//2, 'height': int(os.getenv("QTILE_HEIGHT", "1080"))},
{'x': int(os.getenv("QTILE_WIDTH", "3840"))//2, 'y': 0, 'width': int(os.getenv("QTILE_WIDTH", "3840"))//2, 'height': int(os.getenv("QTILE_HEIGHT", "1080"))},
]
margin_layout = [8, 8, 2, 8]
# Group definitions
groups = [
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
{'name': ''},
]
# Extra keybind definitions
extra_keys = []
# Extra floating window rules
extra_float_rules = []
# Autostart applications
apps_autostart_group = [
{'group': "", 'command': ["firefox"]},
{'group': "", 'command': ["terminator"]},
{'group': "", 'command': ["subl"]},
{'group': "", 'command': ["smerge"]},
{'group': "", 'command': ["thunar"]},
]
# Hide unnecessary widgets
show_temperature = False
show_media_widget = False
# Network variables
wifi_interface = None
wired_interface = "enP20755p0s0"
# Volume widget variables
volume_pulse_sinks = [
"RDPSink",
]
# Override directories to proper homedir. Half of these are probably not even used but meh
x11_wallpaper_config_command = "/home/albek00/bin/wal-nitrogen-noupdate"
desktop_bg = "/home/albek00/Pictures/wallpapers/desktop.png"
desktop_bg_folder = "/home/albek00/Pictures/wallpapers/desktop_rotation/day"
desktop_bg_night_folder = "/home/albek00/Pictures/wallpapers/desktop_rotation/night"
# desktop_bg_override = "/home/albek00/Pictures/safe_wallpaper.jpg"
applauncher_image = "/home/albek00/.config/qtile/kuro/resources/arch.png"
custom_layout_icon_paths = ['/home/albek00/.config/qtile/kuro/resources/layout_icons/']
glava_color_file_path = "/home/albek00/.config/glava/kurobars_color.glsl"
battery_theme_path = "/home/albek00/.config/qtile/kuro/resources/battery"
wifi_theme_path = "/home/albek00/.config/qtile/kuro/resources/wifi"
gpu_theme_path = "/home/albek00/.config/qtile/kuro/resources/gpu"
volume_theme_path = "/home/albek00/.config/qtile/kuro/resources/volume"
bluevol_theme_path = "/home/albek00/.config/qtile/kuro/resources/bluetooth_volume"
@classmethod
def initialize(cls, qtile):
super(Config, cls).initialize(qtile=qtile)
# Add keyboard remapping to autostart apps
cls.apps_autostart['common'] = [
["xmodmap", "-e", "keycode 191 = Super_L"]
]
# Determine screens programatically
qtile_width = int(os.getenv("QTILE_WIDTH", "3840"))
qtile_height = int(os.getenv("QTILE_HEIGHT", "1080"))
logger.warning(f"Determining screens for size {qtile_width}x{qtile_height}...")
# Home office, 1920x1080 horizontal right and 1080x1920 vertical left
if qtile_width == 3000 and (qtile_height > 1912 and qtile_height <= 1920):
cls.screen_kwargs = [
{'x': 0, 'y': 839, 'width': 1920, 'height': qtile_height-839},
{'x': 1920, 'y': 0, 'width': 1080, 'height': qtile_height},
]
# Dual 1680x1050
elif qtile_width == 3360 and (qtile_height > 1040 and qtile_height <= 1050):
cls.screen_kwargs = [
{'x': 0, 'y': 0, 'width': 1680, 'height': 1050},
{'x': 1680, 'y': 0, 'width': 1680, 'height': 1050},
]
# Dual 1920x1080
elif qtile_width == 3840 and (qtile_height > 1070 and qtile_height <= 1080):
cls.screen_kwargs = [
{'x': 0, 'y': 0, 'width': 1920, 'height': 1080},
{'x': 1920, 'y': 0, 'width': 1920, 'height': 1080},
]
# Single 1920x1080
elif qtile_width == 1920 and (qtile_height > 1070 and qtile_height <= 1080):
cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': 1920, 'height': 1080}]
cls.fake_screen_count = 1
# Single 1680x1050
elif qtile_width == 1680 and (qtile_height > 1040 and qtile_height <= 1050):
cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': 1680, 'height': 1050}]
cls.fake_screen_count = 1
# Else, configure for 1 large screen
else:
cls.screen_kwargs = [{'x': 0, 'y': 0, 'width': qtile_width, 'height': qtile_height}]
cls.fake_screen_count = 1
logger.warning(f"Kwargs: {cls.screen_kwargs}")

View file

@ -1,36 +0,0 @@
from kuro.config import Config as GeneralConfig
class Config(GeneralConfig):
"""
Kuro QTile configuration overrides for Meconopsis
"""
config_name = "Temari"
# Default Applications
#app_terminal = "terminator"
cmd_brightness_up = "brightnessctl -d intel_backlight set +5%"
cmd_brightness_down = "brightnessctl -d intel_backlight set 5%-"
network_config = "nm-connection-editor"
#lock_command = "bash /home/kevin/bin/lock.sh"
# Thermal indicator variables
thermal_sensor = "Package id 0"
thermal_chip = "coretemp-isa-0000"
# Network variables
wifi_interface = "wlp0s20f3"
wired_interface = "enp7s0"
# Volume widget variables
volume_pulse_sinks = [
# Analog jack
"alsa_output.pci-0000_00_1f.3.analog-stereo",
]
# Screen organization
laptop_screen_nvidia = "eDP-1-1"
laptop_screen_intel = "eDP1"
# Show battery widget
show_battery_widget = True

View file

@ -1,26 +0,0 @@
from kuro.config import Config as GeneralConfig
class Config(GeneralConfig):
"""
Kuro QTile configuration overrides for Violet
"""
config_name = "Violet"
# Thermal indicator variables
thermal_sensor = "Tdie"
thermal_chip = "zenpower-pci-00c3"
# Network variables
wifi_interface = None
wired_interface = "br1"
# Volume widget variables
volume_pulse_sinks = [
# Behringer USB mixer
"alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo-output",
# Motherboard output (Starship/Matisse)
"alsa_output.pci-0000_0e_00.4.iec958-stereo",
# PCIe card output (CMI8738/CMI8768 PCI Audio)
"alsa_output.pci-0000_08_00.0.analog-stereo",
]

View file

@ -1,22 +1,13 @@
import json
import os
import random
import time
import datetime
import socket
import subprocess
from dateutil import tz
from typing import Optional
from libqtile.backend.base import Window
# QTile 0.33.x fallback (for work laptop)
try:
from libqtile.backend.wayland.window import Window as WaylandWindow, Static as WaylandStatic
except ImportError:
from libqtile.backend.wayland.xwindow import XWindow as WaylandWindow, XStatic as WaylandStatic
from libqtile.backend.wayland.layer import LayerStatic
from libqtile.backend.wayland.xwindow import XWindow as WaylandXWindow, XStatic as WaylandXStatic
from libqtile.backend.x11.window import XWindow as XorgXWindow
# Initialize logging
from libqtile.log_utils import logger
@ -37,8 +28,6 @@ logger.warning("Importing kuro utils...")
import kuro.utils.widgets
from kuro.utils import general as utils
from kuro.utils.bar import KuroBar
from kuro.utils.suntime import Sun
logger.warning("Importing variables and other utils...")
@ -49,11 +38,14 @@ from kuro.utils import layouts as kuro_layouts
logger.warning("Importing configuration...")
from kuro.utils import load_config_class
Config = load_config_class()
if Config is None:
raise ImportError("Could not load theme Config or BaseConfig! Error: {}".format(e))
Config.initialize(qtile)
try:
from kuro.config import Config
except ImportError:
try:
from kuro.baseconfig import BaseConfig as Config
except ImportError:
Config = None
raise ImportError("Could not load theme Config or BaseConfig!")
logger.warning("Imports done")
@ -65,6 +57,9 @@ class Kuro(BaseTheme):
# Screen count
num_screens = 0
# Top bars
topbars = []
# Static windows
static_windows = []
@ -91,8 +86,14 @@ class Kuro(BaseTheme):
Match(wm_class='ssh-askpass'), # ssh-askpass
Match(title='branchdialog'), # gitk
Match(title='pinentry'), # GPG key password entry
# Extra rules from host-specific Config
*[Match(**rule) for rule in Config.get('extra_float_rules', [])]
Match(title='origin.exe', wm_class='Wine'), # Wine Origin game launcher
# Homebank popups
Match(title='Add transaction', wm_class='homebank'),
Match(title='Edit transaction', wm_class='homebank'),
Match(title='Inherit transaction', wm_class='homebank'),
Match(title='Multiple edit transactions', wm_class='homebank'),
Match(title='Transaction splits', wm_class='homebank'),
]
)
@ -109,7 +110,7 @@ class Kuro(BaseTheme):
def init_keys(self):
logger.warning("Initializing keys")
keys = [
return [
# Switch between windows in current stack pane
Key([self.mod], "k", lazy.layout.down()),
Key([self.mod], "j", lazy.layout.up()),
@ -154,6 +155,17 @@ class Kuro(BaseTheme):
# Lock shortcut
Key([self.mod], "l", lazy.spawn(Config.get('lock_command', "i3lock"))),
# Display modes
Key([self.mod], "Prior", lazy.spawn(Config.get('cmd_monitor_mode_3s144', 'true'))),
Key([self.mod], "Next", lazy.spawn(Config.get('cmd_monitor_mode_3s60', 'true'))),
Key([self.mod], "Home", lazy.spawn(Config.get('cmd_monitor_mode_day', 'true'))),
Key([self.mod], "End", lazy.spawn(Config.get('cmd_monitor_mode_night', 'true'))),
Key([self.mod], "Insert", lazy.spawn(Config.get('cmd_monitor_mode_alt', 'true'))),
# Backlight keys
Key([], "XF86MonBrightnessUp", lazy.spawn(Config.get('cmd_brightness_up', 'xbacklight -inc 10'))),
Key([], "XF86MonBrightnessDown", lazy.spawn(Config.get('cmd_brightness_down', 'xbacklight -dec 10'))),
# Media keys
Key([], "XF86AudioPlay", lazy.spawn(Config.get('cmd_media_play', 'true'))),
Key([], "XF86AudioNext", lazy.spawn(Config.get('cmd_media_next', 'true'))),
@ -210,32 +222,27 @@ class Kuro(BaseTheme):
# Spawn a popup, and despawn it after 3 seconds
Key([self.mod, "control"], "p", lazy.function(test_popups)),
]
# Extra keybinds from host-specific Config
keys.extend([
Key([(self.mod if m == 'mod' else m) for m in key['modifiers']], key['key'], key['action'])
for key in Config.get('extra_keys', [])
])
return keys
def init_groups(self):
logger.warning("Initializing groups")
# http://fontawesome.io/cheatsheet
groups = []
for group in Config.get('groups', [{'name': '1'}]):
if 'layout' in group:
if group['layout'] == "floating":
groups.append(Group(
group['name'],
layout="floating",
layouts=[
layout.Floating(**group.get('options', {}))
groups = [
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group(""),
Group("", layout='floating', layouts=[
layout.Floating(
border_focus="#990000",
border_normal="#440000"
)
])
]
))
else:
logger.warning(f"Unknown group layout for group '{group['name']}': {group['layout']}")
else:
groups.append(Group(group['name']))
return groups
def init_layouts(self):
@ -250,7 +257,6 @@ class Kuro(BaseTheme):
border_width=Config.get('width_border', "1"),
grow_amount=Config.get('grow_amount', "5"),
margin=Config.get('margin_layout', "0"),
margin_on_single=Config.get('margin_layout_single', "0"),
),
layout.Max(),
layout.Zoomy(
@ -286,8 +292,8 @@ class Kuro(BaseTheme):
def initialize_colorscheme(self):
colors = None
if os.path.isfile(f"{Config.get('homedir', '~')}/.cache/wal/colors.json"):
with open(f"{Config.get('homedir', '~')}/.cache/wal/colors.json", 'r') as f:
if os.path.isfile("/home/kevin/.cache/wal/colors.json"):
with open("/home/kevin/.cache/wal/colors.json", 'r') as f:
try:
colors = json.load(f)['colors']
except KeyError:
@ -303,34 +309,25 @@ class Kuro(BaseTheme):
Config.bar_background = colors['color1']
def reinit_screens(self):
if Config.get("use_fake_screens", False):
logger.warning(f"Using fake screens!")
self.num_screens = Config.get("fake_screen_count", 1)
else:
# Re-initalize bars
self.topbars.clear()
if qtile.core.name == "x11":
self.num_screens = max(1, utils.get_screen_count())
else:
self.num_screens = max(1, len(qtile.core.get_screen_info()))
logger.warning(f"Detected {self.num_screens} screens.")
screen_kwargs = Config.get("screen_kwargs", [])
screens = []
for x in range(self.num_screens):
logger.warning(f"Reconfiguring bars for screen {x}")
logger.warning("Initializing bars for screen {}".format(x))
topbar = self.build_bar_for_screen(x)
self.topbars.append(topbar)
screens.append(Screen(top=topbar))
try:
screen = self.screens[x]
except IndexError:
try:
kwargs = screen_kwargs[x]
logger.warning(f"Config for screen {x}: {kwargs}")
except IndexError:
logger.warning(f"No kwarg config for screen {x}")
kwargs = {}
screen = Screen(**kwargs)
if screen.top is None:
screen.top = self.build_bar_for_screen(x)
topbar = screen.top
self.screens.append(screen)
self.screens.clear()
for s in screens:
self.screens.append(s)
def update_keys(self):
logger.warning("Updating keys")
@ -369,44 +366,17 @@ class Kuro(BaseTheme):
widgets = [
# Workspaces
kuro.utils.widgets.KuroGroupBox(
# Active group font colour
active=Config.get('colour_groupbox_icon_active', '#ffffff'),
# Inactive group font colour
inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
# Border or line colour for group on this screen when focused.
this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# Border or line colour for group on this screen when unfocused.
this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# Border or line colour for group on other screen when focused.
other_current_screen_border=Config.get('colour_groupbox_border_normal', '#444444'),
# Border or line colour for group on other screen when unfocused.
other_screen_border=Config.get('colour_groupbox_border_normal', '#444444'),
# Urgent group font color
urgent_text=Config.get('colour_groupbox_urgent_text', '#FF0000'),
# Urgent border or line color
urgent_border=Config.get('colour_groupbox_urgent_border', '#FF0000'),
# Method of highlighting ('border', 'block', 'text', or 'line')
highlight_method="line",
# Active group highlight color when using 'line' highlight method
highlight_color=Config.get("colour_groupbox_highlight", '#888888'),
# Method for alerting you of WM urgent hints (one of 'border', 'text', 'block', or 'line')
urgent_alert_method="block",
# To round or not to round box borders
rounded=Config.get('bool_groupbox_rounded_borders', True),
# Disable dragging and dropping of group names on widget
disable_drag=Config.get('bool_groupbox_disable_drag', False),
# Other settings
borderwidth=Config.get('width_groupbox_border', 1),
disable_drag=Config.get('bool_groupbox_disable_drag', False),
font=Config.get('font_groupbox', 'Arial'),
fontsize=Config.get('fontsize_groupbox', 15),
margin=Config.get('margin_groupbox', 0),
highlight_color=Config.get("colour_groupbox_border_normal", '#444444'),
inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
rounded=Config.get('bool_groupbox_rounded_borders', True),
this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
margin=Config.get('margin_groupbox', 0)
),
# Spawn prompt (only shown if activated)
@ -432,7 +402,6 @@ class Kuro(BaseTheme):
]
# Media widget(s)
if Config.get('show_media_widget', False):
widgets.extend([
# An MPRIS widget that shows the media play status as an icon.
widget.Mpris2(
@ -530,10 +499,19 @@ class Kuro(BaseTheme):
])
# Volume widget(s)
for sink_name in Config.get('volume_pulse_sinks', []):
widgets.append(
kuro.utils.widgets.VolumeInfoWidget(
pulse_sink=sink_name,
pulse_sink=Config.get('volume_pulse_sink', None),
fontsize_left=18,
fontsize_right=11,
font_left=Config.get('font_groupbox', None),
)
)
# Violet has multiple volume widgets
if socket.gethostname() in ["Violet"]:
widgets.append(
kuro.utils.widgets.VolumeInfoWidget(
pulse_sink=Config.get('volume_pulse_sink2', None),
fontsize_left=18,
fontsize_right=11,
font_left=Config.get('font_groupbox', None),
@ -546,8 +524,7 @@ class Kuro(BaseTheme):
kuro.utils.widgets.NetworkInfoWidget(
fontsize_left=16, fontsize_right=14,
wireless_interface=Config.get('wifi_interface', None),
wired_interface=Config.get('wired_interface', None),
config_application=Config.get('network_config', None),
wired_interface=Config.get('wired_interface', None)
),
])
@ -572,7 +549,7 @@ class Kuro(BaseTheme):
# Layout switcher, clock and Screen ID
widgets.extend([
widget.CurrentLayout(mode="icon", custom_icon_paths=Config.get('custom_layout_icon_paths', [])),
widget.CurrentLayoutIcon(custom_icon_paths=Config.get('custom_layout_icon_paths', [])),
widget.Clock(format="%a %d %b ", **self.widget_defaults),
widget.Clock(
format="<b>%H</b>:%M",
@ -594,14 +571,11 @@ class Kuro(BaseTheme):
])
# Build the bar
return KuroBar(
return bar.Bar(
background=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity', 'AA')}",
background_normal=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity', 'AA')}",
background_hover=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity_hover', 'AA')}",
opacity=Config.get('bar_opacity', 1.0),
widgets=widgets,
size=Config.get('height_groupbox', 30),
margin=[8, 8, 0, 8]
size=Config.get('height_groupbox', 30)
)
# QTile base callbacks
@ -620,13 +594,12 @@ class Kuro(BaseTheme):
logger.warning("Restoring wallpaper...")
if self.current_wallpaper:
p = utils.execute_once([*Config.get('cmd_wal', ['wallust', 'run']), "{}".format(self.current_wallpaper)])
if p:
p = utils.execute_once(["wallust", "run", "{}".format(self.current_wallpaper)])
p.wait()
else:
wallpaper = None
if os.path.isfile(f"{Config.get('homedir', '~')}/.cache/wal/colors.json"):
with open(f"{Config.get('homedir', '~')}/.cache/wal/colors.json", 'r') as f:
if os.path.isfile("/home/kevin/.cache/wal/colors.json"):
with open("/home/kevin/.cache/wal/colors.json", 'r') as f:
try:
wallpaper = json.load(f)['wallpaper']
except KeyError:
@ -638,6 +611,7 @@ class Kuro(BaseTheme):
# Update color scheme
self.update_colorscheme()
self.startup_completed = True
def callback_startup_complete(self, *args, **kwargs):
logger.warning("Callback Startup Complete")
@ -645,30 +619,17 @@ class Kuro(BaseTheme):
# Save theme instance in qtile
qtile.theme_instance = self
for screen in self.screens:
if screen.top:
screen.top.window.window.set_property(
"_NET_WM_WINDOW_TYPE",
[screen.top.window.window.conn.atoms["_NET_WM_WINDOW_TYPE_DOCK"]]
)
# Update color scheme
self.update_colorscheme()
# Setup XDG Desktop Portal on Wayland
if qtile.core.name == "wayland":
# Setup XDG Desktop Portal
self.setup_xdg_desktop_portal()
# After first startup is complete, autostart configured apps
logger.warning("Autostarting apps...")
for category in Config.get("apps_autostart", {}).keys():
if qtile.core.name == category or category == "common":
logger.warning(f"Autostarting apps for {category}...")
for app in Config.get("apps_autostart", {}).get(category, []):
for app in Config.get("apps_autostart", []):
logger.warning(f"Starting '{app}'...")
utils.execute_once(app)
else:
logger.warning(f"Skipping autostart apps for {category}, because core is {qtile.core.name}...")
for app in Config.get("apps_autostart_group", []):
if all(x in app.keys() for x in ["group", "command"]):
@ -678,22 +639,19 @@ class Kuro(BaseTheme):
logger.warning(f"Invalid app in 'apps_autostart_group', "
f"must have 'group' and 'command' keys: {app}...")
logger.warning("Autostart complete")
cur_time = time.time()
logger.warning(f"QTile startup completed! Started up in {(cur_time - self.startup_time):.1f} seconds!")
self.startup_completed = True
def callback_client_managed(self, *args, **kwargs):
client: Optional[Window] = args[0] if len(args) > 0 else None
# TODO: Move get_pid to an utility function
w_pid = None
if hasattr(client, 'get_pid'):
try:
w_pid = client.get_pid()
elif hasattr(client, 'get_net_wm_pid'):
except AttributeError: # Some windows might not have this .get_pid method. Try other ways
if isinstance(client, WaylandXWindow) or isinstance(client, WaylandXStatic):
w_pid = client.surface.pid
elif isinstance(client, XorgXWindow):
w_pid = client.get_net_wm_pid()
elif hasattr(client, 'surface') and hasattr(client.surface, 'pid'):
w_pod = client.surface.pid
elif client.__class__.__name__ in ["Static", "XStatic", "LayerStatic"]:
elif isinstance(client, LayerStatic):
pass # Wayland background layer 'window'
else:
logger.error(f"Unknown window type {client.__class__.__name__}")
@ -713,14 +671,9 @@ class Kuro(BaseTheme):
del client.is_static_window
self.static_windows.remove(client)
def callback_screen_change(self, *args, **kwargs):
logger.warning(f"Screen configuration changed, reinitializing screens")
def callback_screens_reconfigured(self, *args, **kwargs):
logger.warning(f"Re-configuring screens!")
self.reinit_screens()
qtile.reconfigure_screens()
#qtile.reconfigure_screens() # Twice, see: https://github.com/qtile/qtile/issues/4673#issuecomment-2196459114
#def callback_screens_reconfigured(self, *args, **kwargs):
logger.warning(f"Screens were reconfgured, updating wallpapers and color scheme")
self.set_wallpaper(self.current_wallpaper)
self.update_colorscheme()
@ -759,21 +712,6 @@ class Kuro(BaseTheme):
def set_random_wallpaper(self, *args, **kwargs):
wallpapers = []
wallpaper_dir = Config.get("desktop_bg_folder", "")
# Get current and sunset/sunrise times using util
sun = Sun(Config.get("loc_latitude", 52.357603), Config.get("loc_longitude", 6.663761))
tzone = tz.gettz(Config.get("loc_timezone", "Europe/Amsterdam"))
cur_time = datetime.datetime.now(tz=tzone)
sunset = sun.get_sunset_time(time_zone=tzone)
sunrise = sun.get_sunrise_time(time_zone=tzone)
logger.warning("Using current time {}, sunset {}, sunrise {} to determine wallpaper.".format(cur_time, sunset, sunrise))
# Use a wallpaper from the night folder after sunset and before sunrise
wallpaper_night_dir = Config.get("desktop_bg_night_folder", "")
if wallpaper_night_dir and os.path.isdir(wallpaper_night_dir):
if cur_time > sunset or cur_time < sunrise:
wallpaper_dir = wallpaper_night_dir
try:
wallpapers = [x for x in os.listdir(wallpaper_dir) if ".vertical." not in x]
except os.error as e:
@ -784,15 +722,13 @@ class Kuro(BaseTheme):
wallpaper_file = Config.get("desktop_bg_override", "")
else:
wallpaper_file = os.path.join(wallpaper_dir, random.choice(wallpapers))
logger.warning(f"Selected new wallpaper: {wallpaper_file}")
self.set_wallpaper(wallpaper_file)
else:
logger.warning("Random wallpaper requested but no wallpapers are available.")
def set_wallpaper(self, filename):
if qtile.core.name == "x11":
p = utils.execute_once(f"{Config.get('x11_wallpaper_config_command', 'wal-nitrogen-noupdate')} {filename}")
if p:
p = utils.execute_once(f"{Config.get('wallpaper_config_command', 'wal-nitrogen-noupdate')} {filename}")
p.wait()
else:
# Wayland can set wallpaper in qtile directly per screen
@ -812,13 +748,12 @@ class Kuro(BaseTheme):
def update_colorscheme(self, *args, **kwargs):
if self.current_wallpaper:
logger.warning(f"Updating wal colors for wallpaper {self.current_wallpaper}")
p = utils.execute_once([*Config.get('cmd_wal', ['wallust', 'run']), "{}".format(self.current_wallpaper)])
if p:
p = utils.execute_once(["wallust", "run", "{}".format(self.current_wallpaper)])
p.wait()
colors = None
if os.path.isfile(f"{Config.get('homedir', '~')}/.cache/wal/colors.json"):
with open(f"{Config.get('homedir', '~')}/.cache/wal/colors.json", 'r') as f:
if os.path.isfile("/home/kevin/.cache/wal/colors.json"):
with open("/home/kevin/.cache/wal/colors.json", 'r') as f:
try:
colors = json.load(f)['colors']
except KeyError:
@ -904,7 +839,6 @@ class Kuro(BaseTheme):
try:
logger.warning(f"Calling 'pywalfox update'...")
p = utils.execute(["pywalfox", "update"])
if p:
p.wait()
except subprocess.SubprocessError as e:
logger.error(f"Error running 'pywalfox update': {e}")
@ -921,48 +855,3 @@ class Kuro(BaseTheme):
subprocess.Popen(["systemctl", "--user", "import-environment", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP"])
subprocess.Popen(["dbus-update-activation-environment", "--systemd", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP=qtile"])
subprocess.Popen(["systemctl", "--user", "restart", "xdg-desktop-portal"])
def update_bar_margins(self, screen_ref, layout_ref, extra_windows=None, ignore_windows=None):
if extra_windows is None:
extra_windows = []
if ignore_windows is None:
ignore_windows = []
if screen_ref and screen_ref.top:
logger.warning(f"Updating margins of bar {screen_ref.top} on screen {screen_ref} for layout {layout_ref}")
# Update margins. Bar should have no margins when there is only one client on screen (Max layout or Columns with 1 window)
# and should have margins in all other cases.
if isinstance(layout_ref, layout.max.Max):
logger.warning(f"Layout clients: {layout_ref.clients}")
logger.warning(f"Ignored clients: {ignore_windows}")
logger.warning(f"Extra clients: {extra_windows}")
if (len(layout_ref.clients) == 0 and len(extra_windows) == 0) or (len(ignore_windows) > 0 and all(w in ignore_windows for w in layout_ref.clients)):
screen_ref.top.update_bar_type("floating")
else:
screen_ref.top.update_bar_type("docked")
elif isinstance(layout_ref, layout.columns.Columns):
clients = extra_windows
for column in layout_ref.columns:
clients.extend(column.clients)
is_single = len([c for c in clients if c not in ignore_windows]) == 1
if is_single:
screen_ref.top.update_bar_type("docked")
else:
screen_ref.top.update_bar_type("floating")
else:
screen_ref.top.update_bar_type("floating")
# Re-render the top bar to apply margins
screen_ref.top._configure(qtile=qtile, screen=screen_ref, reconfigure=True)
# Re-render visible clients on the screen to apply margins
layout_ref.group.layout_all()
def callback_layout_change(self, new_layout, new_group):
logger.warning(f"Changed layout in group {new_group} to {new_layout}")
self.update_bar_margins(new_group.screen, new_layout)
def callback_group_window_add(self, group, window):
self.update_bar_margins(group.screen, group.layout, extra_windows=[window])
def callback_group_window_remove(self, group, window):
self.update_bar_margins(group.screen, group.layout, ignore_windows=[window])

View file

@ -1,31 +0,0 @@
import importlib
import socket
import traceback
from libqtile.log_utils import logger
def load_config_class():
# Try to import host-specific configuration first
hostname = socket.gethostname().lower()
if hostname:
try:
host_module = importlib.import_module(f"kuro.config.{hostname}")
return getattr(host_module, "Config")
except ImportError:
pass
logger.warning(f"No host-specific configuration available for {hostname}. Loading general config...")
# If no config yet, load general Kuro Config object
try:
conf_module = importlib.import_module("kuro.config")
return getattr(conf_module, "Config")
except ImportError as e:
logger.error(traceback.format_exc())
logger.error("Could not load Kuro Config. Trying to load BaseConfig. Error: {}".format(e))
# If no config yet, load fallback BaseConfig
try:
base_module = importlib.import_module("kuro.base")
return getattr(base_module, "BaseConfig")
except ImportError as e:
logger.error(traceback.format_exc())
return None

View file

@ -1,70 +0,0 @@
from libqtile import bar
from libqtile.widget.groupbox import GroupBox
from libqtile.log_utils import logger
class KuroBar(bar.Bar):
defaults = [
("background", "#000000", "Background colour."),
("background_normal", "#000000", "Background colour normally."),
("background_hover", "#000000", "Background colour on hover."),
("opacity", 1, "Bar window opacity."),
("margin", 0, "Space around bar as int or list of ints [N E S W]."),
("border_color", "#000000", "Border colour as str or list of str [N E S W]"),
("border_width", 0, "Width of border as int of list of ints [N E S W]"),
(
"reserve",
True,
"Reserve screen space (when set to 'False', bar will be drawn above windows).",
),
]
cur_type: str = "docked"
def _set_bg_transparent(self):
self.background = self.background_normal
# GroupBox Widget background color
for widget in self.widgets:
if isinstance(widget, GroupBox):
if len(widget.highlight_color) in [6, 7]:
widget.highlight_color = widget.highlight_color + "88"
else:
widget.highlight_color = widget.highlight_color[:-2] + "88"
logger.warning(f"Highlight: {widget.highlight_color}")
self.drawer.clear(self.background)
self.draw()
def _set_bg_opaque(self):
self.background = self.background_hover
# GroupBox Widget background color
for widget in self.widgets:
if isinstance(widget, GroupBox):
if len(widget.highlight_color) in [6, 7]:
widget.highlight_color = widget.highlight_color + "FF"
else:
widget.highlight_color = widget.highlight_color[:-2] + "FF"
logger.warning(f"Highlight: {widget.highlight_color}")
self.drawer.clear(self.background)
self.draw()
def process_pointer_enter(self, x: int, y: int) -> None:
super().process_pointer_enter(x=x, y=y)
if self.cur_type == "floating":
self._set_bg_opaque()
def process_pointer_leave(self, x: int, y: int) -> None:
super().process_pointer_leave(x=x, y=y)
if self.cur_type == "floating":
self._set_bg_transparent()
def update_bar_type(self, new_type: str):
if new_type not in ["docked", "floating"]:
return
self.cur_type = new_type
if new_type == "floating":
self.margin = [8, 8, 0, 8]
self._set_bg_transparent()
else:
self.margin = [0, 0, 0, 0]
self._set_bg_opaque()

View file

@ -65,7 +65,7 @@ def start_in_group(theme, qtile, group: str, command: List[str], floating: bool
theme.autostart_app_rules[proc.pid] = rule_id
return proc
except FileNotFoundError as e:
logger.error(f"Could not execute {command}, FileNotFoundError - {e}")
logger.error(f"Could not execute {process}, FileNotFoundError - {e}")
def start_in_group_once(theme, qtile, group: str, command: List[str], floating: bool = False,
@ -90,11 +90,16 @@ def get_screen_count():
logger.info("Using xrandr to detect screen count")
output = subprocess.check_output("xrandr -q".split()).decode('utf-8')
output = [x for x in output.split("\n") if " connected" in x]
return max(1, len(output))
else:
return max(1, len(qtile.core.get_screen_info()))
logger.info("Using lsmon (wallutils) to detect screen count")
output = subprocess.check_output(["lsmon"]).decode('utf-8')
output = output.split("\n")
except subprocess.CalledProcessError:
pass
return 1
if output:
return len(output)
else:
return 1
@ -105,7 +110,7 @@ def bar_separator(config):
)
def init_notify(qtile):
#if qtile and qtile.theme_instance and qtile.theme_instance.startup_completed:
if qtile and qtile.theme_instance and qtile.theme_instance.startup_completed:
try:
if not notify2.is_initted():
logger.warning("Initializing Notify2")
@ -114,8 +119,8 @@ def init_notify(qtile):
logger.error(f"Failed to initialize Notify2 (DBus error), retrying later.")
except Exception:
logger.error(f"Failed to initialize Notify2 (Generic error), retrying later.")
#else:
# logger.warning(f"Not initializing Notify2 yet, QTile startup not completed.")
else:
logger.warning(f"Not initializing Notify2 yet, QTile startup not completed.")
def notify(qtile, title, content, urgency=URGENCY_NORMAL, timeout=5000, image=None):

View file

@ -1,141 +0,0 @@
import math
import warnings
from datetime import datetime, timedelta, time, timezone
# Copied from: https://github.com/SatAgro/suntime/blob/master/suntime/suntime.py
# CONSTANT
TO_RAD = math.pi/180.0
class SunTimeException(Exception):
def __init__(self, message):
super(SunTimeException, self).__init__(message)
class Sun:
"""
Approximated calculation of sunrise and sunset datetimes. Adapted from:
https://stackoverflow.com/questions/19615350/calculate-sunrise-and-sunset-times-for-a-given-gps-coordinate-within-postgresql
"""
def __init__(self, lat, lon):
self._lat = lat
self._lon = lon
self.lngHour = self._lon / 15
def get_sunrise_time(self, at_date=datetime.now(), time_zone=timezone.utc):
"""
:param at_date: Reference date. datetime.now() if not provided.
:param time_zone: pytz object with .tzinfo() or None
:return: sunrise datetime.
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
"""
time_delta = self.get_sun_timedelta(at_date, time_zone=time_zone, is_rise_time=True)
if time_delta is None:
raise SunTimeException('The sun never rises on this location (on the specified date)')
else:
return datetime.combine(at_date, time(tzinfo=time_zone)) + time_delta
def get_sunset_time(self, at_date=datetime.now(), time_zone=timezone.utc):
"""
Calculate the sunset time for given date.
:param at_date: Reference date. datetime.now() if not provided.
:param time_zone: pytz object with .tzinfo() or None
:return: sunset datetime.
:raises: SunTimeException when there is no sunrise and sunset on given location and date.
"""
time_delta = self.get_sun_timedelta(at_date, time_zone=time_zone, is_rise_time=False)
if time_delta is None:
raise SunTimeException('The sun never rises on this location (on the specified date)')
else:
return datetime.combine(at_date, time(tzinfo=time_zone)) + time_delta
def get_sun_timedelta(self, at_date, time_zone, is_rise_time=True, zenith=90.8):
"""
Calculate sunrise or sunset date.
:param at_date: Reference date
:param time_zone: pytz object with .tzinfo() or None
:param is_rise_time: True if you want to calculate sunrise time.
:param zenith: Sun reference zenith
:return: timedelta showing hour, minute, and second of sunrise or sunset
"""
# If not set get local timezone from datetime
if time_zone is None:
time_zone = datetime.now().tzinfo
# 1. first get the day of the year
N = at_date.timetuple().tm_yday
# 2. convert the longitude to hour value and calculate an approximate time
if is_rise_time:
t = N + ((6 - self.lngHour) / 24)
else: # sunset
t = N + ((18 - self.lngHour) / 24)
# 3a. calculate the Sun's mean anomaly
M = (0.9856 * t) - 3.289
# 3b. calculate the Sun's true longitude
L = M + (1.916 * math.sin(TO_RAD*M)) + (0.020 * math.sin(TO_RAD * 2 * M)) + 282.634
L = self._force_range(L, 360) # NOTE: L adjusted into the range [0,360)
# 4a. calculate the Sun's declination
sinDec = 0.39782 * math.sin(TO_RAD*L)
cosDec = math.cos(math.asin(sinDec))
# 4b. calculate the Sun's local hour angle
cosH = (math.cos(TO_RAD*zenith) - (sinDec * math.sin(TO_RAD*self._lat))) / (cosDec * math.cos(TO_RAD*self._lat))
if cosH > 1:
return None # The sun never rises on this location (on the specified date)
if cosH < -1:
return None # The sun never sets on this location (on the specified date)
# 4c. finish calculating H and convert into hours
if is_rise_time:
H = 360 - (1/TO_RAD) * math.acos(cosH)
else: # setting
H = (1/TO_RAD) * math.acos(cosH)
H = H / 15
# 5a. calculate the Sun's right ascension
RA = (1/TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD*L))
RA = self._force_range(RA, 360) # NOTE: RA adjusted into the range [0,360)
# 5b. right ascension value needs to be in the same quadrant as L
Lquadrant = (math.floor(L/90)) * 90
RAquadrant = (math.floor(RA/90)) * 90
RA = RA + (Lquadrant - RAquadrant)
# 5c. right ascension value needs to be converted into hours
RA = RA / 15
# 6. calculate local mean time of rising/setting
T = H + RA - (0.06571 * t) - 6.622
# 7a. adjust back to UTC
UT = T - self.lngHour
if time_zone:
# 7b. adjust back to local time
UT += time_zone.utcoffset(at_date).total_seconds() / 3600
# 7c. rounding and impose range bounds
UT = round(UT, 2)
# if is_rise_time:
UT = self._force_range(UT, 24)
# 8. return timedelta
return timedelta(hours=UT)
@staticmethod
def _force_range(v, max):
# force v to be >= 0 and < max
if v < 0:
return v + max
elif v >= max:
return v - max
return v

View file

@ -16,42 +16,8 @@ from libqtile.widget.wlan import get_status
from libqtile.widget.groupbox import GroupBox
from libqtile.command.base import expose_command
from qtile_extras.widget.decorations import RectDecoration
from kuro.utils.general import notify, BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT, BUTTON_DOWN, BUTTON_UP, BUTTON_MUTE, \
call_process, execute
def get_widget_style(config):
# active=Config.get('colour_groupbox_icon_active', '#ffffff'),
# borderwidth=Config.get('width_groupbox_border', 1),
# disable_drag=Config.get('bool_groupbox_disable_drag', False),
# font=Config.get('font_groupbox', 'Arial'),
# fontsize=Config.get('fontsize_groupbox', 15),
# highlight_color=Config.get("colour_groupbox_border_normal", '#444444'),
# inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
# rounded=Config.get('bool_groupbox_rounded_borders', True),
# this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# margin=Config.get('margin_groupbox', 0),
# Config.foreground = colors['color15']
# Config.background = colors['color0']
# Config.highlight = colors['color3']
# Config.inactive_light = colors['color4']
# Config.inactive_dark = colors['color5']
# Config.bar_background = colors['color1']
return {
"foreground": "#ffffff",
"decorations": [
RectDecoration(
colour=config.background,
radius=12,
filled=True,
group=True,
) # type: ignore
],
}
call_process
class DualPaneTextboxBase(base._Widget):
@ -235,18 +201,18 @@ class DualPaneTextboxBase(base._Widget):
self.changed = False
@expose_command()
def set_font(self, font=None, fontsize_left=None, fontsize_right=None, fontshadow=None):
def set_font(self, font=None, fontsize_left=0, fontsize_right=0, fontshadow=""):
"""
Change the font used by this widget. If font is None, the current
font is used.
"""
if font is not None:
self.font = font
if fontsize_left is not None:
if fontsize_left != 0:
self.fontsize_left = fontsize_left
if fontsize_right is not None:
if fontsize_right != 0:
self.fontsize_right = fontsize_right
if fontshadow is not None:
if fontshadow != "":
self.fontshadow = fontshadow
self.bar.draw()
@ -655,7 +621,6 @@ class NetworkInfoWidget(DualPaneTextboxBase):
('critical_color', "#ffffff", "Color when value is critical"),
('wireless_interface', "wifi0", "Wireless interface device name"),
('wired_interface', "enp7s0", "Wired interface device name"),
('config_application', None, "Application to launch when right/middle clicking"),
]
def __init__(self, **config):
@ -684,7 +649,6 @@ class NetworkInfoWidget(DualPaneTextboxBase):
def _update_values(self):
# Wifi
if self.wireless_interface:
try:
essid, quality = get_status(self.wireless_interface)
status = iwlib.get_iwconfig(self.wireless_interface)
@ -704,7 +668,6 @@ class NetworkInfoWidget(DualPaneTextboxBase):
pass
# Wired
if self.wired_interface:
try:
self.wired_ips = netifaces.ifaddresses(self.wired_interface)
self.wired_ipv4 = self.wired_ips.get(netifaces.AF_INET, [{'addr': ""}])[0]['addr']
@ -730,7 +693,6 @@ class NetworkInfoWidget(DualPaneTextboxBase):
self.draw()
def draw(self):
if self.wireless_interface:
if self.wireless_connected:
strength = ""
if self.wireless_signal < 66:
@ -740,14 +702,9 @@ class NetworkInfoWidget(DualPaneTextboxBase):
self.text_left = strength
else:
self.text_left = ""
else:
self.text_left = ""
if self.wired_interface:
if self.wired_connected:
self.text_right = ""
else:
self.text_right = ""
else:
self.text_right = ""
@ -782,9 +739,6 @@ class NetworkInfoWidget(DualPaneTextboxBase):
notify(None, title, "{}\n\n{}".format(wifi_text, wired_text))
else:
notify(None, title, "\n{}".format(wired_text))
if button == BUTTON_RIGHT or button == BUTTON_MIDDLE:
if self.config_application:
execute(self.config_application)
class BatteryInfoWidget(DualPaneTextboxBase):

View file

@ -4,34 +4,9 @@ notification-daemon
otf-font-awesome
python-osc
qtile-extras
# /optional/
playerctl
xfce4-screenshooter
xfce4-clipman-plugin
wireless_tools
# Utilities
kdeconnect # KDE Connect
vorta # Backup scheduler
# Xorg-only
picom # Compositor
xfce4-clipman # Clipboard manager
dunst # Notification daemon
xiccd # Color profile manager
# Wayland-only
xorg-xwayland
python-pywlroots
wofi # dmenu replacement
grim # Screenshot utility
swappy # Screenshot editor
slurp # Region selector
cliphist # Clipboard history
mako # Notifications daemon
kanshi # Display hotplugging
wallutils # Display/wallpaper utilities (lsmon)
papirus-icon-theme # Icon theme