"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/components/base_list.py" (2 Dec 2019, 13944 Bytes) of package /linux/privat/relax-5.0.0.src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "base_list.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.3_vs_5.0.0.

    1 ###############################################################################
    2 #                                                                             #
    3 # Copyright (C) 2009-2010 Michael Bieri                                       #
    4 # Copyright (C) 2009-2013 Edward d'Auvergne                                   #
    5 #                                                                             #
    6 # This file is part of the program relax (http://www.nmr-relax.com).          #
    7 #                                                                             #
    8 # This program is free software: you can redistribute it and/or modify        #
    9 # it under the terms of the GNU General Public License as published by        #
   10 # the Free Software Foundation, either version 3 of the License, or           #
   11 # (at your option) any later version.                                         #
   12 #                                                                             #
   13 # This program is distributed in the hope that it will be useful,             #
   14 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
   15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
   16 # GNU General Public License for more details.                                #
   17 #                                                                             #
   18 # You should have received a copy of the GNU General Public License           #
   19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
   20 #                                                                             #
   21 ###############################################################################
   22 
   23 # Module docstring.
   24 """Module containing the base GUI element for listing things."""
   25 
   26 # Python module imports.
   27 import wx
   28 import wx.lib.buttons
   29 
   30 # relax module imports.
   31 import dep_check
   32 from gui.components.menu import build_menu_item
   33 from gui.fonts import font
   34 from gui.misc import add_border
   35 from gui.string_conv import str_to_gui
   36 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
   37 from status import Status; status = Status()
   38 from user_functions.data import Uf_info; uf_info = Uf_info()
   39 
   40 
   41 class Base_list(object):
   42     """The GUI element for listing the software used in the analysis."""
   43 
   44     def __init__(self, gui=None, parent=None, box=None, id=None, proportion=0, button_placement='default'):
   45         """Build the base list GUI element.
   46 
   47         @keyword gui:               The main GUI object.
   48         @type gui:                  wx.Frame instance
   49         @keyword parent:            The parent GUI element that this is to be attached to.
   50         @type parent:               wx object
   51         @keyword box:               The box sizer to pack this GUI component into.
   52         @type box:                  wx.BoxSizer instance
   53         @keyword id:                A unique identification string.  This is used to register the update method with the GUI user function observer object.
   54         @type id:                   str
   55         @keyword proportion:        The window proportion parameter.
   56         @type proportion:           bool
   57         @keyword button_placement:  Override the button visibility and placement.  The value of 'default' will leave the buttons at the default setting.  The value of 'top' will place the buttons at the top, 'bottom' will place them at the bottom, and None will turn off the buttons.
   58         @type button_placement:     str or None
   59         """
   60 
   61         # Store the arguments.
   62         self.gui = gui
   63         self.parent = parent
   64         self.proportion = proportion
   65 
   66         # Variables to be overridden.
   67         self.title = ""
   68         self.spacing = 5
   69         self.border = 5
   70         self.observer_base_name = None
   71         self.columns = []
   72         self.button_placement = None
   73         self.button_size = (120, 40)
   74         self.button_spacing = 5
   75         self.button_info = []
   76         self.popup_menus = []
   77 
   78         # Override these base values.
   79         self.setup()
   80 
   81         # Button placement second override on initialisation.
   82         if button_placement != 'default':
   83             self.button_placement = button_placement
   84 
   85         # First create a panel (to allow for tooltips on the buttons).
   86         self.panel = wx.Panel(self.parent)
   87         box.Add(self.panel, self.proportion, wx.ALL|wx.EXPAND, 0)
   88 
   89         # Add a sizer to the panel.
   90         panel_sizer = wx.BoxSizer(wx.VERTICAL)
   91         self.panel.SetSizer(panel_sizer)
   92 
   93         # A static box to hold all the widgets, and its sizer.
   94         self.data_box = wx.StaticBox(self.panel, -1)
   95         self.set_box_label()
   96         self.data_box.SetFont(font.subtitle)
   97         sub_sizer = wx.StaticBoxSizer(self.data_box, wx.VERTICAL)
   98 
   99         # Add the sizer to the static box and the static box to the main box.
  100         panel_sizer.Add(sub_sizer, self.proportion, wx.ALL|wx.EXPAND, 0)
  101 
  102         # Add a border.
  103         box_centre = add_border(sub_sizer, border=self.border)
  104 
  105         # Add buttons to the top.
  106         if self.button_placement == 'top':
  107             self.add_buttons(box_centre)
  108             box_centre.AddSpacer(self.spacing)
  109 
  110         # Initialise the element.
  111         self.init_element(box_centre)
  112 
  113         # Build the element.
  114         self.build_element()
  115 
  116         # Add buttons to the bottom.
  117         if self.button_placement == 'bottom':
  118             box_centre.AddSpacer(self.spacing)
  119             self.add_buttons(box_centre)
  120 
  121         # Initialise observer name.
  122         if self.observer_base_name:
  123             self.name = '%s: %s' % (self.observer_base_name, id)
  124         else:
  125             self.name = id
  126 
  127         # Register the element for updating when a user function completes.
  128         self.observer_register()
  129 
  130 
  131     def Enable(self, enable=True):
  132         """Enable or disable the element.
  133 
  134         @keyword enable:    The flag specifying if the element should be enabled or disabled.
  135         @type enable:       bool
  136         """
  137 
  138         # Call the buttons' methods.
  139         for i in range(len(self.button_info)):
  140             # Get the button.
  141             button = getattr(self, self.button_info[i]['object'])
  142 
  143             # Call the botton's method.
  144             button.Enable(enable)
  145 
  146 
  147     def add_buttons(self, sizer):
  148         """Add the buttons for manipulating the data.
  149 
  150         @param sizer:   The sizer element to pack the buttons into.
  151         @type sizer:    wx.BoxSizer instance
  152         """
  153 
  154         # Button Sizer
  155         button_sizer = wx.BoxSizer(wx.HORIZONTAL)
  156         sizer.Add(button_sizer, 0, wx.ALL|wx.EXPAND, 0)
  157 
  158         # Loop over the buttons.
  159         for i in range(len(self.button_info)):
  160             # The button.
  161             button = wx.lib.buttons.ThemedGenBitmapTextButton(self.panel, -1, None, self.button_info[i]['label'])
  162             button.SetBitmapLabel(wx.Bitmap(self.button_info[i]['icon']))
  163 
  164             # Format.
  165             button.SetFont(font.normal)
  166             button.SetMinSize(self.button_size)
  167 
  168             # Add to the sizer.
  169             button_sizer.Add(button, 0, 0, 0)
  170 
  171             # Bind the method.
  172             self.parent.Bind(wx.EVT_BUTTON, self.button_info[i]['method'], button)
  173 
  174             # Set the tooltip.
  175             button.SetToolTip(wx.ToolTip(self.button_info[i]['tooltip']))
  176 
  177             # Store as a class object.
  178             setattr(self, self.button_info[i]['object'], button)
  179 
  180             # Spacing.
  181             if self.button_spacing:
  182                 button_sizer.AddSpacer(self.button_spacing)
  183 
  184 
  185     def build_element(self):
  186         """Build the grid."""
  187 
  188         # Execution lock, so do nothing.
  189         if status.exec_lock.locked():
  190             return
  191 
  192         # Build the GUI element in a thread safe way.
  193         wx.CallAfter(self.build_element_safe)
  194 
  195 
  196     def build_element_safe(self):
  197         """Build the spectra listing GUI element in a thread safe wx.CallAfter call."""
  198 
  199         # First freeze the element, so that the GUI element doesn't update until the end.
  200         self.element.Freeze()
  201 
  202         # Update the label if needed.
  203         self.set_box_label()
  204 
  205         # Delete the previous data.
  206         self.element.DeleteAllItems()
  207 
  208         # Update the data.
  209         self.update_data()
  210 
  211         # Size the columns.
  212         self.size_cols()
  213 
  214         # Post a size event to get the scroll panel to update correctly.
  215         event = wx.PyCommandEvent(wx.EVT_SIZE.typeId, self.parent.GetId())
  216         wx.PostEvent(self.parent.GetEventHandler(), event)
  217 
  218         # Set the minimum height.
  219         if not self.proportion:
  220             # The number of rows.
  221             n = self.element.GetItemCount()
  222 
  223             # Size of the header, plus a bit.
  224             head = self.height_char + 10
  225 
  226             # Size of the table central element.
  227             centre = (self.height_char + 6) * n 
  228 
  229             # Size of the scrollbar for the end of the table.
  230             foot = wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y)
  231 
  232             # Sum.
  233             height = head + centre + foot
  234 
  235             # Set the minimum size, and force a redraw.
  236             self.element.SetMinSize((-1, height))
  237             self.element.Layout()
  238 
  239         # Unfreeze.
  240         self.element.Thaw()
  241 
  242 
  243     def delete(self):
  244         """Unregister the class."""
  245 
  246         # Unregister the observer methods.
  247         self.observer_register(remove=True)
  248 
  249         # Destroy any wizards present.
  250         if hasattr(self, 'wizard'):
  251             self.wizard.Destroy()
  252             del self.wizard
  253 
  254 
  255     def generate_popup_menu(self, id=None):
  256         """Create and return the popup menu.
  257 
  258         @keyword id:    The ID string for the row that was clicked on.
  259         @type id:       str
  260         @return:        The popup menu.
  261         @rtype:         list of dict of wxID, str, str, method
  262         """
  263 
  264         # Default to returning the first initialised menu.
  265         return self.popup_menus
  266 
  267 
  268     def init_element(self, sizer):
  269         """Initialise the GUI element.
  270 
  271         @param sizer:   The sizer element to pack the element into.
  272         @type sizer:    wx.BoxSizer instance
  273         """
  274 
  275         # The list.
  276         self.element = wx.ListCtrl(self.panel, -1, style=wx.BORDER_SUNKEN|wx.LC_REPORT)
  277 
  278         # Initialise the columns.
  279         for i in range(len(self.columns)):
  280             self.element.InsertColumn(i, str_to_gui(self.columns[i]))
  281 
  282         # Properties.
  283         self.element.SetFont(font.normal)
  284 
  285         # Store the base heights.
  286         self.height_char = self.element.GetCharHeight()
  287 
  288         # Bind some events.
  289         self.element.Bind(wx.EVT_SIZE, self.resize)
  290         self.element.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.on_right_click)  # For wxMSW!
  291         self.element.Bind(wx.EVT_RIGHT_UP, self.on_right_click)   # For wxGTK!
  292 
  293         # Add list to sizer.
  294         sizer.Add(self.element, self.proportion, wx.ALL|wx.EXPAND, 0)
  295 
  296 
  297     def is_complete(self):
  298         """Base method which always returns True.
  299 
  300         @return:    The answer to the question.
  301         @rtype:     bool
  302         """
  303 
  304         # Assume everything is complete.
  305         return True
  306 
  307 
  308     def observer_register(self, remove=False):
  309         """Register and unregister methods with the observer objects.
  310 
  311         @keyword remove:    If set to True, then the methods will be unregistered.
  312         @type remove:       False
  313         """
  314 
  315         # Register.
  316         if not remove:
  317             status.observers.gui_uf.register(self.name, self.build_element, method_name='build_element')
  318             status.observers.pipe_alteration.register(self.name, self.build_element, method_name='build_element')
  319 
  320         # Unregister.
  321         else:
  322             status.observers.gui_uf.unregister(self.name)
  323             status.observers.pipe_alteration.unregister(self.name)
  324 
  325 
  326     def on_right_click(self, event):
  327         """Pop up menu for the right click.
  328 
  329         @param event:   The wx event.
  330         @type event:    wx event
  331         """
  332 
  333         # Obtain the position.
  334         pos = event.GetPosition()
  335 
  336         # Hack to allow the test suite to pass.
  337         wx.Yield()
  338 
  339         # Find the item clicked on.
  340         item, flags = self.element.HitTest(pos)
  341 
  342         # Get the ID string (handling wxPython 2.9 ListCtrl.HitTest() bugs).
  343         id = None
  344         if item != -1:
  345             id = self.element.GetItemText(item)
  346 
  347         # Get the menu.
  348         popup_menus = self.generate_popup_menu(id=id)
  349 
  350         # No popup menus defined.
  351         if popup_menus == []:
  352             return
  353 
  354         # Execution lock, so do nothing.
  355         if status.exec_lock.locked():
  356             return
  357 
  358         # Initialise the menu.
  359         menu = wx.Menu()
  360 
  361         # Loop over the menu items.
  362         for i in range(len(popup_menus)):
  363             # Alias.
  364             info = popup_menus[i]
  365 
  366             # Add the menu item.
  367             build_menu_item(menu, id=info['id'], text=info['text'], icon=info['icon'])
  368 
  369             # Bind clicks.
  370             self.element.Bind(wx.EVT_MENU, info['method'], id=info['id'])
  371 
  372         # Pop up the menu.
  373         if status.show_gui:
  374             self.element.PopupMenu(menu)
  375 
  376         # Cleanup.
  377         menu.Destroy()
  378 
  379 
  380     def resize(self, event):
  381         """Catch the resize to allow the element to be resized.
  382 
  383         @param event:   The wx event.
  384         @type event:    wx event
  385         """
  386 
  387         # Set the column sizes.
  388         self.size_cols()
  389 
  390         # Continue with the normal resizing.
  391         event.Skip()
  392 
  393 
  394     def set_box_label(self):
  395         """Set the label of the StaticBox."""
  396 
  397         # Set the label.
  398         self.data_box.SetLabel(self.title)
  399 
  400 
  401     def size_cols(self):
  402         """Set the column sizes."""
  403 
  404         # The element size.
  405         x, y = self.element.GetSize()
  406 
  407         # Number of columns.
  408         n = self.element.GetColumnCount()
  409 
  410         # Set to equal sizes.
  411         if n == 0:
  412             width = x
  413         else:
  414             width = int(x / n)
  415 
  416         # Set the column sizes.
  417         for i in range(n):
  418             self.element.SetColumnWidth(i, width)