Buenas, a continuación les dejo el código de la versión dos del rompecabezas. El código está comentado por encima e intentaré hacer un tutorial bien explicado sobre como crearlo y como funciona cada elemento utilizado.
Añadida opción de rompecabezas con imágenes a partir del nuevo botón o mediante Drag&Drop sobre el panel.
Soporta imágenes superiores a 160X160 con formato jpg, png o bmp.
Código revisado para un funcionamiento óptimo.
Video:
Requisitos para ejecutar desde código:
Python 2.X
WxPython
PIL
Código:
Lenguaje PYTHON
#!/usr/bin/python#encoding: utf-8############################################################################### Creado por: Fernando Gomez #### Version: 1.0 #### Fecha: 18/05/2011 #### Fecha2.0: 24/09/2011 #### Licencia: Creative Commons Reconocimiento-NoComercial-CompartiIgual 3.0 ################################################################################Importamos los modulos necesariosimport wx,random,timeimport wx.lib.buttonsas buttons
import wx.lib.rcsizeras rcs
from PIL import Image as im
from PIL import ImageDraw asidclass MyFileDropTarget(wx.FileDropTarget):
def__init__(self, parent):
wx.FileDropTarget.__init__(self)self.parent= parent
def OnDropFiles(self, x, y, filenames):
if(filenames[0][-3:]=="jpg")or(filenames[0][-3:]=="png")or(filenames[0][-3:]=="bmp"):
self.parent.usoImg=Trueself.parent.DescomponerImagen(str(filenames[0]))#Frame que contiene la interfazclass Frame(wx.MiniFrame):
def__init__(self):
wx.MiniFrame.__init__(self,None, -1,'', wx.DefaultPosition,(400,400),(wx.DEFAULT_FRAME_STYLE)^(wx.RESIZE_BORDER|wx.MAXIMIZE_BOX))​self.posiciones=["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15",""]self.inicio=0#Para el tiempoself.BitmapNull= wx.EmptyImage(40,40)
vacio = vacio = im.new("RGB",(40,40))#Creamos una imagen vacia de 40X40
vaciod =id.Draw(vacio)
vaciod.rectangle((0,0,40,40), fill=(242,242,242))self.BitmapNull.SetData(vacio.tostring())self.usoImg=False#Variable Auxiliar para saber si se esta usando imagenes o noself.imagen=[]#Lista delos trozos de la imagenself.panel= wx.Panel(self, wx.NewId())self.botones=[wx.BitmapButton(self.panel, -1, bitmap=self.BitmapNull.ConvertToBitmap(), size=(40,40))for i inrange(len(self.posiciones))]#Lista con todos los botones de numerosfor i inrange(len(self.posiciones)):
self.botones[i].SetLabel(str(self.posiciones[i]))
dt = MyFileDropTarget(self)self.panel.SetDropTarget(dt)self.Center()self.sizer= rcs.RowColSizer()#Sizer para organizar los botonesself.vertical= wx.BoxSizer(wx.VERTICAL)#Sizer para !Bravo! y el tiempo tardadoself.s= wx.Button(self.panel, -1,"Mezclar")self.image= wx.Button(self.panel, -1,"Imágen...")self.numero= wx.Button(self.panel, -1,"Números")self.hecho= wx.StaticText(self.panel, -1)self.tiempo= wx.StaticText(self.panel, -1)
lugar =0#Colocamos cada boton en su lugar for r inrange(4):
for c inrange(4):
self.sizer.Add(self.botones[lugar], row=r, col=c)
lugar +=1self.sizer.Add(self.s, row=0, col=4)self.sizer.Add(self.image, row=1, col=4)self.sizer.Add(self.numero, row=2, col=4)self.vertical.Add(self.hecho)self.vertical.Add(self.tiempo)self.sizer.Add(self.vertical, row=3, col=4)self.panel.SetSizerAndFit(self.sizer)#Modificamos el tamaño del panel y la ventana para adaptarla al sizerself.SetClientSize(self.panel.GetSize())for i inself.botones: #Vinculamos cada boton a su funcionself.Bind(wx.EVT_BUTTON,self.moverCelda, i)
i.Bind(wx.EVT_KEY_DOWN,self.moverVacio)self.Bind(wx.EVT_KEY_DOWN,self.moverVacio)self.Bind(wx.EVT_BUTTON,self.remover,self.s)self.Bind(wx.EVT_BUTTON,self.Numeros,self.numero)self.Bind(wx.EVT_BUTTON,self.CargarImg,self.image)def CargarImg(self, event): #Cargamos la imagen que deseamos
dlg = wx.FileDialog(self,"Abrir imagen")
dlg.Centre()
wildcard ="JPG files (.jpg)|*.jpg|" \
"PNG (.png)|*.png|" \
"BMP (.bmp)|*.bmp"
dlg.SetWildcard(wildcard)#Formatos aceptadosif dlg.ShowModal()== wx.ID_OK:
self.usoImg=Trueself.DescomponerImagen(dlg.GetPaths()[0])
dlg.Destroy()def ColocarImagenes(self, trozos): #Colocamos las imagenes en botones
vacio = im.new("RGB",(40,40))#Creamos una imagen vacia de 40X40
vaciod =id.Draw(vacio)
vaciod.rectangle((5,5,35,35), fill=(242,242,242))#Le pintamos un rectangulo blanco en el interiorself.imagen=[wx.EmptyImage(trozos[i].size[0], trozos[i].size[1])for i inrange(16)]#Creamos imagenes vaciasfor i inrange(15):
self.imagen[i].SetData(trozos[i].convert("RGB").tostring())#Isertamos la informacion en las imagenes vaciasself.imagen[15].SetData(vacio.tostring())self.posiciones=["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15",""]self.hecho.SetLabel("")self.tiempo.SetLabel("")for i inrange(len(self.posiciones)):
self.botones[i].SetLabel("")self.botones[i].SetBitmapLabel(self.imagen[i].ConvertToBitmap())def Comprobar(self):
ifself.posiciones==["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15",""]:
self.hecho.SetLabel("¡Bravo!")self.tiempo.SetLabel("%is"%(time.time()-self.inicio))def DescomponerImagen(self, img): #Descomponemos la imagen en 16 trozos
image = im.open(img)if image.size>=(160,160):
image = image.resize((160,160))#La escalamos a 160X160
trozos =[]for r inrange(4): #Recortamos trozos de 40X40 y los guardamosfor c inrange(4):
trozos.append(image.crop((0+40*c,0+40*r,40+40*c,40+40*r)))self.ColocarImagenes(trozos)else:
self.hecho.SetLabel("Imágenes de")self.tiempo.SetLabel("160X160")#Mover celda cuando usamos el ratondef moverCelda(self, evt):
btnPos =abs(evt.GetId()-self.botones[0].GetId())#Obtenemo el boton que se pulso
label =self.posiciones[btnPos]#Obtenemos el numero que le corresponde
lugVacio =0
lugPulsado =0whileself.posiciones[lugVacio]!="": #Averiguamos donde esta la celda movible
lugVacio +=1whileself.posiciones[lugPulsado]!= label: #Averiguamos donde esta el pulsado
lugPulsado +=1if(lugVacio-lugPulsado ==1)or(lugVacio-lugPulsado == -1)or(lugVacio-lugPulsado ==4)or(lugVacio-lugPulsado == -4): #Si el pulsado esta a los lados o arriba o abajoself.posiciones[lugPulsado]=""#Intercambiamos los lugaresself.posiciones[lugVacio]= label
self.Recolocar()self.Comprobar()#Mover celda cuando pulsamos una tecladef moverVacio(self, event):
lugVacio =0whileself.posiciones[lugVacio]!="":
lugVacio +=1if event.GetKeyCode()==314: #Izquierdaif(((lugVacio-1)!=11)and((lugVacio-1)!=7)and((lugVacio-1)!=3)and((lugVacio-1)!=-1)):
self.posiciones[lugVacio]=self.posiciones[lugVacio-1]self.posiciones[lugVacio-1]=""elif event.GetKeyCode()==315:#Arribaif lugVacio-4>=0:
self.posiciones[lugVacio]=self.posiciones[lugVacio-4]self.posiciones[lugVacio-4]=""elif event.GetKeyCode()==316:#Derechaif(((lugVacio+1)!=16)and((lugVacio+1)!=12)and((lugVacio+1)!=8)and((lugVacio+1)!=4)):
self.posiciones[lugVacio]=self.posiciones[lugVacio+1]self.posiciones[lugVacio+1]=""elif event.GetKeyCode()==317:#Abajoif lugVacio+4<=15:
self.posiciones[lugVacio]=self.posiciones[lugVacio+4]self.posiciones[lugVacio+4]=""self.Recolocar()self.Comprobar()def Numeros(self, event): #Funcion para crear el juego solo con numerosself.posiciones=["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15",""]self.usoImg=Falseself.hecho.SetLabel("")self.tiempo.SetLabel("")for i inrange(len(self.posiciones)):
self.botones[i].SetBitmapLabel(self.BitmapNull.ConvertToBitmap())self.botones[i].SetLabel(str(self.posiciones[i]))def Recolocar(self): #Funcion para recolocar los cuadrados cuando sufran cambiosfor i inrange(16):
ifself.usoImg:
ifself.posiciones[i]=="":
self.botones[i].SetBitmapLabel(self.imagen[15].ConvertToBitmap())else:
self.botones[i].SetBitmapLabel(self.imagen[int(self.posiciones[i])-1].ConvertToBitmap())else:
self.botones[i].SetLabel(self.posiciones[i])#Remover todos los elementos y obtener el rompecabezasdef remover(self, evt):
lugar =15for i inrange(1000): #Movemos el vacio 1000 veces
hacia =random.randrange(0,4)#Para indicar aleatoriamente el lado al que se mueveif hacia ==0:#Arribaif lugar >4:
basura =self.posiciones[lugar]self.posiciones[lugar]=self.posiciones[lugar-4]self.posiciones[lugar-4]= basura
lugar -=4elif hacia ==1:#Izquierdaif(lugar !=0)and(lugar !=4)and(lugar !=8)and(lugar !=12):
basura =self.posiciones[lugar]self.posiciones[lugar]=self.posiciones[lugar-1]self.posiciones[lugar-1]= basura
lugar -=1elif hacia ==2:#Abajoif lugar <11:
basura =self.posiciones[lugar]self.posiciones[lugar]=self.posiciones[lugar+4]self.posiciones[lugar+4]= basura
lugar +=4else:#Derechaif(lugar !=3)and(lugar !=7)and(lugar !=11)and(lugar !=15):
basura =self.posiciones[lugar]self.posiciones[lugar]=self.posiciones[lugar+1]self.posiciones[lugar+1]= basura
lugar +=1self.inicio=time.time()#iniciamos el contadorself.Recolocar()#mostramos el frame que contiene la interfazclass App(wx.App):
def OnInit(self):
frame = Frame()
frame.Show()self.SetTopWindow(frame)returnTrue#Iniciamos si no hay erroresif __name__ =='__main__':
app = App()
app.MainLoop()
Cualquier duda pregunten en este mismo tema.
Intentaré ir creando ejecutables para los que no tengan Python.