Exemple #1
0
 def __init__(self, parent, *argv, **argd):
     wx.Panel.__init__(self, parent, *argv, **argd)
     #self.SetBackgroundColour('WHITE')
     
     self.BG_CL         = '#EEEEEE'
     self.BG_CL_DEFAULT = '#E3EDFF'
     self.BG_CL_OVER    = '#8FD6FF'
     self.BG_CL_FOCUS   = '#C1DEA3'
     self.CL_LINE_DOT   = '#777777'
     self.CL_LINE_SOLID = '#333333'
     self.CL_TEXT_VALID = '#000000'
     self.CL_TEXT_INVALID='#DD0000'
     self.CL_TEXT_AUTOTIP='#AAAAAA'
     self.CL_TEXT_HIGHLIGHT='#AA2222'
     
     self.CELL_SIZE = app.nCellSize
     
     self.num = [ [] for n in app.rgLINE ]
     for i in app.rgLINE:
         self.num[i] = [ Number() for n in app.rgLINE ]
     self.default = deepcopy(self.num)
     self.answer = None
     
     self.steps = [] #record change history
     self.cur_step = -1
     self.boxerInfo = BoxerInfo()
     self.choiceNumberPanel = None
     
     self.highlightNum = 0
     self.focusPos     = (-1,-1)
     self.mouseOverPos = (-1,-1)
     
     self.testAnim = anim.AnimBase(self)
     
     self.font    = wx.Font( self.CELL_SIZE*0.4, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, 'Comic Sans MS' )
     self.fontTip = wx.Font( self.CELL_SIZE*0.2, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, 'Comic Sans MS' )
     self.brush_bg        = wx.Brush(self.BG_CL)
     self.brush_bg_defalt = wx.Brush(self.BG_CL_DEFAULT)
     self.brush_bg_over   = wx.Brush(self.BG_CL_OVER)
     self.brush_bg_focus  = wx.Brush(self.BG_CL_FOCUS)
     
     self.penTrans = wx.Pen('#000000', 1, wx.TRANSPARENT)
     
     self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
     self.bmpBorder = None
     self.bmpNum = []
     self.bmpNumInvalid = []
     self.bmpNumAutoTip = []
     
     self.setDefault( [ [8,0,0, 0,0,5, 6,0,7] ,
                        [3,4,6, 7,0,0, 0,8,0] ,
                        [0,0,9, 0,0,0, 3,2,4] ,
                        [4,0,0, 0,0,0, 0,0,0] ,
                        [5,0,7, 8,3,4, 9,0,2] ,
                        [0,0,0, 0,0,0, 0,0,8] ,
                        [6,1,5, 0,0,0, 8,0,0] ,
                        [0,3,0, 0,0,6, 2,7,5] ,
                        [2,0,4, 9,0,0, 0,0,6] ] )
     self.bindEvent()
