class CuteWindow(AcceleratorSavvyWindow, BindSavvyEvtHandler, wx.Window):
    '''
    An improved `wx.Window`.
    
    The advantages of this class over `wx.Window`:
    
      - A `.freezer` property for freezing the window.  
      - A `.create_cursor_changer` method which creates a `CursorChanger`
       context manager for temporarily changing the cursor.
      - A `set_good_background_color` for setting a good background color.
      - A few more features.
      
    This class doesn't require calling its `__init__` when subclassing. (i.e.,
    you *may* call its `__init__` if you want, but it will do the same as
    calling `wx.Window.__init__`.)
    '''
    
    freezer = freezing.FreezerProperty(
        freezer_type=wx_tools.window_tools.WindowFreezer,
        doc='''Freezer for freezing the window while the suite executes.'''
    )

    
    def create_cursor_changer(self, cursor):
        '''
        Create a `CursorChanger` context manager for ...blocktotodoc
        
        `cursor` may be either a `wx.Cursor` object or a constant like
        `wx.CURSOR_BULLSEYE`.
        '''
        return wx_tools.cursors.CursorChanger(self, cursor)

    
    def set_good_background_color(self):
        '''Set a good background color to the window.'''
        self.SetBackgroundColour(wx_tools.colors.get_background_color())
        

    def has_focus(self):
        return wx.Window.FindFocus() == self
        
        
    def set_tool_tip_and_help_text(self, tool_tip=None, help_text=None):
        if tool_tip is not None:
            self.SetToolTipString(tool_tip)
        if help_text is not None:
            self.SetHelpText(help_text)
class EmitterSystem(object):
    '''
    A system of emitters, representing a set of possible events in a program.
    
    `EmitterSystem` offers a few advantages over using plain emitters.
    
    There are the `bottom_emitter` and `top_emitter`, which allow,
    respectively, to keep track of each `emit`ting that goes on, and to
    generate an `emit`ting that affects all emitters in the system.
    
    The `EmitterSystem` also offers a context manager,
    `.freeze_cache_rebuilding`. When you do actions using this context manager,
    the emitters will not rebuild their cache when changing their
    inputs/outputs. When the outermost context manager has exited, all the
    caches for these emitters will get rebuilt.
    '''
    # possible future idea: there is the idea of optimizing by cutting
    # redundant links between boxes. I'm a bit suspicious of it. The next
    # logical step is to make inputs and outputs abstract.
    def __init__(self):
        
        self.emitters = set()
        
        self.bottom_emitter = Emitter(self, name='bottom')
        self.emitters.add(self.bottom_emitter)
        
        self.top_emitter = Emitter(
            self,
            outputs=(self.bottom_emitter,),
            name='top',
        )
        self.emitters.add(self.top_emitter)
        
        
    cache_rebuilding_freezer = freezing.FreezerProperty()
    '''
    Context manager for freezing the cache rebuilding in an emitter system.
    
    When you do actions using this context manager, the emitters will not
    rebuild their cache when changing their inputs/outputs. When the outermost
    context manager has exited, all the caches for these emitters will get
    rebuilt.
    '''    

    
    @cache_rebuilding_freezer.on_thaw
    def _recalculate_all_cache(self):
        '''Recalculate the cache for all the emitters.'''
        self.bottom_emitter._recalculate_total_callable_outputs_recursively()
        
        
            
    def make_emitter(self, inputs=(), outputs=(), name=None):
        '''Create an emitter in this emitter system. Returns the emitter.'''

        # todo: allow one value in inputs and outputs. do in all emitter
        # constructors.
        
        inputs = set(inputs)
        inputs.add(self.top_emitter)
        outputs = set(outputs)
        outputs.add(self.bottom_emitter)
        emitter = Emitter(self, inputs, outputs, name)
        self.emitters.add(emitter)
        return emitter

    
    def remove_emitter(self, emitter):
        '''
        Remove an emitter from this system, disconnecting it from everything.
        '''
        with self.cache_rebuilding_freezer:
            emitter.disconnect_from_all()
        self.emitters.remove(emitter)
