class Spacer(object): """ A spacer represents expandable or fixed 'whitespace' in the GUI. In a XML file you can get this by adding a <Spacer /> inside a VBox or HBox element (Windows implicitly are VBox elements). Attributes ========== As with widgets a number of attributes can be set on a spacer (inside the XML definition). - min_size: Int: The minimal size this Spacer is allowed to have. - max_size: Int: The maximal size this Spacer is allowed to have. - fixed_size: Int: Set min_size and max_size to the same vale - effectively a Fixed size spacer. """ ATTRIBUTES = [ IntAttr('min_size'), IntAttr('size'), IntAttr('max_size'), IntAttr('fixed_size'), ] def __init__(self,parent=None,**kwargs): self.parent = parent self.min_size = 0 self.max_size = 1000 self.size = 0 def __str__(self): return "Spacer(parent.name='%s')" % getattr(self.parent,'name','None') def __repr__(self): return "<Spacer(parent.name='%s') at %x>" % (getattr(self.parent,'name','None'),id(self)) def _getSize(self): self.size = self._size return self._size def _setSize(self,size): self._size = max(self.min_size, min(self.max_size,size)) size = property(_getSize,_setSize) # Alias for size width = property(_getSize,_setSize) height = property(_getSize,_setSize) def _setFixedSize(self,size): self.min_size = self.max_size = size self.size = size fixed_size = property(fset=_setFixedSize) def _isExpanding(self): if self.min_size < self.max_size: return 1 return 0 vexpand = property(_isExpanding) hexpand = property(_isExpanding)
class PercentageBar(Widget): """ A percentage bar widget New Attributes ============== - orientation: 1 = horizontal, 0=vertical - value: int: default 0 """ HORIZONTAL = fifechan.PercentageBar.HORIZONTAL VERTICAL = fifechan.PercentageBar.VERTICAL ATTRIBUTES = Widget.ATTRIBUTES + [ IntAttr('orientation'), IntAttr('value') ] DEFAULT_HEXPAND = True DEFAULT_VEXPAND = False DEFAULT_MIN_SIZE = 10,10 DEFAULT_VALUE = 0 DEFAULT_ORIENTATION = HORIZONTAL def __init__(self, parent = None, name = None, size = None, min_size = None, max_size = None, fixed_size = None, margins = None, padding = None, helptext = None, position = None, style = None, hexpand = None, vexpand = None, font = None, base_color = None, background_color = None, foreground_color = None, selection_color = None, border_color = None, outline_color = None, border_size = None, outline_size = None, position_technique = None, is_focusable = None, comment = None, value = None, orientation = None): self.real_widget = fifechan.PercentageBar() self.orientation = self.DEFAULT_ORIENTATION self.value = self.DEFAULT_VALUE super(PercentageBar, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if orientation is not None: self.orientation = orientation if value is not None: self.value = value self.accepts_data = True self._realSetData = self._setValue self._realGetData = self._getValue def clone(self, prefix): pbarClone = PercentageBar(None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.value, self.orientation) return pbarClone def _getValue(self): """getValue(self) -> int""" return self.real_widget.getValue() def _setValue(self, value): """setValue(self, int value)""" if type(value) != int: raise RuntimeError("PercentageBar only accepts integer values") self.real_widget.setValue(value) value = property(_getValue, _setValue) def _setOrientation(self, orientation): """setOrientation(self, Orientation orientation)""" self.real_widget.setOrientation(orientation) def _getOrientation(self): """getOrientation(self) -> int""" return self.real_widget.getOrientation() orientation = property(_getOrientation, _setOrientation)
class Button(BasicTextWidget): """ A basic push button. New Attributes ============== - active: Bool: If false the button is inactiv then it looks gray. - alignment: Int: Sets the alignment of the caption. The alignment is relative to the center of the button. - offset: Point: The offset of the caption when the button is pressed. """ ATTRIBUTES = BasicTextWidget.ATTRIBUTES + [ BoolAttr('active'), IntAttr('alignment'), PointAttr('offset') ] DEFAULT_ACTIVE = True # 0=Left, 1=Center, 2=Right DEFAULT_ALIGNMENT = 1 DEFAULT_OFFSET = 1, 1 def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, text=None, active=None, alignment=None, offset=None, real_widget=None): if real_widget is None: self.real_widget = fifechan.Button("") else: self.real_widget = real_widget # set the defaults #offset = self.DEFAULT_OFFSET super(Button, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment, text=text) # set provided attributes or defaults if active is not None: self.active = active else: self.active = self.DEFAULT_ACTIVE if alignment is not None: self.alignment = alignment else: self.alignment = self.DEFAULT_ALIGNMENT if offset is not None: self.offset = offset else: self.offset = self.DEFAULT_OFFSET def clone(self, prefix): btnClone = Button( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.text, self.active, self.alignment, self.offset) return btnClone def _setActive(self, active): self.real_widget.setActive(active) def _isActive(self): return self.real_widget.isActive() active = property(_isActive, _setActive) def _setAlignment(self, alignment): self.real_widget.setAlignment(alignment) def _getAlignment(self): return self.real_widget.getAlignment() alignment = property(_getAlignment, _setAlignment) def _setOffset(self, offset): self.real_widget.setDownOffset(offset[0], offset[1]) def _getOffset(self): return (self.real_widget.getDownXOffset(), self.real_widget.getDownYOffset()) offset = property(_getOffset, _setOffset) def _setXOffset(self, x): self.real_widget.setDownXOffset(x) def _getXOffset(self): return self.real_widget.getDownXOffset() x_offset = property(_getXOffset, _setXOffset) def _setYOffset(self, y): self.real_widget.setDownYOffset(y) def _getYOffset(self): return self.real_widget.getDownYOffset() y_offset = property(_getYOffset, _setYOffset)
class ImageProgressBar(Widget): """ An image progress bar. New Attributes ============== - image: String or GuiImage: The source location of the Image or a direct GuiImage - max_icons: Maximal count of icons - icons: Current count of active icons - orientation: Horizontal (0) or Vertical (1) - opaque: True if the widget is opaque, false otherwise. """ ATTRIBUTES = Widget.ATTRIBUTES + [ Attr('bar_image'), Attr('foreground_image'), IntAttr('max_value'), IntAttr('value'), IntAttr('orientation'), BoolAttr('opaque') ] DEFAULT_OPAQUE = True def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, bar_image=None, foreground_image=None, max_value=None, value=None, orientation=None, opaque=None): self.real_widget = fifechan.ImageProgressBar() self.opaque = opaque or self.DEFAULT_OPAQUE super(ImageProgressBar, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) # for the case that image can not be found, e.g. invalid path # the Widget is removed from the manager try: self.bar_image = bar_image self.foreground_image = foreground_image except Exception: get_manager().removeWidget(self) raise if max_value is not None: self.max_value = max_value if value is not None: self.value = value def clone(self, prefix): imageClone = ImageProgressBar( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.bar_image, self.foreground_image, self.max_value, self.value, self.orientation, self.opaque) return imageClone _bar_image = ImageProperty("BarImage") _foreground_image = ImageProperty("ForegroundImage") def _setBarImage(self, source): self._bar_image = source def _getBarImage(self): return self._bar_image bar_image = property(_getBarImage, _setBarImage) def _setForegroundImage(self, source): self._foreground_image = source def _getForegroundImage(self): return self._foreground_image foreground_image = property(_getForegroundImage, _setForegroundImage) def _setMaxValue(self, maxValue): if type(maxValue) != int: raise RuntimeError( "ImageProgressBar max value should be an integer") self.real_widget.setMaxValue(maxValue) def _getMaxValue(self): return self.real_widget.getMaxValue() max_value = property(_getMaxValue, _setMaxValue) def _setValue(self, value): if type(value) != int: raise RuntimeError("ImageProgressBar value should be an integer") self.real_widget.setValue(value) def _getValue(self): return self.real_widget.getValue() value = property(_getValue, _setValue) def _setOrientation(self, orientation): self.real_widget.setOrientation(orientation) def _getOrientation(self): return self.real_widget.getOrientation() orientation = property(_getOrientation, _setOrientation) def _setOpaque(self, opaque): self.real_widget.setOpaque(opaque) def _getOpaque(self): return self.real_widget.isOpaque() opaque = property(_getOpaque, _setOpaque)
class AdjustingContainer(Container): """ This is the adjusting container class. It provides space in which child widgets can be position via the position attribute. New Attributes ============== - colums - Integer: The number of columns to divide the widgets into. - alignments: The alignment per column. """ ATTRIBUTES = Container.ATTRIBUTES + [ IntAttr('columns'), IntListAttr('alignments'), ] DEFAULT_NUMBER_COLUMNS = 3 def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, background_image=None, opaque=None, layout=None, spacing=None, uniform_size=None, _real_widget=None, columns=None, alignments=None): if _real_widget is None: _real_widget = fifechan.AdjustingContainer() super(AdjustingContainer, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment, background_image=background_image, opaque=opaque, layout=layout, spacing=spacing, uniform_size=uniform_size, _real_widget=_real_widget) if columns is not None: self.columns = columns else: self.columns = self.DEFAULT_NUMBER_COLUMNS if alignments is not None: self.alignments = alignments def clone(self, prefix): containerClone = AdjustingContainer( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.background_image, self.opaque, self.layout, self.spacing, self.uniform_size, None, self.columns, self.alignments) containerClone.addChildren(self._cloneChildren(prefix)) return containerClone def _setNumberColumns(self, number): self.real_widget.setNumberOfColumns(number) def _getNumberColumns(self): self.real_widget.getNumberOfColumns() columns = property(_getNumberColumns, _setNumberColumns) def _setColumnAlignment(self, column, alignment): self.real_widget.setColumnAlignment(column, alignment) def _getColumnAlignment(self, column): self.real_widget.getColumnAlignment(column) column_alignment = property(_getColumnAlignment, _setColumnAlignment) def _setColumnAlignments(self, alignments): i = 0 if alignments is not None: for a in alignments: self.real_widget.setColumnAlignment(i, a) i += 1 else: cols = self.columns while i < cols: self.real_widget.setColumnAlignment(i, 0) i += 1 def _getColumnAlignments(self): alignments = [] cols = self.columns i = 0 while i < cols: alignments.append(self.column_alignment(i)) i += 1 return alignments alignments = property(_getColumnAlignments, _setColumnAlignments)
class TabbedArea(Container): """ This is the tabbed area class. New Attributes ============== """ ATTRIBUTES = Container.ATTRIBUTES + [ IntAttr('select_tab_index'), UnicodeAttr('select_tab'), ] DEFAULT_OPAQUE = False DEFAULT_MARGINS = 0, 0 DEFAULT_PADDING = 0 DEFAULT_LAYOUT = 'Horizontal' DEFAULT_POSITION_TECHNIQUE = "automatic" def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, background_image=None, opaque=None, _real_widget=None, select_tab_index=None, select_tab=None): if _real_widget is None: _real_widget = fifechan.TabbedArea() super(TabbedArea, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment, background_image=background_image, opaque=opaque, _real_widget=_real_widget) self.tab_definitions = {} # only use one if select_tab_index is not None: self.select_tab_index = select_tab_index elif select_tab is not None: self.select_tab = select_tab def clone(self, prefix): tabbedareaClone = TabbedArea( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.background_image, self.opaque, select_tab_index) tabbedareaClone.addChilds(self._cloneChildren(prefix)) return tabbedareaClone def addChild(self, widget): """ Adds a child widget to the container. """ # if the widget have no tabwidget, we try to find one if widget.tab is None: tab = self.getTabDefinition(widget) tab.addTabToWidget(widget) widget.parent = self if widget.max_size[0] > self.max_size[0] or widget.max_size[ 1] > self.max_size[1]: widget.max_size = self.max_size self.children.append(widget) # add real tab and real widget self.real_widget.addTab(widget.tab.real_widget, widget.real_widget) # add all to the manager def _add(added_widget): if not added_widget._added: get_manager().addWidget(added_widget) if added_widget._top_added: get_manager().removeTopWidget(added_widget) widget.deepApply(_add) def removeChild(self, widget): if not widget in self.children: raise RuntimeError("%s does not have %s as direct tab widget." % (str(self), str(widget))) # remove all from the manager def _remove(removed_widget): if removed_widget._added: get_manager().removeWidget(removed_widget) if removed_widget._top_added: get_manager().removeTopWidget(removed_widget) widget.deepApply(_remove) i = self.children.index(widget) self.real_widget.removeTabWithIndex(i) self.children.remove(widget) widget.parent = None def addTabDefinition(self, widget): # Only needed because of XML loading # The tab have the content_name that should be equal to # the name of the contained widget self.tab_definitions[widget.content_name] = widget def getTabDefinition(self, widget): # Only needed because of XML loading # Check if we have a tab that is associated with the widget name = widget.name if name in self.tab_definitions: widget = self.tab_definitions[name] del self.tab_definitions[name] return widget else: raise RuntimeError("%s does not have %s in the tab definitions." % (str(self), str(name))) def getNumberOfTabs(self): return self.real_widget.getNumberOfTabs() def isTabSelected(self, widget): i = self.children.index(widget) return self.real_widget.isTabSelected(i) def _setSelectedTab(self, widget): if isinstance(widget, str): widget = self.findChild(name=widget) if widget is not None: i = self.children.index(widget) self.real_widget.setSelectedTab(i) def _getSelectedTab(self): i = self.real_widget.getSelectedTabIndex() return self.children[i] select_tab = property(_getSelectedTab, _setSelectedTab) def _setSelectedTabIndex(self, index): self.real_widget.setSelectedTab(index) def _getSelectedTabIndex(self): return self.real_widget.getSelectedTabIndex() select_tab_index = property(_getSelectedTabIndex, _setSelectedTabIndex)
class Window(VBoxLayoutMixin, Container): """ A L{VBox} with a draggable title bar aka a window New Attributes ============== - title: The Caption of the window - titlebar_height: The height of the window title bar """ ATTRIBUTES = Container.ATTRIBUTES + [ UnicodeAttr('title'), IntAttr('titlebar_height') ] DEFAULT_TITLE = u"title" DEFAULT_TITLE_HEIGHT = 0 DEFAULT_POSITION_TECHNIQUE = "automatic" def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_size=None, position_technique=None, is_focusable=None, comment=None, padding=None, background_image=None, opaque=None, margins=None, _real_widget=None, title=None, titlebar_height=None): super(Window, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_size=border_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment, padding=padding, background_image=background_image, opaque=opaque, margins=margins, _real_widget=fifechan.Window()) if titlebar_height is not None: if titlebar_height == 0: titlebar_height = self.real_font.getHeight() + 4 self.titlebar_height = titlebar_height else: self.titlebar_height = self.real_font.getHeight() + 4 if title is not None: self.title = title else: self.title = self.DEFAULT_TITLE def clone(self, prefix): windowClone = Window( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_size, self.position_technique, self.is_focusable, self.comment, self.padding, self.background_image, self.opaque, self.margins, None, self.title, self.titlebar_height) windowClone.addChildren(self._cloneChildren(prefix)) return windowClone def _getTitle(self): return gui2text(self.real_widget.getCaption()) def _setTitle(self, text): self.real_widget.setCaption(text2gui(text)) title = property(_getTitle, _setTitle) def _getTitleBarHeight(self): return self.real_widget.getTitleBarHeight() def _setTitleBarHeight(self, h): self.real_widget.setTitleBarHeight(h) titlebar_height = property(_getTitleBarHeight, _setTitleBarHeight) # Hackish way of hiding that title bar height in the perceived height. # Fixes VBox calculation def _setHeight(self, h): h = max(self.min_size[1], h) h = min(self.max_size[1], h) self.real_widget.setHeight(h + self.titlebar_height) def _getHeight(self): return self.real_widget.getHeight() - self.titlebar_height height = property(_getHeight, _setHeight)
class Window(Container): """ A L{VBox} with a draggable title bar aka a window New Attributes ============== - title: The Caption of the window - titlebar_height: The height of the window title bar - movable: Can the Window be moved with the mouse """ ATTRIBUTES = Container.ATTRIBUTES + [ UnicodeAttr('title'), IntAttr('titlebar_height'), BoolAttr('movable') ] DEFAULT_LAYOUT = 'Vertical' DEFAULT_TITLE = u"title" DEFAULT_TITLE_HEIGHT = 16 DEFAULT_MOVABLE = True DEFAULT_POSITION_TECHNIQUE = "automatic" def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, background_image=None, opaque=None, layout=None, spacing=None, uniform_size=None, _real_widget=None, title=None, titlebar_height=None, movable=None): if _real_widget is None: _real_widget = fifechan.Window() super(Window, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment, background_image=background_image, opaque=opaque, layout=layout, spacing=spacing, uniform_size=uniform_size, _real_widget=_real_widget) if titlebar_height is not None: if titlebar_height == 0: titlebar_height = self.real_font.getHeight() + 4 self.titlebar_height = titlebar_height else: self.titlebar_height = self.real_font.getHeight() + 4 if title is not None: self.title = title else: self.title = self.DEFAULT_TITLE if movable is not None: self.movable = movable else: self.movable = self.DEFAULT_MOVABLE def clone(self, prefix): windowClone = Window( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.background_image, self.opaque, self.layout, self.spacing, self.uniform_size, None, self.title, self.titlebar_height, self.movable) windowClone.addChildren(self._cloneChildren(prefix)) return windowClone def _getTitle(self): return gui2text(self.real_widget.getCaption()) def _setTitle(self, text): self.real_widget.setCaption(text2gui(text)) title = property(_getTitle, _setTitle) def _getTitleBarHeight(self): return self.real_widget.getTitleBarHeight() def _setTitleBarHeight(self, h): self.real_widget.setTitleBarHeight(h) titlebar_height = property(_getTitleBarHeight, _setTitleBarHeight) def _getMovable(self): return self.real_widget.isMovable() def _setMovable(self, move): self.real_widget.setMovable(move) movable = property(_getMovable, _setMovable)
class FlowContainer(Container): """ An implementation of a flow container that can contain other widgets. The widgets can be sorted vertical per row or horizontal per column. If the space in the container is too small to put all the components in one row or column, it uses multiple rows or columns. """ ATTRIBUTES = Container.ATTRIBUTES + [ IntAttr('alignment'), ] DEFAULT_LAYOUT = 'Horizontal' DEFAULT_ALIGNMENT = 4 # Center def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, background_image=None, opaque=None, layout=None, spacing=None, uniform_size=None, _real_widget=None, alignment=None): if _real_widget is None: _real_widget = fifechan.FlowContainer() super(FlowContainer, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment, background_image=background_image, opaque=opaque, layout=layout, spacing=spacing, uniform_size=uniform_size, _real_widget=_real_widget) if alignment is not None: self.alignment = alignment else: self.alignment = self.DEFAULT_ALIGNMENT def clone(self, prefix): containerClone = FlowContainer( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.background_image, self.opaque, self.layout, self.spacing, self.uniform_size, None, self.alignment) containerClone.addChildren(self._cloneChildren(prefix)) return containerClone def _setAlignment(self, alignment): self.real_widget.setAlignment(alignment) def _getAlignment(self): self.real_widget.getAlignment() alignment = property(_getAlignment, _setAlignment) def adjustContent(self): self.real_widget.adjustContent()
class ScrollArea(Widget): """ A wrapper around another (content) widget. New Attributes ============== - content: The wrapped widget. - vertical_scrollbar: Boolean: Set this to False to hide the Vertical scrollbar - horizontal_scrollbar: Boolean: Set this to False to hide the Horizontal scrollbar """ ATTRIBUTES = Widget.ATTRIBUTES + [ BoolAttr("vertical_scrollbar"), BoolAttr("horizontal_scrollbar"), IntAttr("vertical_scroll_amount"), IntAttr("horizontal_scroll_amount") ] DEFAULT_HEXPAND = True DEFAULT_VEXPAND = True def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, content=None, vertical_scrollbar=None, horizontal_scrollbar=None, vertical_scroll_amount=None, horizontal_scroll_amount=None): self.real_widget = fifechan.ScrollArea() self._content = None super(ScrollArea, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if content is not None: self.content = content if vertical_scrollbar is not None: self.vertical_scrollbar = vertical_scrollbar if horizontal_scrollbar is not None: self.horizontal_scrollbar = horizontal_scrollbar if vertical_scroll_amount is not None: self.vertical_scroll_amount = vertical_scroll_amount if horizontal_scroll_amount is not None: self.horizontal_scroll_amount = horizontal_scroll_amount def clone(self, prefix): scrollareaClone = ScrollArea( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, None, #NOTE since content is a widget hierarchy it should be cloned too self.vertical_scrollbar, self.horizontal_scrollbar, self.vertical_scroll_amount, self.horizontal_scroll_amount) scrollareaClone.content = self.content.clone(prefix) return scrollareaClone def addChild(self, widget): self.content = widget widget.parent = self def removeChild(self, widget): if self._content != widget: raise RuntimeError("%s does not have %s as direct child widget." % (str(self), str(widget))) self.content = None widget.parent = None def _setContent(self, content): if content is None: self.real_widget.setContent(content) else: self.real_widget.setContent(content.real_widget) self._content = content def _getContent(self): return self._content content = property(_getContent, _setContent) def deepApply(self, visitorFunc, leaves_first=True, shown_only=False): if leaves_first: if self._content: self._content.deepApply(visitorFunc, leaves_first=leaves_first, shown_only=shown_only) visitorFunc(self) if not leaves_first: if self._content: self._content.deepApply(visitorFunc, leaves_first=leaves_first, shown_only=shown_only) def _visibilityToScrollPolicy(self, visibility): if visibility: return fifechan.ScrollArea.ShowAuto return fifechan.ScrollArea.ShowNever def _scrollPolicyToVisibility(self, policy): if policy == fifechan.ScrollArea.ShowNever: return False return True def _setHorizontalScrollbar(self, visibility): self.real_widget.setHorizontalScrollPolicy( self._visibilityToScrollPolicy(visibility)) def _setVerticalScrollbar(self, visibility): self.real_widget.setVerticalScrollPolicy( self._visibilityToScrollPolicy(visibility)) def _getHorizontalScrollbar(self): return self._scrollPolicyToVisibility( self.real_widget.getHorizontalScrollPolicy()) def _getVerticalScrollbar(self): return self._scrollPolicyToVisibility( self.real_widget.getVerticalScrollPolicy()) def getVerticalMaxScroll(self): return self.real_widget.getVerticalMaxScroll() def getHorizontalMaxScroll(self): return self.real_widget.getHorizontalMaxScroll() def _getHorizontalScrollAmount(self): return self.real_widget.getHorizontalScrollAmount() def _setHorizontalScrollAmount(self, scroll_amount): return self.real_widget.setHorizontalScrollAmount(scroll_amount) def _getVerticalScrollAmount(self): return self.real_widget.getVerticalScrollAmount() def _setVerticalScrollAmount(self, scroll_amount): return self.real_widget.setVerticalScrollAmount(scroll_amount) vertical_scrollbar = property(_getVerticalScrollbar, _setVerticalScrollbar) horizontal_scrollbar = property(_getHorizontalScrollbar, _setHorizontalScrollbar) horizontal_scroll_amount = property(_getHorizontalScrollAmount, _setHorizontalScrollAmount) vertical_scroll_amount = property(_getVerticalScrollAmount, _setVerticalScrollAmount)
class IconProgressBar(Widget): """ An image icon. New Attributes ============== - image: String or GuiImage: The source location of the Image or a direct GuiImage """ ATTRIBUTES = Widget.ATTRIBUTES + [ Attr('image'), IntAttr('max_icons'), IntAttr('orientation'), BoolAttr('opaque') ] DEFAULT_OPAQUE = True def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_size=None, position_technique=None, is_focusable=None, comment=None, image=None, max_icons=None, orientation=None, opaque=None): self.real_widget = fifechan.IconProgressBar() self.opaque = opaque or self.DEFAULT_OPAQUE super(IconProgressBar, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_size=border_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if image is not None: self.image = image if max_icons is not None: self.max_icons = max_icons def clone(self, prefix): iconClone = IconProgressBar( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_size, self.position_technique, self.is_focusable, self.comment, self.image, self.max_icons) return iconClone _image = ImageProperty("Image") def advance(self): self.real_widget.advance() def reset(self): self.real_widget.reset() def _setImage(self, source): self._image = source def _getImage(self): return self._image image = property(_getImage, _setImage) def _setMaxIcons(self, maxIcons): if type(maxIcons) != int: raise RuntimeError( "IconProgressBar max icons should be an integer") self.real_widget.setMaxIcons(maxIcons) def _getMaxIcons(self): return self.real_widget.getMaxIcons() max_icons = property(_getMaxIcons, _setMaxIcons) def _setOrientation(self, orientation): self.real_widget.setOrientation(orientation) def _getOrientation(self): return self.real_widget.getOrientation() orientation = property(_getOrientation, _setOrientation) def _setOpaque(self, opaque): self.real_widget.setOpaque(opaque) def _getOpaque(self): return self.real_widget.isOpaque() opaque = property(_getOpaque, _setOpaque)
class PieGraph(Widget): """ A pie graph widget New Attributes ============== - coordinates: int list: x and y coordinates - thickness': int: Line thickness, default 1 - pie_color: color: Pie color - opaque: bool: default False """ ATTRIBUTES = Widget.ATTRIBUTES + [ PointAttr('center'), IntAttr('radius'), BoolAttr('opaque'), MixedListAttr('segments') ] DEFAULT_HEXPAND = False DEFAULT_VEXPAND = False DEFAULT_RADIUS = 10 DEFAULT_OPAQUE = False def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, opaque=None, center=None, radius=None, segments=None): self.real_widget = fifechan.PieGraph() self.opaque = self.DEFAULT_OPAQUE self.radius = self.DEFAULT_RADIUS self._segments = [] super(PieGraph, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if opaque is not None: self.opaque = opaque if center is not None: self.center = center if radius is not None: self.radius = radius if segments is not None: self.segments = segments def clone(self, prefix): pieGraphClone = PieGraph( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.opaque, self.center, self.radius, self.segments) return pieGraphClone def update(self): self.real_widget.clearSegments() for s in self._segments: self.real_widget.addSegment(s.start_angle, s.stop_angle, s.color) def _setOpaque(self, opaque): self.real_widget.setOpaque(opaque) def _getOpaque(self): return self.real_widget.isOpaque() opaque = property(_getOpaque, _setOpaque) def _setCenter(self, center): # point check if isinstance(center, fifechan.Point): self.real_widget.setCenter(center) return self.real_widget.setCenter(center[0], center[1]) def _getCenter(self): return (self.real_widget.getCenterX(), self.real_widget.getCenterY()) center = property(_getCenter, _setCenter) def _setRadius(self, radius): self.real_widget.setRadius(radius) def _getRadius(self): return self.real_widget.getRadius() radius = property(_getRadius, _setRadius) def addSegment(self, start, stop, color): segment = PieSegment(self, start, stop, color) self._segments.append(segment) self.real_widget.addSegment(segment.start_angle, segment.stop_angle, segment.color) def removeSegment(self, start, stop, color): tmp_color = color if isinstance(color, tuple): tmp_color = fifechan.Color(*color) for s in self._segments: if s.start_angle == start and s.stop_angle == stop and s.color == tmp_color: self._segments.remove(s) self.update() break def clearSegments(self): self._segments = [] self.real_widget.clearSegments() def _setSegments(self, segments): self._segments = [] if segments is None: self.real_widget.clearSegments() return if isinstance(segments[0], PieSegment): self._segments = segments else: for i in range(0, len(segments), 3): segment = PieSegment(self, segments[i], segments[i + 1], segments[i + 2]) self._segments.append(segment) self.update() def _getSegments(self): return self._segments segments = property(_getSegments, _setSegments)
class StepSlider(Slider): """The StepSlider automatically snaps the steps suggested by stepsize.""" ATTRIBUTES = Widget.ATTRIBUTES + [ IntAttr('orientation'), IntAttr('marker_length'), UnicodeAttr('steps') ] def __init__(self, *args, **kwargs): self._callbacks_by_group = { } # super init calls capture, so we need this here super(StepSlider, self).__init__(*args, **kwargs) self._last_step_value = None # for recognizing new steps, self.value is overwritten in the base class sometimes self._steps = None super(StepSlider, self).capture(self._update, 'action', 'stepslider') super(StepSlider, self).capture(self._mouse_released_snap, 'mouseReleased', 'stepslider') def _mouse_released_snap(self): """ When mouse is released, snap slider marker to discrete value to avoid the marker being displayed in-between steps. """ # Retrieve the value of the step that is closest to marker's current position # (`_get_value`), and set that value on the slider to move the marker (`_set_value`). self.value = self.value def capture(self, callback, event_name='action', group_name='default'): """ Intercept captures for `action` and store the callback in our list. We'll only call them when a step changed. """ if event_name == 'action': self._callbacks_by_group[group_name] = callback else: super(StepSlider, self).capture(callback, event_name, group_name) def _update(self): """ Notify listeners when a different step was selected. """ if self.value != self._last_step_value: self._last_step_value = self.value for callback in self._callbacks_by_group.values(): callback() def _set_steps(self, steps): if isinstance(steps, str): self._steps = [float(s.strip()) for s in steps.split(';')] else: self._steps = steps self.scale_start = 0.0 self.scale_end = float(len(self._steps) - 1) self.step_length = 1.0 def _get_steps(self): return self._steps steps = property(_get_steps, _set_steps) def _get_value(self): value = int(round(self.real_widget.getValue())) return self._steps[value] def _set_value(self, value): try: value = float(self._steps.index(value)) except ValueError: # Invalid value, probably a value before the changes to stepslider. # The values of affected step sliders used to be in [0, N], where N is the # number of steps, so we use the value as index. try: value = self._steps[int(value)] except IndexError: # If that didn't work, reset the slider. value = self._steps[0] self.real_widget.setValue(value) value = property(_get_value, _set_value)
class CheckBox(ImageButton): """ A basic checkbox. New Attributes ============== - marked: Boolean value, whether the checkbox is checked or not. - marker_style: Integer: The visual style of the marker. - background_image: String: Optional image for the background, the size should also include the caption. Data ==== The marked status can be read and set via L{distributeData} and L{collectData} """ ATTRIBUTES = ImageButton.ATTRIBUTES + [ BoolAttr('marked'), IntAttr('marker_style'), Attr('background_image') ] DEFAULT_MARKED = False # 0=Checkmark, 1=Cross, 2=Dot, 3=Rhombus, 4=Image DEFAULT_MARKER_STYLE = 0 DEFAULT_OFFSET = 0, 0 def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, text=None, active=None, alignment=None, offset=None, up_image=None, down_image=None, hover_image=None, in_up_image=None, in_down_image=None, in_hover_image=None, marked=None, marker_style=None, background_image=None): self.real_widget = fifechan.CheckBox() super(CheckBox, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment, text=text, active=active, alignment=alignment, offset=offset, up_image=up_image, down_image=down_image, hover_image=hover_image, in_up_image=in_up_image, in_down_image=in_down_image, in_hover_image=in_hover_image, real_widget=self.real_widget) # set provided attributes or defaults if marked is not None: self.marked = marked else: self.marked = self.DEFAULT_MARKED if marker_style is not None: self.marker_style = marker_style else: self.marker_style = self.DEFAULT_MARKER_STYLE # for the case that image can not be found, e.g. invalid path # the Checkbox is removed from the manager try: self.background_image = background_image except Exception: get_manager().removeWidget(self) raise # Prepare Data collection framework self.accepts_data = True self._realGetData = self._isMarked self._realSetData = self._setMarked def clone(self, prefix): checkboxClone = CheckBox( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.text, self.active, self.alignment, self.offset, self.up_image, self.down_image, self.hover_image, self.in_up_image, self.in_down_image, self.in_hover_image, self.marked, self.marker_style, self.background_image) return checkboxClone def _isMarked(self): return self.real_widget.isSelected() def _setMarked(self, mark): self.real_widget.setSelected(mark) marked = property(_isMarked, _setMarked) def _getMarkerStyle(self): return self.real_widget.getMarkerStyle() def _setMarkerStyle(self, style): self.real_widget.setMarkerStyle(style) marker_style = property(_getMarkerStyle, _setMarkerStyle) background_image = ImageProperty("BackgroundImage")
class BarGraph(Widget): """ A bar graph widget New Attributes ============== - bar_position: x and y coordinate - bar_width': int: default 10 - bar_height: int: default 10 - opaque: bool: default False """ ATTRIBUTES = Widget.ATTRIBUTES + [ PointAttr('bar_position'), IntAttr('bar_width'), IntAttr('bar_height'), BoolAttr('opaque') ] DEFAULT_HEXPAND = False DEFAULT_VEXPAND = False DEFAULT_OPAQUE = False DEFAULT_BAR_POSITION = 0, 0 DEFAULT_BAR_WIDTH = 10 DEFAULT_BAR_HEIGHT = 10 def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, opaque=None, bar_position=None, bar_width=None, bar_height=None): self.real_widget = fifechan.BarGraph() self.opaque = self.DEFAULT_OPAQUE self.bar_positon = self.DEFAULT_BAR_POSITION self.bar_width = self.DEFAULT_BAR_WIDTH self.bar_height = self.DEFAULT_BAR_HEIGHT super(BarGraph, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if opaque is not None: self.opaque = opaque if bar_position is not None: self.bar_position = bar_position if bar_width is not None: self.bar_width = bar_width if bar_height is not None: self.bar_height = bar_height def clone(self, prefix): barGraphClone = BarGraph( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.opaque, self.bar_positon, self.bar_width, self.bar_height) return barGraphClone def _setOpaque(self, opaque): self.real_widget.setOpaque(opaque) def _getOpaque(self): return self.real_widget.isOpaque() opaque = property(_getOpaque, _setOpaque) def _setBarPosition(self, bar_position): self.real_widget.setBarPosition(bar_position[0], bar_position[1]) def _getBarPosition(self): return (self.real_widget.getBarX(), self.real_widget.getBarY()) bar_position = property(_getBarPosition, _setBarPosition) def _setBarWidth(self, bar_width): self.real_widget.setBarWidth(bar_width) def _getBarWidth(self): return self.real_widget.getBarWidth() bar_width = property(_getBarWidth, _setBarWidth) def _setBarHeight(self, bar_height): self.real_widget.setBarHeight(bar_height) def _getBarHeight(self): return self.real_widget.getBarHeight() bar_height = property(_getBarHeight, _setBarHeight)
class Widget(object): """ This is the common widget base class, which provides most of the wrapping functionality. Attributes ========== Widgets are manipulated (mostly) through attributes - and these can all be set by XML attributes. Derived widgets will have other attributes. Please see their B{New Attributes} sections. The types of the attributes are pretty straightforward, but note that Position and Color attribute types will also accept C{fife.Point} and C{fife.Color} values. - name: String: The identification of the widget, most useful if it is unique within a given widget hiarachy. This is used to find widgets by L{mapEvents},L{distributeInitialData},L{distributeData} and L{collectData}. - position: Position: The position relative to the parent widget - or on screen, if this is the root widget. - size: Position: The real size of the widget (including border and margins). Usually you do not need to set this. A notable exception is the L{ScrollArea}. - min_size: Position: The minimal size this widget is allowed to have. This is enforced through the accessor methods of the actual size attribute. - max_size: Position: The maximal size this widget is allowed to have. This is enforced through the accessor methods of the actual size attribute. - margins: Integer list: The margin clears an area around an element (outside the border) and is completely transparent. - padding: Integer list: The padding clears an area around the content (inside the border) of the element. - base_color: Color - background_color: Color - foreground_color: Color - selection_color: Color - border_color: Color - outline_color: Color - font: String: This should identify a font that was loaded via L{loadFonts} before. - helptext: Unicode: Text which can be used for e.g. tooltips. - comment: Unicode: Additional text stored by the widget. Not used by PyChan directly. Can be used by the client for additional info about the widget. - border_size: Integer: The size of the border in pixels. - position_technique: This can be either "automatic" or "explicit" - only L{Window} has this set to "automatic" which results in new windows being centered on screen (for now). If it is set to "explicit" the position attribute will not be touched. - vexpand: Bool: True. Proportion to expand this widget vertically. - hexpand: Bool: True. Proportion to expand this widget horizontally. Convenience Attributes ====================== These attributes are convenience/shorthand versions of above mentioned attributes and assignment will reflect the associated attributes values. E.g. the following is equivalent:: # Set X position, leave Y alone widget.x = 10 # Same here posi = widget.position widget.position = (10, posi[1]) Here they are. - x: Integer: The horizontal part of the position attribute. - y: Integer: The vertical part of the position attribute. - width: Integer: The horizontal part of the size attribute. - height: Integer: The vertical part of the size attribute. """ ATTRIBUTES = [ Attr('name'), PointAttr('position'), PointAttr('min_size'), PointAttr('size'), PointAttr('max_size'), PointAttr('fixed_size'), IntListAttr('margins'), IntListAttr('padding'), ColorAttr('base_color'), ColorAttr('background_color'), ColorAttr('foreground_color'), ColorAttr('selection_color'), ColorAttr('border_color'), ColorAttr('outline_color'), Attr('style'), Attr('font'), IntAttr('border_size'), IntAttr('outline_size'), Attr('position_technique'), BoolAttr('vexpand'), BoolAttr('hexpand'), UnicodeAttr('helptext'), BoolAttr('is_focusable'), UnicodeAttr('comment') ] DEFAULT_NAME = '__unnamed__' DEFAULT_HEXPAND = False DEFAULT_VEXPAND = False DEFAULT_MAX_SIZE = 500000, 500000 DEFAULT_SIZE = -1, -1 DEFAULT_MIN_SIZE = 0, 0 DEFAULT_MARGINS = 0, 0 DEFAULT_PADDING = 0 DEFAULT_HELPTEXT = u"" DEFAULT_POSITION = 0, 0 DEFAULT_FONT = "default" DEFAULT_BORDER_SIZE = 0 DEFAULT_OUTLINE_SIZE = 0 DEFAULT_POSITION_TECHNIQUE = "explicit" DEFAULT_COMMENT = u"" HIDE_SHOW_ERROR = """\ You can only show/hide the top widget of a hierachy. Use 'addChild' or 'removeChild' to add/remove labels for example. """ def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None): # Make sure the real_widget has been created assert (hasattr(self, 'real_widget')) self.event_mapper = events.EventMapper(self) # Flag to indicate if the Widget is added to the Manager self._added = False # Flag to indicate if the Widget is added to # the top Widget list of the Manager self._top_added = False # Only needed for tabs self.tab = None # Data distribution & retrieval settings self.accepts_data = False self.accepts_initial_data = False #set all defaults if get_manager().compat_layout: self.hexpand, self.vexpand = 0, 0 else: self.hexpand = self.DEFAULT_HEXPAND self.vexpand = self.DEFAULT_VEXPAND self.name = self.DEFAULT_NAME self.has_name = False self.position = self.DEFAULT_POSITION self.position_technique = self.DEFAULT_POSITION_TECHNIQUE self.font = self.DEFAULT_FONT self.min_size = self.DEFAULT_MIN_SIZE self.max_size = self.DEFAULT_MAX_SIZE self.size = self.DEFAULT_SIZE self.margins = self.DEFAULT_MARGINS self.padding = self.DEFAULT_PADDING self.border_size = self.DEFAULT_BORDER_SIZE self.outline_size = self.DEFAULT_OUTLINE_SIZE self.helptext = self.DEFAULT_HELPTEXT self.comment = self.DEFAULT_COMMENT self._usedPrefixes = [] # Parent attribute makes sure we only have one parent, # that tests self.__parent - so make sure we have the attr here. self.__parent = None self.parent = parent # Inherit and apply style if style is None and parent: style = parent.style self.style = style or "default" # override everything style has set if vexpand is not None: self.vexpand = vexpand if hexpand is not None: self.hexpand = hexpand if name is not None: self.name = name self.has_name = True if position is not None: self.position = position if position_technique is not None: self.position_technique = position_technique if font is not None: self.font = font # only set this if it's provided if is_focusable is not None: self.is_focusable = is_focusable if min_size is not None: self.min_size = min_size if max_size is not None: self.max_size = max_size if size is not None: self.size = size if fixed_size is not None: self.fixed_size = fixed_size if margins is not None: self.margins = margins if padding is not None: self.padding = padding if border_size is not None: self.border_size = border_size if outline_size is not None: self.outline_size = outline_size if helptext is not None: self.helptext = helptext if comment is not None: self.comment = comment # these are set in the default style if base_color is not None: self.base_color = base_color if background_color is not None: self.background_color = background_color if foreground_color is not None: self.foreground_color = foreground_color if selection_color is not None: self.selection_color = selection_color if border_color is not None: self.border_color = border_color if outline_color is not None: self.outline_color = outline_color # add this widget to the manager get_manager().addWidget(self) def clone(self, prefix): """ Clones this widget. Concrete widgets should implement this one, if not, an exception should be raised. Prefix is used to create the name of the cloned widget. """ raise RuntimeError("No implementation of clone method for %s" % self.__class__) def execute(self, bind, focus=None): """ Execute a dialog synchronously. As argument a dictionary mapping widget names to return values is expected. Events from these widgets will cause this function to return with the associated return value. This function will not return until such an event occurs. The widget will be shown before execution and hidden afterwards. You can only execute root widgets. @param focus: name of child widget which should have focus. Defaults to main widget. Note: This feature is not tested well, and the API will probably change. Otherwise have fun:: # Okay this a very condensed example :-) return pychan.loadXML("contents/gui/dialog.xml").execute({ 'okButton' : True, 'closeButton' : False }) """ if not get_manager().can_execute: raise RuntimeError("Synchronous execution is not set up!") if self.__parent: raise RuntimeError("You can only 'execute' root widgets, not %s!" % str(self)) for name, returnValue in list(bind.items()): def _quitThisDialog(returnValue=returnValue): get_manager().breakFromMainLoop(returnValue) self.hide() self.findChild(name=name).capture(_quitThisDialog, group_name="__execute__") self.show() if focus and self.findChild(name=focus): self.findChild(name=focus).is_focusable = True self.findChild(name=focus).requestFocus() else: self.is_focusable = True self.requestFocus() return get_manager().mainLoop() def requestFocus(self): """ Requests focus. The widget must be focusable in order for this to work. See the is_focusable property. """ if self.isVisible(): self.real_widget.requestFocus() def isModalFocusable(self): """ Checks if a widget is modal focusable. True if no other widget has modal focus, false otherwise. """ return self.real_widget.isModalFocusable() def isModalFocused(self): """ Checks if the widget or it's parent has modal focus. """ return self.real_widget.isModalFocused() def requestModalFocus(self): """ Requests modal focus. When a widget has modal focus, only that widget and it's children may recieve input. The widget must be modal focusable in order for this to work. Therefore, no other widget should has modal focus. """ if self.isVisible(): if self.isModalFocusable(): self.real_widget.requestModalFocus() def releaseModalFocus(self): """ Releases modal focus. """ if self.isModalFocused(): self.real_widget.releaseModalFocus() def isModalMouseInputFocusable(self): """ Checks if a widget is modal mouse input focusable. True if no other widget has modal mouse input focus, false otherwise. """ return self.real_widget.isModalMouseInputFocusable() def isModalMouseInputFocused(self): """ Checks if the widget or it's parent has modal mouse input focus. """ return self.real_widget.isModalMouseInputFocused() def requestModalMouseInputFocus(self): """ Requests modal mouse input focus. When a widget has modal input focus that widget will be the only widget receiving input even if the input occurs outside of the widget and no matter what the input is. The widget must be modal mouse input focusable in order for this to work. Therefore, no other widget should has modal input focus. """ if self.isVisible(): if self.isModalMouseInputFocusable(): self.real_widget.requestModalMouseInputFocus() def releaseModalMouseInputFocus(self): """ Releases modal mouse input focus. """ if self.isModalMouseInputFocused(): self.real_widget.releaseModalMouseInputFocus() def match(self, **kwargs): """ Matches the widget against a list of key-value pairs. Only if all keys are attributes and their value is the same it returns True. """ for k, v in list(kwargs.items()): if v != getattr(self, k, None): return False return True def capture(self, callback, event_name="action", group_name="default"): """ Add a callback to be executed when the widget event occurs on this widget. The callback must be either a callable or None. The old event handler (if any) will be overridden by the callback. If None is given, the event will be disabled. You can query L{isCaptured} wether this widgets events are currently captured. It might be useful to check out L{tools.callbackWithArguments}. @param callback: Event callback - may accept keyword arguments event and widget. @param event_name: The event to capture - may be one of L{events.EVENTS} and defaults to "action" @param group_name: Event group. Event groups are used to have different B{channels} which don't interfere with each other. For derived widgets that need to capture events it's advised to use the group_name 'widget'. The 'default' group is used by default, and should be reserved for the application programmers. """ self.event_mapper.capture(event_name, callback, group_name) def isCaptured(self): """ Check whether this widgets events are captured (a callback is installed) or not. """ return bool(self.event_mapper.getCapturedEvents()) def show(self): """ Show the widget and all contained widgets. """ # add this widget to the manager if not self._added: get_manager().addWidget(self) if self.parent is None and not self._top_added: get_manager().addTopWidget(self) # add childs of this widget to the manager def _show(shown_widget): get_manager().addWidget(shown_widget) self.deepApply(_show) if self.isVisible() and self.isSetVisible(): self.beforeShow() self.adaptLayout() if self.parent is None: get_manager().placeWidget(self, self.position_technique) return self.beforeShow() # Show real widget to distribute a widgetShown event. self.real_widget.setVisible(True) self.adaptLayout() if self.parent is None: get_manager().placeWidget(self, self.position_technique) def hide(self, free=False): """ Hide the widget and all contained widgets. """ # remove this widget from the manager if self._added: get_manager().removeWidget(self) if self.parent is None and self._top_added: get_manager().removeTopWidget(self) # remove childs of this widget from the manager def _hide(hidden_widget): get_manager().removeWidget(hidden_widget) self.deepApply(_hide) if not self.isVisible() and not self.isSetVisible(): self.adaptLayout() self.afterHide() return # Hide real widget to distribute a widgetHidden event. self.real_widget.setVisible(False) if free: if self.parent: self.parent.removeChild(self) self.removeAllChildren() self.adaptLayout() self.afterHide() def isVisible(self): """ Check whether the widget is currently shown, either directly or as part of a container widget. """ return self.real_widget.isVisible() def isSetVisible(self): """ Check the real widget visible flag. It checks not if the widget is currently shown! This is needed e.g. if the parent is already hidden but we want to hide the child too. """ return self.real_widget.isSetVisible() def adaptLayout(self, recurse=True): """ Execute the Layout engine. Automatically called by L{show}. In case you want to relayout a visible widget. This function will automatically perform the layout adaption from the top-most layouted widget. To make this clear consider this arrangement:: VBox 1 - Container VBox 2 - HBox - Label If you call adaptLayout on the Label the layout from the VBox 2 will get recalculated, while the VBox 1 stays untouched. @param recurse: Pass False here to force the layout to start from this widget. """ self.real_widget.adaptLayout(recurse) def beforeShow(self): """ This method is called just before the widget is shown. You can override this in derived widgets to add finalization behaviour. NOTE: - if your widget is a container, you have to call _resetTiling(), as you will loose this call by using your override method """ def afterHide(self): """ This method is called just before the widget is hidden. You can override this in derived widgets to add finalization behaviour. """ def findChildren(self, **kwargs): """ Find all contained child widgets by attribute values. Usage:: closeButtons = root_widget.findChildren(name='close') buttons = root_widget.findChildren(__class__=pychan.widgets.Button) """ children = [] def _childCollector(widget): if widget.match(**kwargs): children.append(widget) self.deepApply(_childCollector) return children def getNamedChildren(self, include_unnamed=False): """ Create a dictionary of child widgets with the keys being their name. This will contain only Widgets which have a name different from "__unnamed__" (which is the default). @param include_unnamed: Defaults to false. If this is true unnamed widgets are added, too. The values are lists of widgets, so not only unique names are handled correctly. Usage:: children = widget.getNamedChildren() for widget in children.get("info",[]) print widget.name , " == info" """ children = {} if include_unnamed: def _childCollector(widget): children.setdefault(widget._name, []).append(widget) else: def _childCollector(widget): if widget.has_name: children.setdefault(widget._name, []).append(widget) self.deepApply(_childCollector) return children def findChild(self, **kwargs): """ Find the first contained child widgets by attribute values. Usage:: closeButton = root_widget.findChild(name='close') """ if list(kwargs.keys()) == ["name"]: return self.findChildByName(kwargs["name"]) children = self.findChildren(**kwargs) if children: return children[0] return None def findChildByName(self, name): """ Find first contained child widget by its name. Note that this is the fast version of findChild(name="...") and that you don't have to call this explicitly, it is used if possible. """ result = [] def _childCollector(widget): if widget._name == name: result.append(widget) raise StopTreeWalking try: self.deepApply(_childCollector) except StopTreeWalking: return result[0] return None def addChild(self, widget): """ This function adds a widget as child widget and is only implemented in container widgets. You'll need to call L{adaptLayout} if the container is already shown, to adapt the layout to the new widget. This doesn't happen automatically. """ raise RuntimeError( "Trying to add a widget to %s, which doesn't allow this." % repr(self)) def insertChild(self, widget, position): """ This function inserts a widget a given index in the child list. See L{addChild} and L{insertChildBefore} """ raise RuntimeError( "Trying to insert a widget to %s, which doesn't allow this." % repr(self)) def insertChildBefore(self, widget, before): """ Inserts a child widget before a given widget. If the widget isn't found, the widget is appended to the children list. See L{addChild} and L{insertChild} """ raise RuntimeError( "Trying to insert a widget to %s, which doesn't allow this." % repr(self)) def addChildren(self, *widgets): """ Add multiple widgets as children. Only implemented for container widgets. See also L{addChild} Usage:: container.addChildren( widget1, widget2, ... ) # or you can use this on a list container.addChildren( [widget1,widget2,...] ) """ if len(widgets) == 1 and not isinstance(widgets[0], Widget): widgets = widgets[0] for widget in widgets: self.addChild(widget) def removeChild(self, widget): """ This function removes a direct child widget and is only implemented in container widgets. You'll need to call L{adaptLayout} if the container is already shown, to adapt the layout to the removed widget. This doesn't happen automatically. """ raise RuntimeError( "Trying to remove a widget from %s, which is not a container widget." % repr(self)) def removeChildren(self, *widgets): """ Remove a list of direct child widgets. All widgets have to be direct child widgets. To 'clear' a container take a look at L{removeAllChildren}. See also L{removeChild}. Usage:: container.removeChildren( widget1, widget2, ... ) # or you can use this on a list container.removeChildren( [widget1,widget2,...] ) """ if len(widgets) == 1 and not isinstance(widgets[0], Widget): widgets = widgets[0] for widget in widgets: self.removeChild(widget) def removeAllChildren(self): """ This function will remove all direct child widgets. This will work even for non-container widgets. """ children = self.findChildren(parent=self) for widget in children: self.removeChild(widget) def mapEvents(self, eventMap, ignoreMissing=False): """ Convenience function to map widget events to functions in a batch. Subsequent calls of mapEvents will merge events with different widget names and override the previously set callback. You can also pass C{None} instead of a callback, which will disable the event completely. @param eventMap: A dictionary with widget/event names as keys and callbacks as values. @param ignoreMissing: Normally this method raises a RuntimeError, when a widget can not be found - this behaviour can be overriden by passing True here. The keys in the dictionary are parsed as C{"widgetName/eventName"} with the slash separating the two. If no slash is found the eventName is assumed to be "action". Additionally you can supply a group name or channel C{"widgetName/eventName/groupName"}. Event handlers from one group are not overridden by handlers from another group. The default group name is C{"default"}. Example:: guiElement.mapEvents({ "button" : guiElement.hide, "button/mouseEntered" : toggleButtonColorGreen, "button/mouseExited" : toggleButtonColorBlue, }) """ children = self.getNamedChildren(include_unnamed=True) for descr, func in list(eventMap.items()): name, event_name, group_name = events.splitEventDescriptor(descr) #print name, event_name, group_name widgets = children.get(name, []) if widgets: for widget in widgets: widget.capture(func, event_name=event_name, group_name=group_name) elif not ignoreMissing: raise RuntimeError("No widget with the name: %s" % name) def setInitialData(self, data): """ Set the initial data on a widget, what this means depends on the Widget. In case the widget does not accept initial data, a L{RuntimeError} is thrown. """ if not self.accepts_initial_data: raise RuntimeError( "Trying to set data on a widget that does not accept initial data. Widget: %s Data: %s " % (repr(self), repr(data))) self._realSetInitialData(data) def setData(self, data): """ Set the user-mutable data on a widget, what this means depends on the Widget. In case the widget does not accept data, a L{RuntimeError} is thrown. This is inverse to L{getData}. """ if not self.accepts_data: raise RuntimeError( "Trying to set data on a widget that does not accept data.") self._realSetData(data) def getData(self): """ Get the user-mutable data of a widget, what this means depends on the Widget. In case the widget does not have user mutable data, a L{RuntimeError} is thrown. This is inverse to L{setData}. """ if not self.accepts_data: raise RuntimeError( "Trying to retrieve data from a widget that does not accept data." ) return self._realGetData() def distributeInitialData(self, initialDataMap): """ Distribute B{initial} (not mutable by the user) data from a dictionary over the widgets in the hierachy using the keys as names and the values as the data (which is set via L{setInitialData}). If more than one widget matches - the data is set on ALL matching widgets. By default a missing widget is just ignored. Use it like this:: guiElement.distributeInitialData({ 'myTextField' : 'Hello World!', 'myListBox' : ["1","2","3"] }) """ children = self.getNamedChildren(include_unnamed=True) for name, data in list(initialDataMap.items()): widgetList = children.get(name, []) for widget in widgetList: widget.setInitialData(data) def distributeData(self, dataMap): """ Distribute data from a dictionary over the widgets in the hierachy using the keys as names and the values as the data (which is set via L{setData}). This will only accept unique matches. Use it like this:: guiElement.distributeData({ 'myTextField' : 'Hello World!', 'myListBox' : ["1","2","3"] }) """ children = self.getNamedChildren(include_unnamed=True) for name, data in list(dataMap.items()): widgetList = children.get(name, []) if len(widgetList) != 1: if get_manager().debug: self.listNamedWidgets() raise RuntimeError( "DistributeData can only handle widgets with unique names." ) widgetList[0].setData(data) def collectDataAsDict(self, widgetNames): """ Collect data from a widget hierachy by names into a dictionary. This can only handle UNIQUE widget names (in the hierachy) and will raise a RuntimeError if the number of matching widgets is not equal to one. Usage:: data = guiElement.collectDataAsDict(['myTextField','myListBox']) print "You entered:",data['myTextField']," and selected ",data['myListBox'] """ children = self.getNamedChildren(include_unnamed=True) dataMap = {} for name in widgetNames: widgetList = children.get(name, []) if len(widgetList) != 1: if get_manager().debug: self.listNamedWidgets() raise RuntimeError( "CollectData can only handle widgets with unique names.") dataMap[name] = widgetList[0].getData() return dataMap def collectData(self, *widgetNames): """ Collect data from a widget hierachy by names. This can only handle UNIQUE widget names (in the hierachy) and will raise a RuntimeError if the number of matching widgets is not equal to one. This function takes an arbitrary number of widget names and returns a list of the collected data in the same order. In case only one argument is given, it will return just the data, with out putting it into a list. Usage:: # Multiple element extraction: text, selected = guiElement.collectData('myTextField','myListBox') print "You entered:",text," and selected item nr",selected # Single elements are handled gracefully, too: test = guiElement.collectData('testElement') """ children = self.getNamedChildren(include_unnamed=True) dataList = [] for name in widgetNames: widgetList = children.get(name, []) if len(widgetList) != 1: if get_manager().debug: self.listNamedWidgets() raise RuntimeError( "CollectData can only handle widgets with unique names.") dataList.append(widgetList[0].getData()) if len(dataList) == 1: return dataList[0] return dataList def listNamedWidgets(self): """ This function will print a list of all currently named child-widgets to the standard output. This is useful for debugging purposes. """ def _printNamedWidget(widget): if widget.name != Widget.DEFAULT_NAME: print(widget.name.ljust(20), repr(widget).ljust(50), repr(widget.__parent)) print("Named child widgets of ", repr(self)) print("name".ljust(20), "widget".ljust(50), "parent") self.deepApply(_printNamedWidget) def stylize(self, style, **kwargs): """ Recursively apply a style to all widgets. """ def _restyle(widget): get_manager().stylize(widget, style, **kwargs) self.deepApply(_restyle) def resizeToContent(self, recurse=True): """ Try to shrink the widget, so that it fits closely around its content. Do not call directly. """ self.real_widget.resizeToContent(recurse) def expandContent(self, recurse=True): """ Try to expand any spacer in the widget within the current size. Do not call directly. """ self.real_widget.expandContent(recurse) def _recursiveResizeToContent(self): """ Recursively call L{resizeToContent}. Uses L{deepApply}. Do not call directly. """ self.real_widget.resizeToContent(True) def _recursiveExpandContent(self): """ Recursively call L{expandContent}. Uses L{deepApply}. Do not call directly. """ self.real_widget.expandContent(True) def deepApply(self, visitorFunc, leaves_first=True, shown_only=False): """ Recursively apply a callable to all contained widgets and then the widget itself. """ visitorFunc(self) def getAbsolutePos(self): """ Get absolute position on screen """ absX = self.x absY = self.y parent = self.parent while parent is not None: absX += parent.x absY += parent.y parent = parent.parent return (absX, absY) def sizeChanged(self): pass def __str__(self): return "%s(name='%s')" % (self.__class__.__name__, self.name) def __repr__(self): return "<%s(name='%s') at %x>" % (self.__class__.__name__, self.name, id(self)) def _setSize(self, size): if isinstance(size, fife.Point): self.width, self.height = size.x, size.y else: self.width, self.height = size def _getSize(self): return self.width, self.height def _setPosition(self, size): if isinstance(size, fife.Point): self.x, self.y = size.x, size.y else: self.x, self.y = size def _getPosition(self): return self.x, self.y def _setX(self, x): self.real_widget.setX(x) def _getX(self): return self.real_widget.getX() def _setY(self, y): self.real_widget.setY(y) def _getY(self): return self.real_widget.getY() def _setWidth(self, w): old_width = self.width self.real_widget.setWidth(w) w = self.real_widget.getWidth() if w != old_width: self.sizeChanged() def _getWidth(self): return self.real_widget.getWidth() def _setHeight(self, h): old_height = self.height self.real_widget.setHeight(h) h = self.real_widget.getHeight() if h != old_height: self.sizeChanged() def _getHeight(self): return self.real_widget.getHeight() def _getMinWidth(self): return self.min_size[0] def _getMaxWidth(self): return self.max_size[0] def _getMinHeight(self): return self.min_size[1] def _getMaxHeight(self): return self.max_size[1] def _setMinWidth(self, w): self.min_size = w, self.min_size[1] def _setMaxWidth(self, w): self.max_size = w, self.max_size[1] def _setMinHeight(self, h): self.min_size = self.min_size[0], h def _setMaxHeight(self, h): self.max_size = self.max_size[0], h def _setMinSize(self, size): self.real_widget.setMinSize(fifechan.Size(size[0], size[1])) def _getMinSize(self): size = self.real_widget.getMinSize() return (size.getWidth(), size.getHeight()) def _setMaxSize(self, size): self.real_widget.setMaxSize(fifechan.Size(size[0], size[1])) def _getMaxSize(self): size = self.real_widget.getMaxSize() return (size.getWidth(), size.getHeight()) def _setFixedSize(self, size): self.real_widget.setFixedSize(fifechan.Size(size[0], size[1])) def _getFixedSize(self): size = self.real_widget.getFixedSize() return (size.getWidth(), size.getHeight()) def isFixedSize(self): return self.real_widget.isFixedSize() def _setFont(self, font): self._font = font self.real_font = get_manager().getFont(font) self.real_widget.setFont(self.real_font) def _getFont(self): return self._font def _getOutlineSize(self): return self.real_widget.getOutlineSize() def _setOutlineSize(self, size): self.real_widget.setOutlineSize(size) def _getBorderSize(self): return self.real_widget.getBorderSize() def _setBorderSize(self, size): self.real_widget.setBorderSize(size) def _setMargins(self, margin): # Shorthand property if isinstance(margin, tuple) or isinstance(margin, list): if len(margin) == 4: # 0=top, 1=right, 2=bottom, 3=left self.real_widget.setMarginTop(margin[0]) self.real_widget.setMarginRight(margin[1]) self.real_widget.setMarginBottom(margin[2]) self.real_widget.setMarginLeft(margin[3]) elif len(margin) == 3: # 0=top, 1=right, 2=bottom, 1=left self.real_widget.setMarginTop(margin[0]) self.real_widget.setMarginRight(margin[1]) self.real_widget.setMarginBottom(margin[2]) self.real_widget.setMarginLeft(margin[1]) elif len(margin) == 2: # 0=top, 1=right, 0=bottom, 1=left self.real_widget.setMarginTop(margin[0]) self.real_widget.setMarginRight(margin[1]) self.real_widget.setMarginBottom(margin[0]) self.real_widget.setMarginLeft(margin[1]) elif len(margin) == 1: # 0=top, 0=right, 0=bottom, 0=left self.real_widget.setMargin(margin[0]) else: self.real_widget.setMargin(margin) def _getMargins(self): return (self.real_widget.getMarginTop(), self.real_widget.getMarginRight(), self.real_widget.getMarginBottom(), self.real_widget.getMarginLeft()) def _setPadding(self, padding): # Shorthand property if isinstance(padding, tuple) or isinstance(padding, list): if len(padding) == 4: # 0=top, 1=right, 2=bottom, 3=left self.real_widget.setPaddingTop(padding[0]) self.real_widget.setPaddingRight(padding[1]) self.real_widget.setPaddingBottom(padding[2]) self.real_widget.setPaddingLeft(padding[3]) elif len(padding) == 3: # 0=top, 1=right, 2=bottom, 1=left self.real_widget.setPaddingTop(padding[0]) self.real_widget.setPaddingRight(padding[1]) self.real_widget.setPaddingBottom(padding[2]) self.real_widget.setPaddingLeft(padding[1]) elif len(padding) == 2: # 0=top, 1=right, 0=bottom, 1=left self.real_widget.setPaddingTop(padding[0]) self.real_widget.setPaddingRight(padding[1]) self.real_widget.setPaddingBottom(padding[0]) self.real_widget.setPaddingLeft(padding[1]) elif len(padding) == 1: # 0=top, 0=right, 0=bottom, 0=left self.real_widget.setPadding(padding[0]) else: self.real_widget.setPadding(padding) def _getPadding(self): return (self.real_widget.getPaddingTop(), self.real_widget.getPaddingRight(), self.real_widget.getPaddingBottom(), self.real_widget.getPaddingLeft()) base_color = ColorProperty("BaseColor") background_color = ColorProperty("BackgroundColor") foreground_color = ColorProperty("ForegroundColor") selection_color = ColorProperty("SelectionColor") outline_color = ColorProperty("OutlineColor") border_color = ColorProperty("BorderColor") def _getStyle(self): return self._style def _setStyle(self, style): self._style = style get_manager().stylize(self, style) style = property(_getStyle, _setStyle) def _getParent(self): if self.__parent is not None: return self.__parent() return None def _setParent(self, parent): if parent and not issubclass(type(parent), Widget): raise RuntimeError("Parent must be subclass of the Widget type.") if self.__parent is not None and self.__parent() is not parent: if self.__parent() is not None and parent is not None: print("Widget containment fumble:", self, self.__parent, parent) self.__parent().removeChild(self) if parent is not None: self.__parent = weakref.ref(parent) else: self.__parent = None parent = property(_getParent, _setParent) def _setName(self, name): self._name = name if name != Widget.DEFAULT_NAME: self.has_name = True def _getName(self): # __str__ relies on self.name return getattr(self, '_name', '__no_name_yet__') name = property(_getName, _setName) def _setFocusable(self, b): self.real_widget.setFocusable(b) def _isFocusable(self): return self.real_widget.isFocusable() def _setHExpand(self, expand): self.real_widget.setHorizontalExpand(expand) def _isHExpand(self): return self.real_widget.isHorizontalExpand() def _setVExpand(self, expand): self.real_widget.setVerticalExpand(expand) def _isVExpand(self): return self.real_widget.isVerticalExpand() def _createNameWithPrefix(self, prefix): if not isinstance(prefix, str): raise RuntimeError("Widget names should be prefixed with a string") if prefix in self._usedPrefixes: raise RuntimeError("Widget %s already cloned with prefix %s" % (self.name, prefix)) if len(prefix) == 0: raise RuntimeError( "New widget name cannot be created with an empty prefix") self._usedPrefixes.append(prefix) return prefix + self.name x = property(_getX, _setX) y = property(_getY, _setY) width = property(_getWidth, _setWidth) height = property(_getHeight, _setHeight) min_width = property(_getMinWidth, _setMinWidth) min_height = property(_getMinHeight, _setMinHeight) max_width = property(_getMaxWidth, _setMaxWidth) max_height = property(_getMaxHeight, _setMaxHeight) size = property(_getSize, _setSize) min_size = property(_getMinSize, _setMinSize) max_size = property(_getMaxSize, _setMaxSize) fixed_size = property(_getFixedSize, _setFixedSize) position = property(_getPosition, _setPosition) font = property(_getFont, _setFont) outline_size = property(_getOutlineSize, _setOutlineSize) border_size = property(_getBorderSize, _setBorderSize) is_focusable = property(_isFocusable, _setFocusable) margins = property(_getMargins, _setMargins) padding = property(_getPadding, _setPadding) is_focusable = property(_isFocusable, _setFocusable) hexpand = property(_isHExpand, _setHExpand) vexpand = property(_isVExpand, _setVExpand)
class Slider(Widget): """ A slider widget Use a callback to read out the slider value every time the marker is moved. New Attributes ============== - orientation: 1 = horizontal, 0=vertical - scale_start: float: default 0.0 - scale_end: float: default 1.0 - step_length: float: default scale_end/10 - marker_length: int: default 10 FIXME: - update docstrings """ HORIZONTAL = fifechan.Slider.Horizontal VERTICAL = fifechan.Slider.Vertical ATTRIBUTES = Widget.ATTRIBUTES + [ IntAttr('orientation'), FloatAttr('scale_start'), FloatAttr('scale_end'), FloatAttr('step_length'), IntAttr('marker_length') ] DEFAULT_HEXPAND = True DEFAULT_VEXPAND = False DEFAULT_SIZE = 10, 10 DEFAULT_MIN_SIZE = 10, 10 DEFAULT_SCALE_START = 0.0 DEFAULT_SCALE_END = 1.0 DEFAULT_STEP_LENGTH = 0.1 DEFAULT_MARKER_LENGTH = 10 DEFAULT_ORIENTATION = HORIZONTAL def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, fixed_size=None, margins=None, padding=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_color=None, outline_color=None, border_size=None, outline_size=None, position_technique=None, is_focusable=None, comment=None, scale_start=None, scale_end=None, step_length=None, marker_length=None, orientation=None): self.real_widget = fifechan.Slider( scale_start or self.DEFAULT_SCALE_START, scale_end or self.DEFAULT_SCALE_END) self.orientation = self.DEFAULT_ORIENTATION self.step_length = self.DEFAULT_STEP_LENGTH self.marker_length = self.DEFAULT_MARKER_LENGTH super(Slider, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if orientation is not None: self.orientation = orientation if scale_start is not None: self.scale_start = scale_start if scale_end is not None: self.scale_end = scale_end if step_length is not None: self.step_length = step_length if marker_length is not None: self.marker_length = marker_length self.accepts_data = True self._realSetData = self._setValue self._realGetData = self._getValue def clone(self, prefix): sliderClone = Slider( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.scale_start, self.scale_end, self.step_length, self.marker_length, self.orientation) return sliderClone def _setScale(self, start, end): """setScale(self, double scaleStart, double scaleEnd)""" if type(start) != float: raise RuntimeError("Slider expects float for start scale") if type(end) != float: raise RuntimeError("Slider expects float for end scale") self.real_widget.setScale(start, end) def _getScaleStart(self): """getScaleStart(self) -> double""" return self.real_widget.getScaleStart() def _setScaleStart(self, start): """setScaleStart(self, double scaleStart)""" if type(start) != float: raise RuntimeError("Slider expects float for start scale") self.real_widget.setScaleStart(start) scale_start = property(_getScaleStart, _setScaleStart) def _getScaleEnd(self): """getScaleEnd(self) -> double""" return self.real_widget.getScaleEnd() def _setScaleEnd(self, end): """setScaleEnd(self, double scaleEnd)""" if type(end) != float: raise RuntimeError("Slider expects float for end scale") self.real_widget.setScaleEnd(end) scale_end = property(_getScaleEnd, _setScaleEnd) def _getValue(self): """getValue(self) -> double""" return self.real_widget.getValue() def _setValue(self, value): """setValue(self, double value)""" if type(value) != float: raise RuntimeError("Slider only accepts float values") self.real_widget.setValue(value) value = property(_getValue, _setValue) def _setMarkerLength(self, length): """setMarkerLength(self, int length)""" if type(length) != int: raise RuntimeError("Slider only accepts int for Marker length") self.real_widget.setMarkerLength(length) def _getMarkerLength(self): """getMarkerLength(self) -> int""" return self.real_widget.getMarkerLength() marker_length = property(_getMarkerLength, _setMarkerLength) def _setOrientation(self, orientation): """setOrientation(self, Orientation orientation)""" self.real_widget.setOrientation(orientation) def _getOrientation(self): """getOrientation(self) -> int""" return self.real_widget.getOrientation() orientation = property(_getOrientation, _setOrientation) def _setStepLength(self, length): """setStepLength(self, double length)""" if type(length) != float: raise RuntimeError("Slider only accepts floats for step length") self.real_widget.setStepLength(length) def _getStepLength(self): """getStepLength(self) -> double""" return self.real_widget.getStepLength() step_length = property(_getStepLength, _setStepLength)
class CurveGraph(Widget): """ A curve graph widget New Attributes ============== - coordinates: int list: x and y coordinates - thickness': int: Line thickness, default 1 - controll_points: bool: Adds internal controll points, default True - opaque: bool: default False """ ATTRIBUTES = Widget.ATTRIBUTES + [ IntListAttr('coordinates'), IntAttr('thickness'), BoolAttr('controll_points'), BoolAttr('opaque') ] DEFAULT_HEXPAND = False DEFAULT_VEXPAND = False DEFAULT_THICKNESS = 1 DEFAULT_CONTROLL_POINTS = True DEFAULT_OPAQUE = False def __init__(self, parent = None, name = None, size = None, min_size = None, max_size = None, fixed_size = None, margins = None, padding = None, helptext = None, position = None, style = None, hexpand = None, vexpand = None, font = None, base_color = None, background_color = None, foreground_color = None, selection_color = None, border_color = None, outline_color = None, border_size = None, outline_size = None, position_technique = None, is_focusable = None, comment = None, opaque = None, coordinates = None, thickness = None, controll_points = None): self.real_widget = fifechan.CurveGraph() self.opaque = self.DEFAULT_OPAQUE self.thickness = self.DEFAULT_THICKNESS self.controll_points = self.DEFAULT_CONTROLL_POINTS super(CurveGraph, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, fixed_size=fixed_size, margins=margins, padding=padding, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_color=border_color, outline_color=outline_color, border_size=border_size, outline_size=outline_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if opaque is not None: self.opaque = opaque if coordinates is not None: self.coordinates = coordinates if thickness is not None: self.thickness = thickness if controll_points is not None: self.controll_points = controll_points def clone(self, prefix): curveGraphClone = CurveGraph(None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.fixed_size, self.margins, self.padding, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_color, self.outline_color, self.border_size, self.outline_size, self.position_technique, self.is_focusable, self.comment, self.opaque, self.coordinates, self.thickness, self.controll_points) return curveGraphClone def _setOpaque(self, opaque): self.real_widget.setOpaque(opaque) def _getOpaque(self): return self.real_widget.isOpaque() opaque = property(_getOpaque, _setOpaque) def _setCoordinates(self, coordinates): # reset if coordinates is None or len(coordinates) == 0: self.real_widget.resetPointVector() return # int list to point vector if isinstance(coordinates[0], int): coords = fifechan.FcnPointVector() for i in range(0, len(coordinates)-1, 2): coords.append(fifechan.Point(coordinates[i], coordinates[i+1])) self.real_widget.setPointVector(coords) return self.real_widget.setPointVector(coordinates) def _getCoordinates(self): return self.real_widget.getPointVector() coordinates = property(_getCoordinates, _setCoordinates) def _setThickness(self, thickness): self.real_widget.setThickness(thickness) def _getThickness(self): return self.real_widget.getThickness() thickness = property(_getThickness, _setThickness) def _setControllPoints(self, controll): self.real_widget.setAutomaticControllPoints(controll) def _getControllPoints(self): return self.real_widget.isAutomaticControllPoints() controll_points = property(_getControllPoints, _setControllPoints)
class Container(Widget): """ This is the basic container class. It provides space in which child widgets can be position via the position attribute. If you want to use the layout engine, you have to use derived containers with vertical or horizontal orientation (L{VBox} or L{HBox}) New Attributes ============== - padding - Integer: Not used in the Container class istelf, distance between child widgets. - background_image - Set this to a GuiImage or a resource location (simply a filename). The image will be tiled over the background area. - opaque - Boolean: Whether the background should be drawn at all. Set this to False to make the widget transparent. - children - Just contains the list of contained child widgets. Do NOT modify. """ ATTRIBUTES = Widget.ATTRIBUTES + [ IntAttr('padding'), Attr('background_image'), BoolAttr('opaque'), PointAttr('margins') ] DEFAULT_OPAQUE = True DEFAULT_MARGINS = 5, 5 DEFAULT_PADDING = 5 DEFAULT_BACKGROUND = None def __init__(self, parent=None, name=None, size=None, min_size=None, max_size=None, helptext=None, position=None, style=None, hexpand=None, vexpand=None, font=None, base_color=None, background_color=None, foreground_color=None, selection_color=None, border_size=None, position_technique=None, is_focusable=None, comment=None, padding=None, background_image=None, opaque=None, margins=None, _real_widget=None): self.real_widget = _real_widget or fifechan.Container() self.children = [] self.children_position_cache = [] self._background = [] self._background_image = None self.background_image = self.DEFAULT_BACKGROUND self.margins = self.DEFAULT_MARGINS self.padding = self.DEFAULT_PADDING self.opaque = self.DEFAULT_OPAQUE super(Container, self).__init__(parent=parent, name=name, size=size, min_size=min_size, max_size=max_size, helptext=helptext, position=position, style=style, hexpand=hexpand, vexpand=vexpand, font=font, base_color=base_color, background_color=background_color, foreground_color=foreground_color, selection_color=selection_color, border_size=border_size, position_technique=position_technique, is_focusable=is_focusable, comment=comment) if margins is not None: self.margins = margins if padding is not None: self.padding = padding if opaque is not None: self.opaque = opaque if background_image is not None: self.background_image = background_image def clone(self, prefix): containerClone = Container( None, self._createNameWithPrefix(prefix), self.size, self.min_size, self.max_size, self.helptext, self.position, self.style, self.hexpand, self.vexpand, self.font, self.base_color, self.background_color, self.foreground_color, self.selection_color, self.border_size, self.position_technique, self.is_focusable, self.comment, self.padding, self.background_image, self.opaque, self.margins) containerClone.addChildren(self._cloneChildren(prefix)) return containerClone def addChild(self, widget): """ Adds a child widget to the container. This makes the childs widgets visible state the same as the containers. i.e. if the containter is visible the child will be as well and if the container widget is hidden so will the child. The child however WILL be shown when you show the container widget. If you want the child to be hidden when you show the container widget you must call child.hide(). """ if isinstance(widget, Spacer): self.addSpacer(widget) return widget.parent = self if widget.max_size[0] > self.max_size[0] or widget.max_size[ 1] > self.max_size[1]: widget.max_size = self.max_size self.children.append(widget) self.children_position_cache.append(widget) self.real_widget.add(widget.real_widget) # add all to the manager def _add(added_widget): if not added_widget._added: get_manager().addWidget(added_widget) if added_widget._top_added: get_manager().removeTopWidget(added_widget) widget.deepApply(_add) def insertChild(self, widget, position): if position > len(self.children) or 0 - position > len(self.children): print "insertChild: Warning: Index overflow.", if position >= 0: self.addChild(widget) else: self.insertChild(widget, 0) return children = self.children[0:position] + [widget ] + self.children[position:] #assert len(children) == len(self.children) + 1 for child_to_remove in self.children[:]: self.removeChild(child_to_remove) for child in children: self.addChild(child) def insertChildBefore(self, widget, before): if before not in self.children: raise RuntimeError( "Couldn't find widget %s as child of %s - in insertChildBefore" % (str(widget), str(before))) self.insertChild(widget, self.children.index(before)) def removeChild(self, widget): if not widget in self.children: raise RuntimeError("%s does not have %s as direct child widget." % (str(self), str(widget))) if widget in self.children: self.children.remove(widget) self.real_widget.remove(widget.real_widget) if widget in self.children_position_cache: self.children_position_cache.remove(widget) widget.parent = None # remove all from the manager def _remove(removed_widget): if removed_widget._added: get_manager().removeWidget(removed_widget) if removed_widget._top_added: get_manager().removeTopWidget(removed_widget) widget.deepApply(_remove) def hideChild(self, child, free=False): # remove child from the manager if child._added: get_manager().removeWidget(child) if child._top_added: get_manager().removeTopWidget(child) # remove childs of the child from the manager def _hide(hidden_widget): get_manager().removeWidget(hidden_widget) child.deepApply(_hide) if child.isVisible() or child.isSetVisible(): # Hide real widget to distribute a widgetHidden event. child.real_widget.setVisible(False) if free: self.removeChild(child) self.adaptLayout() self.afterHide() def showChild(self, child): # add child to the manager if not child._added: get_manager().addWidget(child) # add childs of child to the manager def _show(shown_widget): get_manager().addWidget(shown_widget) child.deepApply(_show) child.beforeShow() if not child.isVisible() or not child.isSetVisible(): # Show real widget to distribute a widgetShown event. child.real_widget.setVisible(True) self.adaptLayout() def add(self, *widgets): print "PyChan: Deprecation warning: Please use 'addChild' or 'addChildren' instead." self.addChildren(*widgets) def getMaxChildrenWidth(self): if not self.children: return 0 w = 0 for widget in self.children: if not widget.real_widget.isVisible(): continue w = max(widget.width, w) return w def getMaxChildrenHeight(self): if not self.children: return 0 h = 0 for widget in self.children: if not widget.real_widget.isVisible(): continue h = max(widget.height, h) return h def deepApply(self, visitorFunc, leaves_first=True, shown_only=False): if not shown_only: children = self.children else: children = filter(lambda w: w.real_widget.isVisible(), self.children) if leaves_first: for child in children: child.deepApply(visitorFunc, leaves_first=leaves_first, shown_only=shown_only) visitorFunc(self) if not leaves_first: for child in children: child.deepApply(visitorFunc, leaves_first=leaves_first, shown_only=shown_only) def beforeShow(self): # This is required because beforeShow() is NOT called on nested # containers or child widgets. This ensures that background tiled # images are shown properly def _resetTilingChildren(widget): tilingMethod = getattr(widget, "_resetTiling", None) if callable(tilingMethod): tilingMethod() self.deepApply(_resetTilingChildren) self._resetTiling() def _resetTiling(self): image = self._background_image if image is None: return back_w, back_h = self.width, self.height map(self.real_widget.remove, self._background) # Now tile the background over the widget self._background = [] icon = fifechan.Icon(image) icon.setTiling(True) icon.setSize(back_w, back_h) self._background.append(icon) map(self.real_widget.add, self._background) icon.requestMoveToBottom() def setBackgroundImage(self, image): self._background = getattr(self, '_background', None) if image is None: self._background_image = None map(self.real_widget.remove, self._background) self._background = [] return # Background generation is done in _resetTiling if not isinstance(image, fife.GuiImage): image = get_manager().loadImage(image) self._background_image = image def getBackgroundImage(self): return self._background_image background_image = property(getBackgroundImage, setBackgroundImage) def _setOpaque(self, opaque): self.real_widget.setOpaque(opaque) def _getOpaque(self): return self.real_widget.isOpaque() opaque = property(_getOpaque, _setOpaque) def _cloneChildren(self, prefix): """ Clones each child and return the clones in a list. """ cloneList = [child.clone(prefix) for child in self.children] return cloneList