changeset 3:eb2aa09653bd default tip

beginnings of CPU colors, daemon
author Brad Greco <brad@bgreco.net>
date Mon, 29 Mar 2021 20:27:09 -0400
parents 091a1f59a79c
children
files daemon.py keyboard_colors/color_profile.py keyboard_colors/custom/#custom.glade# keyboard_colors/custom/custom.glade keyboard_colors/custom/custom.py keyboard_colors/psutilx/__init__.py keyboard_colors/psutilx/psutilx.py main.py ui/main_window.py
diffstat 8 files changed, 286 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/daemon.py	Mon Mar 29 20:27:09 2021 -0400
@@ -0,0 +1,46 @@
+from keyboard_colors.color_profile import ProfileManager, Color
+from threading import Event
+import keyboard_colors
+import keyboard_colors.psutilx.psutilx
+import signal
+
+
+class KeyboardColorDaemon:
+
+    transition_wait_time = 100
+
+    def __init__(self):
+        signal.signal(signal.SIGINT, self.signal_handler)
+        self.profile_manager = ProfileManager()
+        # self.run(keyboard_colors.custom.custom.ColorProfile())
+        self.run(keyboard_colors.psutilx.psutilx.ColorProfile())
+
+    def run(self, profile):
+        # color = profile.color
+        color = Color.from_hex('#FF3300')
+        wait_time = profile.frequency_time - profile.transition_time
+        transition_steps = int(profile.transition_time / self.transition_wait_time)
+
+        # print(Color.get_step_color(color, profile.next_color(), .3))
+        # return
+
+        self.break_event = Event()
+        while not self.break_event.is_set():
+            next_color = profile.next_color()
+            step_colors = Color.get_steps(color, next_color, transition_steps)
+
+            for step_color in step_colors:
+                print(step_color)
+                with open('/sys/devices/platform/system76/leds/system76::kbd_backlight/color_left', 'w') as f:
+                    f.write(step_color.to_hex())
+                self.break_event.wait(self.transition_wait_time / 1000)
+
+            color = next_color
+            self.break_event.wait(wait_time / 1000)
+
+    def signal_handler(self, signal, frame):
+        self.break_event.set()
+
+
+if __name__ == "__main__":
+    KeyboardColorDaemon()
--- a/keyboard_colors/color_profile.py	Tue Mar 02 20:17:36 2021 -0500
+++ b/keyboard_colors/color_profile.py	Mon Mar 29 20:27:09 2021 -0400
@@ -1,8 +1,10 @@
+import colorsys
 import gi
 import importlib
 import json
 import os
 import pathlib
+import re
 import sys
 import types
 import uuid
@@ -19,6 +21,25 @@
     def __init__(self):
         self.name = ''
         self.id = str(uuid.uuid4())
+        self.transition_time = 1000
+        self.frequency_time = 1000
+
+    def color_scale_value(self, value):
+        if value in self.color_scale:
+            return self.color_scale[value]
+
+        thresholds = self.color_scale.keys()
+        thresholds_below = (i for i in thresholds if i < value)
+        thresholds_above = (i for i in thresholds if i > value)
+        threshold_below = max(thresholds_below, default=min(thresholds))
+        threshold_above = min(thresholds_above, default=max(thresholds))
+
+        percent = value / (threshold_above - threshold_below)
+        return Color.get_step_color(
+            self.color_scale[threshold_below],
+            self.color_scale[threshold_above],
+            percent
+        )
 
     def build_settings_ui(self):
         glade_file = sys.modules[self.__module__].__file__.replace('.py', '.glade')
@@ -84,3 +105,84 @@
 
     def get_profile(self, profile_id):
         return self.get_profiles()[profile_id]
+
+
+class Color:
+
+    def __init__(self, r, g, b):
+        self.r = r
+        self.g = g
+        self.b = b
+
+    @staticmethod
+    def from_hsv(h, s, v):
+        return Color(*colorsys.hsv_to_rgb(h, s, v))
+
+    def to_hsv(self):
+        return colorsys.rgb_to_hsv(self.r, self.g, self.b)
+
+    @staticmethod
+    def from_hex(hex):
+        hex = hex.strip().strip('#').lower()
+        if not re.match('[0-9a-f]{6}', hex):
+            raise ValueError('Invalid hex color string: ' + hex)
+
+        r = int(hex[0:2], 16) / 255
+        g = int(hex[2:4], 16) / 255
+        b = int(hex[4:6], 16) / 255
+
+        return Color(r, g, b)
+
+    def to_hex(self):
+        return format(int(self.r * 255), '02x') \
+             + format(int(self.g * 255), '02x') \
+             + format(int(self.b * 255), '02x')
+
+    @staticmethod
+    def get_steps(start_color, end_color, step_count):
+        # (start_h, start_s, start_v) = start_color.to_hsv()
+        # (end_h, end_s, end_v) = end_color.to_hsv()
+
+        # # Find the shortest distance between the two hues.
+        # if abs(start_h - end_h) < 0.5:
+        #     h_step = (end_h - start_h) / step_count
+        # else:
+        #     h_step = (1 - abs(end_h - start_h)) / step_count
+        #     if (start_h < end_h):
+        #         h_step = h_step * -1
+
+        # steps = []
+        # for i in range(0, step_count):
+        #     h = (start_h + h_step * i) % 1
+        #     s = start_s + (end_s - start_s) / step_count * i
+        #     v = start_v + (end_v - start_v) / step_count * i
+        #     steps.append(Color.from_hsv(h, s, v))
+
+        steps = []
+        for i in range(0, step_count):
+            percent = i / step_count
+            steps.append(Color.get_step_color(start_color, end_color, percent))
+
+        return steps
+
+    @staticmethod
+    def get_step_color(start_color, end_color, percent):
+        (start_h, start_s, start_v) = start_color.to_hsv()
+        (end_h, end_s, end_v) = end_color.to_hsv()
+
+        # Find the shortest distance between the two hues.
+        if abs(start_h - end_h) < 0.5:
+            h_diff = end_h - start_h
+        else:
+            h_diff = 1 - abs(end_h - start_h)
+            if (start_h < end_h):
+                h_diff = h_diff * -1
+
+        h = (start_h + h_diff * percent) % 1
+        s = start_s + (end_s - start_s) * percent
+        v = start_v + (end_v - start_v) * percent
+
+        return Color.from_hsv(h, s, v)
+
+    def __repr__(self):
+        return 'Color #' + self.to_hex()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/keyboard_colors/custom/#custom.glade#	Mon Mar 29 20:27:09 2021 -0400
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkBox" id="edit_profile_container">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkColorButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkCheckButton">
+            <property name="label" translatable="yes">checkbutton</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">False</property>
+            <property name="draw_indicator">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkSpinButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
--- a/keyboard_colors/custom/custom.glade	Tue Mar 02 20:17:36 2021 -0500
+++ b/keyboard_colors/custom/custom.glade	Mon Mar 29 20:27:09 2021 -0400
@@ -7,10 +7,44 @@
     <property name="can_focus">False</property>
     <property name="orientation">vertical</property>
     <child>
