Skip to content

Commit

Permalink
Merge pull request #19 from samclane/color-scale
Browse files Browse the repository at this point in the history
Color scale
  • Loading branch information
samclane authored Jun 12, 2018
2 parents 8878e48 + e76e1a9 commit 7eccf6b
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 17 deletions.
6 changes: 3 additions & 3 deletions _constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = "1.3.4"
BUILD_DATE = "2018-06-06T17:00:22.551617"
VERSION = "1.4.0"
BUILD_DATE = "2018-06-10T17:41:52.285612"
AUTHOR = "Sawyer McLane"
DEBUGGING = True
DEBUGGING = True
123 changes: 123 additions & 0 deletions colorscale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import tkinter as tk

from utils import tuple2hex, HueToRGB, KelvinToRGB


class ColorScale(tk.Canvas):

def __init__(self, parent, val=0, height=13, width=100, variable=None, from_=0, to=360, command=None,
gradient='hue', **kwargs):
"""
Create a GradientBar.
Keyword arguments:
* parent: parent window
* val: initially selected value
* variable: IntVar linked to the alpha value
* from_: The minimum value the slider can take on
* to: The maximum value of the slider
* command: A function callback, invoked every time the slider is moved
* gradient: The type of background coloration
* height, width, and any keyword argument accepted by a tkinter Canvas
"""
tk.Canvas.__init__(self, parent, width=width, height=height, **kwargs)
self.parent = parent
self.max = to
self.min = from_
self.range = self.max - self.min
self._variable = variable
self.command = command
self.color_grad = gradient
if variable is not None:
try:
val = int(variable.get())
except Exception as e:
print(e)
else:
self._variable = tk.IntVar(self)
val = max(min(self.max, val), self.min)
self._variable.set(val)
self._variable.trace("w", self._update_val)

self.gradient = tk.PhotoImage(master=self, width=width, height=height)

self.bind('<Configure>', lambda e: self._draw_gradient(val))
self.bind('<ButtonPress-1>', self._on_click)
self.bind('<B1-Motion>', self._on_move)

def _draw_gradient(self, val):
"""Draw the gradient and put the cursor on val."""
self.delete("gradient")
self.delete("cursor")
del self.gradient
width = self.winfo_width()
height = self.winfo_height()

self.gradient = tk.PhotoImage(master=self, width=width, height=height)

line = []

if self.color_grad == 'bw':
def f(i):
line.append(tuple2hex((int(float(i) / width * 255),) * 3))
elif self.color_grad == 'wb':
def f(i):
line.append(tuple2hex((int((1 - (float(i) / width)) * 255),) * 3))
elif self.color_grad == 'kelvin':
def f(i):
line.append(tuple2hex(KelvinToRGB(((float(i) / width) * self.range) + self.min)))
else: # self.color_grad == 'hue'
def f(i):
line.append(tuple2hex(HueToRGB(float(i) / width * 360)))

for i in range(width):
f(i)
line = "{" + " ".join(line) + "}"
self.gradient.put(" ".join([line for j in range(height)]))
self.create_image(0, 0, anchor="nw", tags="gradient", image=self.gradient)
self.lower("gradient")

x = (val - self.min) / float(self.range) * width
self.create_line(x, 0, x, height, width=4, fill='white', tags="cursor")
self.create_line(x, 0, x, height, width=2, tags="cursor")

def _on_click(self, event):
"""Move selection cursor on click."""
x = event.x
if x >= 0:
width = self.winfo_width()
for s in self.find_withtag("cursor"):
self.coords(s, x, 0, x, self.winfo_height())
self._variable.set(round((float(self.range) * x) / width + self.min, 2))
if self.command is not None:
self.command()

def _on_move(self, event):
"""Make selection cursor follow the cursor."""
if event.x >= 0:
w = self.winfo_width()
x = min(max(abs(event.x), 0), w)
for s in self.find_withtag("cursor"):
self.coords(s, x, 0, x, self.winfo_height())
self._variable.set(round((float(self.range) * x) / w + self.min, 2))
if self.command is not None:
self.command()

def _update_val(self, *args):
val = int(self._variable.get())
val = min(max(val, self.min), self.max)
self.set(val)
self.event_generate("<<HueChanged>>")

def get(self):
"""Return val of color under cursor."""
coords = self.coords('cursor')
width = self.winfo_width()
return round(self.range * coords[0] / width, 2)

def set(self, val):
"""Set cursor position on the color corresponding to the value"""
width = self.winfo_width()
x = (val - self.min) / float(self.range) * width
for s in self.find_withtag("cursor"):
self.coords(s, x, 0, x, self.winfo_height())
self._variable.set(val)
4 changes: 2 additions & 2 deletions default.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ defaultmonitor = get_primary_monitor()

