Count how many times tkinter button is pressed - python

I am writing a program for a school project. It is a program to book tickets at a cinema theater. I have used tkinter to create a gui for users to select their desired seats. However, I also need to count the number of seats selected, to print the total cost of tickets. But I am stuck here and I am not sure how to proceed(I have just learned classes and do not fully understand them). So far users can select and deselect the buttons. Here is the code that is used.
class buttonc:
def __init__(self,master,x,ro):
self.x = x
self.ro = ro
self.count = 0
self.button = []
self.button += [Button(root,text = self.x,background = "white",width= 2,height = 1,command = lambda:self.color())]
self.click = 0
def clicks(self,numm):
self.click += numm
def color(self):
if self.count == 1:
self.button[0].configure(background = "white")
self.count = 0
self.clicks(-1)
elif self.count == 0:
self.button[0].configure(background = "green")
self.count = 1
self.clicks(1)
def counter(self):
return self.click
def pos(self):
self.button[0].grid(column = self.x+30,row = self.ro, padx = 14, pady = 14)
fo = open('tickets.txt','w')
for i in range(9):
for k in range(25):
b = buttonc(root,k+1,i+1)
b.pos()
fincount = str(b.counter())
root.mainloop()
fo.write(fincount)
fo.close()
As you can see i have used a counter to write the value into a text file, but this value never gets updated. Any help would be appreciated, and thank you in advance.

