I have created a GUI where you can enter values (x values) manually. If you enter a value x1, trace-method will automatically calculate
f(x1)=x1^2=y1 and mean(y) = (1/5 sum_{i=1}^{5} y_i)
So every time an x-value is entered, the corresponding y-value and mean(y) is calculated. The code below works. If you start it you get:
I would like to remove the initial values 0.0 from some cells. The window should look like this when the code is executed:
To get the desired result, I added at the very end before mainloop()
for i in range(1,5):
y_values[i].set("")
cells[(i,0)].delete(0,END)
where i remove the initial values of certain cells. If you start the code with this change, the program will not work properly anymore. If you enter an x-value, only the corresponding y-value is calculated, but not mean(y).
Do any of you know why the code with y_values[i].set(""), cells[(i,0)].delete(0,END) no longer works correctly and how to solve this problem?
Here is the full Code (from picture 1):
from tkinter import *
import tkinter as tk
root = Tk()
Label(root, text = "x-values",padx = 10).grid(row = 0, column = 0)
Label(root, text = "y-values",padx = 10).grid(row = 0, column = 1)
Label(root, text = "Mean y", padx = 10).grid(row = 0, column = 2)
# Create Variables
x_values, y_values = ["x%d" % x for x in range(5)], ["y%d" % x for x in range(5)]
for i in range (5):
x_values[i], y_values[i] = DoubleVar(), DoubleVar()
mean = DoubleVar()
# Create Table
rows, columns, cells = 5, 2, {}
for i in range(columns):
for j in range(rows):
if i == 0: # x-values that can be entered
b = Entry(root, textvariable=x_values[j])
b.grid(row = j+1, column = i, sticky = W + E)
cells[(j,i)] = b
else: # y-values that are computed by f
b = Label(root, textvariable=y_values[j])
b.grid(row = j+1, column = i, sticky = W + E)
cells[(j,i)] = b
label_mean = Label(root, textvariable = mean).grid(row = 1, column = 2, rowspan = 5)
# compute y-values
def f(name, index, mode):
try:
for i in range(5):
y_values[i].set(x_values[i].get()**2)
except tk.TclError:
pass
# compute mean and standard deviation
def statistic(name, index, mode):
try:
y_sum = 0
for i in range(5):
y_sum += y_values[i].get()
y_normalized = y_sum / 5
mean.set(y_normalized)
except tk.TclError:
pass
# Traces to trigger the above functions
for i in range(5):
x_values[i].trace('w', f)
y_values[i].trace('w', statistic)
mainloop()
Mean is not calculating because it is raising exception when you tried to add None value to y_sum. Add try block in your statistics function.
def statistic(name, index, mode):
try:
y_sum = 0
for i in range(5):
try:
y_sum += y_values[i].get()
except:
pass
y_normalized = y_sum / 5
mean.set(y_normalized)
except tk.TclError:
pass
Related
I have a function that is going to be the main area of this program, but when I add in a new function that needs to be executed by a button, it prevents the window from appearing, or gives out an error. The function I am trying to get to appear after a button press is new_function.
def bowler():
global bowler_win
global batsmen
bowler_win = Toplevel(master)
batsmen_win_2.withdraw()
variable = StringVar(bowler_win)
variable.set(fielding_team[0])
title = Label(bowler_win, text = "Please choose the bowler").grid(row = 0, column = 1)
w = OptionMenu(bowler_win, variable, *fielding_team)
w.grid(row = 1, column = 1)
def ok2():
current_bowler = variable.get()
for players in batting_team:
if players == current_bowler:
fielding_team.remove(current_bowler)
main_play()
button = Button(bowler_win, text="OK", command=ok2).grid(row = 2, column = 1)
#####CODE ABOVE IS ONLY TO SHOW WHERE THE FUNCTION BELOW IS EXECUTED FROM
def main_play():
while innings != 3:
facing_length = len(batsmen)
while over != over_amount or out_full == True or facing_length <= 1:
main_win = Toplevel(master)
bowler_win.withdraw()
ws = Label(main_win, text = " ").grid(row = 1, column = 1)
title = Label(main_win, text = "Current Game").grid(row = 0, column = 1)
score = Label(main_win, text = "Current Score:").grid(row = 2, column = 1)
line = Label(main_win, text = "--------------").grid(row = 1, column = 1)
score = Label(main_win, text = str(runs) + "/" + str(out)).grid(row = 3, column = 1)
line = Label(main_win, text="--------------").grid(row=4, column=1)
cur_bat = Label(main_win, text = "Facing Batsmen: " + batsmen[0]).grid(row = 5, column = 1)
other_bat = Label(main_win, text = "Other Batsmen: " + batsmen[1]).grid(row = 6, column = 1)
current_patner = Label(main_win, text = "Patnership: " + str(partnership_runs)).grid(row = 7, column = 1)
button = Button(main_win, text = "Next Play", command = new_function).grid(row = 8, column = 1) ###THIS IS WHERE THE NEW FUNCTION IS EXECUTED
If I call the function new_function after the button, the main_win window does not appear, this is the same for if I call new_function above the main_play function, the same error occurs.
If I try to nest new_function below the button, I get the error
UnboundLocalError: local variable 'new_func' referenced before assignment
Even though its a function(and I don't have a variable named that)
If anyone can help, that would be amazing
I am attempting to print out tables using the Text widget. I have created a method for doing this by using labels and frames, but the problem is that I will need to present larger tables to the point where I will need to use a scroll bar.
I am using tabs to make a table for the text widget, and it works well, but I can't seem to figure out a way to set the width of the widget to fit the text. I'm attempting to do it with the get_width() method below, but it doesn't always work.
The width option is measured in average sized characters for the font, but the problem is that the size of a tab 'character' varies. The way the tab key works for the text widget is different than the print() functions for a terminal or text file.
Here is my code below:
from tkinter import *
import math
class App(Text):
def __init__(self, matrix, parent, **kwargs):
Text.__init__(self, parent, **kwargs)
self.parent = parent
self.matrix = matrix
temp = []
for row in matrix:
temp.append(len(row))
# Making sure all the rows are of equal length and string values...
for row in matrix:
for _ in range(max(temp) - len(row)):
row.append('')
matrix = [[str(matrix[r][c]) for c in range(len(matrix[0]))] for r in range(len(matrix))]
# I need to get the maximum length for each column in the matrix...
c_lengths = {}
for c in range(len(matrix[0])):
c_lengths.update({c: []})
for r in range(len(matrix)):
c_lengths[c].append(len(matrix[r][c]))
self.tab_list = []
for r in range(len(matrix)):
temp = []
for c in range(len(matrix[0])):
if c != len(matrix[0]) - 1:
tab_count = (math.floor(max(c_lengths[c])/8)+1)
'''if (tab_count + max(c_lengths[c])) % 8 == 0:
tab_count += 1'''
temp.append(tab_count)
tabs = ['\t'] * tab_count
string = matrix[r][c] + ''.join(tabs)
else:
string = matrix[r][c]
self.insert(END, string)
self.tab_list.append(sum(temp))
self.insert(END, '\n')
self.configure(state=DISABLED, wrap=NONE, width=self.get_width())
def get_width(self):
temp = []
for row in self.matrix:
temp.append(len(''.join(row)))
return max(temp) + max(self.tab_list)*4
if __name__ == '__main__':
import random
root = Tk()
root.state('zoomed')
m = [['0' for _ in range(5)] for _ in range(40)]
for i in range(40):
for j in range(5):
m[i][j] = '*' * random.randint(1, 25)
App(m, root, font=('Times', '12'), height=40).pack()
root.mainloop()
Here is the output:
This run worked out good, but sometimes it cuts off some of the table.
from tkinter import *
import math
class App(Text):
def __init__(self, matrix, parent, **kwargs):
Text.__init__(self, parent, **kwargs)
self.parent = parent
self.matrix = matrix
temp = []
for row in matrix:
temp.append(len(row))
# Making sure all the rows are of equal length and string values...
for row in matrix:
for _ in range(max(temp) - len(row)):
row.append('')
matrix = [[str(matrix[r][c]) for c in range(len(matrix[0]))] for r in range(len(matrix))]
# I need to get the maximum length for each column in the matrix...
c_lengths = {}
for c in range(len(matrix[0])):
c_lengths.update({c: []})
for r in range(len(matrix)):
c_lengths[c].append(len(matrix[r][c]))
self.tab_list = []
for r in range(len(matrix)):
temp = []
for c in range(len(matrix[0])):
if c != len(matrix[0]) - 1:
tab_count = (math.floor(max(c_lengths[c])/8)+1)
'''if (tab_count + max(c_lengths[c])) % 8 == 0:
tab_count += 1'''
temp.append(tab_count)
tabs = ['\t'] * tab_count
string = matrix[r][c] + ''.join(tabs)
else:
string = matrix[r][c]
self.insert(END, string)
self.tab_list.append(sum(temp))
self.insert(END, '\n')
self.configure(state=DISABLED, wrap=NONE, width=self.getWidth())
def getWidth(self):
lst = []
for x in range(int(self.index('end-1c').split('.')[0])):
lst.append(len(self.get(f'end-{x} lines linestart', f'end-{x} lines lineend')))
return max(lst)+52
if __name__ == '__main__':
import random
root = Tk()
root.state('zoomed')
m = [['0' for _ in range(5)] for _ in range(40)]
for i in range(40):
for j in range(5):
m[i][j] = '*' * random.randint(1, 25)
App(m, root, font=('Times', '12'), height=40).pack()
root.mainloop()
Explaination:
self.get(f'end-{x} lines linestart', f'end-{x} lines lineend') here get(index1, index2) method is used to get characters between index1 and index2.
You can read what linestart and lineend parameter does here
end-x x goes from 0 to the last line, so end-1 is the last before line and so on. So all end-{x} lines linestart gets the starting index of each line and end-{x} lines lineend get the last index of the same line.
update:
Op your get_width:
def get_width(self):
temp = []
for row in self.matrix:
temp.append(len(''.join(row)))
return max(temp) + max(self.tab_list)*4
is the same as
def getWidth(self):
lst = []
spaces = []
for x in range(int(self.index('end-1c').split('.')[0])+1):
spaces = len([x for x in self.get(f'end-{x} lines linestart', f'end-{x} lines lineend') if x=='\t'])
lst.append(len(self.get(f'end-{x} lines linestart', f'end-{x} lines lineend'))+spaces*4)
return max(lst)
not much difference.
I also found out that the tabs are inconsistent I am not entirely sure of the reason. Setting self['tabs'] = '2c' gives more consistency you might want to check out more it here
these two functions are not working as args in the lamba function
which is calculating the price of the sweets
def mysweets():
b = v.get( ) # get the value of v set
cost=int(mysweets_price_list[b]) #price_display
print(cost)
def quantity_sweets():
q = int(spinbox1.get())
print(q)
price = lambda b, q : b * q # final price to be displayed in myLabel_3
print(price(b, q))
I have tried nested functions but they are not working, help please
anyone
from tkinter import *
myGui = Tk()
myGui.geometry('450x450+200+200')
myGui.title('Auto Sweet Dispenser')
price_display = ""
b = 0
#a = 0
q = 0
mysweets_price_list = {1 :9.00,
2 :7.50,
} # dict for the sweet prices
def mysweets():
b = v.get( ) # get the value of v set
cost=int(mysweets_price_list[b]) #price_display
print(cost)
def quantity_sweets():
q = int(spinbox1.get())
print(q)
price = lambda b, q : b * q # final price to be displayed in myLabel_3
print(price(b, q))
v =IntVar()
price =IntVar()
v.set(1)
myLabel = Label(myGui,text = 'Choose your sweets',font = 14, fg ='brown').place(x=140,y=55)#grid(row=3,column=10,sticky = 'e')
myRadio_1 = Radiobutton(myGui,text = 'Mints',variable = v, value = 1, command = mysweets).place(x= 160, y = 100)
myRadio_2 = Radiobutton(myGui,text = 'Nut log',variable = v, value = 2, command = mysweets).place(x= 160, y = 120)
myLabel_2 = Label(myGui,text = 'Select Quantity',font = 12, fg ='brown').place(x=160,y=160)#grid(row=3,column=10,sticky = 'e')
myLabel_3 = Label(myGui,textvariable = price ,font = "Times 14 bold",width = 14, fg ='white', bg= 'blue' ,relief = RAISED).place(x=160,y=220)#grid(row=3,column=10,sticky = 'e')
spinbox1 = Spinbox(myGui,from_=1,to = 6,command = quantity_sweets, state = NORMAL)
spinbox1.place(x=160,y=180)#
myGui.mainloop()
the code works except that price is not being displayed as the lambda
function is not working.
You don't need lambda here (in general lambda should be extremely rare). You only need a single function to get all the data, do the calculation, and update the Label. Like this:
from tkinter import *
myGui = Tk()
myGui.geometry('450x450+200+200')
myGui.title('Auto Sweet Dispenser')
mysweets_price_list = {1 :9.00,
2 :7.50,
} # dict for the sweet prices
def calculate():
b = v.get( ) # get the value of v set
cost=mysweets_price_list[b] #price
q = int(spinbox1.get()) # get quantity.
final_price = cost * q # final price to be displayed
price.set(final_price)
v =IntVar(value=1) # set initial value to 1
price = IntVar()
Label(myGui,text = 'Choose your sweets',font = 14, fg ='brown').place(x=140,y=55)#grid(row=3,column=10,sticky = 'e')
Radiobutton(myGui,text = 'Mints',variable = v, value = 1, command = calculate).place(x= 160, y = 100)
Radiobutton(myGui,text = 'Nut log',variable = v, value = 2, command = calculate).place(x= 160, y = 120)
Label(myGui,text = 'Select Quantity',font = 12, fg ='brown').place(x=160,y=160)#grid(row=3,column=10,sticky = 'e')
Label(myGui,textvariable = price ,font = "Times 14 bold",width = 14, fg ='white', bg= 'blue' ,relief = RAISED).place(x=160,y=220)#grid(row=3,column=10,sticky = 'e')
spinbox1 = Spinbox(myGui,from_=1,to = 6,command = calculate, state = NORMAL)
spinbox1.place(x=160,y=180)
calculate() # do the calculation at boot
myGui.mainloop()
Also, it's very important to know that if you do name = Widget().place() then the name is set to None and is useless. You need to do
name = Widget()
name.grid() # or pack() or place()
or
Widget().grid() # or pack() or place()
Don't mix those 2 styles! The 2-line style is much better, and what we usually use.
My code dies after about 140+ iterations, and I don't know why. I guess memory leak is a possibility, but I couldn't find it. I also found out that changing some arithmetic constants can prolong the time until the crash.
I have a genetic algorithm that tries to find best (i.e. minimal steps) route from point A (src) to point B (dst).
I create a list of random chromosomes, where each chromosome has:
src + dst [always the same]
list of directions (random)
I then run the algorithm:
find best route and draw it (for visualization purposes)
Given a probability P - replace the chromosomes with cross-overs (i.e. pick 2, and take the "end" of one's directions, and replace the "end" of the second's)
Given probability Q - mutate (replace the next direction with a random direction)
This all goes well, and most of the times I do find a route (usually not the ideal one), but sometimes, when it searches for a long time (say, about 140+ iterations) it just crushes. No warning. No error.
How can I prevent that (a simple iteration limit can work, but I do want the algorithm to run for a long time [~2000+ iterations])?
I think the relevant parts of the code are:
update function inside GUI class
which calls to cross_over
When playing with the update_fitness() score values (changing score -= (weight+1)*2000*(shift_x + shift_y) to score -= (weight+1)*2*(shift_x + shift_y) it runs for a longer time. Could be some kind of an arithmetic overflow?
import tkinter as tk
from enum import Enum
from random import randint, sample
from copy import deepcopy
from time import sleep
from itertools import product
debug_flag = False
class Direction(Enum):
Up = 0
Down = 1
Left = 2
Right = 3
def __str__(self):
return str(self.name)
def __repr__(self):
return str(self.name)[0]
# A chromosome is a list of directions that should lead the way from src to dst.
# Each step in the chromosome is a direction (up, down, right ,left)
# The chromosome also keeps track of its route
class Chromosome:
def __init__(self, src = None, dst = None, length = 10, directions = None):
self.MAX_SCORE = 1000000
self.route = [src]
if not directions:
self.directions = [Direction(randint(0,3)) for i in range(length)]
else:
self.directions = directions
self.src = src
self.dst = dst
self.fitness = self.MAX_SCORE
def __str__(self):
return str(self.fitness)
def __repr__(self):
return self.__str__()
def set_src(self, pixel):
self.src = pixel
def set_dst(self, pixel):
self.dst = pixel
def set_directions(self, ls):
self.directions = ls
def update_fitness(self):
# Higher score - a better fitness
score = self.MAX_SCORE - len(self.route)
score += 4000*(len(set(self.route)) - len(self.route)) # penalize returning to the same cell
score += (self.dst in self.route) * 500 # bonus routes that get to dst
for weight,cell in enumerate(self.route):
shift_x = abs(cell[0] - self.dst[0])
shift_y = abs(cell[1] - self.dst[1])
score -= (weight+1)*2000*(shift_x + shift_y) # penalize any wrong turn
self.fitness = max(score, 0)
def update(self, mutate_chance = 0.9):
# mutate #
self.mutate(chance = mutate_chance)
# move according to direction
last_cell = self.route[-1]
try:
direction = self.directions[len(self.route) - 1]
except IndexError:
print('No more directions. Halting')
return
if direction == Direction.Down:
x_shift, y_shift = 0, 1
elif direction == Direction.Up:
x_shift, y_shift = 0, -1
elif direction == Direction.Left:
x_shift, y_shift = -1, 0
elif direction == Direction.Right:
x_shift, y_shift = 1, 0
new_cell = last_cell[0] + x_shift, last_cell[1] + y_shift
self.route.append(new_cell)
self.update_fitness()
def cross_over(p1, p2, loc = None):
# find the cross_over point
if not loc:
loc = randint(0,len(p1.directions))
# choose one of the parents randomly
x = randint(0,1)
src_parent = (p1, p2)[x]
dst_parent = (p1, p2)[1 - x]
son = deepcopy(src_parent)
son.directions[loc:] = deepcopy(dst_parent.directions[loc:])
return son
def mutate(self, chance = 1):
if 100*chance > randint(0,99):
self.directions[len(self.route) - 1] = Direction(randint(0,3))
class GUI:
def __init__(self, rows = 10, cols = 10, iteration_timer = 100, chromosomes = [], cross_over_chance = 0.5, mutation_chance = 0.3, MAX_ITER = 100):
self.rows = rows
self.cols = cols
self.canv_w = 800
self.canv_h = 800
self.cell_w = self.canv_w // cols
self.cell_h = self.canv_h // rows
self.master = tk.Tk()
self.canvas = tk.Canvas(self.master, width = self.canv_w, height = self.canv_h)
self.canvas.pack()
self.rect_dict = {}
self.iteration_timer = iteration_timer
self.iterations = 0
self.MAX_ITER = MAX_ITER
self.chromosome_list = chromosomes
self.src = chromosomes[0].src # all chromosomes share src + dst
self.dst = chromosomes[0].dst
self.prev_best_route = []
self.cross_over_chance = cross_over_chance
self.mutation_chance = mutation_chance
self.no_obstacles = True
# init grid #
for r in range(rows):
for c in range(cols):
self.rect_dict[(r, c)] = self.canvas.create_rectangle(r *self.cell_h, c *self.cell_w,
(1+r)*self.cell_h, (1+c)*self.cell_w,
fill="gray")
# init grid #
# draw src + dst #
self.color_src_dst()
# draw src + dst #
# after + mainloop #
self.master.after(iteration_timer, self.start_gui)
tk.mainloop()
# after + mainloop #
def start_gui(self):
self.start_msg = self.canvas.create_text(self.canv_w // 2,3*self.canv_h // 4, fill = "black", font = "Times 25 bold underline",
text="Starting new computation.\nPopulation size = %d\nCross-over chance = %.2f\nMutation chance = %.2f" %
(len(self.chromosome_list), self.cross_over_chance, self.mutation_chance))
self.master.after(2000, self.update)
def end_gui(self, msg="Bye Bye!"):
self.master.wm_attributes('-alpha', 0.9) # transparency
self.canvas.create_text(self.canv_w // 2,3*self.canv_h // 4, fill = "black", font = "Times 25 bold underline", text=msg)
cell_ls = []
for idx,cell in enumerate(self.prev_best_route):
if cell in cell_ls:
continue
cell_ls.append(cell)
self.canvas.create_text(cell[0]*self.cell_w, cell[1]*self.cell_h, fill = "purple", font = "Times 16 bold italic", text=str(idx+1))
self.master.after(3000, self.master.destroy)
def color_src_dst(self):
r_src = self.rect_dict[self.src]
r_dst = self.rect_dict[self.dst]
c_src = 'blue'
c_dst = 'red'
self.canvas.itemconfig(r_src, fill=c_src)
self.canvas.itemconfig(r_dst, fill=c_dst)
def color_route(self, route, color):
for cell in route:
try:
self.canvas.itemconfig(self.rect_dict[cell], fill=color)
except KeyError:
# out of bounds -> ignore
continue
# keep the src + dst
self.color_src_dst()
# keep the src + dst
def compute_shortest_route(self):
if self.no_obstacles:
return (1 +
abs(self.chromosome_list[0].dst[0] - self.chromosome_list[0].src[0]) +
abs(self.chromosome_list[0].dst[1] - self.chromosome_list[0].src[1]))
else:
return 0
def create_weighted_chromosome_list(self):
ls = []
for ch in self.chromosome_list:
tmp = [ch] * (ch.fitness // 200000)
ls.extend(tmp)
return ls
def cross_over(self):
new_chromosome_ls = []
weighted_ls = self.create_weighted_chromosome_list()
while len(new_chromosome_ls) < len(self.chromosome_list):
try:
p1, p2 = sample(weighted_ls, 2)
son = Chromosome.cross_over(p1, p2)
if son in new_chromosome_ls:
continue
else:
new_chromosome_ls.append(son)
except ValueError:
continue
return new_chromosome_ls
def end_successfully(self):
self.end_gui(msg="Got to destination in %d iterations!\nBest route length = %d" % (len(self.prev_best_route), self.compute_shortest_route()))
def update(self):
# first time #
self.canvas.delete(self.start_msg)
# first time #
# end #
if self.iterations >= self.MAX_ITER:
self.end_gui()
return
# end #
# clean the previously best chromosome route #
self.color_route(self.prev_best_route[1:], 'gray')
# clean the previously best chromosome route #
# cross over #
if 100*self.cross_over_chance > randint(0,99):
self.chromosome_list = self.cross_over()
# cross over #
# update (includes mutations) all chromosomes #
for ch in self.chromosome_list:
ch.update(mutate_chance=self.mutation_chance)
# update (includes mutations) all chromosomes #
# show all chromsome fitness values #
if debug_flag:
fit_ls = [ch.fitness for ch in self.chromosome_list]
print(self.iterations, sum(fit_ls) / len(fit_ls), fit_ls)
# show all chromsome fitness values #
# find and display best chromosome #
best_ch = max(self.chromosome_list, key=lambda ch : ch.fitness)
self.prev_best_route = deepcopy(best_ch.route)
self.color_route(self.prev_best_route[1:], 'gold')
# find and display best chromosome #
# check if got to dst #
if best_ch.dst == best_ch.route[-1]:
self.end_successfully()
return
# check if got to dst #
# after + update iterations #
self.master.after(self.iteration_timer, self.update)
self.iterations += 1
# after + update iterations #
def main():
iter_timer, ITER = 10, 350
r,c = 20,20
s,d = (13,11), (7,8)
population_size = [80,160]
cross_over_chance = [0.2,0.4,0.5]
for pop_size, CO_chance in product(population_size, cross_over_chance):
M_chance = 0.7 - CO_chance
ch_ls = [Chromosome(src=s, dst=d, directions=[Direction(randint(0,3)) for i in range(ITER)]) for i in range(pop_size)]
g = GUI(rows=r, cols=c, chromosomes = ch_ls, iteration_timer=iter_timer,
cross_over_chance=CO_chance, mutation_chance=M_chance, MAX_ITER=ITER-1)
del(ch_ls)
del(g)
if __name__ == "__main__":
main()
I do not know if you know the Python Profiling tool of Visual Studio, but it is quite useful in cases as yours (though I usually program with editors, like VS Code).
I have run your program and, as you said, it sometimes crashes. I have analyzed the code with the profiling tool and it seems that the problem is the function cross_over, specifically the random function:
I would strongly suggest reviewing your cross_over and mutation functions. The random function should not be called so many times (2 millions).
I have previously programmed Genetic Algorithms and, to me, it seems that your program is falling into a local minimum. What is suggested in these cases is playing with the percentage of mutation. Try to increase it a little bit so that you could get out of the local minimum.
For my coursework i am making a booking system and i have been messing around trying to make a page which shows current week lessons and when the button is clicked it comes up with that students details on a separate page.But i don't know how to go about passing that time into my open page sub(which writes a txt file which is going to be used for SQL to get the students details). The current way i have done it just passes the max times into the sub.
from tkinter import *
import datetime
class Application(Frame):
def __init__(self, master):
""" Initialize the frame. """
super(Application, self).__init__(master)
self.grid()
self.timetable_button_gen_weekdays()
self.timetable_button_gen_weekends()
def timetable_button_gen_weekdays(self):
c = datetime.datetime(100,1,1,16,00,00)
self.Monday_lbl = Label(self, text = "Monday")
self.Monday_lbl.grid(row = 1, column = 0)
self.Tuesday_lbl = Label(self, text = "Tuesday")
self.Tuesday_lbl.grid(row = 2, column = 0)
self.Wednesday_lbl = Label(self, text = "Wednesday")
self.Wednesday_lbl.grid(row = 3, column = 0)
self.Thursday_lbl = Label(self, text = "Thursday")
self.Thursday_lbl.grid(row = 4, column = 0)
self.Friday_lbl = Label(self, text = "Friday")
self.Friday_lbl.grid(row = 5, column = 0)
for k in range(8):
b = c + datetime.timedelta(minutes = (30 * k))
d = b + datetime.timedelta(minutes = (30))
self.i_time_weekdays_lbl = Label(self, text = b.time().strftime('%H:%M')+" to "+d.time().strftime('%H:%M'))
self.i_time_weekdays_lbl.grid(row = 0, column = k + 1)
for i in range(5):
for a in range(8):
b = c + datetime.timedelta(minutes = (30 * a))
d = b + datetime.timedelta(minutes = (30))
bttn_i_a = Button(self, text = "available",command = lambda: self.OpenPage(b.time().strftime('%H:%M'),d.time().strftime('%H:%M')))
bttn_i_a.grid(row = i + 1, column = a + 1)
bttn_i_a.config(height = 2, width = 10)
def timetable_button_gen_weekends(self):
c = datetime.datetime(100,1,1,10,00,00)
self.Saturday_lbl = Label(self, text = "Saturday")
self.Saturday_lbl.grid(row = 8, column = 0)
self.Sunday_lbl = Label(self, text = "Sunday")
self.Sunday_lbl.grid(row = 9, column = 0)
self.weekend_lbl = Label(self, text = "Weekend")
self.weekend_lbl.grid(row = 6, column = 1, sticky = W)
for k in range(10):
b = c + datetime.timedelta(minutes = (30 * k))
d = b + datetime.timedelta(minutes = (30))
self.i_time_weekdays_lbl = Label(self, text = b.time().strftime('%H:%M')+" to "+d.time().strftime('%H:%M'))
self.i_time_weekdays_lbl.grid(row = 7, column = k + 1)
for i in range(2):
for a in range(10):
b = c + datetime.timedelta(minutes = (30 * a))
d = b + datetime.timedelta(minutes = (30))
bttn_i_a = Button(self, text = "available",command = lambda: self.OpenPage(b.time().strftime('%H:%M'),d.time().strftime('%H:%M')))
bttn_i_a.grid(row = i + 8, column = a + 1)
bttn_i_a.config(height = 2, width = 10)
def OpenPage(self,startime,finishtime):
file = open("PassTimes.txt","w")
file.write(startime)
file.write("\n")
file.write(finishtime)
print(startime)
print(finishtime)
filepath = "PresentStudent.py"
global_namespace = {"__file__": filepath, "__name__": "__main__"}
with open(filepath, 'rb') as file:
exec(compile(file.read(), filepath, 'exec'), global_namespace)
root = Tk()
root.title("test")
root.geometry("2000x2000")
app = Application(root)
root.mainloop()
Welcome to SO.
General
IMHO, running the main routine of "PresentStudent.py" does not look that clean.
It works, but a main routine is built for when the script is called directly, not when it is imported and used in some other script.
Are you aware of the modules functionality in python?
I would recommend creating a function in PresentStudent.py that does what you are doing inside your main routine. Give the function parameters to pass the .txt-Filename.
e.g.
def presentStudentCall(inputFile):
and use it inside your script like:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# here we import PresentStudent.py, as we import it __main__ will not run
import PresentStudent
#[...]
def OpenPage(self, stime, etime):
#[...]
# Instead of executing a file we call the function from the module
PresentStudent.presentStudentCall(file)
If you want to display the data inside a second frame, you could also declare a class in PresentStudent.py and use it like:
def OpenPage(self, stime, etime):
#[...]
student=PresentStudent.Student() # assuming to name the class "Student"
student.presentStudentCall(file)
Your question itself
using the lambda does not need to be the best way. In matters of scope and garbage collecting your code only passes the last generated "b"s and "c"s to the definition.
What you could do to make it work is calculating the sender item in OpenPage:
To achieve that, I recommend having arrays for your time spans storing starting times.
Like
c = datetime.datetime(100,1,1,16,00,00)
self.weektimes = ["%s"%(c+datetime.timedelta(minutes=30*k)) for k in range(8)]
self.weekendtimes = ["%s"%((c+datetime.timedelta(minutes=30*k)) for k in range(10)]
First you need to bind the click event to the widget(in that case your button)
bttn_i_a.bind("<Button-1>", self.OnPage)
Your OpenPage could then look like this:
def OpenPage(self, event):
import time
# With that, we get the row and column where we clicked in
grid_info=event.widget.grid_info()
# week or weekend?
if grid_info["row"] > 5: #may depend on amount of headers
_timearray=self.weekendtimes
else:
_timearray=self.weektimes
# get the column
col=grid_info["column"]
# get the startTime
stime=_timearray[col]
# end time is +30 minutes
etime="%s"%(time.strptime("%s"%stime, "%H:%M")+time.struct_time(tm_min=30))
# now call the handler...