[Up]

Pentomino (PyQt4版)

wxPython版のPentominoをそのままPyQt4に移行しようとしても、うまく動かなかった。

最も要となる関数solveがrecursive(再帰的)に使われているため、そこからPanelの画描をしようとしてもダメらしい。

このため、プログラムの本体から、探索を行うスレッドを立ち上げ、そのスレッドより画描メッセージを発するという形にすると、うまく行くようになった。ただこれも次のメッセージが来るまでに画描が終了していなくてはならないようだ。

PyQt4でのGraphics、thread、mutex、event messegeの例として参考になる。

#!/usr/bin/env python

# Pentomino puzzle solver
#    Jean-Claude Rimbault (pynokio.org, 2005)
#
#    modified for PyQt4
#       by keiji imoto
#          department of information physiology
#          national institute for physiological sciences
#          okazaki, japan
# 
#       it was necessapy to create a thread to trigger 
#       paintEvent from the recursive function.
#
#       version 
#         21-november-2011 initial version 
#         23-november-2011 isStopping flag
#
import sys
import time
from PyQt4 import QtCore, QtGui

global n
global board2
global mutex
global isRunning

class Window(QtGui.QWidget):

    def __init__(self, parent = None):
        
        QtGui.QWidget.__init__(self, parent)

        self.thread = Pentomino(self)
        self.connect(self.thread, QtCore.SIGNAL("output()"), 
                     self.drawResult)
        self.initUI()


    def initUI(self):

        self.panel = Panel(self)
        self.panel.move(50,20)

        self.lcd = QtGui.QLCDNumber(5,self)
        self.lcd.setSegmentStyle(QtGui.QLCDNumber.Flat)
        self.lcd.move(50, 220) 

        modeButton = QtGui.QPushButton("&Mode",self)
        modeButton.setFocusPolicy(QtCore.Qt.NoFocus)
        modeButton.move(150, 220) 
        modeButton.clicked.connect(self.changeMode)

        startButton = QtGui.QPushButton("&Start",self)
        startButton.setFocusPolicy(QtCore.Qt.NoFocus)
        startButton.move(220, 220) 
        startButton.clicked.connect(self.startSearch)

        quitButton = QtGui.QPushButton("&Quit",self)
        quitButton.setFocusPolicy(QtCore.Qt.NoFocus)
        quitButton.move(290, 220) 
        quitButton.clicked.connect(self.finishSearch)

        self.setWindowTitle("PyQt4 PENTOMINO")
        self.resize(400, 270)
        self.show()


    def drawResult(self):
        global n
        self.lcd.display(n)
        self.panel.repaint() 

    def changeMode(self):
        self.thread.changeDisplayMode()

    def startSearch(self):
        self.thread.startSolve()

    def finishSearch(self):
        global mutex
        global isRunning
        mutex.lock()
        isRunning = False
        self.close()
        mutex.unlock()



class Panel(QtGui.QFrame):

    def __init__(self, parent=None):

        super(Panel,self).__init__(parent)

        self.w = parent.thread.w
        self.h = parent.thread.h

        self.resize(300,180)

        self.colors = {
            ' ': 0xffffff,
            'X': 0xED6D46,
            'T': 0xF8B856, 
            'C': 0xFFF462,
            'W': 0xB6D56A, 
            'I': 0x3EB370,
            'S': 0x2BB7B3,
            'F': 0x00B9EF,
            'P': 0x4C8DCB, 
            'L': 0x6356A3, 
            'V': 0xB062A3, 
            'Y': 0xEB6EA5, 
            'N': 0xEC6D7B, 
        }
  	 	  	

    def paintEvent(self, event):
        global board2
        global mutex

        mutex.lock()

        qp = QtGui.QPainter(self)
        for col in range(self.h):
            for row in range(self.w):
                x = 30*row
                y = 30*col

                color = QtGui.QColor(self.colors[board2[row*10+col+11]])
                qp.fillRect(x, y, 30, 30, color)

                qp.setPen(color.light())
                qp.drawLine(x, y + 29, x, y)
                qp.drawLine(x, y, x + 29, y)

                qp.setPen(color.dark())
                qp.drawLine(x + 1, y + 29, x + 29, y + 29)
                qp.drawLine(x + 29, y + 29, x + 29, y + 1)

        mutex.unlock()