# Used for diagnostic purposes. Please do not change.
[Info]
version = 1.3.4
version = 1.4.0
author = Sawyer McLane
builddate = 2018-06-06T17:00:22.551617
builddate = 2018-06-10T17:41:52.285612
35 changes: 23 additions & 12 deletions gui.pyw
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ from tkinter import messagebox
from tkinter import ttk
from tkinter.colorchooser import *
from win32gui import GetCursorPos
import sys

from PIL import Image as pImage
from lifxlan import *
Expand All @@ -18,10 +17,11 @@ import SysTrayIcon
import audio
import color_thread
import settings
from _constants import *
from colorscale import ColorScale
from keypress import Keystroke_Watcher
from settings import config
from utils import *
from _constants import *

HEARTBEAT_RATE = 3000 # 3 seconds
LOGFILE = 'lifx-control-panel.log'
Expand Down Expand Up @@ -239,22 +239,22 @@ class LightFrame(ttk.Labelframe):
IntVar(self, init_color.saturation, "Saturation"),
IntVar(self, init_color.brightness, "Brightness"),
IntVar(self, init_color.kelvin, "Kelvin"))
for i in self.hsbk:
i.trace('w', self.set_bulb_updated)
self.hsbk_labels = (
Label(self, text='%.3g' % (360 * (self.hsbk[0].get() / 65535))),
Label(self, text=str('%.3g' % (100 * self.hsbk[1].get() / 65535)) + "%"),
Label(self, text=str('%.3g' % (100 * self.hsbk[2].get() / 65535)) + "%"),
Label(self, text=str(self.hsbk[3].get()) + " K")
)
self.hsbk_scale = (
Scale(self, from_=0, to=65535, orient=HORIZONTAL, variable=self.hsbk[0], command=self.update_color_from_ui,
showvalue=False),
Scale(self, from_=0, to=65535, orient=HORIZONTAL, variable=self.hsbk[1], command=self.update_color_from_ui,
showvalue=False),
Scale(self, from_=0, to=65535, orient=HORIZONTAL, variable=self.hsbk[2], command=self.update_color_from_ui,
showvalue=False),
Scale(self, from_=2500, to=9000, orient=HORIZONTAL, variable=self.hsbk[3],
command=self.update_color_from_ui,
showvalue=False))
ColorScale(self, to=65535., variable=self.hsbk[0], command=self.update_color_from_ui),
ColorScale(self, from_=0, to=65535, variable=self.hsbk[1], command=self.update_color_from_ui,
gradient='wb'),
ColorScale(self, from_=0, to=65535, variable=self.hsbk[2], command=self.update_color_from_ui,
gradient='bw'),
ColorScale(self, from_=2500, to=9000, variable=self.hsbk[3], command=self.update_color_from_ui,
gradient='kelvin'))
RELIEF = GROOVE
self.hsbk_display = (
Canvas(self, background=tuple2hex(HueToRGB(360 * (init_color.hue / 65535))), width=20, height=20,
Expand Down Expand Up @@ -343,6 +343,9 @@ class LightFrame(ttk.Labelframe):
""" Get color values entered into GUI"""
return Color(*tuple(v.get() for v in self.hsbk))

def set_bulb_updated(self, *args):
self.bulb.updated = True

def stop_threads(self):
""" Stop all ColorRunner threads """
for thread in self.threads.values():
Expand All @@ -361,7 +364,13 @@ class LightFrame(ttk.Labelframe):
def set_color(self, color, rapid=False):
""" Should be called whenever the bulb wants to change color. Sends bulb command and updates UI accordingly. """
self.stop_threads()
self.bulb.set_color(color, rapid)
try:
self.bulb.set_color(color, rapid)
except WorkflowException as e:
if rapid: # If we're going fast we don't care if we miss a packet.
pass
else:
raise e
self.update_status_from_bulb(run_once=True) # Force UI to update from bulb
self.logger.debug('Color changed to HSBK: {}'.format(color)) # Don't pollute log with rapid color changes

Expand All @@ -381,6 +390,7 @@ class LightFrame(ttk.Labelframe):
if key == 0:
self.hsbk_display[0].config(background=tuple2hex(HueToRGB(360 * (h / 65535))))
elif key == 1:
s = 65535 - s
self.hsbk_display[1].config(
background=tuple2hex((int(255 * (s / 65535)), int(255 * (s / 65535)), int(255 * (s / 65535)))))
elif key == 2:
Expand Down Expand Up @@ -473,6 +483,7 @@ class LightFrame(ttk.Labelframe):
for choice in new_choices:
self.user_dropdown["menu"].add_command(label=choice, command=_setit(self.uservar, choice))


class BulbIconList(Frame):
def __init__(self, *args):
self.window_width = 285
Expand Down
Binary file modified res/lightbulb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7eccf6b

Please sign in to comment.