Immagine puzzle PIL tkinter

Sep 12 2020

Sono un programmatore hobbista (mi sto solo divertendo) ha creato un piccolo script che prende una foto e la ridimensiona, quindi la taglia in 24 pezzi. I pezzi vengono trasposti e trasformati in pulsanti. Il tuo compito è riportarlo al suo stato originale senza perdere punti negativi. Le mie domande sono: è stato divertente? Potresti riprodurlo su piattaforme Windows o ISO?

from PIL.ImageTk import Image, PhotoImage
from PIL import ImageChops
from tkinter import (Canvas,Tk,Frame,Button,YES,BOTH,filedialog,
                     Toplevel,IntVar,Label)
from tkinter.messagebox import showinfo, showerror
import logging
from random import  choice
from functools import partial

#logging.basicConfig(level= logging.DEBUG)
logging.disable(logging.CRITICAL)

class Puzzle_One(Frame):
    """Grab a picture, then cuts it up into 24 pieces and transpose,
       make them into buttons. Repeatedly pressing the buttons to it's
       origional state before getting a negative score."""
    def __init__(self, parent=None):
        self.parent= parent
        Frame.__init__(self, self.parent)
        self.pack(expand=YES, fill=BOTH)
        
        self.canvas= Canvas(self)
        self.canvas.config(width= 800, height= 700, bg='gray90')
        self.canvas.pack(expand=YES, fill=BOTH)
        
        self.puzzle_points= IntVar()
        self.lbl_1= Label(self.canvas, textvariable=self.puzzle_points,
                          font=('arial',50,'bold'))
        self.lbl_1.place(x=500,y=30)
        self.puzzle_points.set(2500)
        
        
        self.btn= Button(self.canvas, text='find image',
                         command= self.get_image)
        self.btn.place(x=400,y=600)       
        
        self.buttons= []
        self.image_ref= []
        
        self.transitions= [ Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM,
                            Image.ROTATE_180, Image.ROTATE_270,
                            Image.ROTATE_90]
        self.t_count=0
        self.t_pics=[]
        self.buttons= []
        self.image_compare= []
        self.mydict= {}
        
        
        
    def get_image(self):
        """Find your photo, it will be displayed on the canvas as a reference.
           A new button will be made to create the puzzle"""
        self.btn.config(state='disabled')
        self.file= filedialog.askopenfilename(filetypes=[('PNG','.png'),
                                                         ('JPG','.jpg'),
                                                         ('GIF','.gif'),
                                                         ]
                                              )
        self.image= Image.open(self.file)
        self.im= self.image.resize((600,400))
        image= PhotoImage(self.im)
        
        self.canvas.create_image(360,320,image=image,tag='my_image')
        
        self.t_pics.append(image)
        self.btn2= Button(self.canvas,text='make puzzle',
                          command=self.make_puzzle)
        self.btn2.place(x=200,y=600)   
      

    def rotate_btn(self,pic_name, index):
        """Reapeatedly pressing the image buttons scrolls through a list of
           image transitions. Find the correct position"""
        if self.t_count >4:
            self.t_count =0        
        
        img= pic_name.transpose(self.transitions[self.t_count])
        self.mydict[index]= img
        image= PhotoImage(image=img)
        
        self.buttons[index].config(image=image)
        
        self.t_pics.append(image)
        self.t_count +=1
    def compare(self):
        """Compares the puzzle button with the original. Numbering starts
           in the upper left corner with 0, lower right corner is 23.
           Need a better way to indicate which buttons image is incorrect.
           Updating the score
           """
        points= self.puzzle_points.get()
        res= []
        for num in range(0,24):
            image_1= self.image_compare[num]
            image_2= self.mydict.get(num, '')
            diff= ImageChops.difference(image_1,image_2)
            
            if diff.getbbox():
                pts= points- 500
                if pts < 0:
                    self.puzzle_points.set(pts)
                    self.buttons[num].config(borderwidth=10)
                    
                    txt= 'Your total points is {}'.format(pts)
                    showinfo('Loss',txt)
                    
                    self.buttons[num].config(borderwidth=2)
                    self.btn3.config(state='disabled')
                    break
                else:
                    self.puzzle_points.set(pts)
                    self.buttons[num].config(borderwidth=10)
                    
                    text_= "There's an error {}".format(num)                
                    showerror('No bueno', text_)
                    
                    self.buttons[num].config(borderwidth=2)
                    
                    break
            
            else:
                res.append(num)
        
        if len(res) == 24:
            total= self.puzzle_points.get()
            text='Winner your total points are {}'.format(total)
            showinfo('Win', text)
            self.btn3.config(state='disabled')
        
        
    def make_puzzle(self):
        """A popup window containing 24 buttons. x1,y1,x2,y2 are the position of the cut pieces
           of the image. A reference of each region is transposed and converted to a Tk image,
           then saved to a list to prevent being garbage collected. Passing the region of each
           image and the index as an argument for each button. The buttons are in a list so they
           can be accessed by the index."""
        self.btn3= Button(self.canvas, text='I solved it!',
                          command=self.compare)
        self.btn3.place(x=600,y=600)
        self.btn2.config(state='disabled')
        self.top= Toplevel()
        x1=0
        y1=0
        x2=100
        y2=100
        count=0
        b_count=0
        for r in range(1,5):
            for c in range(1,7):               
                box= x1,y1,x2,y2
                reg= self.im.crop(box)
                self.image_compare.append(reg)
                tran= choice(self.transitions)
                reg= reg.transpose(tran)
                self.mydict[b_count]= reg
                image= PhotoImage(reg)
                self.buttons.append(Button(self.top,image=image,
                                           command=partial(self.rotate_btn,reg,b_count))
                                    )
                
                self.buttons[-1].grid(row=r,column=c,padx=0.5,pady=0.5)
                self.image_ref.append(image)
                if count == 5:
                    count= 0
                    b_count +=1
                    x1 = 0
                    y1 += 100
                    x2= 100
                    y2 += 100
                else:
                    x1 +=100
                    x2 +=100
                    count +=1
                    b_count +=1
                    
                
if __name__ == '__main__':
    root= Tk()
    Puzzle_One(root)
    root.mainloop()

Risposte

1 BrunoVermeulen Sep 17 2020 at 12:56

Bel gioco e ottimo lavoro, a mia figlia piace!

Ha funzionato in esecuzione su Windows. Al termine del puzzle non sono riuscito a selezionare un'altra immagine e giocare di nuovo. Penso che questo possa essere facilmente implementato aggiungendo un metodo play_game(self)che prende il controllo generale del gioco e avrà un pulsante di chiusura.

Un enigma successivo sarebbe quello di spingere gli elementi dell'immagine dove devi metterli nell'ordine giusto.

Quindi un suggerimento, puoi provare a implementare lo stesso gioco in PyQt5. Personalmente mi piace questa GUI meglio di tkinter.