diff --git a/README.md b/README.md index 711435c..50ed477 100644 --- a/README.md +++ b/README.md @@ -676,6 +676,7 @@ This function returns sequences of rainbow colors hexcodes in order. - [**v1.0.2**](https://github.com/Saadmairaj/tkmacosx/releases/tag/v1.0.2) - Fix unknown color name "systemWindowBackgroundColor" ([#20](https://github.com/Saadmairaj/tkmacosx/issues/20)) + - Partial "borderless" button option support for ttk widgets ([#19](https://github.com/Saadmairaj/tkmacosx/issues/19)) - Exclude test package in setup.py - [**v1.0.1**](https://github.com/Saadmairaj/tkmacosx/releases/tag/v1.0.1) diff --git a/test/test_tkmacosx/test_widgets.py b/test/test_tkmacosx/test_widgets.py index ad766fd..3a0b9ba 100644 --- a/test/test_tkmacosx/test_widgets.py +++ b/test/test_tkmacosx/test_widgets.py @@ -1,12 +1,12 @@ -import os import unittest import tkinter import tkmacosx +import tkinter.ttk as ttk -from test.support import findfile from test.widget_tests import (add_standard_options, noconv, - StandardOptionsTests, IntegerSizeTests, - PixelSizeTests, AbstractWidgetTest) + StandardOptionsTests, ButtonOptionsTests, + IntegerSizeTests, PixelSizeTests, + AbstractWidgetTest) def float_round(x): @@ -59,68 +59,93 @@ def test_configure_highlightthickness(self): 0, 1.3, 2.6, 6, -2, '10p') -@add_standard_options(StandardOptionsTests) -class ButtonTest(AbstractWidgetTest, PixelSizeTests, unittest.TestCase): - OPTIONS = ( - 'activebackground', 'activeforeground', 'activeimage', - 'activebitmap', 'anchor', 'background', 'bitmap', 'bordercolor', - 'borderless', 'borderwidth', 'command', 'compound', 'cursor', 'disabledbackground', - 'disabledforeground', 'font', 'focuscolor', 'focusthickness', - 'foreground', 'height', 'highlightbackground', 'highlightcolor', - 'highlightthickness', 'image', 'justify', 'overbackground', - 'overforeground', 'overrelief', 'padx', 'pady', 'relief', - 'repeatdelay', 'repeatinterval', 'state', 'takefocus', 'text', - 'textvariable', 'underline', 'width') +class AbstractButtonTest(AbstractWidgetTest, PixelSizeTests): - _conv_pixels = round + _ttk_parent = False + _ttk_parent_with_style = False + _ttk_style = "TFrame" + _type = 'normal' def create(self, **kwargs): - return tkmacosx.Button(self.root, **kwargs) + self._master = self.root + if self._ttk_parent and self._ttk_parent_with_style: + style = ttk.Style(self.root) + self._ttk_style = "Custom.TFrame" + style.configure(self._ttk_style, background="pink") + self._master = ttk.Frame(self.root, style=self._ttk_style) + self._master.pack() + elif self._ttk_parent: + self._master = ttk.Frame(self.root) + self._master.pack() + if self._type == 'normal': + return tkmacosx.Button(self._master, **kwargs) + if self._type == 'circle': + return tkmacosx.CircleButton(self._master, **kwargs) def test_configure_state(self): widget = self.create() self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal', 'pressed') - def test_configure_activebitmap(self): + def test_configure_highlightbackground(self): widget = self.create() - self.checkBitmapParam(widget, 'activebitmap') + self.checkColorParam(widget, 'highlightbackground') - def test_configure_activeimage(self): - widget = self.create() - self.checkImageParam(widget, 'activeimage') + widget['borderless'] = True + for c in ('#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey'): + widget['highlightbackground'] = c + self.assertNotEqual(widget['highlightbackground'], c) - def test_configure_bordercolor(self): - widget = self.create() - self.checkColorParam(widget, 'bordercolor') + widget['borderless'] = False + for c in ('#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey'): + widget['highlightbackground'] = c + self.assertEqual(widget['highlightbackground'], c) - def test_configure_borderless(self): - widget = self.create() - self.checkBooleanParam(widget, 'borderless') - def test_configure_disabledbackground(self): - widget = self.create() - self.checkColorParam(widget, 'disabledbackground') +@add_standard_options(StandardOptionsTests, ButtonOptionsTests) +class ButtonTest(AbstractButtonTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'activeimage', + 'activebitmap', 'anchor', 'background', 'bitmap', 'bordercolor', + 'borderless', 'borderwidth', 'command', 'compound', 'cursor', 'disabledbackground', + 'disabledforeground', 'font', 'focuscolor', 'focusthickness', + 'foreground', 'height', 'highlightbackground', 'highlightcolor', + 'highlightthickness', 'image', 'justify', 'overbackground', + 'overforeground', 'overrelief', 'padx', 'pady', 'relief', + 'repeatdelay', 'repeatinterval', 'state', 'takefocus', 'text', + 'textvariable', 'underline', 'width') - def test_configure_focuscolor(self): - widget = self.create() - self.checkColorParam(widget, 'focuscolor') + _conv_pixels = round + _ttk_parent = False + _ttk_parent_with_style = False + _type = 'normal' - def test_configure_focusthickness(self): - widget = self.create() - self.checkIntegerParam( - widget, 'focusthickness', 1, 20, 30, 0) - def test_configure_overforeground(self): - widget = self.create() - self.checkColorParam(widget, 'overforeground') +@add_standard_options(StandardOptionsTests, ButtonOptionsTests) +class Button_ttk_Test(AbstractButtonTest, unittest.TestCase): + OPTIONS = ( + 'bordercolor', 'borderless', 'highlightbackground', + 'highlightcolor', 'highlightthickness') - def test_configure_overbackground(self): - widget = self.create() - self.checkColorParam(widget, 'overbackground') + _conv_pixels = round + _ttk_parent = True + + +@add_standard_options(StandardOptionsTests, ButtonOptionsTests) +class Button_ttk_with_style_Test(AbstractButtonTest, unittest.TestCase): + OPTIONS = ( + 'bordercolor', 'borderless', 'highlightbackground', + 'highlightcolor', 'highlightthickness') + + _conv_pixels = round + _ttk_parent = True + _ttk_parent_with_style = True -class CircleButtonTest(ButtonTest): +@add_standard_options(StandardOptionsTests, ButtonOptionsTests) +class CircleButtonTest(AbstractButtonTest, unittest.TestCase): OPTIONS = ( 'activebackground', 'activeforeground', 'activeimage', 'activebitmap', 'anchor', 'background', 'bitmap', 'bordercolor', @@ -133,15 +158,36 @@ class CircleButtonTest(ButtonTest): 'textvariable', 'underline', 'width') _conv_pixels = round - - def create(self, **kwargs): - return tkmacosx.CircleButton(self.root, **kwargs) + _type = 'circle' def test_configure_radius(self): widget = self.create() self.checkIntegerParam(widget, 'radius', 402, -402, 0) +@add_standard_options(StandardOptionsTests, ButtonOptionsTests) +class CircleButton_ttk_Test(AbstractButtonTest, unittest.TestCase): + OPTIONS = ( + 'bordercolor', 'borderless', 'highlightbackground', + 'highlightcolor', 'highlightthickness') + + _conv_pixels = round + _ttk_parent = True + _type = 'circle' + + +@add_standard_options(StandardOptionsTests, ButtonOptionsTests) +class CirclButton_ttk_with_style_Test(AbstractButtonTest, unittest.TestCase): + OPTIONS = ( + 'bordercolor', 'borderless', 'highlightbackground', + 'highlightcolor', 'highlightthickness') + + _conv_pixels = round + _ttk_parent = True + _ttk_parent_with_style = True + _type = 'circle' + + @add_standard_options(StandardOptionsTests, PixelSizeTests) class ColorscaleTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( diff --git a/test/widget_tests.py b/test/widget_tests.py index 9e776a7..8d3921b 100644 --- a/test/widget_tests.py +++ b/test/widget_tests.py @@ -1,4 +1,5 @@ import tkinter +import tkinter.ttk as ttk from tkmacosx import ColorVar from tkmacosx.utils import pixels_conv from tkmacosx.basewidgets.button_base import ButtonBase @@ -523,6 +524,88 @@ def test_configure_width(self): ) +class ButtonOptionsTests: + + def test_configure_activebitmap(self): + widget = self.create() + self.checkBitmapParam(widget, 'activebitmap') + + def test_configure_activeimage(self): + widget = self.create() + self.checkImageParam(widget, 'activeimage') + + def test_configure_bordercolor(self): + widget = self.create() + self.checkColorParam(widget, 'bordercolor') + widget['borderless'] = True + for c in ('#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey'): + widget['bordercolor'] = c + self.assertNotEqual(widget['bordercolor'], c) + + widget['borderless'] = False + for c in ('#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey'): + widget['bordercolor'] = c + self.assertEqual(widget['bordercolor'], c) + + def test_configure_borderless(self): + widget = self.create() + self.checkBooleanParam(widget, 'borderless') + + if not (self._ttk_parent or self._ttk_parent_with_style): + org = self._master['bg'] + + widget['borderless'] = True + for c in ('#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey'): + if self._ttk_parent or self._ttk_parent_with_style: + print(self._ttk_style) + self.assertEqual(widget['highlightbackground'], ttk.Style( + self._master).lookup(self._ttk_style, "background")) + ttk.Style(self._master).configure(self._ttk_style, bg=c) + else: + self.assertEqual( + widget['highlightbackground'], self._master['bg']) + self._master['bg'] = c + + widget['borderless'] = False + for c in ('#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey'): + if self._ttk_parent or self._ttk_parent_with_style: + self.assertNotEqual(widget['highlightbackground'], ttk.Style( + self._master).lookup(self._ttk_style, "background")) + ttk.Style(self._master).configure(self._ttk_style, bg=c) + else: + self.assertNotEqual( + widget['highlightbackground'], self._master['bg']) + self._master['bg'] = c + + if not (self._ttk_parent or self._ttk_parent_with_style): + self._master['bg'] = org + + def test_configure_disabledbackground(self): + widget = self.create() + self.checkColorParam(widget, 'disabledbackground') + + def test_configure_focuscolor(self): + widget = self.create() + self.checkColorParam(widget, 'focuscolor') + + def test_configure_focusthickness(self): + widget = self.create() + self.checkIntegerParam( + widget, 'focusthickness', 1, 20, 30, 0) + + def test_configure_overforeground(self): + widget = self.create() + self.checkColorParam(widget, 'overforeground') + + def test_configure_overbackground(self): + widget = self.create() + self.checkColorParam(widget, 'overbackground') + + def add_standard_options(*source_classes): # This decorator adds test_configure_xxx methods from source classes for # every xxx option in the OPTIONS class attribute if they are not defined diff --git a/tkmacosx/basewidgets/button_base.py b/tkmacosx/basewidgets/button_base.py index e2b4fe0..aaa64c6 100644 --- a/tkmacosx/basewidgets/button_base.py +++ b/tkmacosx/basewidgets/button_base.py @@ -13,6 +13,7 @@ # limitations under the License. import tkinter +import tkinter.ttk as ttk from tkmacosx.utils import (SYSTEM_DEFAULT, STDOUT_WARNING, _cnfmerge, _bind, _Canvas, check_param, _info_button, _on_press_color, @@ -136,25 +137,32 @@ def bitmap(self, kw): def borderless(self, kw): def _get_master_bg(): """Internal function.""" - try: - return self.master['bg'] - except tkinter.TclError: - if not _warning_msg_shown[0] and STDOUT_WARNING: - print('WARNING: "borderless" option of ' - 'tkmacosx Button doesn\'t work with ttk widgets. ' - 'Bordercolor/highlightbackground can be set manually ' - 'with "highlightbackground" or "bordercolor" options.\n') - _warning_msg_shown[0] = True - return self.cnf.get('bordercolor', self.cnf['highlightbackground']) + ttk_widget_types = tuple( + getattr(ttk, i) for i in ttk.__all__ if isinstance(getattr(ttk, i), type)) + if isinstance(self.master, ttk_widget_types): + try: + style = self.master['style'] or 'T' + self.master.__class__.__name__ + ttk_bg = ttk.Style(self.master).lookup(style, "background") + return ttk_bg + except tkinter.TclError: + if not _warning_msg_shown[0] and STDOUT_WARNING: + print('WARNING: "borderless" option is partially supported with ttk widgets. ' + 'Bordercolor/highlightbackground can be set manually ' + 'with "highlightbackground" or "bordercolor" options.\n') + _warning_msg_shown[0] = True + return self.cnf.get('bordercolor', self.cnf['highlightbackground']) + return self.master['bg'] _opt = {} if bool(kw.get('borderless')) or self.cnf.get('borderless'): if not check_function_equality(self.master.config, self._get_functions('borderless', kw)): self.master.config = self.master.configure = self._get_functions( 'borderless', kw) - self.cnf['highlightbackground'] = self.cnf['bordercolor'] = _get_master_bg() - _opt[1] = [('itemconfigure', '_bd_color'), {'outline': _get_master_bg()}, None] - _opt[2] = ['configure', {'highlightbackground': _get_master_bg()}, None] + master_bg = _get_master_bg() + self.cnf['highlightbackground'] = self.cnf['bordercolor'] = master_bg + _opt[1] = [('itemconfigure', '_bd_color'), {'outline': master_bg}, None] + _opt[2] = ['configure', {'highlightbackground': master_bg}, None] + elif not bool(kw.get('borderless', True)) or not self.cnf.get('borderless'): if self.cnf.get('bordercolor') == _get_master_bg(): self.cnf.pop('bordercolor', None)