class Pentomino(QtCore.QThread):

    def __init__(self, parent=None):

        global board2

        self.parent = parent
        QtCore.QThread.__init__(self, parent)

        self.w = 10
        self.h = 6
        self.board = ['#'] * 250

        if len(sys.argv) == 1:    # no arguments
            self.runMode = 0
            self.st = 0.010
        else:
            self.runMode = 1
            self.st = 1.0

        self.st2 = 0.010


        for row in range(self.w):
            for col in range(self.h):
                self.board[row*10+col+11] = ' '

        board2 = self.board[:]


        self.pieces = [ 'C', 'X', 'T', 'Y', 'F', 'W', 
                        'P', 'I', 'S', 'L', 'V', 'N' ]

        self.shapes = {
            'C': ((0, 1, 10, 20, 21),
                (0, 1, 11, 20, 21),
                (0, -10, -9, -8, 2),
                (0, 10, 11, 12, 2)),
            'F': ((0, 1, -9, 2, 12),
                (0, 1, 11, 2, -8),
                (0, 10, 11, 21, 12),
                (0, -10, -9, -19, -8),
                (0, 1, 11, 21, 12),
                (0, 1, -9, -19, -8),
                (0, 1, 11, -9, 12),
                (0, 1, 11, -9, -8)),
            'I': ((0, 1, 2, 3, 4),
                (0, 10, 20, 30, 40)),
            'L': ((0, 1, 2, 3, 13),
                (0, 1, 2, 3, -7),
                (0, 10, 20, 30, 1),
                (0, -10, -20, -30, 1),
                (0, 1, 11, 21, 31),
                (0, 1, -9, -19, -29),
                (0, 10, 1, 2, 3),
                (0, -10, 1, 2, 3)),
            'N': ((0, 1, 11, 12, 13),
                (0, 1, -9, -8, -7),
                (0, 1, 2, 12, 13),
                (0, 1, 2, -8, -7),
                (0, 10, 20, 21, 31),
                (0, 10, 20, 19, 29),
                (0, 10, 11, 21, 31),
                (0, 10, 9, 19, 29)),
            'P': ((0, 1, 2, 11, 12),
                (0, 1, 2, -9, -8),
                (0, 1, 2, 10, 11),
                (0, 1, 2, -10, -9),
                (0, 1, 10, 11, 20),
                (0, 1, 10, 11, 21),
                (0, 1, -10, -9, -20),
                (0, 1, -10, -9, -19)),
            'S': ((0, 1, 11, 21, 22),
                (0, 1, -9, -19, -18),
                (0, 10, 11, 12, 22),
                (0, -10, -9, -8, -18)),
            'T': ((0, 1, 11, 21, 2),
                (0, 1, -9, -19, 2),
                (0, 1, 2, -8, 12),
                (0, 10, -10, 1, 2)),
            'V': ((0, 1, 2, 12, 22),
                (0, 1, 2, -8, -18),
                (0, 1, 2, 10, 20),
                (0, 1, 2, -10, -20)),
            'W': ((0, 1, 11, 12, 22),
                (0, 1, -9, -8, -18),
                (0, 10, 11, 21, 22),
                (0, -10, -9, -19, -18)),
            'X': ((0, -9, 1, 11, 2),),
            'Y': ((0, 1, 2, 3, 12),
                (0, 1, 2, 3, -8),
                (0, 10, 20, 30, 11),
                (0, -10, -20, -30, -9),
                (0, 1, 11, 21, -9),
                (0, 1, -9, -19, 11),
                (0, 11, 1, 2, 3),
                (0, -9, 1, 2, 3))
        }

    def __del__(self):
        self.wait()

    def startSolve(self):
        self.start()

    def changeDisplayMode(self):
        global mutex

        mutex.lock()
        if self.runMode :
            self.runMode = 0
            self.st = 0.0010
        else:
            self.runMode = 1
            self.st = 1.0
        mutex.unlock()

    def run(self):
        self.solve()

    def solve(self):
        global n
        global board2
        global mutex
        global isRunning
        global runMode

        for q in range(len(self.board)):
            if self.board[q] == ' ':
                for p in self.pieces:
                    for s in self.shapes[p]:
                        for c in s:
                            for d in s:
                                if self.board[q+d-c] != ' ':
                                    break
                                pass
                            else:   # for-break-else
                                for d in s:
                                    self.board[q+d-c] = p
                                #end for d    
                                i = self.pieces.index(p)
                                self.pieces.remove(p)
                                if not self.pieces:
                                    n += 1
                                    mutex.lock()
                                    board2 = self.board[:]
                                    self.emit(QtCore.SIGNAL("output()"))
                                    mutex.unlock()
                                    time.sleep(self.st)
                                else:
                                    if self.runMode:
                                        mutex.lock()
                                        board2 = self.board[:]
                                        self.emit(QtCore.SIGNAL("output()"))
                                        mutex.unlock()
                                        time.sleep(self.st2)
                                    if isRunning :
                                        self.solve()
                                    else:
                                        return
                                # end if-else
                                self.pieces.insert(i, p)
                                for d in s:
                                    self.board[q+d-c] = ' '
                                # end for d
                            pass # end of for-else
                        pass # end of for c
                    pass # end of fors        
                pass # end of for p
                return
            pass # end if
        pass # end for q

def main():

    global n
    global mutex 
    global isRunning

    n = 0
    mutex = QtCore.QMutex()
    mutex.RecursionMode(QtCore.QMutex.NonRecursive)
    isRunning = True

    app = QtGui.QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec_())
  
if __name__ == '__main__':
    main()

[Up]