grid()/grid_remove() functions don't do i would expect - python

So I am making a "game", where there is a grid with random number from 1 to 9 in each cell. And when you move the cursor on a cell, and press the number on your keyboard which is written in the cell, it should disappear, and another cell should reappear. I am trying to use grid_remove() for hide the object, and grid() to show it, but it doesn't work as I would like to it. Firstly I make a 3x3 grid, and i remove some of its cells, but the shown cells can be shifted to the place of a hidden one and I would avoid of this. In this code, I write the index in the each cell (instead of a random number 1-9). I highlight a part of the code in the beginning, where I remove the cells at the program starting, then i past the whole code.
Choosing which indexes will be removed (I think it should be ok):
exist=[]
t=0
for k in range(0,int((2*size*size)/3)):
while t in exist:
t=randint(0,size*size-1)
print(t)
exist.append(t)
Removing some of the cells, right after making them:
button[index].grid(row=i, column=j)
if index in exist:
button[index].grid_remove()
And my whole code finally:
from random import randint
import tkinter
from tkinter import Button
from tkinter import font
top = tkinter.Tk()
top.title("Game")
top.geometry("500x500")
class table:
def __init__(self,size):
self.size=size
index=0
def onClick(index):
if button[index].cget("bg")=="Red":
button[index].config(bg="Blue")
else:
button[index].config(bg="Red")
def key(event):
if event.widget.cget("text")==event.char:
event.widget.config(text=str(randint(1,9)))
event.widget.show=False
f=randint(0,size*size-1)
if not button[f].winfo_exists():
button[f].grid() #row=int(f/size), column=f%size
def enter(event):
event.widget.focus_set()
def leave(event):
event.widget.config(bg="Red")
button=[]
exist=[]
t=0
for k in range(0,int((2*size*size)/3)):
while t in exist:
t=randint(0,size*size-1)
print(t)
exist.append(t)
for i in range(0, size):
for j in range (0,size):
button.append(Button(top,command=lambda index=index: onClick(index),height=5,width=10,bg="Red",font=5,text=str(index)))
button[index].bind("<Key>",key)
button[index].bind("<Enter>",enter)
button[index].bind("<Leave>",leave)
button[index].grid(row=i, column=j)
if index in exist:
button[index].grid_remove()
index=index+1
racs=table(3)
top.mainloop()

Related

Bubblesort visualization in tkinter

I can't figure out how can I finish one simple program written in Python. Program basically generates array of ten random numbers and then sorts them using bubblesort algorithm. Whole shorting process should be shown on screen - such as this one
My current code is this:
import tkinter
import random
canvas = tkinter.Canvas(bg='white',width='800',height='400')
canvas.pack()
c = []
for i in range(0,10):
c=c+[random.randrange(10)]
print(c)
print('Zoradenie...', c)
def sort(c):
x=300
for i in range(0,10):
for j in range(0,len(c)-1-1):
if c[j+1]<c[j]:
c[j+1],c[j]=c[j],c[j+1]
canvas.create_text(300,80,text=c[j],fill='Red')
x+=25
canvas.update()
canvas.after(1000)
print(c)
return c
sort(c)
But I can't figure out how to show numbers on screen. Any ideas?
To display the digits on the canvas, you must create a text item for each digit. See the end of my code. The harder part is moving the digits. One way is to delete and recreate; the other is to move. I choose the latter.
The hardest part, perhaps, is the time delays. If one uses mainloop, one should use after rather than time.sleep (which blocks the looping) and not use for-loops for animation. The problem is that the function (here sort) that naturally contains for-loops must be broken into pieces whose joint operation may be hard to understand. If one is running just one function and does not care about user interaction (for instance, a pause button), one can use time.sleep and update. I have done so here to make what is going on clearer.
from random import randrange
from time import sleep
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, bg='white', width='800', height='400')
canvas.pack()
ndigits = 10
digits = [randrange(10) for _ in range(ndigits)]
tdelta1, tdelta2 = .8, .2
xstart = 300
xdelta = 25
y = 80
def color(i, swap):
"Temporarily color digits i and i+i according to swap needed."
x = xstart + xdelta * i
dcolor = 'Red' if swap else 'green'
canvas.itemconfigure(items[i], fill=dcolor)
canvas.itemconfigure(items[i+1],fill=dcolor)
canvas.update()
sleep(tdelta1)
canvas.itemconfigure(items[i], fill='Black')
canvas.itemconfigure(items[i+1], fill='Black')
canvas.update()
sleep(tdelta2)
def swap(i):
digits[i], digits[i+1] = digits[i+1], digits[i]
canvas.move(items[i], xdelta, 0)
canvas.move(items[i+1], -xdelta, 0)
items[i], items[i+1] = items[i+1], items[i]
def bubsort():
"Sort digits and animate."
for stop in reversed(range(1, ndigits)):
# stop = index of position whose entry will be determined.
for i in range(stop):
swap_needed = digits[i] > digits[i+1]
color(i, swap_needed)
if swap_needed:
swap(i)
color(i, False)
# Create display items and pause.
items = [canvas.create_text(xstart + xdelta*i, y, text=str(digit))
for i, digit in enumerate(digits)]
canvas.update()
sleep(tdelta1)
bubsort()
This code makes it fairly easy to replace the text digit display with, for instance, a colored bar display. To develop this further, I would define a class of items combining int values and display items as attributes. There would them be only one array of combined items. With comparison methods defines, the array could be passed to any sort function.

