combobox.py

来自「Wxpython Implemented on Windows CE, Sou」· Python 代码 · 共 672 行 · 第 1/2 页

PY
672
字号
#----------------------------------------------------------------------------
# Name:         masked.combobox.py
# Authors:      Will Sadkin
# Email:        wsadkin@nameconnector.com
# Created:      02/11/2003
# Copyright:    (c) 2003 by Will Sadkin, 2003
# RCS-ID:       $Id: combobox.py,v 1.8 2005/04/08 21:31:25 RD Exp $
# License:      wxWidgets license
#----------------------------------------------------------------------------
#
# This masked edit class allows for the semantics of masked controls
# to be applied to combo boxes.
#
#----------------------------------------------------------------------------

"""
Provides masked edit capabilities within a ComboBox format, as well as
a base class from which you can derive masked comboboxes tailored to a specific
function.  See maskededit module overview for how to configure the control.
"""

import  wx, types, string
from wx.lib.masked import *

# jmg 12/9/03 - when we cut ties with Py 2.2 and earlier, this would
# be a good place to implement the 2.3 logger class
from wx.tools.dbg import Logger
##dbg = Logger()
##dbg(enable=1)

## ---------- ---------- ---------- ---------- ---------- ---------- ----------
## Because calling SetSelection programmatically does not fire EVT_COMBOBOX
## events, we have to do it ourselves when we auto-complete.
class MaskedComboBoxSelectEvent(wx.PyCommandEvent):
    """
    Because calling SetSelection programmatically does not fire EVT_COMBOBOX
    events, the derived control has to do it itself when it auto-completes.
    """
    def __init__(self, id, selection = 0, object=None):
        wx.PyCommandEvent.__init__(self, wx.wxEVT_COMMAND_COMBOBOX_SELECTED, id)

        self.__selection = selection
        self.SetEventObject(object)

    def GetSelection(self):
        """Retrieve the value of the control at the time
        this event was generated."""
        return self.__selection


class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
    """
    Base class for generic masked edit comboboxes; allows auto-complete of values.
    It is not meant to be instantiated directly, but rather serves as a base class
    for any subsequent refinements.
    """
    def __init__( self, parent, id=-1, value = '',
                  pos = wx.DefaultPosition,
                  size = wx.DefaultSize,
                  choices = [],
                  style = wx.CB_DROPDOWN,
                  validator = wx.DefaultValidator,
                  name = "maskedComboBox",
                  setupEventHandling = True,        ## setup event handling by default):
                  **kwargs):


        kwargs['choices'] = choices                 ## set up maskededit to work with choice list too

        ## Since combobox completion is case-insensitive, always validate same way
        if not kwargs.has_key('compareNoCase'):
            kwargs['compareNoCase'] = True

        MaskedEditMixin.__init__( self, name, **kwargs )

        self._choices = self._ctrl_constraints._choices
##        dbg('self._choices:', self._choices)

        if self._ctrl_constraints._alignRight:
            choices = [choice.rjust(self._masklength) for choice in choices]
        else:
            choices = [choice.ljust(self._masklength) for choice in choices]

        wx.ComboBox.__init__(self, parent, id, value='',
                            pos=pos, size = size,
                            choices=choices, style=style|wx.WANTS_CHARS,
                            validator=validator,
                            name=name)
        self.controlInitialized = True

        self._PostInit(style=style, setupEventHandling=setupEventHandling,
                       name=name, value=value, **kwargs)
        

    def _PostInit(self, style=wx.CB_DROPDOWN,
                  setupEventHandling = True,        ## setup event handling by default):
                  name = "maskedComboBox", value='', **kwargs):

        # This is necessary, because wxComboBox currently provides no
        # method for determining later if this was specified in the
        # constructor for the control...
        self.__readonly = style & wx.CB_READONLY == wx.CB_READONLY

        if not hasattr(self, 'controlInitialized'):
            
            self.controlInitialized = True          ## must have been called via XRC, therefore base class is constructed
            if not kwargs.has_key('choices'):
                choices=[]
                kwargs['choices'] = choices         ## set up maskededit to work with choice list too
            self._choices = []

            ## Since combobox completion is case-insensitive, always validate same way
            if not kwargs.has_key('compareNoCase'):
                kwargs['compareNoCase'] = True

            MaskedEditMixin.__init__( self, name, **kwargs )

            self._choices = self._ctrl_constraints._choices
