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()
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