How to change button colour back to original when clicked again - python

I'm trying to change the button colour to black when clicked then change it back to white when clicked again. I'm trying to make Game Of Life for a school project.
I tried if statements but it doesn't change back to white, maybe I missed something simple. Here is the code:
from tkinter import *
class GUI(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
master.title("Window") #Window title
self.pack()
master.geometry("1280x720") #Window size
self.button={}#Dictionary for buttons
self.create_button()
def create_button(self):
indexList =[i for i in range(1000)]
self._button = Button(self, bg='white')
print(self._button.cget('bg'))
xPos = 0
yPos = 0
for index in indexList:
if(yPos == 40):
xPos = xPos + 20
yPos = 0
if(xPos == 10):
yPos = 8
self._button = Button(self, height=2, width=4, command = lambda
i = index: self.changecolour(i))
self.button[index] = self._button
self._button.grid(row=xPos, column =yPos)
yPos = yPos + 1
def changecolour(self,index):
aList = []
for i in range(1000):
aList.append([i,0])
for i in aList:
if index == i[0]:
if 0 == i[1]:
self.button[index].configure(bg = 'black')
i[1] = 1
else:
self.button[index].configure(bg = 'white')
i[1] = 0
root = Tk()
game_gui = GUI(master=root)
game_gui.mainloop()
As you can see it changes the button colour to black and it should change it back to white when clicked again, but it seems to just ignore the if statement.

I think this is the problem:
aList is not a global list
aList is created in changecolour() as a local list each time the subroutine is run
this means that when you do i[1] = 1 or i[1] = 0 it only changes the local list- aList. When the subroutine is run again, a new aList is created as a new local list.
to solve the problem define aList in the main program and make it a global list:
from tkinter import *
class GUI(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
master.title("Window") #Window title
self.pack()
master.geometry("1280x720") #Window size
self.button={}#Dictionary for buttons
self.create_button()
def create_button(self):
indexList =[i for i in range(1000)]
self._button = Button(self, bg='white')
print(self._button.cget('bg'))
xPos = 0
yPos = 0
for index in indexList:
if(yPos == 40):
xPos = xPos + 20
yPos = 0
if(xPos == 10):
yPos = 8
self._button = Button(self, height=2, width=4, command = lambda
i = index: self.changecolour(i))
self.button[index] = self._button
self._button.grid(row=xPos, column =yPos)
yPos = yPos + 1
def changecolour(self,index):
#aList IS NO LONGER CREATED HERE
for i in range(1000):
aList.append([i,0])
for i in aList:
if index == i[0]:
if 0 == i[1]:
self.button[index].configure(bg = 'black')
i[1] = 1
else:
self.button[index].configure(bg = 'white')
i[1] = 0
global aList #MAKE IT A GLOBAL LIST
aList = [] #CREATE THE EMPTY aList LIST
root = Tk()
game_gui = GUI(master=root)
game_gui.mainloop()

Related

Infinite loop due to calling functions

I am attempting to make Minesweeper in python using Tkinter. I have completed all of the initial logic but I am having trouble with the squares with zero surrounding bombs. The program is meant to open all of the surrounding squares when a square with zero surrounding bombs is clicked. The problem here is that it somehow creates an infinite loop. This may be due to the fact that I am calling the function click multiple times using a for loop but I am not sure if this is correct or how to fix it.
My code so far:
import tkinter as tk
from random import choices
from math import floor
##•
def openSurroundings(i):
trash = [i+1, i+8, i+9, i+10, i-1, i-8, i-9, i-10]
for a in trash:
if a<81:
click(floor(a/9), a-((a//9)*9))
def click(row, col):
text = numbers[row][col]
widget = buttons[row][col]
widget.grid_forget()
clickSurrounding = False
if text == 0:
text = ''
clickSurrounding = True
lab = tk.Label(window, text = text, height = 2, width = 5)
lab.grid(row = row, column = col)
if clickSurrounding == True:
openSurroundings((row*9)+col)
def placeBombs():
global widgets, bombs
numOfBombs = 10
widgets = list(range(1, (len(buttons)**2)+1))
bombs = choices(widgets, k = numOfBombs)
def findNumbers():
global numbers
numbers = []
lis_num = []
for i in widgets:
if i in bombs:
lis_num.append('•')
continue
else:
trash = [i+1, i+8, i+9, i+10, i-1, i-8, i-9, i-10]
num = 0
for a in trash:
if a > 0 and a in bombs:
num += 1
lis_num.append(num)
for i in range(0, len(lis_num), 9):
numbers.append(lis_num[i:i + 9])
window = tk.Tk()
window.title('Minesweeper')
sideLen = 9
row_index = 0
cell_index = 0
buttons = []
while row_index != sideLen:
button_row = []
while cell_index != sideLen:
button = tk.Button(window, width=5, height = 2, command=lambda row_index=row_index, cell_index=cell_index: click(row_index, cell_index))
button_row.append(button)
button.grid(row=row_index, column=cell_index)
cell_index += 1
buttons.append(button_row)
cell_index = 0
row_index += 1
placeBombs()
findNumbers()

How to have different actions happen every second and then repeat every four seconds

My title may be kind of confusing, but I essentially want to draw a different set of shapes every four seconds then start over. For example, second one I want to draw triangles, second two I want to draw lines, second three I want to draws rectangles, and second four I want to draw ovals. The for second five I want this process to repeat. My Idea was to use the remainder which works up to second seven. Also is this possible to do without importing any additional modules. Any help would be great and if something was not clear please ask.
Thanks,
Scott
Here is my current code to run if you want:
from tkinter import *
from random import *
#creates a class
class mainWindow():
def __init__(self,theWindow,winName):
#Sets values for variable
self.running = 0
#makes theWindow and instnace var
self.theWindow = theWindow
#creates the main frames
self.mainFrame = Frame(theWindow,width=600,height=200,bg="light gray")
self.secondframe = Frame(theWindow,width=600, height =287, bg = "green")
self.mainFrame.grid()
self.secondframe.grid()
# gives the window a title
theWindow.title(winName)
# do not resize window based on widgets
self.mainFrame.grid_propagate(False)
# window location
theWindow.geometry('+500+300')
#Makes it so the window wont resize
self.theWindow.resizable(0,0)
#
self.theWindow.wm_attributes("-topmost",1)
`enter code here`#Creates the three frames that will be used
#Inerts a blank label to help with formating
self.blank1 = Label(self.mainFrame, bg= "light gray", height =3, width=19)
self.blank1.grid(row=0, column=0, rowspan =1)
#Creates and places the start button
self.startbutton = Label(self.mainFrame,text = "Start", bg= "green", height =1, width=10,relief=RAISED)
self.startbutton.bind("<Button-1>", self.startTimer)
self.startbutton.grid(row=0, column=2, rowspan =1, sticky = E)
#Creates and places the stop button
self.stopbutton = Label(self.mainFrame,text = "Stop", bg= "red", height =1, width=10,relief=RAISED)
self.stopbutton.bind("<Button-1>", self.endTimer)
self.stopbutton.grid(row=0, column=3, rowspan =1, sticky =W)
#Creates and places the timer
self.timerLabel = Label(self.mainFrame,text = "Time: 0", bg= "white", height =2, width=14,relief=SUNKEN)
self.timerLabel.bind("<Button-1>",)
self.timerLabel.grid(row=3, column=2, columnspan =2, pady =25)
#draws the canvas
self.drawCanvas = Canvas(self.secondframe,width=598,height=285, bg= "light green")
self.drawCanvas.grid()
#Function for strting the timer
def startTimer(self,event):
if self.running == 0:
self.running = 1
self.countElapse(0)
#function for ening the timer
def endTimer(self,event):
if self.running == 1:
self.running = 0
self.timecount = 0
self.drawCanvas.delete(ALL)
self.timerLabel.configure(text = "Time: %d" %0)
#function for showing the time
def countElapse(self,localTime = NONE):
#self.timerLabel.configure()
if self.running:
if localTime is not NONE:
self.timecount = localTime
self.timerLabel.configure(text ="Time: "+str(self.timecount+1))
self.timecount = self.timecount+1
self.drawshapes(self.timecount)
self.mainFrame.after(1000,self.countElapse)
#function for drawing the shpaes
def drawshapes(self,timecount):
self.drawCanvas.delete(ALL)
color = ["red","white","purple","green","lime green", "cyan", "black","light blue","blue","pink","brown","gray"]
numshapes = 100
counter = 0
print(self.timecount)
var = self.timecount
#draws ovals
if var % 4 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
#draws the shape
self.drawCanvas.create_oval(x,y,x1,y1,fill=choice(color))
#draws rectangles
elif var % 3 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
self.drawCanvas.create_rectangle(x,y,x1,y1,fill= choice(color))
#draws Lines
elif var % 2 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
self.drawCanvas.create_line(x,y,x1,y1,fill= choice(color))
#draws triangles
elif var % 1 ==0:
while counter != numshapes:
counter += 1
x = randint(10,600)
y = randint(10,600)
x1 = randint(10,600)
y1 = randint(10,600)
x2 = randint(10,600)
y2 = randint(10,600)
self.drawCanvas.create_polygon(x,y,x1,y1,x2,y2,fill= choice(color))
def main():
# create a tkinter object
mainWin = Tk()
# create a mainWindow object
theMainWin = mainWindow(mainWin,"Scott Rodgers")
# keep the window displaying
mainWin.mainloop()
#calls main
main()
What you are looking for is how to use the .after method.
Here is how it works.
Lbl = Label()
Lbl.pack()
Lbl.after([Amount of milliseconds], lamba: [Function])
For more advanced notation,
after(delay_ms, callback=None, *args)
What this does is that it registers an alarm callback that is called after a given time

Why does my canvas image disappear even though I keep a reference?

The idea is to have the user click on the color bar and the entry field on the left will get a number from 0 to 100 entered for the user. The problem is the color bar image is blanked, even though I try to keep a reference to it so that it doesn't get garbage collected.
import Tkinter as Tk
class Test(Tk.Frame):
'''Test color bar for user input'''
def __init__(self, parent):
Tk.Frame.__init__(self, parent)
photo_color_bar_01 = Tk.PhotoImage(file = 'color_bar_blue_to_red_020.GIF')
pic_width = photo_color_bar_01.width()
pic_height = photo_color_bar_01.height()
shift_x_01 = 0 # 10
shift_y_01 = 0 # 10
pic_width_plus_border = pic_width + shift_x_01
pic_height_plus_border = pic_height + shift_y_01
x_center = (pic_width_plus_border) / 2.0
y_center = (pic_height_plus_border) / 2.0
My_Labels = ["Input_One", "Input_Two", "Input_Three", "Input_Four"]
Labels = []
variables = []
entrys = []
pic_frames = []
canvases = []
label_row = 0
entry_row = 1
for index in range(len(My_Labels)):
Labels.append(Tk.Label(root, text=My_Labels[index]))
Labels[-1].grid(padx=0, pady=0, row=label_row, column=0)
variables.append(Tk.StringVar())
entrys.append(Tk.Entry(root, textvariable =variables[index]))
entrys[-1].grid(padx=0, pady=0, row=entry_row, column=0)
entrys[-1].delete(0, Tk.END)
entrys[-1].insert(0, "50.00")
pic_frames.append(Tk.Frame(root, bd=4, relief=Tk.RAISED))
pic_frames[-1].grid(padx=0, pady=0, row=entry_row, column=2)
canvases.append(Tk.Canvas(pic_frames[-1], width=pic_width, height=pic_height))
canvases[-1].create_image(x_center, y_center, image=photo_color_bar_01, anchor = Tk.CENTER)
# keep a reference
# http://effbot.org/pyfaq/why-do-my-tkinter-images-not-appear.htm
canvases[-1].image = photo_color_bar_01 # keep a reference
canvases[-1].config(cursor="X_cursor")
canvases[-1].grid(padx=0, pady=0, row=entry_row, column=3)
canvases[-1].bind("<ButtonPress-1>", lambda event, arg=index: self.pick_LMB(event, index))
canvases[-1].bind("<ButtonPress-2>", self.pick_MMB)
label_row += 2
entry_row += 2
def pick_LMB(self, event, index):
print "\nindex = " + str(index)
canvas = event.widget
x = canvas.canvasx(event.x)
width_x = 180.0
scaled_x = x/width_x * 100.0
print "scaled_x = " + '{0:.2f}'.format(scaled_x)
#entrys[index].delete(0, Tk.END)
#entrys[index].insert(0, '{0:.2f}'.format(scaled_x))
def pick_MMB(self, event):
canvas = event.widget
x = canvas.canvasx(event.x)
print "\nx = " + str(x)
width_x = 180.0
scaled_x = x/width_x * 100.0
print "scaled_x = " + '{0:.2f}'.format(scaled_x)
if __name__ == "__main__":
root = Tk.Tk()
Test(root)
root.mainloop()
I forgot the .create_image command, so it wasn't a problem with the reference after all.

How to implement a mouse hovering callback on canvas items in tkinter?

I used the code below which I found on internet to implement a mouse hovering action in python:
from tkinter import *
import numpy as np
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.scale.bind('<ButtonRelease>', self.show)
self.canvas.bind('<Motion>', self.motion)
self.board = []
self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
self.canvas.pack()
self.scale.pack()
def motion(self,event):
if self.canvas.find_withtag(CURRENT):
current_color = self.canvas.itemcget(CURRENT, 'fill')
self.canvas.itemconfig(CURRENT, fill="cyan")
self.canvas.update_idletasks()
self.canvas.after(150)
self.canvas.itemconfig(CURRENT, fill=current_color)
def show(self,event):
self.canvas.delete('all')
x = 50
y = 50
row = []
self.board.clear()
for i in range(self.scale.get()):
row = []
for j in range(self.scale.get()):
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
x += 50
row.append(rectangle)
x -= j*50
y +=50
self.board.append(row)
print(self.board)
root = Tk()
a = rect(root)
root.mainloop()
The problem with the execution is that the color of the item changes to blue only for a limited time.
I need the color of each item in the canvas to be changed whenever I enter its zone and remain blue until the mouse is leaving the item.
You can pass the argument activefill when creating your rectangle.
From effboot.org:
Fill color to use when the mouse pointer is moved over the item, if
different from fill.
To do so, replace:
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
By:
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red', activefill='cyan')
This removes the need to bind Motion to your canvas, and also makes the code noticebly shorter:
from tkinter import *
import numpy as np
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.scale.bind('<ButtonRelease>', self.show)
self.board = []
self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
self.canvas.pack()
self.scale.pack()
def show(self,event):
self.canvas.delete('all')
x = 50
y = 50
row = []
self.board.clear()
for i in range(self.scale.get()):
row = []
for j in range(self.scale.get()):
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red', activefill='cyan')
x += 50
row.append(rectangle)
x -= j*50
y +=50
self.board.append(row)
print(self.board)
root = Tk()
a = rect(root)
root.mainloop()
I changed motion method and added self.last = None to __init__ method:
from tkinter import *
import numpy as np
class rect:
def __init__(self, root):
self.root = root
self.size = IntVar()
self.canvas = Canvas(self.root, width=800, height=300)
self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
self.scale.bind('<ButtonRelease>', self.show)
self.canvas.bind('<Motion>', self.motion)
self.board = []
self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
self.canvas.pack()
self.scale.pack()
self.last = None
def motion(self, event):
temp = self.canvas.find_withtag(CURRENT)
if temp == self.last:
self.canvas.itemconfig(CURRENT, fill="cyan")
self.canvas.update_idletasks()
else:
self.canvas.itemconfig(self.last, fill="red")
self.last = temp
def show(self,event):
self.canvas.delete('all')
x = 50
y = 50
row = []
self.board.clear()
for i in range(self.scale.get()):
row = []
for j in range(self.scale.get()):
rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
x += 50
row.append(rectangle)
x -= j*50
y +=50
self.board.append(row)
print(self.board)
root = Tk()
a = rect(root)
root.mainloop()

How to make a button in a game class with tkinter

I have been trying to make a button in a class with tkinter, but the button doesn't appear. The rest of the game is fine. I is just when i try to add a quit button in the game class it doesn't appear. I am using python 3.4.3 . I am make a game that you pop bubbles with a submarine. I have tried self.button_quit = tkinter.Button(window, text="Quit") and this is the class code:
class Game():
height = 500
width = 800
mid_x = width / 2
mid_y = height / 2
bonus_score = 700
bubble_chance = 10
gap = 100
time_limit = 30
speed = 0.01
def __init__(self):
self.score = 0
self.bonus = 0
self.window = tkinter.Tk()
self.window.title('Bubble Blaster')
self.canvas = tkinter.Canvas(self.window, width=self.width,
height=self.height, bg='darkblue')
self.end = time.time() + self.time_limit
Text(self.canvas,50,30,'TIME')
Text(self.canvas,150,30,'SCORE')
self.gui_score = Text(self.canvas,150,50)
self.gui_time = Text(self.canvas,50,50)
self.canvas.pack()
self.bubbles = list()
self.ship = Ship(self.canvas)
self.ship.move(self.mid_x, self.mid_y)
def coords_of(cid):
pos = c.coords(cid)
x = (pos[0] + pos[2]) / 2
y = (pos[1] + pos[3]) / 2
return x, y
def create_bubble(self):
x = self.width + self.gap
y = random.randint(0,self.height)
self.bubbles.append(Bubble(self.canvas,x,y))
def move_bubbles(self):
for bubble in self.bubbles:
bubble.move(-bubble.speed,0)
def destroy_bubble(self,bubble):
self.bubbles.remove(bubble)
bubble.destroy()
def clean_up_bubbles(self):
for bubble in self.bubbles:
if bubble.x < -self.gap:
self.destroy_bubble(bubble)
def buttons(self):
self.button1 = tkinter.Button(window, text="Quit")
self.button1.tkinter.pack()
def run(self):
while time.time() < self.end:
if random.randint(1, self.bubble_chance) == 1:
self.create_bubble()
self.move_bubbles()
self.clean_up_bubbles()
self.score += self.ship_bubble_collision()
if (int(self.score / self.bonus_score)) > self.bonus:
self.bonus += 1
self.end += self.time_limit
self.time_left = int(self.end - time.time())
self.update_gui()
self.window.update()
self.ship.step()
time.sleep(self.speed)
Text(self.canvas,self.mid_x, self.mid_y,'GAME OVER',
font=('Helvetica',30))
Text(self.canvas,self.mid_x, self.mid_y + 30,
'Score ' + str(self.score))
Text(self.canvas,self.mid_x, self.mid_y + 45,'Bonus Time ' +
str(self.bonus * self.time_limit))
input()
def distance(self,x1,y1,x2,y2):
return math.sqrt((x2-x1)**2+(y2-y1)**2)
def ship_bubble_collision(self):
points = 0
for bubble in self.bubbles:
distance = self.distance(self.ship.x,self.ship.y,\
bubble.x,bubble.y)
boundary = self.ship.radius + bubble.radius
if distance < boundary:
points += bubble.radius + bubble.speed
self.destroy_bubble(bubble)
return points
def update_gui(self):
self.gui_score.update(str(self.score))
self.gui_time.update(str(self.time_left))
if __name__ == '__main__':
Game().run()
Add
buttons()
Where you need it to be called. Buttons can't appear without being called!
If you need more info on def, click here.
Also for future questions, please elaborate on your question more. It is hard to tell exactly what you are asking.

Categories

Resources