Are you looking into the file "tickets.txt" and see a 0? If so, it's not really surprising. You extract b.counter() before you enter the main loop.
Rearrange your code like:
buttons = []
for i in range(9):
for k in range(25):
b = buttonc(root,k+1,i+1)
b.pos()
buttons.append(b)
root.mainloop()
fo = open('tickets.txt','w')
for b in buttons:
fo.write(str(b.counter() + "\n")
fo.close()

Related

python - how to go to next image in slideshow using tkinter gui

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

Python self.after() changes running order

I am (still!) writing a Noughts and Crosses program using Tkinter in Python 3.
I wanted to implement a short pause between the player's move and the computer's move, so in the add_move_comp() method implemented self.after() function.
I expected this to pause the program for 1000ms then continue by 'drawing' on the computer's move.
However when I run my code, the program only recognises the computer's move the next move after, causing problems with identifying a 3-in-a-row.
I think this is due to line 62 running after 1000ms while the rest of the program continues running, but I don't know how to stop the whole program and continue running with line 62 after the pause.
from tkinter import *
from functools import partial
from itertools import *
import random
class Window(Frame):
def __init__(self, master = None): # init Window class
Frame.__init__(self, master) # init Frame class
self.master = master # allows us to refer to root as master
self.rows = 3
self.columns = 3
self.guiGrid = [[None for x in range(self.rows)] for y in range(self.columns)] # use this for the computer's moves
self.noText = StringVar(value = '')
self.cross = StringVar(value = 'X')
self.nought = StringVar(value = 'O')
self.button_ij = None
self.myMove = True
self.create_window()
self.add_buttons()
def create_window(self):
self.master.title('Tic Tac Toe')
self.pack(fill = BOTH, expand = 1)
for i in range(0,3): # allows buttons to expand to frame size
self.grid_columnconfigure(i, weight = 1)
self.grid_rowconfigure(i, weight = 1)
def add_buttons(self):
rows = 3
columns = 3
for i in range (rows):
for j in range(columns):
self.button_ij = Button(self, textvariable = self.noText, command = lambda i=i, j=j: self.add_move(i,j))
self.guiGrid[i][j] = self.button_ij
self.button_ij.grid(row = i,column = j, sticky =E+W+S+N)
def add_move(self, i,j):
# player's move
while self.myMove:
pressedButton = self.guiGrid[i][j]
self.guiGrid[i][j].config(textvariable = self.cross)
# computer's turn
self.myMove = False
while (self.myMove == False):
self.add_move_comp()
def add_move_comp(self):
repeat = True
while repeat:
i = random.randint(0,0) # row - only allow O to be drawn on 1st row for testing purposes
j = random.randint(0,2) # column
testText = self.guiGrid[i][j].cget('text')
if testText == '':
self.after(1000,lambda i = i, j = j :self.guiGrid[i][j].config(textvariable = self.nought)) # search up rules with returning values using lambda
print('Should plot O at row: ',i,' column: ',j)
print('TEXT ACTUALLY PLOTTED IS: ',self.guiGrid[i][j].cget('text'))
self.myMove = True
repeat = False
print(self.guiGrid[0][0].cget('text'), self.guiGrid[0][1].cget('text'), self.guiGrid[0][2].cget('text')+'THIS IS PRINTING')
root = Tk() # creating Tk instance
rootWidth = '500'
rootHeight = '500'
root.geometry(rootWidth+'x'+rootHeight)
ticTacToe = Window(root) # creating Window object with root as master
root.mainloop() # keeps program running
This is due to the fact that self.after imeadiately returns and starts the given function after passing trough a queque to prevent the actual Window from beeing unable to responde because there is some code running. A way to work around this is to create a BooleanVariable for validation and using self.wait_variable as follows:
from tkinter import *
from functools import partial
from itertools import *
import random
class Window(Frame):
def __init__(self, master = None): # init Window class
Frame.__init__(self, master) # init Frame class
self.master = master # allows us to refer to root as master
self.rows = 3
self.columns = 3
self.guiGrid = [[None for x in range(self.rows)] for y in range(self.columns)] # use this for the computer's moves
self.noText = StringVar(value = '')
self.cross = StringVar(value = 'X')
self.nought = StringVar(value = 'O')
self.button_ij = None
self.myMove = True
self.validationVar = BooleanVar(self)
self.create_window()
self.add_buttons()
def create_window(self):
self.master.title('Tic Tac Toe')
self.pack(fill = BOTH, expand = 1)
for i in range(0,3): # allows buttons to expand to frame size
self.grid_columnconfigure(i, weight = 1)
self.grid_rowconfigure(i, weight = 1)
def add_buttons(self):
rows = 3
columns = 3
for i in range (rows):
for j in range(columns):
self.button_ij = Button(self, textvariable = self.noText, command = lambda i=i, j=j: self.add_move(i,j))
self.guiGrid[i][j] = self.button_ij
self.button_ij.grid(row = i,column = j, sticky =E+W+S+N)
def add_move(self, i,j):
# player's move
while self.myMove:
pressedButton = self.guiGrid[i][j]
self.guiGrid[i][j].config(textvariable = self.cross)
# computer's turn
self.myMove = False
while (self.myMove == False):
self.add_move_comp()
def add_move_comp(self):
repeat = True
while repeat:
i = random.randint(0,0) # row - only allow O to be drawn on 1st row for testing purposes
j = random.randint(0,2) # column
testText = self.guiGrid[i][j].cget('text')
if testText == '':
self.after(1000, self.validationVar.set, True)
self.wait_variable(self.validationVar)
self.guiGrid[i][j].config(textvariable = self.nought) # search up rules with returning values using lambda
print('Should plot O at row: ',i,' column: ',j)
print('TEXT ACTUALLY PLOTTED IS: ',self.guiGrid[i][j].cget('text'))
self.myMove = True
repeat = False
print(self.guiGrid[0][0].cget('text'), self.guiGrid[0][1].cget('text'), self.guiGrid[0][2].cget('text')+'THIS IS PRINTING')
root = Tk() # creating Tk instance
rootWidth = '500'
rootHeight = '500'
root.geometry(rootWidth+'x'+rootHeight)
ticTacToe = Window(root) # creating Window object with root as master
root.mainloop() # keeps program running

Tkinter: Identifying button by row and column

I want to be able to select a button based on what row and column it is in on a grid and the button and control its Text and Relief. I haven't been able to find anything on widgets or cells used in this manner.
Edit:
I changed where root is placed and now it says that I can't use a tuple that I recieved for 'relief' which makes sense, I need to access the widget itself. Any reccomendations
import tkinter
import functools
import random
from time import sleep
width = input('Enter the grid width. ')
height = input('Enter the grid height. ')
numb = input('Enter the number of bombs. ')
Matrix = [[0 for lp in range(int(width))] for fg in range(int(height))]
def ranintx():
return random.randint(0,int(width))
def raninty():
return random.randint(0,int(height))
def placemines():
y = ranintx()
x = raninty()
for ranintformine in range(int(numb)):
x = ranintx()
y = raninty()
Matrix[y-1][x-1] = 1
placemines()
def sunken(event, self, x, y):
button = event.widget
button['relief'] = 'sunken'
if x - 1 < 0 :
return
if x > int(width) + 1 :
return
if y - 1 < 0 :
return
if y > int(height) + 1 :
return
if Matrix[x][y] == 1 :
top = tkinter.Toplevel()
top.title("About this application...")
msg = tkinter.Message(top, text="You Lose")
msg.pack()
button = tkinter.Button(top, text="Dismiss", command=top.destroy)
button.pack()
print('Column = {}\nRow = {}'.format(x, y))
else:
n1 = x - 1
n2 = y - 1
for lp in range(3):
for lp2 in range(3):
abutton = root.grid_location(n1, n2)
abutton['relief'] = ['sunken']
# I want to be able to change and select the button here. This was one of my poor attempt
n2 =+ 1
n1 =+ 1
def push(event, self, x, y):
button = event.widget
if Matrix[x][y] == 1 :
print('Column = {}\nRow = {}'.format(x, y))
class MineSweep(tkinter.Frame):
#classmethod
def main(cls, width, height):
window = cls(root, width, height)
'''placemine()'''
root.mainloop()
def __init__(self, master, width, height):
super().__init__(master)
self.__width = width
self.__height = height
self.__build_buttons()
self.grid()
#def sunken(event):
# button = event.widget
# button['relief'] = 'sunken'
def __build_buttons(self):
self.__buttons = []
for y in range(self.__height):
row = []
for x in range(self.__width):
button = tkinter.Button(self, state='disabled')
button.grid(column=x, row=y)
button['text'] = ' '
print(grid.slaves)
self.checked = True
#button['command'] = functools.partial(self.__push, x, y)
button.bind("<Button-3>",
lambda event, arg=x, brg=y: push(event, self, arg, brg))
button['relief'] = 'raised'
button.bind("<Button-1>",
lambda event, arg=x, brg=y: sunken(event, self, arg, brg))
#button['command'] = sunken
row.append(button)
self.__buttons.append(row)
root = tkinter.Tk()
if __name__ == '__main__':
MineSweep.main(int(width), int(height))
You have a few things wrong with your program. First, sunken should be a method on the class. It's very weird to have it outside the class, and then you pass in self as some other argument. It works, but it makes the code very confusing.
That being said, you're actually very close to making this work. You're already saving a reference to each button in a list of lists, so you should be able to get the widget with self.__buttons[y][x]. However, because sunken is not part of the class, and because you named the variable with two underscores, the variable is not accessible to the sunken function.
If you change the variable to have a single underscore instead of a double, your code should work more-or-less exactly as it is (once you fix the syntax and indentation errors). The other solution is to make sunken a method on the class and fix how you call it (remove the self argument, call it as self.sunken), it will work with two underscores.
Frankly, using two underscores has zero practical benefit. Avoid the temptation to use it. At the very least, don't use it until you have your basic logic working, then you can go back and hide attributes you don't want to be exposed.

Python in Tkinter: What options can I use to resize a button?

I am a beginner programmer. I would like to resize my button, which is defined as "but", and it's defined in the Square class. What option would I use to adjust the size of this button to adjust the height and width? Any help is greatly appreciated and if you could add comments in your code that would be helpful!
import Tkinter
class TicWindow(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.squares = []
self.turn = 0
for r in range(3):
for c in range(3):
b = Square(self).grid(row=r,column=c)
self.squares.append(b)
self.geometry("500x500")
def turn(self):
return self.turn
def changeTurn(self):
if (self.turn == 0):
self.turn = 1
else:
self.turn = 0
class Square(Tkinter.Button):
def __init__(self,parent):
but = Tkinter.Button.__init__(self,parent, text=" ", command=self.changeButtonText)
self.canClick = True
def changeButtonText(self):
if (self.master.turn == 0) and (self.canClick == True):
self.config(text = "X")
elif (self.master.turn == 1) and (self.canClick == True):
self.config(text = "O")
self.master.changeTurn()
self.hasBeenClicked()
def canClick(self):
return self.canClick
def hasBeenClicked(self):
self.canClick = False
class ScoreBoard(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.board = Tkinter.Label(self, text = "No Score Yet")
self.board.pack()
self.geometry("500x500+300+300")
top = TicWindow()
scoreboard = ScoreBoard()
top.mainloop()
This has a simple answer.
Basically, just add the height and width variables when you create the buttons:
Tkinter.Button.__init__(self,parent, text=" ", command=self.changeButtonText, height = 20, width = 30)
You can modify the height and width buttons to change the size of the button.

In Python Tkinter: What option can I use to resize a window?

I am a beginner programmer! My program is not stellar. I just need to figure out how to resize the two windows I am calling on: TicWindow and ScoreBoard. Underneath my ScoreBoard class I have programmed self.board = TicWindow() & self.board.geometry("500x500+300+300"). I have read that to resize a window you need to call upon a root window, which is my TicWindow. I know that it is wrong because it looks like it is in the wrong place and it opens a third window that is resized. Any help is appreciated!
import Tkinter
class TicWindow(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.squares = []
self.turn = 0
for r in range(3):
for c in range(3):
b = Square(self).grid(row=r,column=c)
self.squares.append(b)
def turn(self):
return self.turn
def changeTurn(self):
if (self.turn == 0):
self.turn = 1
else:
self.turn = 0
class Square(Tkinter.Button):
def __init__(self,parent):
Tkinter.Button.__init__(self,parent, text=" ", command=self.changeButtonText)
self.canClick = True
def changeButtonText(self):
if (self.master.turn == 0) and (self.canClick == True):
self.config(text = "X")
elif (self.master.turn == 1) and (self.canClick == True):
self.config(text = "O")
self.master.changeTurn()
self.hasBeenClicked()
def canClick(self):
return self.canClick
def hasBeenClicked(self):
self.canClick = False
class ScoreBoard(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.board = Tkinter.Label(self, text = "No Score Yet").pack()
self.board = TicWindow()
self.board.geometry("500x500+300+300")
top = TicWindow()
scoreboard = ScoreBoard()
top.mainloop()
Sounds like you want to resize your ScoreBoard.
Inside ScoreBoard.__init__, there's no need to create another TicWindow instance. That's why you're getting three windows. Additionally, you shouldn't try to assign a widget and pack it on the same line - the variable will only contain None that way.
class ScoreBoard(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.board = Tkinter.Label(self, text = "No Score Yet")
self.board.pack()
self.geometry("500x500+300+300")
Result:

Categories