class EmittingOrderedSet(OrderedSet):
    '''An ordered set that emits to `.emitter` every time it's modified.'''
    def __init__(self, iterable=(), *, emitter=None):
        if emitter:
            from python_toolbox.emitting import Emitter
            assert isinstance(emitter, Emitter)
        self.emitter = emitter
        OrderedSet.__init__(self, iterable)

    def add(self, key, last=True):
        '''
        Add an element to a set.
    
        This has no effect if the element is already present.
        '''
        if key not in self._map:
            super().add(key, last=last)
            self._emit()

    def discard(self, key):
        '''
        Remove an element from a set if it is a member.
        
        If the element is not a member, do nothing.
        '''
        if key in self._map:
            super().discard(key)
            self._emit()

    def clear(self):
        '''Clear the ordered set, removing all items.'''
        if self:
            super().clear()
            self._emit()

    def set_emitter(self, emitter):
        '''Set `emitter` to be emitted with on every modification.'''
        self.emitter = emitter

    def _emit(self):
        if (self.emitter is not None) and not self._emitter_freezer.frozen:
            self.emitter.emit()

    def move_to_end(self, key, last=True):
        '''
        Move an existing element to the end (or start if `last=False`.)
        '''
        # Inefficient implementation until someone cares.
        with self._emitter_freezer:
            self.remove(key)
        self.add(key, last=last)

    _emitter_freezer = freezing.FreezerProperty()

    def __eq__(self, other):
        return ((type(self) is type(other)) and (len(self) == len(other))
                and (self.emitter is other.emitter)
                and all(itertools.starmap(operator.eq, zip(self, other))))

    def get_without_emitter(self):
        '''Get a version of this ordered set without an emitter attached.'''
        return OrderedSet(self)
Beispiel #4
0
class Textual(CutePanel):
    '''Display (and allow modifying) the hue as a number 0-359.'''
    def __init__(self, hue_selection_dialog):
        wx.Panel.__init__(self, parent=hue_selection_dialog, size=(75, 100))
        self.set_good_background_color()
        self.SetHelpText('Set the hue in angles (0°-359°).')

        self.hue_selection_dialog = hue_selection_dialog
        self.hue = hue_selection_dialog.hue

        self.main_v_sizer = wx.BoxSizer(wx.VERTICAL)

        self.hue_static_text = wx.StaticText(self, label='&Hue:')

        self.main_v_sizer.Add(self.hue_static_text,
                              0,
                              wx.ALIGN_LEFT | wx.BOTTOM,
                              border=5)

        self.h_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self.main_v_sizer.Add(self.h_sizer, 0)

        self.spin_ctrl = wx.SpinCtrl(self,
                                     min=0,
                                     max=359,
                                     initial=ratio_to_round_degrees(self.hue),
                                     size=(70, -1),
                                     style=wx.SP_WRAP)
        if wx_tools.is_mac:
            self.spin_ctrl.SetValue(ratio_to_round_degrees(self.hue))

        self.h_sizer.Add(self.spin_ctrl, 0)

        self.degree_static_text = wx.StaticText(self, label=unichr(176))

        self.h_sizer.Add(self.degree_static_text, 0)

        self.SetSizerAndFit(self.main_v_sizer)

        self.Bind(wx.EVT_SPINCTRL, self._on_spin, source=self.spin_ctrl)
        self.Bind(wx.EVT_TEXT, self._on_text, source=self.spin_ctrl)

    value_freezer = freezing.FreezerProperty()

    def update(self):
        '''Update to show the new hue.'''
        if not self.value_freezer.frozen and \
           self.hue != self.hue_selection_dialog.hue:
            self.hue = self.hue_selection_dialog.hue
            self.spin_ctrl.SetValue(ratio_to_round_degrees(self.hue))

    def _on_spin(self, event):
        self.hue_selection_dialog.setter(degrees_to_ratio(
            self.spin_ctrl.Value))

    def _on_text(self, event):
        with self.value_freezer:
            self.hue_selection_dialog.setter(
                degrees_to_ratio(self.spin_ctrl.Value))

    def set_focus_on_spin_ctrl_and_select_all(self):
        '''


        The "select all" part works only on Windows and generic `wx.SpinCtrl`
        implementations.
        '''
        self.spin_ctrl.SetFocus()
        self.spin_ctrl.SetSelection(-1, -1)