Related
I'm trying to make a memory game for fun and as a learning experience, and I've run into the issue where even with something like time.sleep(.5) I still can't get buttons to update correctly with a delay. In fact the second button seems to update to hidden as it's about to show the proper image. I'm assuming the issue lies somewhere in the buttonClicked() function.
I'm trying to figure out how I can make it show one button, then the second, then wait half a second and hide both. And if someone understands why this is happening or where I could look into the issue and read up on my own, that would be helpful.
Thanks.
from re import A
import time
import tkinter as tk
from tkinter import *
from typing_extensions import Self
from PIL import Image, ImageTk
import glob
import os, os.path
import numpy as np
from sqlalchemy import null
#resize images and button
imgButtonWidth = 100
imgButtonHeight = 100
imgButtonSize = (imgButtonWidth,imgButtonHeight)
#Set the height and width of the game by number of items.
width = 6
height = 6
#buttons = [[Button]*width]*height
#Total number of items 36 (0-35)
count = width*height-1
buttonList = []
#Will be a 2d array of [button, id]
answersList = []
clickedCount = 0
imgs = []
hiddenImg = null
# Create frame, set default size of frame and background color.
root = Tk()
root.title('Memory Game')
root.geometry(str(imgButtonWidth * (width+1)) + "x" + str(imgButtonHeight * (height+1)))
root.config(bg='darkblue')
frame = Frame(root, bg='darkblue')
# Fetch images from location and create a list of Image objects, then return.
def getImages():
imgs = []
path = "/home/paul/Programming/Python/MyMiniProjects/Mid/MemoryGame/"
valid_images = [".jpg",".gif",".png",".tga"]
for f in os.listdir(path):
ext = os.path.splitext(f)[1]
if ext.lower() not in valid_images:
continue
imgs.append([Image.open(os.path.join(path,f)).resize(imgButtonSize), f])
return imgs + imgs
#Shuffle images for the game
imgs = getImages()
random.shuffle(imgs)
#Simple image to cover the tiles
hiddenImg = ImageTk.PhotoImage(Image.new('RGB', (imgButtonWidth, imgButtonHeight), (0,0,105)))
#Disable buttons after a match
def disable():
global clickedCount, answersList
clickedCount = 0
for a in answersList:
a[0]["state"] = "disabled"
a[0]["bg"] = "green"
answersList = []
#Hide buttons again
def hide():
global clickedCount, answersList
clickedCount = 0
for a in answersList:
#a[0].config(image = hiddenImg)
a[0]["image"] = hiddenImg
a[0]["state"] = "normal"
a[0]["bg"] = "white"
answersList = []
def wrong():
for a in answersList:
a[0]["bg"] = "red"
def buttonClicked(picture, id, button):
global clickedCount, answersList
print(clickedCount, len(answersList))
#print(button.image, "1", hiddenImg, picture)
if button.image is hiddenImg and clickedCount < 2:
button["image"] = picture
button["state"] = "disabled"
clickedCount += 1
answersList.append([button, id])
if len(answersList) == 2:
#Check id but make sure it's not the same button pressed twice
if answersList[0][1] is answersList[1][1]:#and answersList[0][0] is not answersList[1][0]:
disable()
else:
wrong()
hide()
#Create the actual buttons with their respective image
for h in range(height): #print(buttons[w][::],"\n")
newList = []
for w in range(width):
tempImage = imgs.pop(count)
picture = ImageTk.PhotoImage(tempImage[0])
id = tempImage[1]
button = Button(frame, image=hiddenImg, state=NORMAL, height=imgButtonHeight, width=imgButtonWidth)
#Need to split this up because of how python handles closures
button["command"] = lambda pic_temp=picture, id_temp=id, button_temp = button: buttonClicked(pic_temp, id_temp, button_temp)
button.image = hiddenImg
#buttons[w][h].name = str(w + h)
#buttons[w][h].grid(row=w, column=h, ipadx=random.randint(0,40), ipady=random.randint(0,40), padx=random.randint(0,5), pady=random.randint(0,5))
button.grid(row=h, column=w, padx=1, pady=1)
#Button(frame, image=picture).grid(row=w, column=h, ipadx=random.randint(0,40), ipady=random.randint(0,40), padx=random.randint(0,5), pady=random.randint(0,5))
count -= 1
# buttonList.append(buttons[h][w])
newList.append(button)
buttonList.append(newList)
# for y in range(height):
# for x in range(width):
# print(ButtonList[y][x])
# print("")
frame.pack(expand=True)
root.mainloop()```
i'm trying to make a photo slideshow program. it isn't working as i want to go to the next photo in the list using the self.counter variable, but the value of self.counter is going back to 0 as it forgets the fact that i changed the value. i didn't think i could use configure as then the buttons to go to the next image wouldn't work either.
i hope you can help :) much appreciated.
from tkinter import *
class SlideShowGUI:
def __init__(self, parent, counter = 0):
self.counter = counter
self.images_list = ["smiley.png", "carrot.png", "bunny.jpg"]
self.photo = PhotoImage(file = self.images_list[self.counter])
self.b1 = Button(parent, text = "", image = self.photo, bg = "white")
self.b1.grid(row = 0, column = 0)
back = Button(parent, width = 2, anchor = W, text = "<", relief = RIDGE, command = self.previous_image)
back.grid(row = 1, column = 0)
forward = Button(parent, width = 2, anchor = E, text = ">", relief = RIDGE, command = self.next_image)
forward.grid(row = 1, column = 1)
def previous_image(self):
self.counter -= 1
self.photo.configure(file = self.images_list[self.counter])
def next_image(self):
self.counter += 1
self.photo.configure(file = self.images_list[self.counter])
#main routine
if __name__ == "__main__":
root = Tk()
root.title("hello")
slides = SlideShowGUI(root)
root.mainloop()
sorry it will not work without getting images !
error message if i click next button two times
use this instead:
def previous_image(self):
self.counter -= 1
self.photo.configure(file = images_list[self.counter])
def next_image(self):
self.counter += 1
self.photo.configure(file = images_list[self.counter])
except You have to watch out for List Out Of Index errors
Also why are You using global images_list? There seems to be no point. If You wanted to reuse that list in the class You could have just named it self.images_list = [your, items].
The error You are getting: read this
Thanks in advance for your time. I am writing a chess program using the tkinter module. I have put the board interface on the left side of the screen, while on the right side I would like to have the algebraic notation (e.g. 1. e4 e5 2. Nf3 Nc6) printed and updating as the players make their moves.
Since the text is updating, I tried to create a Label widget and a StringVar() object. But it is having some trouble : Basically, there is no right side to the screen, and the with each move the window flickers violently before showing the move in algebraic notation in the center of the right side of the window. Then, for each move, the same spasm occurs, the window expands a tiny bit, and eventually the string runs off the screen.
Ideally, every two moves, there would be a new line, but for now I would be content to just have the text 'wrap' once it hits the end of the frame.
I tried to create a Canvas widget and use the create_text() method to do it, but it was a bit unwieldy (though that seems to have been the best so far).
import tkinter as tk
master = tk.Tk()
class Chessboard(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.pack()
self.master.title('Chess')
self.mainframe = tk.Frame(master, width = 1600, height = 800)
self.mainframe.pack(side = 'top', anchor = 'n')
self.move_data = {'x': 0, 'y': 0, 'piece': None, 'to': None, 'from': None, 'last': None, 'color':'w', 'enem_color':'b'}
self.draw_board(master)
self.counter=2
self.en_passant = []
self.pgn_frame = tk.Canvas(self.mainframe, width = 500, height = 640, bd = 5, relief='ridge', cursor = 'trek')
self.pgn_frame.pack(side='right')
self.pgn_txt = ''
self.pgn_str = tk.StringVar()
self.pgn_label = tk.Label(self.pgn_frame, textvariable = self.pgn_str)
self.pgn_label.pack(fill='both', expand = True)
def draw_board(self, master):
self.board = tk.Canvas(self.mainframe, width = 680, height = 680, borderwidth = 20)
self.board.pack(fill='both', expand=True, side='left')
for r in range(7, -1, -1):
for c in range(8):
if c&1 ^ r&1:
fill = 'sienna2'
else:
fill = 'wheat1'
x0, y0 = c*80+24, r*80+24
x1, y1 = c*80+104, r*80+104
coords = (x0, y0, x1, y1)
center = ((x0+x1)/2, (y0+y1)/2)
sq = files[c] + ranks[-r-1]
self.board.create_rectangle(coords, fill=fill, width = 0, tags = ('square', sq))
Chessboard().mainloop()
I will note that the draw_board() function creates a canvas within the self.mainframe Frame and places it to the 'left'. Again, thank you for your time !
After searching the annals of SO, I stumbled across an ancient piece of wisdom from this question's first commenter :
"tkinter widgets are designed to shrink (or expand) to exactly fit their children." -Label within a Frame (Tkinter)
This little tidbit has put me on the right track to solving my problem : previously, I was trying to pack a Label widget into a Frame or Canvas widget, with the logic that the child should adapt to fit its parent. But, thanks to Mr. Oakley's insight, I have simplified the code to evict the superfluous widget and simply design my Label widget to meet the needs of the program. So, reformatted, it looks like
import tkinter as tk
master = tk.Tk()
class Chessboard(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.pack()
self.master.title('Chess')
self.mainframe = tk.Frame(master, width = 1600, height = 800)
self.mainframe.pack(side = 'top', anchor = 'n')
self.move_data = {'x': 0, 'y': 0, 'piece': None, 'to': None, 'from': None, 'last': None, 'color':'w', 'enem_color':'b'}
self.draw_board(master)
self.counter=2
self.en_passant = []
self.pgn_txt = ''
self.pgn_str = tk.StringVar()
self.pgn_frame = tk.Label(self.mainframe, width = 500, height = 640, bd = 5, relief='ridge', cursor = 'trek', textvariable = self.pgn_str)
self.pgn_frame.pack(side='right')
def draw_board(self, master):
self.board = tk.Canvas(self.mainframe, width = 680, height = 680, borderwidth = 20)
self.board.pack(fill='both', expand=True, side='left')
for r in range(7, -1, -1):
for c in range(8):
if c&1 ^ r&1:
fill = 'sienna2'
else:
fill = 'wheat1'
x0, y0 = c*80+24, r*80+24
x1, y1 = c*80+104, r*80+104
coords = (x0, y0, x1, y1)
center = ((x0+x1)/2, (y0+y1)/2)
self.board.create_rectangle(coords, fill=fill, width = 0, tags = ('square'))
Chessboard().mainloop()
It ain't pretty at the moment, but soon, it will be. In the actual program, the text updates without the aforementioned spasms, though will need to work a little on the formatting.
I wrote a module that provides several methods to render text and create PDF files. Now I find that using Core Text along with higher level text rendering messes up the size of the Core Text layout. The later is mainly displayed too large.
Scenario:
Create a PDF context
Beginn a page
Render text via CTRunDraw (B) *
End a page
Close PDF context
Save PDF
*) This gets rendered properly, as expected. But only as long as I don’t draw any text using a NSAttributedString and drawAtPoint_ (A) before.
This means, both methods work perfectly on their own, but the core text drawing gets too large when they both come together.
Is there something to take care of when saving and restoring the graphics context? I tried almost anything. I can put some code here, but it’s quite a lot and in a complex setup, so if you can specify what you exactly want to see, please let me know.
(A) The high level text drawing:
class Text(object):
def __init__(self, text="", font="Monaco", fontSize=12, position=None ):
self.text = text
self.font = font
self.fontSize = fontSize
self.position = position
self.fontRGBColor = 0, 1, 1, 1
def drawText(self):
textAttributes = {}
textAttributes[NSFontAttributeName] = NSFont.fontWithName_size_( self.font, self.fontSize )
textAttributes[NSForegroundColorAttributeName] = NSColor.colorWithCalibratedRed_green_blue_alpha_( *self.fontRGBColor )
textAttributedString = NSAttributedString.alloc().initWithString_attributes_( self.text, textAttributes )
textAttributedString.drawAtPoint_((self.position[0], self.position[1]))
(B) The core text method:
class TextBox(object):
def __init__(self):
self.font = "Monaco"
self.fontSize = 12
self.text = "This is my Text"
self.fontColor = 0, 1, 1, 0, 0.75
self.pdfContext = NSGraphicsContext.currentContext().graphicsPort()
def drawTextBox(self):
CGContextSaveGState(self.pdfContext)
myBounds = (0, 0), (100, 100)
posX, posY = myBounds[0]
width, height = myBounds[1]
self.fontObject = NSFont.fontWithName_size_(self.font, self.fontSize)
self.fontAttributes = {}
self.fontAttributes[NSFontAttributeName] = self.fontObject
self.fontAttributes[NSForegroundColorAttributeName] = NSColor.colorWithDeviceCyan_magenta_yellow_black_alpha_(*self.fontColor)
TextBoxWithFeaturesAttributedString = NSMutableAttributedString.alloc().initWithString_attributes_(self.text, self.fontAttributes)
setter = CTFramesetterCreateWithAttributedString(TextBoxWithFeaturesAttributedString)
path = CGPathCreateMutable()
CGPathAddRect(path, None, CGRectMake(0, 0, width, height))
box = CTFramesetterCreateFrame(setter, (0, 0), path, None)
ctLines = CTFrameGetLines(box)
origins = CTFrameGetLineOrigins(box, (0, len(ctLines)), None)
for i, (originX, originY) in enumerate(origins):
ctLine = ctLines[i]
bounds = CTLineGetImageBounds(ctLine, self.pdfContext)
if bounds.size.width == 0:
continue
ctRuns = CTLineGetGlyphRuns(ctLine)
for ctRun in ctRuns:
CGContextSetTextPosition(self.pdfContext, posX + originX, posY + originY)
CTRunDraw(ctRun, self.pdfContext, (0, 0))
CGContextRestoreGState(self.pdfContext)
Many thanks and appreciations in advance.
That's my code and I get the error message:
... return getattr(self.tk, attr)
AttributeError: temp_pic"...
I need to program two buttons: [zoom in] and [zoom out].
If you have any better ideas for doing that, please, just say it.
I'm going to use this image to develop maps through graphs (structure)
from Tkinter import *
from PIL import Image, ImageTk, ImageDraw, ImageOps, ImageEnhance
bairro = "botafogo.jpg"
class Painel(Tk):
def __init__(self):
Tk.__init__(self) #create ui
self.zoom = Frame(self)
self.zoom.pack()
self.zin = Button(self.zoom, command = self.zoom_in, text = "Zoom In")
self.zin.pack()
self.zout = Button(self.zoom, command = self.zoom_out, text = "Zoom Out")
self.zout.pack()
self.c = Canvas(self, bd=0, highlightthickness=0, width=100, height=100)
self.c.pack(fill='both', expand=1)
self.main_pic = Image.open(bairro) #load image
self.main_pic.thumbnail((800, 600))
self.tkphoto = ImageTk.PhotoImage(self.main_pic)
self.canvasItem = self.c.create_image(0, 0, anchor='nw', image = self.tkphoto)
self.c.config(width = self.main_pic.size[0], height = self.main_pic.size[1])
self.temp = self.main_pic.copy() # 'working' image
def update_painel(self):
self.tkphoto = ImageTk.PhotoImage(self.temp_pic)
self.c.itemconfigure(self.canvasItem, image = self.tkphoto)
def zoom_in(self):
self.temp_pic = self.temp_pic.transform( ( self.temp_pic.size[0]/2,
self.temp_pic.size[0]/2
),
Image.EXTEND,
( 0, 0, self.temp_pic[0], self.temp_pic[1]
)
)
self.update_painel()
def zoom_out(self):
self.temp_pic = self.main_pic
self.update_painel()
app = Painel()
app.mainloop()
a deep-copy instruction shall read
self.temp_pic = self.main_pic.copy() # 'working' image
instead of
self.temp = self.main_pic.copy() # 'working' image**