Python Memory Game with Tkinter - Trouble with definitions and buttons

I am creating a memory game and am running into a problem with the buttons. I create the buttons using a for loop and place them into an array, but I have no idea how to call a definition OnButtonClick for each one of them. Each button should have a random picture, chosen from eight options, with no more than two duplicates.
from Tkinter import *
import Tkinter
import random
root = Tkinter.Tk()
poop=PhotoImage(file="poop.gif")
apple=PhotoImage(file="apple.gif")
earth=PhotoImage(file="earth.gif")
fish=PhotoImage(file="fish.gif")
frowny=PhotoImage(file="frowny.gif")
heart=PhotoImage(file="heart.gif")
smiley=PhotoImage(file="images.gif")
water=PhotoImage(file="water.gif")
back=PhotoImage(file="card back.gif")
images = [poop,apple,earth,fish,frowny,heart,smiley,water]
row = 1
column = 0
buttonList=[]
def OnButtonClick():
self.Button.config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back, width=150,height=250,command=OnButtonClick()))
buttonList[i].grid(row = row,column = column)
column += 1
if column == 4:
column = 0
row += 1
root.mainloop()
How would I go about changing the picture when the buttons are pressed?
I didn't check if your code is working properly but if it works than you have to do something like this:
def OnButtonClick(number):
buttonList[number].config(image=random.choice(images))
for i in range(16):
buttonList.append(Button(root, image=back,width=150,height=250,command=lambda e=i: OnButtonClick(e)))
buttonList[i].grid(row=row, column=column)

Beginner Python Keyboard GUI setup

I am beginning GUI in Python 3.5, and I am trying to setup a simple qwerty keyboard. Based on the examples, I tried the following code
from tkinter import Tk, Label, RAISED, Button, Entry
self.window = Tk()
#Keyboard
labels = [['q','w','e','r','t','y','u','i','o','p'],
['a','s','d','f','g','h','j','k','l'],
['z','x','c','v','b','n','m','<']]
n = 10
for r in range(3):
for c in range(n):
n -= 1
label = Label(self.window,
relief=RAISED,
text=labels[r][c])
label.grid(row=r,column=c)
continue
This gives me the first row, but it does not return anything else. I tried simply using 10 as the range, which created the first two rows of the keyboard, but it still did not continue onto the last row.
Your issue is in the line n -= 1. Every time a label is created, you make n one less- after the first whole row, n==0, and thus the range is 0>0, and ranges never include the high bound- for c in range(0) will just drop from the loop (as it has looped through all the nonexistent contents).
A better solution involves iterating through the lists instead of through the indexes- for loops take any iterable (list, dictionary, range, generator, set, &c.);
for lyst in labels:
# lyst is each list in labels
for char in lyst:
# char is the character in that list
label = Label(... text=char) # everything else in the Label() looks good.
label.grid(...) # You could use counters for this or use ennumerate()-ask if you need.
# The continue here was entirely irrelevant.
Is this what you want it to do? Let me know if you need me to explain it further but basically what I'm doing is first filling the columns in each row. So row remains 0 and then as I loop through the column (the inner list) I fill in each of the keys, then on to the next row and etc.
from tkinter import Tk, Label, RAISED, Button, Entry
window = Tk()
#Keyboard
labels = [['q','w','e','r','t','y','u','i','o','p'],
['a','s','d','f','g','h','j','k','l'],
['z','x','c','v','b','n','m','<']]
for r in labels:
for c in r:
label = Label(window, relief=RAISED, text=str(c))
label.grid(row=labels.index(r), column=r.index(c))
window.mainloop()

How to change button image at mouse hover over in tkinter in Python?

