# panel.py: wxPanel objects # $Id: panel.py,v 1.38 2007/08/07 12:15:21 agriggio Exp $ # # Copyright (c) 2002-2007 Alberto Griggio # License: MIT (see license.txt) # THIS PROGRAM COMES WITH NO WARRANTY import wx import common, misc from tree import Tree from widget_properties import * from edit_windows import ManagedBase, TopLevelBase class PanelBase(object): _custom_base_classes = True def __init__(self, style=wx.TAB_TRAVERSAL): """\ Class to handle wxPanel objects """ super(PanelBase, self).__init__() self.top_sizer = None # sizer to handle the layout of children # ------ ALB 2005-11-19: option to disable custom class code generation self.no_custom_class = False self.access_functions['no_custom_class'] = (self.get_no_custom_class, self.set_no_custom_class) self.properties['no_custom_class'] = CheckBoxProperty( self, 'no_custom_class', label="Don't generate code for this custom class") # ------ self.style = style self.access_functions['style'] = (self.get_style, self.set_style) self.style_pos = [wx.SIMPLE_BORDER, wx.DOUBLE_BORDER, wx.SUNKEN_BORDER, wx.RAISED_BORDER, wx.STATIC_BORDER, wx.NO_BORDER, wx.NO_3D, wx.TAB_TRAVERSAL, wx.WANTS_CHARS, wx.NO_FULL_REPAINT_ON_RESIZE, wx.FULL_REPAINT_ON_RESIZE, wx.CLIP_CHILDREN] style_labels = ('#section#Style', 'wxSIMPLE_BORDER', 'wxDOUBLE_BORDER', 'wxSUNKEN_BORDER', 'wxRAISED_BORDER', 'wxSTATIC_BORDER', 'wxNO_BORDER', 'wxNO_3D', 'wxTAB_TRAVERSAL', 'wxWANTS_CHARS', 'wxNO_FULL_REPAINT_ON_RESIZE', 'wxFULL_REPAINT_ON_RESIZE', 'wxCLIP_CHILDREN') self.properties['style'] = CheckListProperty(self, 'style', None, style_labels) self.access_functions['scrollable'] = (self.get_scrollable, self.set_scrollable) self.scrollable = False self.properties['scrollable'] = CheckBoxProperty( self, 'scrollable', None) self.scroll_rate = (10, 10) self.access_functions['scroll_rate'] = (self.get_scroll_rate, self.set_scroll_rate) self.properties['scroll_rate'] = TextProperty(self, 'scroll_rate', None, can_disable=True) def finish_widget_creation(self): super(PanelBase, self).finish_widget_creation( sel_marker_parent=self.widget) if not self.scrollable: self.widget.SetScrollRate(0, 0) else: self.widget.SetScrollRate(*self.scroll_rate) # this must be done here since ManagedBase.finish_widget_creation # normally sets EVT_LEFT_DOWN to update_wiew if not self.widget.Disconnect(-1, -1, wx.wxEVT_LEFT_DOWN): print "EditPanel: Unable to disconnect the event hanlder" wx.EVT_LEFT_DOWN(self.widget, self.drop_sizer) #wx.EVT_SCROLLWIN(self.widget, self._update_markers) def _update_markers(self, event): def get_pos(): x, y = self.widget.GetPosition() xx, yy = self.widget.GetViewStart() return x+xx, y+yy old = self.widget.GetPosition self.widget.GetPosition = get_pos #print self.widget, self.sel_marker.owner self.sel_marker.update() self.widget.GetPosition = old event.Skip() def create_properties(self): super(PanelBase, self).create_properties() panel = wx.ScrolledWindow(self.notebook, -1, style=wx.TAB_TRAVERSAL) panel.SetScrollRate(5, 5) szr = wx.BoxSizer(wx.VERTICAL) self.properties['no_custom_class'].display(panel) szr.Add(self.properties['no_custom_class'].panel, 0, wx.EXPAND) label = self.properties['no_custom_class'].cb label.SetToolTip( wx.ToolTip('If this is a custom class, setting this property ' 'prevents wxGlade\nfrom generating the class definition' ' code')) self.properties['style'].display(panel) szr.Add(self.properties['style'].panel, 0, wx.EXPAND) self.properties['scrollable'].display(panel) szr.Add(self.properties['scrollable'].panel, 0, wx.EXPAND) self.properties['scroll_rate'].display(panel) szr.Add(self.properties['scroll_rate'].panel, 0, wx.EXPAND) panel.SetAutoLayout(True) panel.SetSizer(szr) szr.Fit(panel) self.notebook.AddPage(panel, 'Widget') def on_enter(self, event): if not self.top_sizer and common.adding_sizer: self.widget.SetCursor(wx.CROSS_CURSOR) else: self.widget.SetCursor(wx.STANDARD_CURSOR) def set_sizer(self, sizer): self.top_sizer = sizer if self.top_sizer and self.top_sizer.widget and self.widget: self.widget.SetAutoLayout(True) self.widget.SetSizer(self.top_sizer.widget) self.widget.Layout() elif self.top_sizer is None and self.widget: self.widget.SetSizer(None) def drop_sizer(self, event): if self.top_sizer or not common.adding_sizer: self.on_set_focus(event) # default behaviour: call show_properties return self.widget.SetCursor(wx.NullCursor) common.widgets[common.widget_to_add](self, None, None) common.adding_widget = common.adding_sizer = False common.widget_to_add = None common.app_tree.app.saved = False def get_widget_best_size(self): if self.top_sizer and self.widget.GetSizer(): self.top_sizer.fit_parent() return self.widget.GetSize() return wx.ScrolledWindow.GetBestSize(self.widget) def get_style(self): retval = [0] * len(self.style_pos) try: for i in range(len(self.style_pos)): if self.style & self.style_pos[i]: retval[i] = 1 except AttributeError: pass return retval def set_style(self, value): value = self.properties['style'].prepare_value(value) self.style = 0 for v in range(len(value)): if value[v]: self.style |= self.style_pos[v] def get_scrollable(self): return self.scrollable def set_scrollable(self, value): self.scrollable = bool(int(value)) if self.scrollable: if self.klass == 'wxPanel': self.klass = 'wxScrolledWindow' self.klass_prop.set_value(self.klass) else: if self.klass == 'wxScrolledWindow': self.klass = 'wxPanel' self.klass_prop.set_value(self.klass) if not self.widget: return if self.scrollable: self.properties['scroll_rate'].toggle_active(True) self.widget.SetScrollRate(*self.scroll_rate) else: self.properties['scroll_rate'].toggle_active(False) self.widget.SetScrollRate(0, 0) def get_scroll_rate(self): return '%d, %d' % self.scroll_rate def set_scroll_rate(self, value): invalid = False try: srx, sry = [int(t) for t in value.split(',', 1)] if srx < 0 or sry < 0: invalid = True except: invalid = True if invalid: self.properties['scroll_rate'].set_value(self.get_scroll_rate()) return self.scroll_rate = srx, sry if self.widget: self.widget.SetScrollRate(srx, sry) def get_no_custom_class(self): return self.no_custom_class def set_no_custom_class(self, value): self.no_custom_class = bool(int(value)) # end of class PanelBase class EditPanel(PanelBase, ManagedBase): def __init__(self, name, parent, id, sizer, pos, property_window, show=True, style=wx.TAB_TRAVERSAL): """\ Class to handle wxPanel objects """ ManagedBase.__init__(self, name, 'wxPanel', parent, id, sizer, pos, property_window, show=show) PanelBase.__init__(self, style) def create_widget(self): #self.widget = wx.Panel(self.parent.widget, self.id, style=0) self.widget = wx.ScrolledWindow(self.parent.widget, self.id, style=0) wx.EVT_ENTER_WINDOW(self.widget, self.on_enter) self.widget.GetBestSize = self.get_widget_best_size if self.sizer.is_virtual(): def GetBestSize(): if self.widget and self.widget.GetSizer(): return self.widget.GetSizer().GetMinSize() #return wx.Panel.GetBestSize(self.widget) return wx.ScrolledWindow.GetBestSize(self.widget) self.widget.GetBestSize = GetBestSize def set_sizer(self, sizer): super(EditPanel, self).set_sizer(sizer) if self.top_sizer and self.top_sizer.widget and self.widget: self.sizer.set_item(self.pos, size=self.widget.GetBestSize()) def set_scrollable(self, value): super(EditPanel, self).set_scrollable(value) if self.scrollable: # 2003-06-26 ALB: change the "class name", to allow code generation # for a wxScrolledWindow (see Tree.Node.write and # common.class_names usage in xml_parse.py) self._classname = 'EditScrolledWindow' else: self._classname = self.__class__.__name__ def popup_menu(self, event): if self.widget: if not self._rmenu: COPY_ID, REMOVE_ID, CUT_ID = [wx.NewId() for i in range(3)] self._rmenu = misc.wxGladePopupMenu(self.name) misc.append_item(self._rmenu, REMOVE_ID, _('Remove\tDel'), wx.ART_DELETE) misc.append_item(self._rmenu, COPY_ID, _('Copy\tCtrl+C'), wx.ART_COPY) misc.append_item(self._rmenu, CUT_ID, _('Cut\tCtrl+X'), wx.ART_CUT) def bind(method): return lambda e: misc.wxCallAfter(method) wx.EVT_MENU(self.widget, REMOVE_ID, bind(self.remove)) wx.EVT_MENU(self.widget, COPY_ID, bind(self.clipboard_copy)) wx.EVT_MENU(self.widget, CUT_ID, bind(self.clipboard_cut)) # paste PASTE_ID = wx.NewId() misc.append_item(self._rmenu, PASTE_ID, _('Paste\tCtrl+V'), wx.ART_PASTE) wx.EVT_MENU(self.widget, PASTE_ID, bind(self.clipboard_paste)) PREVIEW_ID = wx.NewId() self._rmenu.AppendSeparator() misc.append_item(self._rmenu, PREVIEW_ID, _('Preview')) wx.EVT_MENU(self.widget, PREVIEW_ID, bind(self.preview_parent)) self.setup_preview_menu() self.widget.PopupMenu(self._rmenu, event.GetPosition()) def clipboard_paste(self, *args): import clipboard, xml_parse size = self.widget.GetSize() try: if clipboard.paste(self, None, 0): common.app_tree.app.saved = False self.widget.SetSize(size) except xml_parse.XmlParsingError, e: print '\nwxGlade-WARNING: only sizers can be pasted here' # end of class EditPanel class EditTopLevelPanel(PanelBase, TopLevelBase): _is_toplevel = False # used to avoid to appear in the "Top Window" property # of the app def __init__(self, name, parent, id, property_window, klass='wxPanel', show=True, style=wx.TAB_TRAVERSAL): TopLevelBase.__init__(self, name, klass, parent, id, property_window, show=show, has_title=False) PanelBase.__init__(self, style) self.base = 'wxPanel' self.skip_on_size = False def create_widget(self): win = wx.Frame(common.palette, -1, misc.design_title(self.name), size=(400, 300)) import os icon = wx.EmptyIcon() xpm = os.path.join(common.wxglade_path, 'icons', 'panel.xpm') icon.CopyFromBitmap(misc.get_xpm_bitmap(xpm)) win.SetIcon(icon) #self.widget = wx.Panel(win, self.id, style=0) self.widget = wx.ScrolledWindow(win, self.id, style=0) wx.EVT_ENTER_WINDOW(self.widget, self.on_enter) self.widget.GetBestSize = self.get_widget_best_size #self.widget.SetSize = win.SetSize wx.EVT_CLOSE(win, self.hide_widget) if wx.Platform == '__WXMSW__': win.CentreOnScreen() def show_widget(self, yes): oldval = self.get_size() super(EditTopLevelPanel, self).show_widget(yes) if self.widget: if yes and not self.properties['size'].is_active() \ and self.top_sizer: self.top_sizer.fit_parent() self.widget.GetParent().Show(yes) self.set_size(oldval) def hide_widget(self, *args): super(EditTopLevelPanel, self).hide_widget(*args) self.widget.GetParent().Hide() def set_name(self, name): super(EditTopLevelPanel, self).set_name(name) if self.widget: self.widget.GetParent().SetTitle(misc.design_title(self.name)) def delete(self): win = None if self.widget: win = self.widget.GetParent() super(EditTopLevelPanel, self).delete() if win is not None: win.Destroy() def on_size(self, event): w, h = event.GetSize() if self.skip_on_size: self.skip_on_size = False return super(EditTopLevelPanel, self).on_size(event) self.skip_on_size = True if self.widget.GetParent().GetClientSize() != (w, h): self.widget.GetParent().SetClientSize((w+2, h+2)) def set_scrollable(self, value): super(EditTopLevelPanel, self).set_scrollable(value) if self.scrollable: # 2003-06-26 ALB: change the "class name", to allow code generation # for a wxScrolledWindow (see Tree.Node.write and # common.class_names usage in xml_parse.py) self._classname = 'EditTopLevelScrolledWindow' else: self._classname = self.__class__.__name__ # end of class EditTopLevelPanel def builder(parent, sizer, pos, number=[1]): """\ factory function for EditPanel objects. """ name = 'panel_%d' % number[0] while common.app_tree.has_name(name): number[0] += 1 name = 'panel_%d' % number[0] panel = EditPanel(name, parent, wx.NewId(), sizer, pos, common.property_panel) node = Tree.Node(panel) panel.node = node panel.set_option(1) panel.set_flag("wxEXPAND") panel.show_widget(True) common.app_tree.insert(node, sizer.node, pos-1) sizer.set_item(panel.pos, 1, wx.EXPAND) def xml_builder(attrs, parent, sizer, sizeritem, pos=None): """\ factory to build EditPanel objects from an xml file """ from xml_parse import XmlParsingError try: name = attrs['name'] except KeyError: raise XmlParsingError, "'name' attribute missing" if not sizer or not sizeritem: raise XmlParsingError, "sizer or sizeritem object cannot be None" panel = EditPanel(name, parent, wx.NewId(), sizer, pos, common.property_panel, True, style=0) sizer.set_item(panel.pos, option=sizeritem.option, flag=sizeritem.flag, border=sizeritem.border) node = Tree.Node(panel) panel.node = node if pos is None: common.app_tree.add(node, sizer.node) else: common.app_tree.insert(node, sizer.node, pos-1) return panel def xml_toplevel_builder(attrs, parent, sizer, sizeritem, pos=None): from xml_parse import XmlParsingError try: label = attrs['name'] except KeyError: raise XmlParsingError, "'name' attribute missing" panel = EditTopLevelPanel(label, parent, wx.NewId(), common.property_panel, show=False, style=0) node = Tree.Node(panel) panel.node = node common.app_tree.add(node) return panel def initialize(): """\ initialization function for the module: returns a wxBitmapButton to be added to the main palette. """ common.widgets['EditPanel'] = builder common.widgets_from_xml['EditPanel'] = xml_builder #common.widgets['EditScrolledWindow'] = builder common.widgets_from_xml['EditScrolledWindow'] = xml_builder common.widgets_from_xml['EditTopLevelPanel'] = xml_toplevel_builder common.widgets_from_xml['EditTopLevelScrolledWindow'] = \ xml_toplevel_builder from tree import WidgetTree import os.path icon = os.path.join(common.wxglade_path, 'icons/panel.xpm') WidgetTree.images['EditTopLevelPanel'] = icon WidgetTree.images['EditScrolledWindow'] = icon WidgetTree.images['EditTopLevelScrolledWindow'] = icon # these are for backwards compatibility (may be removed someday...) common.widgets_from_xml['SplitterPane'] = xml_builder WidgetTree.images['SplitterPane'] = os.path.join(common.wxglade_path, 'icons/panel.xpm') common.widgets_from_xml['NotebookPane'] = xml_builder WidgetTree.images['NotebookPane'] = os.path.join(common.wxglade_path, 'icons/panel.xpm') return common.make_object_button('EditPanel', 'icons/panel.xpm', tip='Add a Panel/ScrolledWindow')