Conway's Game of Life component

This commit is contained in:
tassaron 2017-08-09 16:46:59 -04:00
parent 3ed84e1c3e
commit 8b253717f7
4 changed files with 592 additions and 0 deletions

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>586</width>
<height>197</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

155
src/components/life.py Normal file
View File

@ -0,0 +1,155 @@
from PyQt5 import QtGui, QtCore, QtWidgets
from PIL import ImageDraw, ImageEnhance, ImageChops, ImageFilter
import os
import math
from component import Component
from toolkit.frame import BlankFrame, FramePainter
class Component(Component):
name = 'Conway\'s Game of Life'
version = '1.0.0a'
def widget(self, *args):
super().widget(*args)
self.scale = 32
self.updateGridSize()
self.startingGrid = {}
self.trackWidgets({
'tickRate': self.page.spinBox_tickRate,
'scale': self.page.spinBox_scale,
'color': self.page.lineEdit_color,
'shapeType': self.page.comboBox_shapeType,
'shadow': self.page.checkBox_shadow,
}, colorWidgets={
'color': self.page.pushButton_color,
})
self.page.spinBox_scale.setValue(self.scale)
def update(self):
self.updateGridSize()
super().update()
def previewClickEvent(self, pos, size, button):
pos = (
math.ceil((pos[0] / size[0]) * self.gridWidth) - 1,
math.ceil((pos[1] / size[1]) * self.gridHeight) - 1
)
if button == 1:
self.startingGrid[pos] = True
elif button == 2 and pos in self.startingGrid:
self.startingGrid.pop(pos)
def updateGridSize(self):
w, h = self.core.resolutions[-1].split('x')
self.gridWidth = int(int(w) / self.scale)
self.gridHeight = int(int(h) / self.scale)
self.pxWidth = math.ceil(self.width / self.gridWidth)
self.pxHeight = math.ceil(self.height / self.gridHeight)
def previewRender(self):
return self.drawGrid(self.startingGrid)
def preFrameRender(self, *args, **kwargs):
super().preFrameRender(*args, **kwargs)
self.progressBarSetText.emit("Computing evolution...")
self.tickGrids = {0: self.startingGrid}
tick = 0
for frameNo in range(
self.tickRate, len(self.completeAudioArray), self.sampleSize
):
if frameNo % self.tickRate == 0:
tick += 1
self.tickGrids[tick] = self.gridForTick(tick)
# update progress bar
progress = int(100*(frameNo/len(self.completeAudioArray)))
if progress >= 100:
progress = 100
pStr = "Computing evolution: "+str(progress)+'%'
self.progressBarSetText.emit(pStr)
self.progressBarUpdate.emit(int(progress))
def frameRender(self, frameNo):
tick = math.floor(frameNo / self.tickRate)
grid = self.tickGrids[tick]
return self.drawGrid(grid)
def drawGrid(self, grid):
frame = BlankFrame(self.width, self.height)
drawer = ImageDraw.Draw(frame)
for x, y in grid:
drawPtX = x * self.pxWidth
drawPtY = y * self.pxHeight
rect = (
(drawPtX, drawPtY),
(drawPtX + self.pxWidth, drawPtY + self.pxHeight)
)
if self.shapeType == 0:
drawer.rectangle(rect, fill=self.color)
elif self.shapeType == 1:
drawer.ellipse(rect, fill=self.color)
elif self.shapeType == 2:
drawer.pieslice(rect, 290, 250, fill=self.color)
elif self.shapeType == 3:
drawer.pieslice(rect, 20, 340, fill=self.color)
if self.shadow:
shadImg = ImageEnhance.Contrast(frame).enhance(0.0)
shadImg = shadImg.filter(ImageFilter.GaussianBlur(5.00))
shadImg = ImageChops.offset(shadImg, -2, 2)
shadImg.paste(frame, box=(0, 0), mask=frame)
frame = shadImg
return frame
def gridForTick(self, tick):
'''Given a tick number over 0, returns a new grid dict of tuples'''
lastGrid = self.tickGrids[tick - 1]
def nearbyCoords(x, y):
yield x + 1, y + 1
yield x + 1, y - 1
yield x - 1, y + 1
yield x - 1, y - 1
yield x, y + 1
yield x, y - 1
yield x + 1, y
yield x - 1, y
def neighbours(x, y):
nearbyCells = [
lastGrid.get(cell) for cell in nearbyCoords(x, y)
]
return [
nearbyCell for nearbyCell in nearbyCells
if nearbyCell is not None
]
newGrid = {}
for x, y in lastGrid:
surrounding = len(neighbours(x, y))
if surrounding == 2 or surrounding == 3:
newGrid[(x, y)] = True
potentialNewCells = set([
coordTup for origin in lastGrid
for coordTup in list(nearbyCoords(*origin))
])
for x, y in potentialNewCells:
if (x, y) in newGrid:
continue
surrounding = len(neighbours(x, y))
if surrounding == 3:
newGrid[(x, y)] = True
return newGrid
def savePreset(self):
pr = super().savePreset()
pr['GRID'] = self.startingGrid
return pr
def loadPreset(self, pr, *args):
super().loadPreset(pr, *args)
self.startingGrid = pr['GRID']

302
src/components/life.ui Normal file
View File

@ -0,0 +1,302 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>586</width>
<height>197</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Simulation Speed</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_tickRate">
<property name="suffix">
<string> frames per tick</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="value">
<number>15</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_color">
<property name="maximumSize">
<size>
<width>0</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0,0,0</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Grid Scale</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_scale">
<property name="minimum">
<number>24</number>
</property>
<property name="maximum">
<number>128</number>
</property>
<property name="value">
<number>32</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_color_3">
<property name="maximumSize">
<size>
<width>0</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0,0,0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_color">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_16">
<property name="text">
<string>Shape</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_shapeType">
<item>
<property name="text">
<string>Rectangle</string>
</property>
</item>
<item>
<property name="text">
<string>Circle</string>
</property>
</item>
<item>
<property name="text">
<string>Lilypad</string>
</property>
</item>
<item>
<property name="text">
<string>Pac-Man</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QCheckBox" name="checkBox_shadow">
<property name="text">
<string>Shadow</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="textBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Click the preview window to place a cell. Right-click to remove.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- A cell with less than 2 neighbours will die from underpopulation&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- A cell with more than 3 neighbours will die from overpopulation.&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;- An empty space surrounded by 3 live cells will cause reproduction.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="tabStopWidth">
<number>80</number>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
<property name="openLinks">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -50,6 +50,22 @@ class PreviewWindow(QtWidgets.QLabel):
self.pixmap = QtGui.QPixmap(img)
self.repaint()
def mousePressEvent(self, event):
if self.parent.encoding:
return
i = self.parent.window.listWidget_componentList.currentRow()
if i >= 0:
component = self.parent.core.selectedComponents[i]
if not hasattr(component, 'previewClickEvent'):
return
pos = (event.x(), event.y())
size = (self.width(), self.height())
component.previewClickEvent(
pos, size, event.button()
)
self.parent.core.updateComponent(i)
@QtCore.pyqtSlot(str)
def threadError(self, msg):
self.parent.showMessage(