Jigsaw game prototype for storage and access.

This commit is contained in:
serialexperiments0815 2025-11-28 15:04:47 +01:00
commit b6a7c7aed1
8 changed files with 395 additions and 81 deletions

9
data/aboutwindow.py Normal file
View file

@ -0,0 +1,9 @@
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QDialog
class AboutWindow(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("About")
layout = QVBoxLayout()
layout.addWidget(QLabel("About this application."))
self.setLayout(layout)

20
data/imageconversion.py Normal file
View file

@ -0,0 +1,20 @@
from PyQt5.QtWidgets import QFileDialog
class ImageConversion():
def __init__(self):
pass
def openImageFile(self):
filePath, _ = QFileDialog.getOpenFileName(
None,
"Select an image",
"",
"Image Files (*.png *.jpg *.jpeg *.bmp *.gif)"
)
if filePath:
return filePath
def openFolder(self):
folderPath = QFileDialog.getExistingDirectory(None, "Select Folder")
if folderPath:
return folderPath

99
data/imagetojigsaw.py Normal file
View file

@ -0,0 +1,99 @@
from PyQt5.QtWidgets import (
QGraphicsScene, QGraphicsPixmapItem, QMessageBox
)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import Qt
from PIL import Image
import random
class JigsawPiece(QGraphicsPixmapItem):
number = 0
def __init__(self, pixmap, correctPos):
super().__init__()
self.setPixmap(pixmap)
self.correctPos = correctPos
self.setFlags(
QGraphicsPixmapItem.ItemIsMovable |
QGraphicsPixmapItem.ItemIsSelectable
)
self.setPos(0, 0)
JigsawPiece.number += 1
self.number = JigsawPiece.number
self.cluster = {self}
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.dragPosition = event.pos()
print(event.pos(), self.correctPos, " ", self.number)
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton and self.dragPosition:
delta = self.mapToParent(event.pos()) - self.dragPosition - self.pos()
for piece in self.cluster:
piece.setPos(piece.pos() + delta)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
firstRow = 0
lastRow = (PuzzleWindow.getRows)
print(firstRow, lastRow)
if (self.number):
None
class PuzzleWindow(QGraphicsScene):
def __init__(self, filePath, rows, cols):
super().__init__()
self.filePath = filePath
self.rows = rows
print(self.rows-1)
self.cols = cols
print(self.cols-1)
if (filePath != None):
self.createPuzzle(rows, cols)
def createPuzzle(self, rows, cols):
image = Image.open(self.filePath)
w, h = image.size
pieceW, pieceH = w // cols, h // rows
if (w >= 800 and h >= 800):
msg = QMessageBox()
msg.setWindowTitle("Error")
msg.setText("Only image below the height 800 and width 800 are supported\n"
f"Current image width: {w}\n"
f"Current image height: {h}")
msg.setIcon(QMessageBox.Information)
msg.exec_()
else:
for i in range(rows):
for j in range(cols):
box = (j*pieceW, i*pieceH, (j+1)*pieceW, (i+1)*pieceH)
pieceImg = image.crop(box)
width, height = pieceImg.width, pieceImg.height
data = pieceImg.tobytes("raw", "RGB")
qimage = QImage(data, width, height, 3 * width, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qimage)
piece = JigsawPiece(pixmap, (j*pieceW, i*pieceH))
print(f"Piecenumber: {piece.number} | Row: {i} Col: {j} | H: {pieceImg.height} W: {pieceImg.width}")
piece.setPos(random.randint(0, w-pieceW), random.randint(0, h-pieceH))
self.addItem(piece)
piece.setPos(random.randint(0, int(self.width()-pieceW)), random.randint(0, int(self.height()-pieceH)))
print("1111")
def getRows(self):
return self.rows
def getCols(self):
return self.cols

32
data/jsonhandler.py Normal file
View file

@ -0,0 +1,32 @@
import os, json
class JSONHandling:
def __init__(self):
if os.path.exists("settings.json"):
self.settingsFile = "settings.json"
else:
data = {"color": "Blue", "puzzlePieces": 50}
with open("settings.json", "w") as f:
json.dump(data, f)
def getFileLocation(self):
return self.settingsFile
def getDataFull(self):
if os.path.exists("settings.json"):
try:
with open(self.settingsFile) as f:
rValue = json.load(f)
return rValue
except Exception:
pass
def setData(self, attribute, parameter):
data = self.getDataFull()
data[attribute] = parameter
with open(self.settingsFile, "w") as f:
json.dump(data, f)
def getData(self, attribute):
data = self.getDataFull()
return data[attribute]

83
data/mainwindow.py Normal file
View file

@ -0,0 +1,83 @@
from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QGraphicsView, QToolBar, QAction, QMenu
from PyQt5.QtGui import QIcon
from data.settingswindow import SettingsWindow
from data.jsonhandler import JSONHandling
from data.imagetojigsaw import JigsawPiece, PuzzleWindow
from data.imageconversion import ImageConversion
class MainWindow(QMainWindow):
def __init__(self):
self.settings = JSONHandling()
self.settingsWindow = SettingsWindow()
self.imageConversion = ImageConversion()
super().__init__()
self.setWindowTitle("Jigsaw")
self.resize(1024, 768)
central = QWidget()
self.setCentralWidget(central)
layout = QHBoxLayout(central)
leftSide = QWidget()
leftLayout = QVBoxLayout(leftSide)
leftLayout.setContentsMargins(0, 0, 0, 0)
leftLayout.setSpacing(5)
self.scene = PuzzleWindow(None, self.settingsWindow.piecesToGrid(self.settings.getData("puzzlePieces"))[0], self.settingsWindow.piecesToGrid(self.settings.getData("puzzlePieces"))[1])
self.view = QGraphicsView(self.scene)
self.view.setStyleSheet(f"background-color: {self.settings.getData("color")}; border: 1px solid black;")
leftLayout.addWidget(self.view)
topSide = QToolBar("Main Toolbar")
self.addToolBar(topSide)
settingsAction = QAction("Settings", self)
settingsMenu = QMenu()
settingsMenu.addAction("Settings", self.settingsWindow.show)
settingsAction.setMenu(settingsMenu)
self.settingsWindow.settingsChanged.connect(self.reloadSettings)
aboutAction = QAction(QIcon(), "About", self)
aboutMenu = QMenu()
aboutMenu.addAction("About App")
aboutAction.setMenu(aboutMenu)
puzzleAction = QAction(QIcon(), "Puzzle", self)
puzzleMenu = QMenu()
addImageAction = puzzleMenu.addAction("Add Image")
selectedImage = addImageAction.triggered.connect(lambda: self.handlerInput("image"))
addFolderAction = puzzleMenu.addAction("Add Folder")
selectedFolder = addFolderAction.triggered.connect(lambda: self.handlerInput("folder"))
puzzleAction.setMenu(puzzleMenu)
topSide.addAction(puzzleAction)
topSide.addAction(settingsAction)
topSide.addAction(aboutAction)
layout.addWidget(leftSide, stretch=3)
def reloadSettings(self):
self.view.setStyleSheet(f"background-color: {self.settings.getData("color")}; border: 1px solid #ccc;")
JigsawPiece.number = 0
def handlerInput(self, typeInput):
if typeInput == "image":
selectedImage = self.imageConversion.openImageFile()
if selectedImage:
self.constructPuzzle(selectedImage)
elif typeInput == "folder":
selectedFolder = self.imageConversion.openFolder()
if selectedFolder:
self.constructPuzzle(selectedFolder)
def constructPuzzle(self, input):
print(input)
self.scene = PuzzleWindow(input, self.settingsWindow.piecesToGrid(self.settings.getData("puzzlePieces"))[0], self.settingsWindow.piecesToGrid(self.settings.getData("puzzlePieces"))[1])
self.view.setScene(self.scene)

75
data/settingswindow.py Normal file
View file

@ -0,0 +1,75 @@
from PyQt5.QtWidgets import (
QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QDialog, QComboBox, QSlider
)
from PyQt5.QtCore import Qt, pyqtSignal
import json, math
from data.jsonhandler import JSONHandling
class SettingsWindow(QDialog):
settingsChanged = pyqtSignal()
def __init__(self):
super().__init__()
self.settings = JSONHandling()
self.setWindowTitle("Settings")
self.resize(400,200)
layout = QVBoxLayout()
layout.addWidget(QLabel("Select Color Theme:"))
self.colorBox = QComboBox()
self.colorBox.addItems(["Blue", "Red", "Green", "Yellow", "Purple", "Gray"])
self.colorBox.setCurrentText(self.settings.getData("color"))
layout.addWidget(self.colorBox)
layout.addWidget(QLabel("Warning, due to the nature of the calculations. The exact number may not be used to fill the grid."))
layout.addWidget(QLabel("Number of Puzzle Pieces:"))
self.slider = QSlider(Qt.Horizontal)
self.slider.setMinimum(12)
self.slider.setMaximum(64)
self.slider.setSingleStep(2)
self.slider.setPageStep(2)
self.slider.setValue(int(self.settings.getData("puzzlePieces")))
self.slider.valueChanged.connect(self.updateSliderValue)
self.sliderText = QLabel("Number of puzzle pieces: " + f"{self.settings.getData("puzzlePieces")}")
layout.addWidget(self.slider)
layout.addWidget(self.sliderText)
buttonLayout = QHBoxLayout()
saveBtn = QPushButton("Save")
cancelBtn = QPushButton("Cancel")
buttonLayout.addWidget(saveBtn)
buttonLayout.addWidget(cancelBtn)
layout.addLayout(buttonLayout)
saveBtn.clicked.connect(self.saveSettings)
cancelBtn.clicked.connect(self.reject)
self.setLayout(layout)
def updateSliderValue(self, value):
print(value)
snapped = 2 * round(value / 2)
self.sliderText.setText(f"Number of puzzle pieces: {snapped}")
def saveSettings(self):
data = {
"color": self.colorBox.currentText(),
"puzzlePieces": self.slider.value()
}
with open (self.settings.getFileLocation(), "w") as f:
json.dump(data, f, indent=4)
self.accept()
self.settingsChanged.emit()
def piecesToGrid(self, totalPieces):
cols = math.ceil(math.sqrt(totalPieces))
rows = math.ceil(totalPieces / cols)
return rows, cols