Exemple #2
0
class NumberBoard(wx.Panel):
    def __init__(self, parent, *argv, **argd):
        wx.Panel.__init__(self, parent, *argv, **argd)
        #self.SetBackgroundColour('WHITE')
        
        self.BG_CL         = '#EEEEEE'
        self.BG_CL_DEFAULT = '#E3EDFF'
        self.BG_CL_OVER    = '#8FD6FF'
        self.BG_CL_FOCUS   = '#C1DEA3'
        self.CL_LINE_DOT   = '#777777'
        self.CL_LINE_SOLID = '#333333'
        self.CL_TEXT_VALID = '#000000'
        self.CL_TEXT_INVALID='#DD0000'
        self.CL_TEXT_AUTOTIP='#AAAAAA'
        self.CL_TEXT_HIGHLIGHT='#AA2222'
        
        self.CELL_SIZE = app.nCellSize
        
        self.num = [ [] for n in app.rgLINE ]
        for i in app.rgLINE:
            self.num[i] = [ Number() for n in app.rgLINE ]
        self.default = deepcopy(self.num)
        self.answer = None
        
        self.steps = [] #record change history
        self.cur_step = -1
        self.boxerInfo = BoxerInfo()
        self.choiceNumberPanel = None
        
        self.highlightNum = 0
        self.focusPos     = (-1,-1)
        self.mouseOverPos = (-1,-1)
        
        self.testAnim = anim.AnimBase(self)
        
        self.font    = wx.Font( self.CELL_SIZE*0.4, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, 'Comic Sans MS' )
        self.fontTip = wx.Font( self.CELL_SIZE*0.2, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, 'Comic Sans MS' )
        self.brush_bg        = wx.Brush(self.BG_CL)
        self.brush_bg_defalt = wx.Brush(self.BG_CL_DEFAULT)
        self.brush_bg_over   = wx.Brush(self.BG_CL_OVER)
        self.brush_bg_focus  = wx.Brush(self.BG_CL_FOCUS)
        
        self.penTrans = wx.Pen('#000000', 1, wx.TRANSPARENT)
        
        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
        self.bmpBorder = None
        self.bmpNum = []
        self.bmpNumInvalid = []
        self.bmpNumAutoTip = []
        
        self.setDefault( [ [8,0,0, 0,0,5, 6,0,7] ,
                           [3,4,6, 7,0,0, 0,8,0] ,
                           [0,0,9, 0,0,0, 3,2,4] ,
                           [4,0,0, 0,0,0, 0,0,0] ,
                           [5,0,7, 8,3,4, 9,0,2] ,
                           [0,0,0, 0,0,0, 0,0,8] ,
                           [6,1,5, 0,0,0, 8,0,0] ,
                           [0,3,0, 0,0,6, 2,7,5] ,
                           [2,0,4, 9,0,0, 0,0,6] ] )
        self.bindEvent()

    @property
    def boxer(self):
        return SudokuBoxer(self.num)

    def bindEvent(self):
        self.Bind(wx.EVT_PAINT,        self.onDraw)
        self.Bind(wx.EVT_MOTION,       self.onMouseMove)
        self.Bind(wx.EVT_LEFT_DOWN,    self.onMouseDown)
        self.Bind(wx.EVT_RIGHT_DOWN,   self.onMouseRDown)
        self.Bind(wx.EVT_KEY_DOWN,     self.onKeyDown)
        self.Bind(wx.EVT_KEY_UP,       self.onKeyUp)
        self.Bind(wx.EVT_LEAVE_WINDOW, self.onMouseLeave)
    
    def unbindEvent(self, skipPaint=True):
        if not skipPaint:
            self.Unbind(wx.EVT_PAINT)
        self.Unbind(wx.EVT_MOTION)
        self.Unbind(wx.EVT_LEFT_DOWN)
        self.Unbind(wx.EVT_KEY_DOWN)
        self.Unbind(wx.EVT_KEY_UP)
        self.Unbind(wx.EVT_LEAVE_WINDOW)
        
    def setDefault(self, d, cur_d=[]):
        assert len(d)==len(self.default)
        #Default
        for i in app.rgLINE:
            for j in app.rgLINE:
                self.default[i][j].val = d[i][j]
                self.default[i][j].isDefault  =  d[i][j]!=0
                    
        #Current Number of puzzle
        if cur_d:
            for i in app.rgLINE:
                for j in app.rgLINE:
                    self.num[i][j].val = cur_d[i][j]
                    self.num[i][j].isDefault = self.default[i][j].isDefault
        else:
            self.num = deepcopy(self.default)
        self._updateAutoTip()
        self._initToDefault()
        self.Refresh()
    
    def _initToDefault(self):
        self.clearStepInfo()
        self.answer = None
        self.highlightNum = 0
        self.focusPos     = (-1,-1)
        self.mouseOverPos = (-1,-1)
        self.clearBoxerInfo()
        
    def clearToNull(self):
        num = []
        for i in app.rgLINE:
            num.append( [0 for j in app.rgLINE] )
        self.setDefault(num)
        
    def clearToDefault(self):
        self.num = deepcopy(self.default)
        self._updateAutoTip()
        self.clearStepInfo()
        self.clearBoxerInfo()
        self.Refresh()
        
    def clearStepInfo(self):
        self.steps = []
        self.cur_step = -1
    
    def clearBoxerInfo(self):
        if self.boxerInfo.clear():
            self.Refresh()
    
    def queryAnswer(self):
        ori_num = deepcopy(self.num)
        
        while True:
            gotOne = False
            while True:
                if self.guessNext(mode='easy', silentFill=True):
                    gotOne = True
                else:
                    break
            
            if self.guessNext(mode='medium', silentFill=True):
                gotOne = True
            
            if not gotOne:
                break
        
        #[TODO] Fix, should finish and don't use brute method.
        if not self.checkFinish():
            self.boxer.boxerBrute(self.default, bCheckFromDefault=False)
            
        self.answer = self.num
        self.num = ori_num
        self.focusPos = (-1,-1)
        self.clearBoxerInfo()
        
    def guessNext(self, autoFill=True, silentFill=False, mode='easy'):
        ret = self.boxer.boxerNext(mode)
        if ret:
            self.dirtyCell(*self.focusPos) #dirty original focus cell
            pos, v, info = ret
            self.focusPos = pos
            if autoFill:
                if silentFill:
                    self._setVal(pos[0],pos[1],v)
                else:
                    self.setVal(pos[0],pos[1],v)
            
            if info:
                self.boxerInfo = info
            self.Refresh()
                
        return ret
    
    def getNum(self, i, j):
        return self.num[i][j]
    
    def _setVal(self, i, j, v):
        self.num[i][j].val = v
        self.num[i][j].valid = self.boxer.checkValidInput(v, i, j)
        
        #refresh
        if app.bShowAutoTip:
            self._updateAutoTip(i,j)
            self.Refresh()
        else:
            self.dirtyCell(i,j)
            
    def setVal(self, i, j, v):
        '''
        @(i,j) coordinate
        @v value
        '''
        assert 0 <= i < app.nLINE
        assert 0 <= j < app.nLINE
        
        if len(self.steps)-1 > self.cur_step:
            self.steps = self.steps[:self.cur_step+1]
        
        ori_v = self.num[i][j].val
        if ori_v == v:
            return
            
        self.steps.append( Step(i,j,ori_v,v) )
        self.cur_step = len(self.steps)-1
        self._setVal(i,j,v)
        
        self.clearBoxerInfo()
        
        #check finish
        if self.checkFinish():
            #wx.PostEvent(self, EVT_FINISH)
            evt = FunFinishEvent()
            wx.PostEvent(self, evt)
            
    def _updateAutoTip(self, i=-1, j=-1):
        if not app.bShowAutoTip:
            return
        if i==-1 or j==-1:
            #Update all
            for x in app.rgLINE:
                for y in app.rgLINE:
                    if self.num[x][y] == 0:
                        self.num[x][y].autoTipList = self.boxer.getValidNum(x,y)
        else:
            #Update grid & line base on (i,j)
            for x in app.rgGRID:
                for y in app.rgGRID:
                    posX, posY = int(i/3)*3 + x, int(j/3*3) + y
                    if self.num[posX][posY] == 0:
                        self.num[posX][posY].autoTipList = self.boxer.getValidNum(posX, posY)
            #Update vertical line
            for n in app.rgLINE:
                if self.num[i][n] == 0:
                    self.num[i][n].autoTipList = self.boxer.getValidNum(i,n)
            #Update horizantol line
            for n in app.rgLINE:
                if self.num[n][j] == 0:
                    self.num[n][j].autoTipList = self.boxer.getValidNum(n,j)
        pass
    
    def getDefaultPuzzle(self, bString=False):
        if not bString:
            return deepcopy(self.default)
        else:
            return util.puzzle2str(self.default)
            
    def getCurrentPuzzle(self, bString=False):
        if not bString:
            return deepcopy(self.num)
        else:
            return util.puzzle2str(self.num)
        
    def canUndo(self):
        return self.cur_step >= 0
        
    def canRedo(self):
        return self.cur_step < len(self.steps)-1
        
    def redo(self):
        if not self.canRedo():
            return
        self.cur_step+=1
        s = self.steps[self.cur_step]
        self._setVal(*s.infoRedo())
        
    def undo(self):
        if not self.canUndo():
            return
        s = self.steps[self.cur_step]
        self._setVal(*s.infoUndo())
        self.cur_step-=1
        self.clearBoxerInfo()

    def checkValid(self):
        return boxer_util.check_valid(self.num)

    def checkFinish(self):
        return boxer_util.check_finish(self.num)

    def pt2pos(self, x, y):
        _x, _y = int(x / self.CELL_SIZE), int(y / self.CELL_SIZE)
        if 0 <= _x < app.nLINE or 0 <= _y < app.nLINE:
            return _x, _y
        else:
            return -1, -1
            
    def onKeyUp(self, evt):
        #logger.info('KeyUp = %s', evt.GetKeyCode())
        key = evt.GetKeyCode()
        if key in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_UP, wx.WXK_DOWN]:
            x, y = 0, 0
            if   key==wx.WXK_LEFT:  x-=1
            elif key==wx.WXK_RIGHT: x+=1
            elif key==wx.WXK_UP:    y-=1
            elif key==wx.WXK_DOWN:  y+=1
            self.doMoveFocus(x,y)
            
            evt.Skip()
        pass
        
    def onKeyDown(self, evt):
        if self.focusPos != (-1,-1) and self.num[self.focusPos[0]][self.focusPos[1]].isDefault:
            return
        
        dirty = True
        num_idx = -1
        key = evt.GetKeyCode()
        if key < 256 and \
           key not in [wx.WXK_BACK, wx.WXK_RETURN, wx.WXK_ESCAPE, wx.WXK_SPACE, wx.WXK_DELETE]:
            key = chr(key)
        
        try:
            num_list = [wx.WXK_NUMPAD1, wx.WXK_NUMPAD2, wx.WXK_NUMPAD3, wx.WXK_NUMPAD4, 
                        wx.WXK_NUMPAD5, wx.WXK_NUMPAD6, wx.WXK_NUMPAD7, wx.WXK_NUMPAD8, wx.WXK_NUMPAD9]
            num_idx = num_list.index(key) + 1
        except ValueError:
            pass
        logger.info('KeyDown key=%s, num_idx=%d', str(key), num_idx)
        
        if num_idx > -1:
            self.setVal(self.focusPos[0], self.focusPos[1], num_idx)
            
        elif key in [ str(i) for i in range(1, 10) ]:
            self.setVal(self.focusPos[0], self.focusPos[1], int(key))
            
        elif key in [wx.WXK_DELETE, wx.WXK_BACK, wx.WXK_SPACE]:
            self.setVal(self.focusPos[0], self.focusPos[1], 0)
            if self.highlightNum != 0:
                self.highlightNum = 0
                self.Refresh()
            
        else:
            dirty = False
        
        if dirty :
            evt.Skip()
    
    def doMoveFocus(self, x, y):
        if self.focusPos == (-1,-1):
            return
        curX, curY = self.focusPos
        newX, newY = curX, curY
        if x > 0:
            r = range(curX+1, app.nLINE)
        elif x < 0:
            r = range(curX-1, -1, -1)
        else:
            r = []
        for i in r:
            if self.default[i][curY] == 0:
                newX = i
                break
        
        if y > 0:
            r = range(curY+1, app.nLINE)
        elif y < 0:
            r = range(curY-1, -1, -1)
        else:
            r = []
        for i in r:
            if self.default[curX][i] == 0:
                newY = i
                break
        
        if self.focusPos != (newX, newY):
            self.dirtyCell(newX, newY)
            self.dirtyCell(*self.focusPos)
            self.focusPos = (newX, newY)
        
    def onMouseDown(self, evt):
        x,y = evt.GetPosition()
        pos = self.pt2pos(x,y)
        if self.focusPos != pos:
            self.dirtyCell(*pos)
            self.dirtyCell(*self.focusPos)
            self.focusPos = pos
        
        #Set highlight for one number
        num = self.getNum(*self.focusPos)
        if num != 0:
            if self.highlightNum == 0 or num != self.highlightNum:
                self.highlightNum = num.val
            else:
                self.highlightNum = 0
            self.Refresh()
            
        if self.choiceNumberPanel:
            _pos = self.choiceNumberPanel.getCellPos()
            self.getNum(*_pos).tipList = self.choiceNumberPanel.getChoiceNums()
            self.choiceNumberPanel.Destroy()
            self.Refresh()
        #self.testAnim.setType(anim.AnimBase.DECAY)
        #self.testAnim.assign(0)
        #self.testAnim.reset(1, 100)
    
    def onMouseRDown(self, evt):
        self.clearBoxerInfo()
        if app.bShowAutoTip:
           return
           
        x,y = evt.GetPosition()
        cellPos = self.pt2pos(x,y)
        if self.getNum(*cellPos) > 0:
            return
            
        s = (app.nCellSize*0.55)*3
        pos = [cellPos[0]*app.nCellSize + (app.nCellSize-s)/2,
               cellPos[1]*app.nCellSize + (app.nCellSize-s)/2]
        if pos[0] < 0:
            pos[0] = 0
        if pos[1] < 0:
            pos[1] = 0
            
        w, h = self.GetSize()
        if pos[0]+s > w:
            pos[0] = w-s
        if pos[1]+s > h:
            pos[1] = h-s
            
        import ui
        if self.choiceNumberPanel:
            _pos = self.choiceNumberPanel.getCellPos()
            self.getNum(*_pos).tipList = self.choiceNumberPanel.getChoiceNums()
            self.choiceNumberPanel.Destroy()
        self.choiceNumberPanel = ui.ChoiceNumberPanel(self, -1, pos=pos, size=(s,s))
        self.choiceNumberPanel.setChoiceNums( self.num[cellPos[0]][cellPos[1]].tipList )
        self.choiceNumberPanel.setCellSize(s/3)
        self.choiceNumberPanel.setCellPos(*cellPos)
        pass
        
    def onMouseMove(self, evt):
        x,y = evt.GetPosition()
        pos = self.pt2pos(x,y)
        if self.mouseOverPos != pos:
            self.dirtyCell(*self.mouseOverPos)
            self.dirtyCell(*pos)
            self.mouseOverPos = pos
            
    def onMouseLeave(self, evt):
        self.dirtyCell(*self.mouseOverPos)
        self.mouseOverPos = (-1, -1)
    
    def dirtyCell(self, i, j):
        if i == -1 or j == -1:
            return
        self.RefreshRect((i*self.CELL_SIZE, j*self.CELL_SIZE, self.CELL_SIZE, self.CELL_SIZE))
    
    def initBmpBorder(self):
        bmp = wx.EmptyBitmap(app.nLINE*self.CELL_SIZE, app.nLINE*self.CELL_SIZE)
        mdc = wx.MemoryDC()
        mdc.SelectObject(bmp)
        mdc.SetBackground(wx.BLACK_BRUSH) #set black for mask
        mdc.Clear()
        
        self.onDrawBorder(mdc)
        
        mdc.SelectObject(wx.NullBitmap) #release bmp
        bmp.SetMask(wx.Mask(bmp, wx.BLACK)) #Color mask
        self.bmpBorder = bmp
    
    def initBmpNum(self):
        size = self.CELL_SIZE
        content_list = [ {'bmp':self.bmpNum,        'font':self.font,    'textColor':self.CL_TEXT_VALID,   'size': size },
                         {'bmp':self.bmpNumInvalid, 'font':self.font,    'textColor':self.CL_TEXT_INVALID, 'size': size },
                         {'bmp':self.bmpNumAutoTip, 'font':self.fontTip, 'textColor':self.CL_TEXT_AUTOTIP, 'size': size/3 } ]
        
        for content in content_list:
            for i in range(1, 10):
                bmp = wx.EmptyBitmap(content['size'], content['size'])
                mdc = wx.MemoryDC()
                mdc.SelectObject(bmp)
                mdc.SetBackground(wx.WHITE_BRUSH) #set white for mask
                mdc.Clear()
                
                mdc.SetPen(wx.Pen('#000000', 1, wx.TRANSPARENT)) #Don't draw the border on BG rect
                mdc.SetFont( content['font'] )
                mdc.SetTextForeground( content['textColor'] )
                mdc.DrawLabel(str(i), (0,0,content['size'],content['size']), wx.ALIGN_CENTER)
                
                mdc.SelectObject(wx.NullBitmap) #release bmp
                bmp.SetMask(wx.Mask(bmp, wx.WHITE)) #Color mask
                content['bmp'].append(bmp)
        
    def onDraw(self, event):
        bpdc = wx.BufferedPaintDC(self)
        dc = wx.GCDC(bpdc)#for alpha effect
        
        #No use temp bitmap number, because the quality is sucks!
        #Replace by RefreshRect mechanism
        #if not self.bmpNum:
        #    self.initBmpNum()
        
        if not self.bmpBorder:
            self.initBmpBorder()
        
        self.onDrawText(dc)
        
        #draw border
        dc.DrawBitmap(self.bmpBorder, 0, 0, True)
        
        #draw boxer info
        self.boxerInfo.draw(dc, self.CELL_SIZE)
        
    def onDrawText(self, dc):
        '''
        Paint text and background color
        '''
        r = self.GetUpdateRegion()
        dirtyR = r.GetBox()
        
        dc.SetFont(self.font)
        dc.SetPen(self.penTrans) #Don't draw the border on BG rect
        for i in app.rgLINE:
            for j in app.rgLINE:
                _r = (i*self.CELL_SIZE, j*self.CELL_SIZE, self.CELL_SIZE, self.CELL_SIZE)
                
                #skip the cell doesn't in dirty region
                if not dirtyR.Intersects(_r):
                    continue
                    
                if self.default[i][j] != 0:
                    dc.SetBrush(self.brush_bg_defalt)
                elif (i,j) == self.focusPos:
                    dc.SetBrush(self.brush_bg_focus)
                elif (i,j) == self.mouseOverPos:
                    dc.SetBrush(self.brush_bg_over)
                else:
                    dc.SetBrush(self.brush_bg)
                
                dc.DrawRectangle(*_r)
                dc.SetBrush(wx.NullBrush)
                
                if self.num[i][j] == 0:
                    self.onDrawTipList(dc, i, j, app.bShowAutoTip)
                    continue
                
                dc.SetFont( self.font )
                if self.num[i][j] == self.highlightNum:
                    dc.SetTextForeground( self.CL_TEXT_HIGHLIGHT )
                elif self.num[i][j].valid:
                    dc.SetTextForeground( self.CL_TEXT_VALID )
                else:
                    dc.SetTextForeground( self.CL_TEXT_INVALID )
                dc.DrawLabel( str(self.num[i][j]), _r, wx.ALIGN_CENTER )
                
        dc.SetPen(wx.NullPen)
        pass
    
    def onDrawTipList(self, dc, i, j, autoTip=True):
        if autoTip:
            tips = self.num[i][j].autoTipList
        else:
            tips = self.num[i][j].tipList
        if not tips:
            return
        l, t = i*self.CELL_SIZE, j*self.CELL_SIZE
        size = self.CELL_SIZE/3
        dc.SetFont( self.fontTip )
        dc.SetTextForeground( self.CL_TEXT_AUTOTIP )
        for n in tips:
            r = (l + int((n-1)%3)*size, t + int((n-1)/3)*size,size,size)
            dc.DrawLabel(str(n), r, wx.ALIGN_CENTER)
        
    def onDrawBorder(self, dc):
        dc.SetBrush(wx.NullBrush)
        
        s  = self.CELL_SIZE
        sl = s * app.nLINE
        
        #Draw Dot Line
        dc.SetPen(wx.Pen(self.CL_LINE_DOT, 1, wx.DOT))
        for i in app.rgGRID:
            _x = i*s*app.nGRID
            dc.DrawLine( _x+s,   0, _x+s,   sl )
            dc.DrawLine( _x+s*2, 0, _x+s*2, sl )
            
            _y = i*s*app.nGRID
            dc.DrawLine( 0, _y+s,   sl, _y+s   )
            dc.DrawLine( 0, _y+s*2, sl, _y+s*2 )
        dc.SetPen(wx.NullPen)
        
        #Draw Solid Line
        w = 2 #line width
        dc.SetPen(wx.Pen(self.CL_LINE_SOLID, w, wx.SOLID))
        for i in range(app.nGRID+1):
            _pos = i*s*app.nGRID
            dc.DrawLine( _pos, 0, _pos, sl )
            dc.DrawLine( 0, _pos, sl, _pos )
        dc.SetPen(wx.NullPen)
        pass