diff --git a/tests/test_linklabel.py b/tests/test_linklabel.py index 35c79e41..11295321 100644 --- a/tests/test_linklabel.py +++ b/tests/test_linklabel.py @@ -1,30 +1,31 @@ # Copyright (c) RedFantom 2017 # For license see LICENSE -from ttkwidgets import LinkLabel -from tests import BaseWidgetTest import tkinter as tk +from tests import BaseWidgetTest +from ttkwidgets import LinkLabel + class TestLinkLabel(BaseWidgetTest): def test_linklabel_init(self): label = LinkLabel(self.window, link="www.google.com", text="Visit Google") label.pack() - self.window.update() def test_linklabel_events(self): label = LinkLabel(self.window, link="www.google.com", text="Visit Google") label.pack() + self.window.update() label._on_enter() self.window.update() label._on_leave() self.window.update() label.open_link() - self.window.update() def test_linklabel_config(self): label = LinkLabel(self.window, link="www.google.com", text="Visit Google") label.pack() + self.window.update() label.keys() self.window.update() @@ -36,12 +37,16 @@ def test_linklabel_config(self): self.window.update() label["clicked_color"] = "purple" self.window.update() + label.config(cursor="hand1") + self.window.update() + assert str(label.cget("cursor")) == "hand1" def test_linklabel_cget(self): label = LinkLabel(self.window, link="www.google.com", text="Visit Google") label.pack() - assert label.cget("hover_color") == label._hover_color + assert label.cget("link") == label._link assert label.cget("normal_color") == label._normal_color + assert label.cget("hover_color") == label._hover_color assert label.cget("clicked_color") == label._clicked_color assert label.cget("text") == "Visit Google" diff --git a/tests/test_toggledframe.py b/tests/test_toggledframe.py index da54915c..9cc5e67f 100644 --- a/tests/test_toggledframe.py +++ b/tests/test_toggledframe.py @@ -1,26 +1,36 @@ # Copyright (c) RedFantom 2017 # For license see LICENSE -from ttkwidgets.frames import ToggledFrame -from tests import BaseWidgetTest import tkinter as tk +from tests import BaseWidgetTest +from ttkwidgets.frames import ToggledFrame + class TestToggledFrame(BaseWidgetTest): def test_toggledframe_init(self): frame = ToggledFrame(self.window) frame.pack() - self.window.update() def test_toggledframe_open(self): frame = ToggledFrame(self.window) frame.pack() + self.window.update() frame.toggle() - self.assertTrue(frame._open) + assert frame.opened def test_toggledframe_open_close(self): frame = ToggledFrame(self.window) frame.pack() + self.window.update() frame.toggle() - self.assertTrue(frame._open) - frame.toggle() - self.assertFalse(frame._open) + self.window.update() + assert frame.opened + frame.close() + self.window.update() + assert not frame.opened + frame.open() + self.window.update() + assert frame.opened + frame._button.invoke() + self.window.update() + assert not frame.opened diff --git a/ttkwidgets/frames/toggledframe.py b/ttkwidgets/frames/toggledframe.py index cd475b69..ce56cc05 100644 --- a/ttkwidgets/frames/toggledframe.py +++ b/ttkwidgets/frames/toggledframe.py @@ -2,13 +2,18 @@ Author: RedFantom License: GNU GPLv3 Source: This repository + +Improved by rdbende """ + import tkinter as tk +from pathlib import Path from tkinter import ttk -import os -from PIL import Image, ImageTk + from ttkwidgets.utilities import get_assets_directory +assets_dir = Path(get_assets_directory()) + class ToggledFrame(ttk.Frame): """ @@ -17,44 +22,98 @@ class ToggledFrame(ttk.Frame): :ivar interior: :class:`ttk.Frame` in which to put the widgets to be toggled with any geometry manager. """ - def __init__(self, master=None, text="", width=20, compound=tk.LEFT, **kwargs): + def __init__(self, master=None, *, text=None, cursor="arrow", width=20, **kwargs): """ Create a ToggledFrame. :param master: master widget :type master: widget - :param text: text to display next to the toggle arrow + :param text: text to in the header of the ToggledFrame :type text: str :param width: width of the closed ToggledFrame (in characters) :type width: int - :param compound: "center", "none", "top", "bottom", "right" or "left": - position of the toggle arrow compared to the text - :type compound: str + :param cursor: cursor that appears on the ToggledFrame's button + :type cursor: str :param kwargs: keyword arguments passed on to the :class:`ttk.Frame` initializer """ + self._open = tk.BooleanVar(value=False) + ttk.Frame.__init__(self, master, **kwargs) - self._open = False - self.__checkbutton_var = tk.BooleanVar() - self._open_image = ImageTk.PhotoImage(Image.open(os.path.join(get_assets_directory(), "open.png"))) - self._closed_image = ImageTk.PhotoImage(Image.open(os.path.join(get_assets_directory(), "closed.png"))) - self._checkbutton = ttk.Checkbutton(self, style="Toolbutton", command=self.toggle, - variable=self.__checkbutton_var, text=text, compound=compound, - image=self._closed_image, width=width) - self.interior = ttk.Frame(self, relief=tk.SUNKEN) - self._grid_widgets() - - def _grid_widgets(self): - self._checkbutton.grid(row=0, column=0, sticky="we") - def toggle(self): - """Toggle :obj:`ToggledFrame.interior` opened or closed.""" - if self._open: - self._open = False - self.__checkbutton_var.set(False) + self.interior = ttk.Frame(self) + + self._open_image = tk.PhotoImage(file=assets_dir / "open.png") + self._closed_image = tk.PhotoImage(file=assets_dir / "closed.png") + + self._button = ttk.Checkbutton( + self, + style="Toolbutton", + compound="right", + cursor=cursor, + image=self._closed_image, + text=text, + variable=self._open, + command=self._toggle_when_clicked, + width=width, + ) + self._button.grid(row=0, column=0, sticky="ew") + + def __getitem__(self, key): + return self.cget(key) + + def __setitem__(self, key, value): + self.configure(**{key: value}) + + def _toggle_when_clicked(self): + # when clicking the checkbutton it inverts its variable, so we can't simply use self.toggle + if self._open.get(): + self.interior.grid(row=1, column=0, sticky="nswe") + self._button.config(image=self._open_image) + self.event_generate("<>") + else: self.interior.grid_forget() - self._checkbutton.config(image=self._closed_image) + self._button.config(image=self._closed_image) + self.event_generate("<>") + + def open(self): + self.interior.grid(row=1, column=0, sticky="nswe") + self._open.set(True) + self._button.config(image=self._open_image) + self.event_generate("<>") + + def close(self): + self.interior.grid_forget() + self._open.set(False) + self._button.config(image=self._closed_image) + self.event_generate("<>") + + def toggle(self): + if self._open.get(): + self.close() else: - self._open = True - self.__checkbutton_var.set(True) - self.interior.grid(row=1, column=0, sticky="nswe") - self._checkbutton.config(image=self._open_image) + self.open() + + @property + def opened(self): + return self._open.get() + + def configure(self, **kwargs): + """Configure resources of the widget""" + button_options = { + key: kwargs.pop(key) for key in ("cursor", "text", "width") if key in kwargs + } + self._button.configure(**button_options) + ttk.Frame.configure(self, **kwargs) + + config = configure + + def cget(self, key): + """Return the resource value for a KEY given as string""" + if key in {"cursor", "text", "width"}: + return self._button.cget(key) + else: + return ttk.Frame.cget(self, key) + + def keys(self): + """Return a list of all resource names of this widget""" + return sorted(ttk.Frame.keys(self) + ["text"]) diff --git a/ttkwidgets/itemscanvas.py b/ttkwidgets/itemscanvas.py index b79ca11f..d7e5a5ce 100644 --- a/ttkwidgets/itemscanvas.py +++ b/ttkwidgets/itemscanvas.py @@ -108,7 +108,7 @@ def left_motion(self, event): item = results[0] rectangle = self.items[item] - self.config(cursor="exchange") + self.config(cursor="fleur") self.canvas.itemconfigure(item, fill="blue") xc, yc = self.canvas.canvasx(event.x), self.canvas.canvasy(event.y) dx, dy = xc - self.current_coords[0], yc - self.current_coords[1] diff --git a/ttkwidgets/linklabel.py b/ttkwidgets/linklabel.py index 31e837ec..1ff8a921 100644 --- a/ttkwidgets/linklabel.py +++ b/ttkwidgets/linklabel.py @@ -2,41 +2,51 @@ Author: RedFantom License: GNU GPLv3 Source: This repository + +Edited by rdbende: change default widget colors, use native cursors by default, add virtual event """ # Based on an idea by Nelson Brochado (https://www.github.com/nbrol/tkinter-kit) +# Available from fork: https://www.github.com/RedFantom/tkinter-kit import tkinter as tk -from tkinter import ttk import webbrowser +from tkinter import ttk class LinkLabel(ttk.Label): """ - A :class:`ttk.Label` that can be clicked to open a link with a default blue color, a purple color when clicked and a bright - blue color when hovering over the Label. + A :class:`ttk.Label` that can be clicked to open a link with a default blue color, + a purple when clicked and dark blue when hovering over the Label. """ + def __init__(self, master=None, **kwargs): """ Create a LinkLabel. - + :param master: master widget :param link: link to be opened :type link: str - :param normal_color: text color when widget is created + :param normal_color: text color when the widget is in neutral state :type normal_color: str :param hover_color: text color when hovering over the widget :type hover_color: str - :param clicked_color: text color when link is clicked + :param clicked_color: text color when the widget has been clicked :type clicked_color: str :param kwargs: options to be passed on to the :class:`ttk.Label` initializer """ - self._cursor = kwargs.pop("cursor", "hand1") self._link = kwargs.pop("link", "") - self._normal_color = kwargs.pop("normal_color", "#0563c1") - self._hover_color = kwargs.pop("hover_color", "#057bc1") - self._clicked_color = kwargs.pop("clicked_color", "#954f72") + self._normal_color = kwargs.pop("normal_color", "#005fff") + self._hover_color = kwargs.pop("hover_color", "#000fff") + self._clicked_color = kwargs.pop("clicked_color", "#6600a6") + + is_mac = (master or tk._default_root).tk.call("tk", "windowingsystem") == "aqua" + kwargs.setdefault("cursor", "pointinghand" if is_mac else "hand2") + ttk.Label.__init__(self, master, **kwargs) - self.config(foreground=self._normal_color) - self.__clicked = False + + if "disabled" not in self.state(): + self.configure(foreground=self._normal_color) + + self._clicked = False self.bind("", self.open_link) self.bind("", self._on_enter) self.bind("", self._on_leave) @@ -49,27 +59,47 @@ def __setitem__(self, key, value): def _on_enter(self, *args): """Set the text color to the hover color.""" - self.config(foreground=self._hover_color, cursor=self._cursor) + if self._clicked: + self.config(foreground=self._clicked_color) + else: + self.config(foreground=self._hover_color) def _on_leave(self, *args): """Set the text color to either the normal color when not clicked or the clicked color when clicked.""" - if self.__clicked: + if self._clicked: self.config(foreground=self._clicked_color) else: self.config(foreground=self._normal_color) - self.config(cursor="") def reset(self): """Reset Label to unclicked status if previously clicked.""" - self.__clicked = False + self._clicked = False self._on_leave() def open_link(self, *args): """Open the link in the web browser.""" - if "disabled" not in self.state(): - webbrowser.open(self._link) - self.__clicked = True - self._on_leave() + if "disabled" in self.state(): + return + + webbrowser.open(self._link) + self._clicked = True + self._on_leave() + self.event_generate("<>") + + def configure(self, **kwargs): + """ + Configure resources of the widget. + + To get the list of options for this widget, call the method :meth:`~LinkLabel.keys`. + See :meth:`~LinkLabel.__init__` for a description of the widget specific option. + """ + self._link = kwargs.pop("link", self._link) + self._normal_color = kwargs.pop("normal_color", self._normal_color) + self._hover_color = kwargs.pop("hover_color", self._hover_color) + self._clicked_color = kwargs.pop("clicked_color", self._clicked_color) + ttk.Label.configure(self, **kwargs) + + config = configure def cget(self, key): """ @@ -92,23 +122,9 @@ def cget(self, key): else: return ttk.Label.cget(self, key) - def configure(self, **kwargs): - """ - Configure resources of the widget. - - To get the list of options for this widget, call the method :meth:`~LinkLabel.keys`. - See :meth:`~LinkLabel.__init__` for a description of the widget specific option. - """ - self._link = kwargs.pop("link", self._link) - self._hover_color = kwargs.pop("hover_color", self._hover_color) - self._normal_color = kwargs.pop("normal_color", self._normal_color) - self._clicked_color = kwargs.pop("clicked_color", self._clicked_color) - ttk.Label.configure(self, **kwargs) - self._on_leave() - def keys(self): """Return a list of all resource names of this widget.""" keys = ttk.Label.keys(self) keys.extend(["link", "normal_color", "hover_color", "clicked_color"]) + keys.sort() return keys -