diff --git a/coretk/coretk/dialogs/colorpicker.py b/coretk/coretk/dialogs/colorpicker.py new file mode 100644 index 00000000..9734468d --- /dev/null +++ b/coretk/coretk/dialogs/colorpicker.py @@ -0,0 +1,251 @@ +""" +custom color picker +""" +import logging +import tkinter as tk +from tkinter import ttk + +from coretk.dialogs.dialog import Dialog + + +class ColorPicker(Dialog): + def __init__(self, master, app, initcolor="#000000"): + super().__init__(master, app, "color picker", modal=True) + self.red_entry = None + self.blue_entry = None + self.green_entry = None + self.hex_entry = None + self.red_label = None + self.green_label = None + self.blue_label = None + self.display = None + self.color = initcolor + red, green, blue = self.get_rgb(initcolor) + self.red = tk.IntVar(value=red) + self.blue = tk.IntVar(value=blue) + self.green = tk.IntVar(value=green) + self.hex = tk.StringVar(value=initcolor) + self.red_scale = tk.IntVar(value=red) + self.green_scale = tk.IntVar(value=green) + self.blue_scale = tk.IntVar(value=blue) + self.draw() + self.set_bindings() + + def askcolor(self): + self.show() + return self.color + + def draw(self): + self.top.columnconfigure(0, weight=1) + # rgb frames + frame = ttk.Frame(self.top) + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + frame.columnconfigure(2, weight=6) + frame.columnconfigure(3, weight=2) + label = ttk.Label(frame, text="R: ") + label.grid(row=0, column=0) + self.red_entry = ttk.Entry( + frame, + width=4, + textvariable=self.red, + validate="key", + validatecommand=(self.app.validation.rgb, "%P"), + ) + self.red_entry.grid(row=0, column=1, sticky="nsew") + scale = ttk.Scale( + frame, + from_=0, + to=255, + value=0, + # length=200, + orient=tk.HORIZONTAL, + variable=self.red_scale, + command=lambda x: self.scale_callback(self.red_scale, self.red), + ) + scale.grid(row=0, column=2, sticky="nsew") + self.red_label = ttk.Label( + frame, background="#%02x%02x%02x" % (self.red.get(), 0, 0), width=5 + ) + self.red_label.grid(row=0, column=3, sticky="nsew") + frame.grid(row=0, column=0, sticky="nsew") + + frame = ttk.Frame(self.top) + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + frame.columnconfigure(2, weight=6) + frame.columnconfigure(3, weight=2) + label = ttk.Label(frame, text="G: ") + label.grid(row=0, column=0) + self.green_entry = ttk.Entry( + frame, + width=4, + textvariable=self.green, + validate="key", + validatecommand=(self.app.validation.rgb, "%P"), + ) + self.green_entry.grid(row=0, column=1, sticky="nsew") + scale = ttk.Scale( + frame, + from_=0, + to=255, + value=0, + # length=200, + orient=tk.HORIZONTAL, + variable=self.green_scale, + command=lambda x: self.scale_callback(self.green_scale, self.green), + ) + scale.grid(row=0, column=2, sticky="nsew") + self.green_label = ttk.Label( + frame, background="#%02x%02x%02x" % (0, self.green.get(), 0), width=5 + ) + self.green_label.grid(row=0, column=3, sticky="nsew") + frame.grid(row=1, column=0, sticky="nsew") + + frame = ttk.Frame(self.top) + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + frame.columnconfigure(2, weight=6) + frame.columnconfigure(3, weight=2) + label = ttk.Label(frame, text="B: ") + label.grid(row=0, column=0) + self.blue_entry = ttk.Entry( + frame, + width=4, + textvariable=self.blue, + validate="key", + validatecommand=(self.app.validation.rgb, "%P"), + ) + self.blue_entry.grid(row=0, column=1, sticky="nsew") + scale = ttk.Scale( + frame, + from_=0, + to=255, + value=0, + # length=200, + orient=tk.HORIZONTAL, + variable=self.blue_scale, + command=lambda x: self.scale_callback(self.blue_scale, self.blue), + ) + scale.grid(row=0, column=2, sticky="nsew") + self.blue_label = ttk.Label( + frame, background="#%02x%02x%02x" % (0, 0, self.blue.get()), width=5 + ) + self.blue_label.grid(row=0, column=3, sticky="nsew") + frame.grid(row=2, column=0, sticky="nsew") + + # hex code and color display + frame = ttk.Frame(self.top) + frame.columnconfigure(0, weight=1) + label = ttk.Label(frame, text="Selection: ") + label.grid(row=0, column=0, sticky="nsew") + self.hex_entry = ttk.Entry( + frame, + textvariable=self.hex, + validate="key", + validatecommand=(self.app.validation.hex, "%P"), + ) + self.hex_entry.grid(row=1, column=0, sticky="nsew") + self.display = tk.Frame(frame, background=self.color, width=100, height=100) + self.display.grid(row=2, column=0) + frame.grid(row=3, column=0, sticky="nsew") + + # button frame + frame = ttk.Frame(self.top) + frame.columnconfigure(0, weight=1) + frame.columnconfigure(1, weight=1) + button = ttk.Button(frame, text="OK", command=self.button_ok) + button.grid(row=0, column=0, sticky="nsew") + button = ttk.Button(frame, text="Cancel", command=self.destroy) + button.grid(row=0, column=1, sticky="nsew") + frame.grid(row=4, column=0, sticky="nsew") + + def set_bindings(self): + self.red_entry.bind("", lambda x: self.current_focus("rgb")) + self.green_entry.bind("", lambda x: self.current_focus("rgb")) + self.blue_entry.bind("", lambda x: self.current_focus("rgb")) + self.hex_entry.bind("", lambda x: self.current_focus("hex")) + self.red.trace_add("write", self.update_color) + self.green.trace_add("write", self.update_color) + self.blue.trace_add("write", self.update_color) + self.hex.trace_add("write", self.update_color) + + def button_ok(self): + logging.debug("not implemented") + self.color = self.hex.get() + self.destroy() + + def get_hex(self): + """ + convert current RGB values into hex color + + :rtype: str + :return: hex color + """ + red = self.red_entry.get() + blue = self.blue_entry.get() + green = self.green_entry.get() + return "#%02x%02x%02x" % (int(red), int(green), int(blue)) + + def current_focus(self, focus): + self.focus = focus + + def update_color(self, arg1=None, arg2=None, arg3=None): + if self.focus == "rgb": + red = self.red_entry.get() + blue = self.blue_entry.get() + green = self.green_entry.get() + self.set_scale(red, green, blue) + if red and blue and green: + hex_code = "#%02x%02x%02x" % (int(red), int(green), int(blue)) + self.hex.set(hex_code) + self.display.config(background=hex_code) + self.set_label(red, green, blue) + elif self.focus == "hex": + hex_code = self.hex.get() + if len(hex_code) == 4 or len(hex_code) == 7: + red, green, blue = self.get_rgb(hex_code) + else: + return + self.set_entry(red, green, blue) + self.set_scale(red, green, blue) + self.display.config(background=hex_code) + self.set_label(red, green, blue) + + def scale_callback(self, var, color_var): + color_var.set(var.get()) + self.focus = "rgb" + self.update_color() + + def set_scale(self, red, green, blue): + self.red_scale.set(red) + self.green_scale.set(green) + self.blue_scale.set(blue) + + def set_entry(self, red, green, blue): + self.red.set(red) + self.green.set(green) + self.blue.set(blue) + + def set_label(self, red, green, blue): + self.red_label.configure(background="#%02x%02x%02x" % (int(red), 0, 0)) + self.green_label.configure(background="#%02x%02x%02x" % (0, int(green), 0)) + self.blue_label.configure(background="#%02x%02x%02x" % (0, 0, int(blue))) + + def get_rgb(self, hex_code): + """ + convert a valid hex code to RGB values + + :param string hex_code: color in hex + :rtype: tuple(int, int, int) + :return: the RGB values + """ + if len(hex_code) == 4: + red = hex_code[1] + green = hex_code[2] + blue = hex_code[3] + else: + red = hex_code[1:3] + green = hex_code[3:5] + blue = hex_code[5:] + return int(red, 16), int(green, 16), int(blue, 16) diff --git a/coretk/coretk/dialogs/shapemod.py b/coretk/coretk/dialogs/shapemod.py index a6da0e3b..62fed9f9 100644 --- a/coretk/coretk/dialogs/shapemod.py +++ b/coretk/coretk/dialogs/shapemod.py @@ -2,8 +2,9 @@ shape input dialog """ import tkinter as tk -from tkinter import colorchooser, font, ttk +from tkinter import font, ttk +from coretk.dialogs.colorpicker import ColorPicker from coretk.dialogs.dialog import Dialog from coretk.graph import tags from coretk.graph.shapeutils import is_draw_shape, is_shape_text @@ -134,18 +135,21 @@ class ShapeDialog(Dialog): button.grid(row=0, column=1, sticky="ew") def choose_text_color(self): - color = colorchooser.askcolor(color="black") - self.text_color = color[1] + color_picker = ColorPicker(self, self.app, "#000000") + color = color_picker.askcolor() + self.text_color = color def choose_fill_color(self): - color = colorchooser.askcolor(color=self.fill_color) - self.fill_color = color[1] - self.fill.config(background=color[1], text=color[1]) + color_picker = ColorPicker(self, self.app, self.fill_color) + color = color_picker.askcolor() + self.fill_color = color + self.fill.config(background=color, text=color) def choose_border_color(self): - color = colorchooser.askcolor(color=self.border_color) - self.border_color = color[1] - self.border.config(background=color[1], text=color[1]) + color_picker = ColorPicker(self, self.app, self.border_color) + color = color_picker.askcolor() + self.border_color = color + self.border.config(background=color, text=color) def cancel(self): self.shape.delete() diff --git a/coretk/coretk/graph/graph.py b/coretk/coretk/graph/graph.py index acce0e28..77cc2264 100644 --- a/coretk/coretk/graph/graph.py +++ b/coretk/coretk/graph/graph.py @@ -471,6 +471,8 @@ class CanvasGraph(tk.Canvas): ) logging.info("ratio: %s", self.ratio) logging.info("offset: %s", self.offset) + self.app.statusbar.zoom.config(text="%s" % (int(self.ratio * 100)) + "%") + if self.wallpaper: self.redraw_wallpaper() diff --git a/coretk/coretk/statusbar.py b/coretk/coretk/statusbar.py index 0528bbf2..5ed6f09d 100644 --- a/coretk/coretk/statusbar.py +++ b/coretk/coretk/statusbar.py @@ -46,7 +46,11 @@ class StatusBar(ttk.Frame): self.status.grid(row=0, column=1, sticky="ew") self.zoom = ttk.Label( - self, text="ZOOM TBD", anchor=tk.CENTER, borderwidth=1, relief=tk.RIDGE + self, + text="%s" % (int(self.app.canvas.ratio * 100)) + "%", + anchor=tk.CENTER, + borderwidth=1, + relief=tk.RIDGE, ) self.zoom.grid(row=0, column=2, sticky="ew") diff --git a/coretk/coretk/validation.py b/coretk/coretk/validation.py index c7923229..955a7faf 100644 --- a/coretk/coretk/validation.py +++ b/coretk/coretk/validation.py @@ -15,6 +15,8 @@ class InputValidation: self.positive_float = None self.name = None self.ip4 = None + self.rgb = None + self.hex = None self.register() def register(self): @@ -22,6 +24,8 @@ class InputValidation: self.positive_float = self.master.register(self.check_positive_float) self.name = self.master.register(self.check_node_name) self.ip4 = self.master.register(self.check_ip4) + self.rgb = self.master.register(self.check_rbg) + self.hex = self.master.register(self.check_hex) def ip_focus_out(self, event): value = event.widget.get() @@ -100,7 +104,7 @@ class InputValidation: return False for _8bits in _32bits: if ( - (_8bits and int(_8bits) > 225) + (_8bits and int(_8bits) > 255) or len(_8bits) > 3 or (_8bits.startswith("0") and len(_8bits) > 1) ): @@ -108,3 +112,29 @@ class InputValidation: return True else: return False + + def check_rbg(self, s): + if not s: + return True + if s.startswith("0") and len(s) >= 2: + return False + try: + value = int(s) + if 0 <= value <= 255: + return True + else: + return False + except ValueError: + return False + + def check_hex(self, s): + if not s: + return True + pat = re.compile("^([#]([0-9]|[a-f])+)$|^[#]$") + if pat.match(s): + if 0 <= len(s) <= 7: + return True + else: + return False + else: + return False