-      <object class="GtkLabel">
+      <object class="GtkGrid">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="label" translatable="yes">label</property>
+        <child>
+          <object class="GtkColorButton">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
       </object>
       <packing>
         <property name="expand">False</property>
@@ -18,11 +52,5 @@
         <property name="position">0</property>
       </packing>
     </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
   </object>
 </interface>
--- a/keyboard_colors/custom/custom.py	Tue Mar 02 20:17:36 2021 -0500
+++ b/keyboard_colors/custom/custom.py	Mon Mar 29 20:27:09 2021 -0400
@@ -1,4 +1,4 @@
-from keyboard_colors.color_profile import ColorProfileBase
+from keyboard_colors.color_profile import ColorProfileBase, Color
 
 
 class ColorProfile(ColorProfileBase):
@@ -7,3 +7,7 @@
 
     def __init__(self):
         super().__init__()
+        self.color = Color.from_hex('#FF3300')
+
+    def next_color(self):
+        return Color.from_hex('#FF0033')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/keyboard_colors/psutilx/psutilx.py	Mon Mar 29 20:27:09 2021 -0400
@@ -0,0 +1,17 @@
+import psutil
+from keyboard_colors.color_profile import ColorProfileBase, Color
+
+
+class ColorProfile(ColorProfileBase):
+
+    type_name = 'Custom colors'
+    color_scale = {
+        0:   Color.from_hex('#00FF00'),
+        100: Color.from_hex('#FF0000'),
+    }
+
+    def __init__(self):
+        super().__init__()
+
+    def next_color(self):
+        return self.color_scale_value(psutil.cpu_percent())
--- a/main.py	Tue Mar 02 20:17:36 2021 -0500
+++ b/main.py	Mon Mar 29 20:27:09 2021 -0400
@@ -1,7 +1,9 @@
 from ui.main_window import MainWindow
 
+
 def main():
     MainWindow().init()
 
+
 if __name__ == "__main__":
     main()
--- a/ui/main_window.py	Tue Mar 02 20:17:36 2021 -0500
+++ b/ui/main_window.py	Mon Mar 29 20:27:09 2021 -0400
@@ -1,3 +1,4 @@
+import itertools
 import gi
 import os
 from keyboard_colors.color_profile import ProfileManager
@@ -50,6 +51,10 @@
         window.show_all()
         Gtk.main()
 
+    def load_color_profiles(self):
+        for profile in self.profile_manager.get_profiles().values():
+            self.color_profile_list_store.append([profile.id, profile.name])
+
     def create_profile(self, widget, profile_type):
         # todo move to profile manager
         profile = profile_type()
@@ -61,7 +66,8 @@
         self.builder.get_object('edit_profile_name_entry').grab_focus()
 
     def edit_profile(self, profile):
-        self.builder.get_object('edit_profile_box').show()
+        edit_profile_box = self.builder.get_object('edit_profile_box')
+        edit_profile_box.show()
 
         self.builder.get_object('edit_profile_type_name_label').set_text(profile.type_name)
         self.builder.get_object('edit_profile_name_entry').set_text(profile.name)
@@ -71,6 +77,22 @@
             edit_profile_details_box.remove(child)
         edit_profile_details_box.add(profile.build_settings_ui())
 
+        edit_profile_entries = self.find_descendents(edit_profile_box, Gtk.Entry)
+        pass
+
+    def find_descendents(self, widget, type):
+        if isinstance(widget, type):
+            return [widget]
+
+        try:
+            matches = []
+            for child in widget.get_children():
+                matches = matches + self.find_descendents(child, type)
+            return matches
+        except AttributeError:
+            return []
+
+
     def profile_tree_view_selection_changed(self, selection):
         (model, tree_iter) = selection.get_selected()
         profile_id = model.get_value(tree_iter, 0)
@@ -85,10 +107,6 @@
     def edit_profile_name_entry_focus_out(self, entry, event):
         pass
 
-    def load_color_profiles(self):
-        for profile in self.profile_manager.get_profiles().values():
-            self.color_profile_list_store.append([profile.id, profile.name])
-
 class KeyboardSection(Gtk.Grid):
 
     def __init__(self, model):