Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

UIX: TabbedPanel introduce TabbedPanelItem to make usage easier. #727

Closed
wants to merge 2 commits into from

2 participants

@akshayaurora
Owner

introduce TabbedPanelItem to make usage easier.

@tshirtman
Owner

Seems good to me, makes things easier while preserving flexibility if needed :)

@akshayaurora

seems, there are some small issues still left to fix in this. I'll re-open this request once they are solved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 8, 2012
  1. @akshayaurora
Commits on Oct 12, 2012
  1. @akshayaurora

    UIX:TabbedPanel introduce `do_default_tab` booolean

    akshayaurora authored
    Improve handeling of TabbedPanelItem
This page is out of date. Refresh to see the latest.
View
62 examples/widgets/tabbed_panel_showcase.py
@@ -100,12 +100,9 @@ class CloseableHeader(TabbedPanelHeader):
size_hint: None, 1
orientation: 'vertical'
width: 22
- Widget:
- Button:
- border: 0,0,0,0
- background_normal: 'tools/theming/defaulttheme/close.png'
- on_release: root.panel.remove_widget(root)
- Widget:
+ Image:
+ source: 'tools/theming/defaulttheme/close.png'
+ on_touch_down: if self.collide_point(*args[1].pos) : root.panel.remove_widget(root)
<PanelRight>
@@ -170,29 +167,23 @@ class CloseableHeader(TabbedPanelHeader):
tab_pos: 'bottom_left'
size_hint: (.45, .45)
pos_hint: {'center_x': .25, 'y': .02}
- default_tab_text: 'Settings'
- default_tab_content: default_content
- FloatLayout:
+ do_default_tab: False
+
+ TabbedPanelItem:
+ id: settings
+ text: 'Settings'
RstDocument:
- id: default_content
text: '\\n'.join(("Normal tabs", "-------------",\
- "Tabs in \\'%s\\' position" %root.tab_pos))
- Image:
- id: tab_2_content
- pos: self.parent.pos
- size: self.parent.size
- source: 'data/images/defaulttheme-0.png'
- Image:
- id: tab_3_content
- pos:self.parent.pos
- size: self.parent.size
- source: 'data/images/image-loading.gif'
- TabbedPanelHeader:
+ "Tabs in \\'%s\\' position" %root.tab_pos))
+ TabbedPanelItem:
text: 'tab2'
- content: tab_2_content
- TabbedPanelHeader:
+ BubbleButton:
+ text: 'switch to settings'
+ on_press: root.switch_to(settings)
+ TabbedPanelItem:
text: 'tab3'
- content: tab_3_content
+ Image:
+ source: 'data/images/image-loading.gif'
<PanelbRight>
tab_pos: 'right_top'
@@ -257,26 +248,27 @@ class CloseableHeader(TabbedPanelHeader):
class Tp(TabbedPanel):
+
#override tab switching method to animate on tab switch
def switch_to(self, header):
- anim = Animation(color=(1, 1, 1, 0), d =.24, t = 'in_out_quad')
+ # if header is TabbedPanelItem then header = item.header
+ if hasattr(header, 'header'):
+ header = header.header
+
+ anim = Animation(opacity=0, d=.24, t='in_out_quad')
def start_anim(_anim, child, in_complete, *lt):
- if hasattr(child, 'color'):
- _anim.start(child)
- elif not in_complete:
- _on_complete()
+ _anim.start(child)
def _on_complete(*lt):
- if hasattr(header.content, 'color'):
- header.content.color = (0, 0, 0, 0)
- anim = Animation(color =
- (1, 1, 1, 1), d =.23, t = 'in_out_quad')
+ if header.content:
+ header.content.opacity = 0
+ anim = Animation(opacity=1, d=.43, t='in_out_quad')
start_anim(anim, header.content, True)
super(Tp, self).switch_to(header)
anim.bind(on_complete = _on_complete)
- if self.content:
+ if self.current_tab.content:
start_anim(anim, self.current_tab.content, False)
else:
_on_complete()
View
31 examples/widgets/tabbedpanel.py
@@ -21,24 +21,19 @@
id: set1_content
text: 'First tab content area'
- BoxLayout:
- id: set2_content
- Label:
- text: 'Second tab content area'
- Button:
- text: 'Button that does nothing'
-
- RstDocument:
- id: set3_content
- text: '\\n'.join(("Hello world", "-----------", "You are in the third tab."))
-
- # now categorize widgets inserted above in a specific header
- TabbedPanelHeader:
- text: 'Tab 2'
- content: set2_content
- TabbedPanelHeader:
- text: 'Tab 3'
- content: set3_content
+ TabbedPanelItem:
+ text: 'tab2'
+ BoxLayout:
+ Label:
+ text: 'Second tab content area'
+ Button:
+ text: 'Button that does nothing'
+ TabbedPanelItem:
+ text: 'tab3'
+ RstDocument:
+ id: set3_content
+ text: '\\n'.join(("Hello world", "-----------", "You are in the third tab."))
+
""")
class Test(TabbedPanel):
View
1  kivy/factory_registers.py
@@ -107,6 +107,7 @@
r('Switch', module='kivy.uix.switch')
r('TabbedPanel', module='kivy.uix.tabbedpanel')
r('TabbedPanelHeader', module='kivy.uix.tabbedpanel')
+r('TabbedPanelItem', module='kivy.uix.tabbedpanel')
r('TextInput', module='kivy.uix.textinput')
r('ToggleButton', module='kivy.uix.togglebutton')
r('TreeView', module='kivy.uix.treeview')
View
207 kivy/uix/tabbedpanel.py
@@ -108,7 +108,7 @@
'''
__all__ = ('TabbedPanel', 'TabbedPanelContent', 'TabbedPanelHeader',
- 'TabbedPanelStrip', 'TabbedPanelException')
+ 'TabbedPanelItem', 'TabbedPanelStrip', 'TabbedPanelException')
from functools import partial
from kivy.clock import Clock
@@ -121,7 +121,7 @@
from kivy.uix.floatlayout import FloatLayout
from kivy.logger import Logger
from kivy.properties import ObjectProperty, StringProperty, OptionProperty, \
- ListProperty, NumericProperty, AliasProperty
+ ListProperty, NumericProperty, AliasProperty, BooleanProperty
class TabbedPanelException(Exception):
@@ -154,7 +154,7 @@ def on_touch_down(self, touch):
else:
super(TabbedPanelHeader, self).on_touch_down(touch)
- def on_release(self, *l):
+ def on_release(self, *largs):
parent = self.parent
while parent is not None and not isinstance(parent, TabbedPanel):
parent = parent.parent
@@ -163,6 +163,55 @@ def on_release(self, *l):
parent.switch_to(self)
+class TabbedPanelItem(Widget):
+ '''This is a convenience widget that provides a header of type
+ TabbedPanelHeader and links the widget added to it with the header setting
+ the content automatically. Thus facilitating you to simply do the following
+ in kv language::
+
+ <TabbedPanel>:
+ ...other settings
+ TabbedPanelItem:
+ BoxLayout:
+ Label:
+ text: 'Second tab content area'
+ Button:
+ text: 'Button that does nothing'
+
+ .. versionadded:: 1.5.0
+ '''
+
+ text = StringProperty('')
+ '''text displayed on the tab head associated with this item.
+
+ :data:`text` is a :class:`~kivy.properties.StringProperty` default
+ to ''.
+ '''
+
+ def add_widget(self, widget, index=0):
+ if not hasattr(self, 'header'):
+ self.header = TabbedPanelHeader(text=self.text, content=widget)
+ if self.parent:
+ self.parent.add_widget(self.header)
+ elif self.header.content:
+ Logger.warning('TabbedPanel: TabbedPanelItem supports only' +
+ ' one child')
+ return
+ self.header.content = widget
+
+ def remove_widget(self, widget):
+ if hasattr(self, 'header'):
+ header = self.header
+ if header.content == widget:
+ if widget.parent:
+ widget.parent.remove_widget(widget)
+ header.content = None
+
+ def on_text(self, instance, value):
+ if hasattr(self, 'header'):
+ self.header.text = value
+
+
class TabbedPanelStrip(GridLayout):
'''A strip intended to be used as background for Heading/Tab.
'''
@@ -248,6 +297,15 @@ def get_current_tab(self):
default to 100.
'''
+ do_default_tab = BooleanProperty(True)
+ '''Specifies weather a default_tab head is provided.
+
+ .. versionadded:: 1.5.0
+
+ :data:`do_default_tab` is a :class:`~kivy.properties.BooleanProperty`,
+ defaults to 'True'.
+ '''
+
default_tab_text = StringProperty('Default tab')
'''Specifies the text displayed on the default tab header.
@@ -326,29 +384,38 @@ def set_def_tab_content(self, *l):
'''
def __init__(self, **kwargs):
+ # these variables need to be initialised the kv lang is processed
+ # setup the base layout for the tabbed panel
self._tab_layout = GridLayout(rows=1)
+ self.rows = 1
+ # bakground_image
self._bk_img = Image(
source=self.background_image, allow_stretch=True,
keep_ratio=False, color=self.background_color)
self._tab_strip = _tabs = TabbedPanelStrip(tabbed_panel=self,
rows=1, cols=99, size_hint=(None, None),
height=self.tab_height, width=self.tab_width)
-
self._partial_update_scrollview = None
self.content = content = TabbedPanelContent()
-
self._current_tab = default_tab = self._original_tab \
= self._default_tab = TabbedPanelHeader()
+
super(TabbedPanel, self).__init__(**kwargs)
+ if not self.do_default_tab:
+ Clock.schedule_once(self._switch_to_first_tab)
+ return
+
content = self._default_tab.content
cls = self.default_tab_cls
+
if not issubclass(cls, TabbedPanelHeader):
raise TabbedPanelException('`default_tab_class` should be\
subclassed from `TabbedPanelHeader`')
- if cls != type(default_tab):
- self._default_tab = cls()
+ # no need to instanciate if class is TabbedPanelHeader
+ if cls != TabbedPanelHeader:
+ self._current_tab = self._original_tab = self._default_tab = cls()
default_tab = self.default_tab
default_tab.text = self.default_tab_text
default_tab.height = self.tab_height
@@ -367,7 +434,13 @@ def __init__(self, **kwargs):
else:
Clock.schedule_once(self._load_default_tab_content)
self._current_tab = default_tab
- self.on_tab_pos()
+
+ def _switch_to_first_tab(self, *l):
+ ltl = len(self.tab_list) - 1
+ if ltl > 1:
+ self._current_tab = dt = self._original_tab \
+ = self.tab_list[ltl]
+ self.switch_to(dt)
def _load_default_tab_content(self, dt):
self.switch_to(self.default_tab)
@@ -378,7 +451,12 @@ def on_default_tab_text(self, *args):
def switch_to(self, header):
'''Switch to a specific panel header.
'''
+ # if TabbedPaneItem: header = item.header
+ if hasattr(header, 'header'):
+ header = header.header
header_content = header.content
+ self._current_tab.state = 'normal'
+ header.state = 'down'
self._current_tab = header
self.clear_widgets()
if header_content is None:
@@ -394,15 +472,19 @@ def add_widget(self, widget, index=0):
if content is None:
return
parent = widget.parent
- if widget.parent:
+ if parent:
parent.remove_widget(widget)
- if widget == content or widget == self._tab_layout:
+ if widget in (content, self._tab_layout):
super(TabbedPanel, self).add_widget(widget, index)
elif isinstance(widget, TabbedPanelHeader):
self_tabs = self._tab_strip
self_tabs.add_widget(widget)
widget.group = '__tab%r__' % self_tabs.uid
self.on_tab_width()
+ elif isinstance(widget, TabbedPanelItem):
+ widget.parent = self
+ if hasattr(widget, 'header'):
+ self.add_widget(widget.header)
else:
widget.pos_hint = {'x': 0, 'top': 1}
content.add_widget(widget, index)
@@ -411,7 +493,7 @@ def remove_widget(self, widget):
content = self.content
if content is None:
return
- if widget == content or widget == self._tab_layout:
+ if widget in (content, self._tab_layout):
super(TabbedPanel, self).remove_widget(widget)
elif isinstance(widget, TabbedPanelHeader):
if widget != self._default_tab:
@@ -419,13 +501,23 @@ def remove_widget(self, widget):
self_tabs.width -= widget.width
self_tabs.remove_widget(widget)
if widget.state == 'down':
- self._default_tab.on_release()
+ if self.do_default_tab:
+ self._default_tab.on_release()
self.reposition_tabs()
else:
Logger.info('TabbedPanel: default tab! can\'t be removed.\n' +
'Change `default_tab` to a different tab.')
+ elif isinstance(widget, TabbedPanelItem):
+ content = widget.header.content
+ parent = content.parent
+ if content and parent:
+ parent.remove_widget(content)
+ self.remove_widget(widget.header)
+ widget.parent = None
+ widget.header = None
else:
- content.remove_widget(widget)
+ if widget in content.children:
+ content.remove_widget(widget)
def clear_widgets(self, **kwargs):
content = self.content
@@ -439,52 +531,17 @@ def clear_widgets(self, **kwargs):
def clear_tabs(self, *l):
self_tabs = self._tab_strip
self_tabs.clear_widgets()
- self_default_tab = self._default_tab
- self_tabs.add_widget(self_default_tab)
- self_tabs.width = self_default_tab.width
+ if self.do_default_tab:
+ self_default_tab = self._default_tab
+ self_tabs.add_widget(self_default_tab)
+ self_tabs.width = self_default_tab.width
self.reposition_tabs()
def reposition_tabs(self, *l):
- Clock.unschedule(self.on_tab_pos)
- Clock.schedule_once(self.on_tab_pos, 0)
-
- def on_background_image(self, *l):
- self._bk_img.source = self.background_image
-
- def on_background_color(self, *l):
- if self.content is None:
- return
- self._bk_img.color = self.background_color
-
- def on_tab_width(self, *l):
- Clock.unschedule(self.update_tab_width)
- Clock.schedule_once(self.update_tab_width, 0)
-
- def update_tab_width(self, *l):
- if self.tab_width:
- for tab in self.tab_list:
- tab.size_hint_x = 1
- tsw = self.tab_width * len(self._tab_strip.children)
- else:
- # tab_width = None
- tsw = 0
- for tab in self.tab_list:
- if tab.size_hint_x:
- # size_hint_x: x/.xyz
- tab.size_hint_x = 1
- #drop to default tab_width
- tsw += 100
- else:
- # size_hint_x: None
- tsw += tab.width
- self._tab_strip.width = tsw
- self.reposition_tabs()
+ Clock.unschedule(self._update_tabs)
+ Clock.schedule_once(self._update_tabs, 0)
- def on_tab_height(self, *l):
- self._tab_layout.height = self._tab_strip.height = self.tab_height
- self.reposition_tabs()
-
- def on_tab_pos(self, *l):
+ def _update_tabs(self, *l):
self_content = self.content
if not self_content:
return
@@ -611,6 +668,46 @@ def on_tab_pos(self, *l):
for widg in widget_list:
add(widg)
+ def on_background_image(self, *l):
+ self._bk_img.source = self.background_image
+
+ def on_background_color(self, *l):
+ if self.content is None:
+ return
+ self._bk_img.color = self.background_color
+
+ def on_tab_width(self, *l):
+ Clock.unschedule(self.update_tab_width)
+ Clock.schedule_once(self.update_tab_width, 0)
+
+ def update_tab_width(self, *l):
+ if self.tab_width:
+ for tab in self.tab_list:
+ tab.size_hint_x = 1
+ tsw = self.tab_width * len(self._tab_strip.children)
+ else:
+ # tab_width = None
+ tsw = 0
+ for tab in self.tab_list:
+ if tab.size_hint_x:
+ # size_hint_x: x/.xyz
+ tab.size_hint_x = 1
+ #drop to default tab_width
+ tsw += 100
+ else:
+ # size_hint_x: None
+ tsw += tab.width
+ self._tab_strip.width = tsw
+ self.reposition_tabs()
+
+ def on_tab_height(self, *l):
+ self._tab_layout.height = self._tab_strip.height = self.tab_height
+ self.reposition_tabs()
+
+ def on_tab_pos(self, *l):
+ # ensure canvas
+ self.reposition_tabs()
+
def _update_top(self, *args):
sctr, top, scrl_v_width, x, y = args
Clock.unschedule(partial(self._updt_top, sctr, top, scrl_v_width))
Something went wrong with that request. Please try again.