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