kvex.widgets.theme
Home of ThemeManager
and ThemeSelector
.
1"""Home of `ThemeManager` and `ThemeSelector`.""" 2 3from .. import kivy as kv 4from ..colors import THEME_NAMES 5from .label import XLabel 6from .layouts import XBox, XDBox, XAnchor, XFrame 7from .scroll import XScroll 8from .spinner import XSpinner, XSpinnerOption 9 10 11class XThemeSelector(XSpinner): 12 """A button for selecting the app's theme.""" 13 14 def __init__(self, **kwargs): 15 """Initialize the class.""" 16 kwargs = dict(option_cls=self._spinner_factory) | kwargs 17 super().__init__( 18 text=self.app.theme_name, 19 values=THEME_NAMES, 20 text_autoupdate=True, 21 **kwargs, 22 ) 23 self.set_size(y="50dp") 24 self.bind(text=self._on_select) 25 self.app.bind(on_theme=self._on_app_theme) 26 27 def _on_app_theme(self, *args): 28 self.text = self.app.theme_name 29 30 def _on_select(self, *args): 31 self.app.set_theme(self.text) 32 33 def _spinner_factory(self, *args, **kwargs): 34 return XSpinnerOption(*args, subtheme_name="secondary", **kwargs) 35 36 37class XThemePreview(kv.FocusBehavior, XBox): 38 """Widget to preview the current theme.""" 39 40 def __init__(self): 41 """Initialize the class.""" 42 self._palette_box = XBox() 43 self._title_label = XLabel( 44 font_size="24sp", 45 color=(0, 0, 0), 46 outline_color=(1, 1, 1), 47 outline_width=2, 48 valign="top", 49 enable_theming=False, 50 ) 51 super().__init__(orientation="vertical") 52 self._make_widgets() 53 self.app.bind(on_theme=self._refresh_palette_box) 54 55 def keyboard_on_key_down(self, w, keycode, text, modifiers): 56 """Switch theme using arrow keys or [shift] tab.""" 57 code, key = keycode 58 shifted = "shift" in modifiers 59 tabbed = key == "tab" 60 cindex = THEME_NAMES.index(self.app.theme_name) 61 if key == "right" or (tabbed and not shifted): 62 cindex += 1 63 elif key == "left" or (tabbed and shifted): 64 cindex -= 1 65 elif key.isdigit(): 66 cindex = int(key) - 1 67 else: 68 return True 69 cindex = cindex % len(THEME_NAMES) 70 self.app.set_theme(THEME_NAMES[cindex]) 71 return True 72 73 def _make_widgets(self): 74 # Palette 75 self._palette_frame = XAnchor() 76 self._palette_frame.add_widgets(self._palette_box, self._title_label) 77 self._palette_frame.set_size(y="50sp") 78 self._refresh_palette_box() 79 # Subthemes 80 primary = XSubThemePreview(subtheme_name="primary") 81 primary.set_size(hy=1.5) 82 secondary = XSubThemePreview(subtheme_name="secondary") 83 secondary.set_size(hx=3) 84 accent = XSubThemePreview(subtheme_name="accent") 85 right_frame = XBox() 86 right_frame.add_widgets(secondary, accent) 87 outer_frame = XBox(orientation="vertical") 88 outer_frame.add_widgets(primary, right_frame) 89 self.add_widgets( 90 self._palette_frame, 91 outer_frame, 92 ) 93 94 def _refresh_palette_box(self, *args): 95 self._title_label.text = self.app.theme_name.capitalize() 96 self._palette_box.clear_widgets() 97 for c in self.app.theme.palette: 98 pb = XLabel( 99 text=c.hex.upper(), 100 outline_color=(0, 0, 0), 101 outline_width=2, 102 valign="bottom", 103 enable_theming=False, 104 ) 105 pb.make_bg(c) 106 self._palette_box.add_widget(pb) 107 108 109class XSubThemePreview(XFrame): 110 """Widget to preview a SubTheme.""" 111 112 def __init__(self, *args, **kwargs): 113 """Initialize the class.""" 114 super().__init__(*args, **kwargs) 115 with self.app.subtheme_context(self.subtheme_name): 116 self._make_widgets() 117 118 def _make_widgets(self): 119 self._title_label = XLabel(font_size="24sp", shorten=True, shorten_from="right") 120 self._title_label.set_size(y="32sp") 121 self._br = XAnchor() 122 self._br.set_size(y="8dp") 123 self._detail_label = XLabel( 124 fixed_width=True, 125 padding=(10, 10), 126 halign="left", 127 valign="top", 128 ) 129 self._br2 = XAnchor() 130 self._br2.set_size(hx=0.5, y="4dp") 131 br2_wrapped = XAnchor.wrap(self._br2) 132 br2_wrapped.set_size(y="10dp") 133 self._lorem_label = XLabel( 134 text=LOREM_IPSUM, 135 fixed_width=True, 136 padding=(10, 10), 137 halign="left", 138 valign="top", 139 ) 140 scroll_view = XDBox() 141 scroll_view.add_widgets( 142 self._detail_label, 143 br2_wrapped, 144 self._lorem_label, 145 ) 146 self._label_scroll = XScroll(view=scroll_view) 147 main_frame = XBox(orientation="vertical", padding="10sp") 148 main_frame.add_widgets( 149 self._title_label, 150 self._br, 151 self._label_scroll, 152 ) 153 self.add_widget(main_frame) 154 155 def on_subtheme(self, subtheme): 156 """Apply colors.""" 157 super().on_subtheme(subtheme) 158 self._br.make_bg(subtheme.accent1) 159 self._br2.make_bg(subtheme.accent2) 160 self._title_label.text = self.subtheme_name.capitalize() 161 self._detail_label.text = self._get_detail_text(subtheme) 162 163 def _get_detail_text(self, subtheme): 164 bullet = subtheme.accent2.markup("•") 165 fg2m = subtheme.fg2.markup 166 hexes = "\n".join(( 167 f"{bullet} {color_name} color {fg2m(color.hex.upper())}" 168 for color_name, color in subtheme._asdict().items() 169 )) 170 name = self.app.theme_name.capitalize() 171 return f"[size=20sp][u]{name} theme[/u][/size]\n{hexes}" 172 173 174LOREM_IPSUM = ( 175 "Non eram nescius, Brute, cum, quae summis ingeniis exquisitaque doctrina" 176 " philosophi Graeco sermone tractavissent, ea Latinis litteris mandaremus, fore" 177 " ut hic noster labor in varias reprehensiones incurreret.\n\nNam quibusdam, et iis" 178 " quidem non admodum indoctis, totum hoc displicet philosophari. quidam autem non" 179 " tam id reprehendunt, si remissius agatur, sed tantum studium tamque multam" 180 " operam ponendam in eo non arbitrantur. erunt etiam, et ii quidem eruditi" 181 " Graecis litteris, contemnentes Latinas, qui se dicant in Graecis legendis" 182 " operam malle consumere. postremo aliquos futuros suspicor, qui me ad alias" 183 " litteras vocent, genus hoc scribendi, etsi sit elegans, personae tamen et" 184 " dignitatis esse negent.\n\nContra quos omnis dicendum breviter existimo. Quamquam" 185 " philosophiae quidem vituperatoribus satis responsum est eo libro, quo a nobis" 186 " philosophia defensa et collaudata est, cum esset accusata et vituperata ab" 187 " Hortensio. qui liber cum et tibi probatus videretur et iis, quos ego posse" 188 " iudicare arbitrarer, plura suscepi veritus ne movere hominum studia viderer," 189 " retinere non posse.\n\nQui autem, si maxime hoc placeat, moderatius tamen id" 190 " volunt fieri, difficilem quandam temperantiam postulant in eo, quod semel" 191 " admissum coerceri reprimique non potest, ut propemodum iustioribus utamur" 192 " illis, qui omnino avocent a philosophia, quam his, qui rebus infinitis modum" 193 " constituant in reque eo meliore, quo maior sit, mediocritatem desiderent.\n\nSive" 194 " enim ad sapientiam perveniri potest, non paranda nobis solum ea, sed fruenda" 195 " etiam [sapientia] est; sive hoc difficile est, tamen nec modus est ullus" 196 " investigandi veri, nisi inveneris, et quaerendi defatigatio turpis est, cum id," 197 " quod quaeritur, sit pulcherrimum. etenim si delectamur, cum scribimus, quis est" 198 " tam invidus, qui ab eo nos abducat?\n\nSin laboramus, quis est, qui alienae modum" 199 " statuat industriae? nam ut Terentianus Chremes non inhumanus, qui novum vicinum" 200 " non vult 'fodere aut arare aut aliquid ferre denique' -- non enim illum ab" 201 " industria, sed ab inliberali labore deterret -- sic isti curiosi, quos" 202 " offendit noster minime nobis iniucundus labor. Iis igitur est difficilius satis" 203 " facere, qui se Latina scripta dicunt contemnere.\n\nIn quibus hoc primum est in" 204 " quo admirer, cur in gravissimis rebus non delectet eos sermo patrius, cum idem" 205 " fabellas Latinas ad verbum e Graecis expressas non inviti legant. Quis enim tam" 206 " inimicus paene nomini Romano est, qui Ennii Medeam aut Antiopam Pacuvii spernat" 207 " aut reiciat, quod se isdem Euripidis fabulis delectari dicat, Latinas litteras" 208 " oderit?" 209) 210 211 212__all__ = ( 213 "XThemeSelector", 214 "XThemePreview", 215)
12class XThemeSelector(XSpinner): 13 """A button for selecting the app's theme.""" 14 15 def __init__(self, **kwargs): 16 """Initialize the class.""" 17 kwargs = dict(option_cls=self._spinner_factory) | kwargs 18 super().__init__( 19 text=self.app.theme_name, 20 values=THEME_NAMES, 21 text_autoupdate=True, 22 **kwargs, 23 ) 24 self.set_size(y="50dp") 25 self.bind(text=self._on_select) 26 self.app.bind(on_theme=self._on_app_theme) 27 28 def _on_app_theme(self, *args): 29 self.text = self.app.theme_name 30 31 def _on_select(self, *args): 32 self.app.set_theme(self.text) 33 34 def _spinner_factory(self, *args, **kwargs): 35 return XSpinnerOption(*args, subtheme_name="secondary", **kwargs)
A button for selecting the app's theme.
XThemeSelector(**kwargs)
15 def __init__(self, **kwargs): 16 """Initialize the class.""" 17 kwargs = dict(option_cls=self._spinner_factory) | kwargs 18 super().__init__( 19 text=self.app.theme_name, 20 values=THEME_NAMES, 21 text_autoupdate=True, 22 **kwargs, 23 ) 24 self.set_size(y="50dp") 25 self.bind(text=self._on_select) 26 self.app.bind(on_theme=self._on_app_theme)
Initialize the class.
Inherited Members
- kivy.uix.spinner.Spinner
- values
- text_autoupdate
- option_cls
- dropdown_cls
- is_open
- sync_height
- on_is_open
- kivy.uix.button.Button
- background_color
- background_normal
- background_down
- background_disabled_normal
- background_disabled_down
- border
- kivy.uix.behaviors.button.ButtonBehavior
- state
- last_touch
- min_state_time
- always_release
- cancel_event
- on_touch_down
- on_touch_move
- on_touch_up
- on_press
- on_release
- trigger_action
- kivy.uix.label.Label
- texture_update
- on_ref_press
- disabled_color
- text
- text_size
- base_direction
- text_language
- font_context
- font_family
- font_name
- font_size
- font_features
- line_height
- bold
- italic
- underline
- strikethrough
- padding_x
- padding_y
- padding
- halign
- valign
- color
- outline_width
- outline_color
- disabled_outline_color
- texture
- texture_size
- mipmap
- shorten
- shorten_from
- is_shortened
- split_str
- ellipsis_options
- unicode_errors
- markup
- refs
- anchors
- max_lines
- strip
- font_hinting
- font_kerning
- font_blended
- kivy.uix.widget.Widget
- proxy_ref
- apply_class_lang_rules
- collide_point
- collide_widget
- on_motion
- on_kv_post
- add_widget
- remove_widget
- clear_widgets
- register_for_motion_event
- unregister_for_motion_event
- export_to_png
- export_as_image
- get_root_window
- get_parent_window
- walk
- walk_reverse
- to_widget
- to_window
- to_parent
- to_local
- get_window_matrix
- x
- y
- width
- height
- pos
- size
- get_right
- set_right
- right
- get_top
- set_top
- top
- get_center_x
- set_center_x
- center_x
- get_center_y
- set_center_y
- center_y
- center
- cls
- children
- parent
- size_hint_x
- size_hint_y
- size_hint
- pos_hint
- size_hint_min_x
- size_hint_min_y
- size_hint_min
- size_hint_max_x
- size_hint_max_y
- size_hint_max
- ids
- opacity
- on_opacity
- canvas
- get_disabled
- set_disabled
- inc_disabled
- dec_disabled
- disabled
- motion_filter
- kivy._event.EventDispatcher
- register_event_type
- unregister_event_types
- unregister_event_type
- is_event_type
- bind
- unbind
- fbind
- funbind
- unbind_uid
- get_property_observers
- events
- dispatch
- dispatch_generic
- dispatch_children
- setter
- getter
- property
- properties
- create_property
- apply_property
38class XThemePreview(kv.FocusBehavior, XBox): 39 """Widget to preview the current theme.""" 40 41 def __init__(self): 42 """Initialize the class.""" 43 self._palette_box = XBox() 44 self._title_label = XLabel( 45 font_size="24sp", 46 color=(0, 0, 0), 47 outline_color=(1, 1, 1), 48 outline_width=2, 49 valign="top", 50 enable_theming=False, 51 ) 52 super().__init__(orientation="vertical") 53 self._make_widgets() 54 self.app.bind(on_theme=self._refresh_palette_box) 55 56 def keyboard_on_key_down(self, w, keycode, text, modifiers): 57 """Switch theme using arrow keys or [shift] tab.""" 58 code, key = keycode 59 shifted = "shift" in modifiers 60 tabbed = key == "tab" 61 cindex = THEME_NAMES.index(self.app.theme_name) 62 if key == "right" or (tabbed and not shifted): 63 cindex += 1 64 elif key == "left" or (tabbed and shifted): 65 cindex -= 1 66 elif key.isdigit(): 67 cindex = int(key) - 1 68 else: 69 return True 70 cindex = cindex % len(THEME_NAMES) 71 self.app.set_theme(THEME_NAMES[cindex]) 72 return True 73 74 def _make_widgets(self): 75 # Palette 76 self._palette_frame = XAnchor() 77 self._palette_frame.add_widgets(self._palette_box, self._title_label) 78 self._palette_frame.set_size(y="50sp") 79 self._refresh_palette_box() 80 # Subthemes 81 primary = XSubThemePreview(subtheme_name="primary") 82 primary.set_size(hy=1.5) 83 secondary = XSubThemePreview(subtheme_name="secondary") 84 secondary.set_size(hx=3) 85 accent = XSubThemePreview(subtheme_name="accent") 86 right_frame = XBox() 87 right_frame.add_widgets(secondary, accent) 88 outer_frame = XBox(orientation="vertical") 89 outer_frame.add_widgets(primary, right_frame) 90 self.add_widgets( 91 self._palette_frame, 92 outer_frame, 93 ) 94 95 def _refresh_palette_box(self, *args): 96 self._title_label.text = self.app.theme_name.capitalize() 97 self._palette_box.clear_widgets() 98 for c in self.app.theme.palette: 99 pb = XLabel( 100 text=c.hex.upper(), 101 outline_color=(0, 0, 0), 102 outline_width=2, 103 valign="bottom", 104 enable_theming=False, 105 ) 106 pb.make_bg(c) 107 self._palette_box.add_widget(pb)
Widget to preview the current theme.
XThemePreview()
41 def __init__(self): 42 """Initialize the class.""" 43 self._palette_box = XBox() 44 self._title_label = XLabel( 45 font_size="24sp", 46 color=(0, 0, 0), 47 outline_color=(1, 1, 1), 48 outline_width=2, 49 valign="top", 50 enable_theming=False, 51 ) 52 super().__init__(orientation="vertical") 53 self._make_widgets() 54 self.app.bind(on_theme=self._refresh_palette_box)
Initialize the class.
def
keyboard_on_key_down(self, w, keycode, text, modifiers):
56 def keyboard_on_key_down(self, w, keycode, text, modifiers): 57 """Switch theme using arrow keys or [shift] tab.""" 58 code, key = keycode 59 shifted = "shift" in modifiers 60 tabbed = key == "tab" 61 cindex = THEME_NAMES.index(self.app.theme_name) 62 if key == "right" or (tabbed and not shifted): 63 cindex += 1 64 elif key == "left" or (tabbed and shifted): 65 cindex -= 1 66 elif key.isdigit(): 67 cindex = int(key) - 1 68 else: 69 return True 70 cindex = cindex % len(THEME_NAMES) 71 self.app.set_theme(THEME_NAMES[cindex]) 72 return True
Switch theme using arrow keys or [shift] tab.
Inherited Members
- kivy.uix.behaviors.focus.FocusBehavior
- ignored_touch
- keyboard
- is_focusable
- focus
- focused
- keyboard_suggestions
- focus_next
- focus_previous
- keyboard_mode
- input_type
- unfocus_on_touch
- keyboard_on_textinput
- on_touch_down
- get_focus_next
- get_focus_previous
- keyboard_on_key_up
- show_keyboard
- hide_keyboard
- kivy.uix.boxlayout.BoxLayout
- spacing
- padding
- orientation
- minimum_width
- minimum_height
- minimum_size
- do_layout
- add_widget
- remove_widget
- kivy.uix.layout.Layout
- layout_hint_with_bounds
- kivy.uix.widget.Widget
- proxy_ref
- apply_class_lang_rules
- collide_point
- collide_widget
- on_motion
- on_touch_move
- on_touch_up
- on_kv_post
- clear_widgets
- register_for_motion_event
- unregister_for_motion_event
- export_to_png
- export_as_image
- get_root_window
- get_parent_window
- walk
- walk_reverse
- to_widget
- to_window
- to_parent
- to_local
- get_window_matrix
- x
- y
- width
- height
- pos
- size
- get_right
- set_right
- right
- get_top
- set_top
- top
- get_center_x
- set_center_x
- center_x
- get_center_y
- set_center_y
- center_y
- center
- cls
- children
- parent
- size_hint_x
- size_hint_y
- size_hint
- pos_hint
- size_hint_min_x
- size_hint_min_y
- size_hint_min
- size_hint_max_x
- size_hint_max_y
- size_hint_max
- ids
- opacity
- on_opacity
- canvas
- get_disabled
- set_disabled
- inc_disabled
- dec_disabled
- disabled
- motion_filter
- kivy._event.EventDispatcher
- register_event_type
- unregister_event_types
- unregister_event_type
- is_event_type
- bind
- unbind
- fbind
- funbind
- unbind_uid
- get_property_observers
- events
- dispatch
- dispatch_generic
- dispatch_children
- setter
- getter
- property
- properties
- create_property
- apply_property