##        dbg('self._choices:', self._choices)

            if self._ctrl_constraints._alignRight:
                choices = [choice.rjust(self._masklength) for choice in choices]
            else:
                choices = [choice.ljust(self._masklength) for choice in choices]
            wx.ComboBox.Clear(self)
            wx.ComboBox.AppendItems(self, choices)


        # Set control font - fixed width by default
        self._setFont()

        if self._autofit:
            self.SetClientSize(self._CalcSize())
            width = self.GetSize().width
            height = self.GetBestSize().height
            self.SetBestFittingSize((width, height))


        if value:
            # ensure value is width of the mask of the control:
            if self._ctrl_constraints._alignRight:
                value = value.rjust(self._masklength)
            else:
                value = value.ljust(self._masklength)

        if self.__readonly:
            self.SetStringSelection(value)
        else:
            self._SetInitialValue(value)


        self._SetKeycodeHandler(wx.WXK_UP, self._OnSelectChoice)
        self._SetKeycodeHandler(wx.WXK_DOWN, self._OnSelectChoice)

        if setupEventHandling:
            ## Setup event handlers
            self.Bind(wx.EVT_SET_FOCUS, self._OnFocus )         ## defeat automatic full selection
            self.Bind(wx.EVT_KILL_FOCUS, self._OnKillFocus )    ## run internal validator
            self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick)  ## select field under cursor on dclick
            self.Bind(wx.EVT_RIGHT_UP, self._OnContextMenu )    ## bring up an appropriate context menu
            self.Bind(wx.EVT_CHAR, self._OnChar )               ## handle each keypress
            self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDownInComboBox ) ## for special processing of up/down keys
            self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown )        ## for processing the rest of the control keys
                                                                ## (next in evt chain)
            self.Bind(wx.EVT_TEXT, self._OnTextChange )         ## color control appropriately & keep
                                                                ## track of previous value for undo



    def __repr__(self):
        return "<MaskedComboBox: %s>" % self.GetValue()


    def _CalcSize(self, size=None):
        """
        Calculate automatic size if allowed; augment base mixin function
        to account for the selector button.
        """
        size = self._calcSize(size)
        return (size[0]+20, size[1])


    def SetFont(self, *args, **kwargs):
        """ Set the font, then recalculate control size, if appropriate. """
        wx.ComboBox.SetFont(self, *args, **kwargs)
        if self._autofit:
##            dbg('calculated size:', self._CalcSize())            
            self.SetClientSize(self._CalcSize())
            width = self.GetSize().width
            height = self.GetBestSize().height
##            dbg('setting client size to:', (width, height))
            self.SetBestFittingSize((width, height))


    def _GetSelection(self):
        """
        Allow mixin to get the text selection of this control.
        REQUIRED by any class derived from MaskedEditMixin.
        """
##        dbg('MaskedComboBox::_GetSelection()')
        return self.GetMark()

    def _SetSelection(self, sel_start, sel_to):
        """
        Allow mixin to set the text selection of this control.
        REQUIRED by any class derived from MaskedEditMixin.
        """
##        dbg('MaskedComboBox::_SetSelection: setting mark to (%d, %d)' % (sel_start, sel_to))
        return self.SetMark( sel_start, sel_to )


    def _GetInsertionPoint(self):
##        dbg('MaskedComboBox::_GetInsertionPoint()', indent=1)
##        ret = self.GetInsertionPoint()
        # work around new bug in 2.5, in which the insertion point
        # returned is always at the right side of the selection,
        # rather than the start, as is the case with TextCtrl.
        ret = self.GetMark()[0]