I've started my adventure with Python only recently, so please go easy on me :D
I've been working on my 2nd program with a gui in tkinter for a couple of days now, and it looks like I've run into a wall.
It's a game that creates a 2D map by making a 2D list.
Each element of the list is created as a Button tkinter object instance with a tile_basic_off image. So far so good.
Should I try to bind each of these buttons to 2 functions for mouse over/mouse leave things go south...
It should work like:
mouse over -> change image to tile_basic_on
mouse leave -> change tile_basic_off
However, once code below is run (there is a function that draws it by .grid() method) everything is the same, with tiles not changing image at mouse over or mouse leave.
Here is the question-wise important piece of code.
(note that redMagenta is a var holding an RGB, not a typo)
from tkinter import * #Python 3.4 here
def createMap (): #creates rows x columns 2D list - a map
global rowsEntryVar, columnsEntryVar, mapList
mapList = []
for row in range(rowsEntryVar):
tempList = []
for column in range(columnsEntryVar):
tempList.append(Button(root, bd=0, bg=redMagenta, activebackground=redMagenta))
mapList.append(tempList)
setTilesProperties()
tileBasicOffImage = PhotoImage(file="Resources/Tile/Tile_basic_off.png")
tileBasicOnImage = PhotoImage(file="Resources/Tile/Tile_basic_on.png")
def turnTileOn (row, column): #changes tile image to On
global mapList
mapList[row][column].configure(image=tileBasicOnImage)
mapList[row][column].image = tileBasicOnImage
def turnTileOff (row, column): #changes tile image to Off
global mapList
mapList[row][column].configure(image=tileBasicOffImage)
mapList[row][column].image = tileBasicOffImage
def setTilesProperties (): #sets basic properties that apply to all tiles
global mapList, tileBasicOffImage
for row in range(len(mapList)):
for column in range(len(mapList[row])):
mapList[row][column].configure(image=tileBasicOffImage)
mapList[row][column].bind("<Enter>", turnTileOn(row, column))
mapList[row][column].bind("<Leave>", turnTileOff(row, column))
What is wrong with my code?
Thanks for help and for not TLDRing this. ;]

Python: change entry colour dynamically with Tkinter

I am getting problems with Tkinter after() method.
Actually, what I want to do is to change the background colour of some entry boxes as soon as times passes. Let's take this piece of code (which is different from the script I'm working on, but the situation described is the same):
import Tkinter as tk
root = tk.Tk()
root.option_add("*Entry.Font","Arial 32 bold")
emptyLabel=tk.Label()
emptyLabel.grid(row=4) #Empty label for geometry purpose
entryList=[]
for x in range(4):
entryList.append([])
for y in range(4):
entryList[x].append('')
entryList[x][y]=tk.Entry(root, bg="white",width=2,justify="center",
takefocus=True,insertofftime=True)
entryList[x][y].grid(row=x,column=y)
solvebt=tk.Button(root,text='Solve').grid(row=5,column=2)
newgamebt=tk.Button(root,text='New').grid(row=5,column=1)
#BROKEN PART STARTS HERE
def changebg(x,y):
entryList[x][y]['bg']='yellow'
for x in range(4):
for y in range(4):
entryList[x][y].after(300,changebg(x,y))
#Same result with root.after(300,changebg(x,y))
root.mainloop()
The problem is that when I start the program, I would expect it to show me as it "paints", one at time, all of the entry boxes in yellow. What happens, instead, is that the program freezes for (300*16) milliseconds and then, all of a sudded, every entry boxes is yellow!
The problem is here:
def changebg(x,y):
entryList[x][y]['bg']='yellow'
for x in range(4):
for y in range(4):
entryList[x][y].after(300,changebg(x,y))
#Same result with root.after(300,changebg(x,y))
You're calling changebg to immediately in the double for loop -- You're then passing the return value (None) to root.after. This won't lead to the delay that you describe. Perhaps your actual code looks like:
for x in range(4):
for y in range(4):
entryList[x][y].after(300,lambda x=x,y=y : changebg(x,y))
That will lead to the behavior you actually describe. Ultimately, what you need is to flatten your list of widgets and then pass then one at a time -- registering the next one if it exists:
import itertools
all_entries = itertools.chain.from_iterable(entryList)
def changebg(ientries):
ientries = iter(ientries) #allow passing a list in as well ...
entry = next(ientries,None)
if entry is not None:
entry['bg'] = 'yellow' #change the color of this widget
root.after(300,lambda : changebg(ientries)) #wait 300ms and change color of next one.
changebg(all_entries)

Categories