##        dbg('returned', ret, indent=0)
        return ret

    def _SetInsertionPoint(self, pos):
##        dbg('MaskedComboBox::_SetInsertionPoint(%d)' % pos)
        self.SetInsertionPoint(pos)


    def _GetValue(self):
        """
        Allow mixin to get the raw value of the control with this function.
        REQUIRED by any class derived from MaskedEditMixin.
        """
        return self.GetValue()

    def _SetValue(self, value):
        """
        Allow mixin to set the raw value of the control with this function.
        REQUIRED by any class derived from MaskedEditMixin.
        """
        # For wxComboBox, ensure that values are properly padded so that
        # if varying length choices are supplied, they always show up
        # in the window properly, and will be the appropriate length
        # to match the mask:
        if self._ctrl_constraints._alignRight:
            value = value.rjust(self._masklength)
        else:
            value = value.ljust(self._masklength)

        # Record current selection and insertion point, for undo
        self._prevSelection = self._GetSelection()
        self._prevInsertionPoint = self._GetInsertionPoint()
        wx.ComboBox.SetValue(self, value)
        # text change events don't always fire, so we check validity here
        # to make certain formatting is applied:
        self._CheckValid()

    def SetValue(self, value):
        """
        This function redefines the externally accessible .SetValue to be
        a smart "paste" of the text in question, so as not to corrupt the
        masked control.  NOTE: this must be done in the class derived
        from the base wx control.
        """
        if not self._mask:
            wx.ComboBox.SetValue(value)   # revert to base control behavior
            return
        # else...
        # empty previous contents, replacing entire value:
        self._SetInsertionPoint(0)
        self._SetSelection(0, self._masklength)

        if( len(value) < self._masklength                # value shorter than control
            and (self._isFloat or self._isInt)            # and it's a numeric control
            and self._ctrl_constraints._alignRight ):   # and it's a right-aligned control
            # try to intelligently "pad out" the value to the right size:
            value = self._template[0:self._masklength - len(value)] + value
##            dbg('padded value = "%s"' % value)

        # For wxComboBox, ensure that values are properly padded so that
        # if varying length choices are supplied, they always show up
        # in the window properly, and will be the appropriate length
        # to match the mask:
        elif self._ctrl_constraints._alignRight:
            value = value.rjust(self._masklength)
        else:
            value = value.ljust(self._masklength)


        # make SetValue behave the same as if you had typed the value in:
        try:
            value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True)
            if self._isFloat:
                self._isNeg = False     # (clear current assumptions)
                value = self._adjustFloat(value)
            elif self._isInt:
                self._isNeg = False     # (clear current assumptions)
                value = self._adjustInt(value)
            elif self._isDate and not self.IsValid(value) and self._4digityear:
                value = self._adjustDate(value, fixcentury=True)
        except ValueError:
            # If date, year might be 2 digits vs. 4; try adjusting it:
            if self._isDate and self._4digityear:
                dateparts = value.split(' ')
                dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
                value = string.join(dateparts, ' ')
##                dbg('adjusted value: "%s"' % value)
                value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
            else:
                raise

        self._SetValue(value)
####        dbg('queuing insertion after .SetValue', replace_to)
        wx.CallAfter(self._SetInsertionPoint, replace_to)
        wx.CallAfter(self._SetSelection, replace_to, replace_to)


    def _Refresh(self):
        """
        Allow mixin to refresh the base control with this function.
        REQUIRED by any class derived from MaskedEditMixin.
        """
        wx.ComboBox.Refresh(self)

    def Refresh(self):
        """
        This function redefines the externally accessible .Refresh() to
        validate the contents of the masked control as it refreshes.
        NOTE: this must be done in the class derived from the base wx control.
        """
        self._CheckValid()
        self._Refresh()


    def _IsEditable(self):
        """
        Allow mixin to determine if the base control is editable with this function.
        REQUIRED by any class derived from MaskedEditMixin.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?