Related
Hi im trying to write simple app with posibility to add and delete Entry boxes and i cant get the expected result.
What i want : add button add a row with entry and delete this entry button
what i get :Entry is not created and delete button is not his column
here is my code:
from tkinter import *
class App():
def __init__(self, root):
self.root=root
self.all_entries = []
self.addboxButton = Button(root, text='<Add >', command=self.addBox)
self.addboxButton.grid(row=0, column=0)
self.rows =[]
def addBox(self):
print ("ADD")
new_row = Row(self,root)
self.rows.append(new_row)
self.render()
def render(self):
for i in range(len(self.rows)):
self.rows[i].grid(row=i + 1, column=1, sticky=W,columnspan=2, padx=(5, 0), pady=(5, 0))
class Row(Frame):
def __init__(self,parent, main_frame, **kwargs):
super(Row, self).__init__()
self.main_frame = main_frame
self.entryRowVar = StringVar()
self.entryRow= Entry(self.main_frame, textvariable=self.entryRowVar)
self.entryRow.grid(row=0, column=1, sticky=W,)
self.del_btn = Button(self, text='Delete')
self.del_btn.grid(row=0, column=2, sticky=W,)
if __name__ == "__main__":
root = Tk()
app = App(root)
root.mainloop()
class calculate:
def __init__(self):
self.window = tk.Tk()
self.numbers = tk.Frame(master = self.window)
self.signs = tk.Frame(master = self.window)
self.lst, self.lst2 = (1,2,3,4,5,6,7,8,9,0,".","="),("C","+","-","*","/")
def buttons(self):
val = 0
for i in range(4):
for j in range(3):
self.numbutton = tk.Button(master = self.numbers,text = self.lst[val])
self.numbutton.grid(row = i,column = j)
val += 1
for i in range(5):
self.signbutton = tk.Button(master = self.signs,text = self.lst2[i])
self.signbutton.grid(row = i)
def packing(self):
self.numbers.grid(column= 0,row = 0,rowspan = 4)
self.signs.grid(column = 1,row = 0,rowspan = 4)
self.window.mainloop()
calculator = calculate()
calculator.buttons()
calculator.packing()
I am trying to put some buttons by using gird in python tkinter.
and I want that numbers and signs have same height.
but the signs is of larger height.
please help.
Thanks for help in advance.
You can force the size of a frame by giving the frame an height:
self.numbers = tk.Frame(master = self.window, height=200)
self.signs = tk.Frame(master = self.window, height=200)
Both frames will have a height of 200 but you can change it to whatever you want
for i in range(4):
for j in range(3):
self.numbutton = tk.Button(master = self.numbers,text = self.lst[val],width=5,height=5)
self.numbutton.grid(row = i,column = j)
val += 1
for i in range(5):
self.signbutton = tk.Button(master = self.signs,text = self.lst2[i],width=5, height=4)
self.signbutton.grid(row = i)
Add a padding in y of some 0.1 or 0.2 to make it look good.
You can add sticky="nsew" to all .grid(...) and add self.numbers.rowconfigure("all", weight=1) to make the number buttons to use all the available space inside self.numbers frame.
Below is modified code:
import tkinter as tk
class calculate:
def __init__(self):
self.window = tk.Tk()
self.numbers = tk.Frame(master=self.window)
self.signs = tk.Frame(master=self.window)
self.lst, self.lst2 = (1,2,3,4,5,6,7,8,9,0,".","="), ("C","+","-","*","/")
def buttons(self):
val = 0
for i in range(4):
for j in range(3):
self.numbutton = tk.Button(master=self.numbers, text=self.lst[val])
self.numbutton.grid(row=i, column=j, sticky="nsew") # added sticky
val += 1
for i in range(5):
self.signbutton = tk.Button(master=self.signs, text=self.lst2[i])
self.signbutton.grid(row=i, sticky="nsew") # added sticky
def packing(self):
self.numbers.grid(column=0, row=0, sticky="nsew") # added sticky
self.signs.grid(column=1, row=0, sticky="nsew") # added sticky
self.numbers.rowconfigure("all", weight=1) # use all available vertical space
self.window.mainloop()
calculator = calculate()
calculator.buttons()
calculator.packing()
I'm a beginner in python GUI using tkinter. I'm searching for Sudoku puzzle code so I found one (code given below) its working fine but I'm not able to add more difficulty level such as very easy, easy, medium, hard, very hard in it. Please help me!
import random
import time
import os
import tkinter.tix
import pickle
from tkinter import *
from tkinter.constants import *
from tkinter.tix import FileSelectBox, Tk
random.seed(time.time())
# There are probably a few bugs in this class, and it could be implemented
# better I think.
class SudokuBoard:
"""
Data structure representing the board of a Sudoku game.
"""
def __init__(self):
self.clear()
def clear(self):
"""
Empty the board.
"""
self.grid = [[0 for x in range(9)] for y in range(9)]
self.locked = []
def get_row(self, row):
return self.grid[row]
def get_cols(self, col):
return [y[col] for y in self.grid]
def get_nearest_region(self, col, row):
"""
Regions are 3x3 sections of the grid.
"""
def make_index(v):
if v <= 2:
return 0
elif v <= 5:
return 3
else:
return 6
return [y[make_index(col):make_index(col)+3] for y in
self.grid[make_index(row):make_index(row)+3]]
def set(self, col, row, v, lock=False):
if v == self.grid[row][col] or (col, row) in self.locked:
return
for v2 in self.get_row(row):
if v == v2:
raise ValueError()
for v2 in self.get_cols(col):
if v == v2:
raise ValueError()
for y in self.get_nearest_region(col, row):
for x in y:
if v == x:
raise ValueError()
self.grid[row][col] = v
if lock:
self.locked.append((col, row))
def get(self, col, row):
return self.grid[row][col]
def __str__(self):
strings = []
newline_counter = 0
for y in self.grid:
strings.append("%d%d%d %d%d%d %d%d%d" % tuple(y))
newline_counter += 1
if newline_counter == 3:
strings.append('')
newline_counter = 0
return '\n'.join(strings)
def sudogen_1(board):
"""
Algorithm:
Add a random number between 1-9 to each subgrid in the
board, do not add duplicate random numbers.
"""
board.clear()
added = [0]
for y in range(0, 9, 3):
for x in range(0, 9, 3):
if len(added) == 10:
return
i = 0
while i in added:
i = random.randint(1, 9)
try:
board.set(random.randint(x, x+1), random.randint(y, y+1), i, lock=True)
except ValueError:
print("Board rule violation, this shouldn't happen!")
added.append(i)
def rgb(red, green, blue):
"""
Make a tkinter compatible RGB color.
"""
return "#%02x%02x%02x" % (red, green, blue)
class SudokuGUI(Frame):
board_generators = {"SudoGen v1 (Very Easy)":sudogen_1}
board_generator = staticmethod(sudogen_1)
def new_game(self):
self.board.clear()
self.board_generator(self.board)
self.sync_board_and_canvas()
def make_modal_window(self, title):
window = Toplevel()
window.title(title)
window.attributes('-topmost', True)
window.grab_set()
window.focus_force()
return window
def load_game(self):
def _load_game(filename):
with open(filename, 'rb') as f:
board = pickle.load(f)
if not isinstance(board, SudokuBoard):
# TODO: Report bad file
return
self.board = board
self.sync_board_and_canvas()
window.destroy()
window = self.make_modal_window("Load Game")
fbox = FileSelectBox(window, command=_load_game)
fbox.pack()
window.mainloop()
def save_game(self):
def _save_game(filename):
with open(filename, 'wb') as f:
pickle.dump(self.board, f, protocol=2)
window.destroy()
window = self.make_modal_window("Save Game")
fbox = FileSelectBox(window, command=_save_game)
fbox.pack()
window.mainloop()
def query_board(self):
window = self.make_modal_window("Set Board Algorithm")
scroll = Scrollbar(window)
scroll.pack(side='right', fill='y')
listbox = Listbox(window, yscrollcommand=scroll.set)
scroll.config(command=listbox.yview)
bframe = Frame(window)
for s in self.board_generators.keys():
listbox.insert(-1, s)
def do_ok():
self.board_generator = self.board_generators[listbox.get(ACTIVE)]
window.destroy()
def do_cancel():
window.destroy()
cancel = Button(bframe, command=do_cancel, text="Cancel")
cancel.pack(side='right', fill='x')
ok = Button(bframe, command=do_ok, text="Ok")
ok.pack(side='right', fill='x')
listbox.pack(side='top', fill='both', expand='1')
bframe.pack(side='top', fill='x', expand='1')
window.mainloop()
def make_grid(self):
c = Canvas(self, bg=rgb(128,128,128), width='512', height='512')
c.pack(side='top', fill='both', expand='1')
self.rects = [[None for x in range(9)] for y in range(9)]
self.handles = [[None for x in range(9)] for y in range(9)]
rsize = 512/9
guidesize = 512/3
for y in range(9):
for x in range(9):
(xr, yr) = (x*guidesize, y*guidesize)
self.rects[y][x] = c.create_rectangle(xr, yr, xr+guidesize,
yr+guidesize, width=3)
(xr, yr) = (x*rsize, y*rsize)
r = c.create_rectangle(xr, yr, xr+rsize, yr+rsize)
t = c.create_text(xr+rsize/2, yr+rsize/2, text="SUDO",
font="System 15 bold")
self.handles[y][x] = (r, t)
self.canvas = c
self.sync_board_and_canvas()
def sync_board_and_canvas(self):
g = self.board.grid
for y in range(9):
for x in range(9):
if g[y][x] != 0:
self.canvas.itemconfig(self.handles[y][x][1],
text=str(g[y][x]))
else:
self.canvas.itemconfig(self.handles[y][x][1],
text='')
def canvas_click(self, event):
print("Click! (%d,%d)" % (event.x, event.y))
self.canvas.focus_set()
rsize = 512/9
(x,y) = (0, 0)
if event.x > rsize:
x = int(event.x/rsize)
if event.y > rsize:
y = int(event.y/rsize)
print(x,y)
if self.current:
(tx, ty) = self.current
#self.canvas.itemconfig(self.handles[ty][tx][0], fill=rgb(128,128,128))
self.current = (x,y)
# BUG: Changing the color of the background of a tile erases parts of
# the thick gridlines
#self.canvas.itemconfig(self.handles[y][x][0], fill=rgb(255,255,255))
def canvas_key(self, event):
print("Clack! (%s)" % (event.char))
if event.char.isdigit() and int(event.char) > 0 and self.current:
(x,y) = self.current
#self.canvas.itemconfig(self.handles[y][x][0], fill=rgb(128,128,128))
try:
self.board.set(x, y, int(event.char))
self.sync_board_and_canvas()
except ValueError:
# TODO: I'd rather set the erroneous value anyway and simply
# not consider it valid, and perhaps set the text color
# to red.
pass
def __init__(self, master, board):
Frame.__init__(self, master)
if master:
master.title("SudokuGUI")
self.board = board
self.board_generator(board)
bframe = Frame(self)
self.ng = Button(bframe, command=self.new_game, text="New Game")
self.ng.pack(side='left', fill='x', expand='1')
self.sg = Button(bframe, command=self.save_game, text="Save Game")
self.sg.pack(side='left', fill='x', expand='1')
self.lg = Button(bframe, command=self.load_game, text="Load Game")
self.lg.pack(side='left', fill='x', expand='1')
self.query = Button(bframe, command=self.query_board, text="Set Board Algorithm")
self.query.pack(side='left', fill='x', expand='1')
bframe.pack(side='bottom', fill='x', expand='1')
self.make_grid()
self.canvas.bind("<Button-1>", self.canvas_click)
self.canvas.bind("<Key>", self.canvas_key)
self.current = None
self.pack()
if __name__ == '__main__':
board = SudokuBoard()
tk = Tk()
gui = SudokuGUI(tk, board)
gui.mainloop()
I need more difficulty level to add in it such as very easy, easy, medium, hard, very hard.
To do that you would need to go through the entire code and understand what is going on. Only then will you be implement difficulty levels. That would require a good understanding of tkinter.
I personally would recommend to try and create a small app , instead of trying to figure out someone else's code.
This simple app could be a good start:
import tkinter as tk
class App(tk.Frame):
def __init__(self, master):
super().__init__(master)
tk.Label(self, text='Hello', bg="white", fg="black", font = ['Helvetica', 30]).pack()
tk.Button(self, text='Quit', command=self.quit).pack()
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
app.pack()
root.mainloop()
Since you have three mutually exclusive options (easy, medium, hard), this could be a good candidate for "radio buttons." Implementation is described well here. Modifying their example, you could add something like this into your __init__ method.
master = Tk()
game_level = IntVar()
Radiobutton(master, text="Easy", variable=game_level, value=1).pack(anchor=W)
Radiobutton(master, text="Medium", variable=game_level, value=2).pack(anchor=W)
Radiobutton(master, text="Hard", variable=game_level, value=3).pack(anchor=W)
I think there's something fundamental I don't understand about tkinter but having read loads of tutorials and other answers, I still don't get it. I'm trying to organise a simple GUI. It should have a left hand pane with a game board (the game is othello) which has active buttons (I've left out the action function as that works ok), then a right hand pane which has 3 panes, top to bottom: one with radio buttons to toggle between 1 or 2 player, one with the current score, and one with a game evaluation. For now these latter 2 are just lines of text.
I thought I could set up a grid structure in a parent frame, then have 4 frames inside that grid, and then widgets inside them. Here's the code (you can ignore Board class unless you want to run it: the bit I'm struggling with is in Master)
from tkinter import *
from collections import defaultdict
from PIL import Image as PIL_Image, ImageTk
class Master:
def __init__(self):
self.board = Board()
self.display = Tk()
self.f = Frame(self.display, width=1050, height=700)
self.f.grid(row=0, column=0, rowspan=8, columnspan=8)
self.frame2 = Frame(self.f)
self.frame2.grid(row=0, column=8, rowspan=4, columnspan=4)
self.frame3 = Frame(self.f)
self.frame3.grid(row=4, column=8, rowspan=2, columnspan=4)
self.frame4 = Frame(self.f)
self.frame4.grid(row=6, column=8, rowspan=2, columnspan=4)
self.text1 = Text(self.frame3)
self.text1.pack()
self.text2 = Text(self.frame4)
self.text2.pack()
self.square = defaultdict(Button)
self.images = [ImageTk.PhotoImage(PIL_Image.open(f)) for f in ['white.png', 'empty.png', 'black.png']]
modes = [('{} vs {}'.format(i,j), (i, j)) for i in ['human','computer']
for j in ['human', 'computer']]
v = StringVar()
v.set(modes[0][1])
for text, mode in modes:
b = Radiobutton(self.frame2, text=text, variable=v, value=mode, command=lambda mode=mode: self.cp_set(mode))
b.pack(anchor=W)
self.text1.insert(END, 'score')
self.text2.insert(END, 'evaluation')
self.draw_board()
self.display.mainloop()
def draw_board(self):
for i, j in [(x,y) for x in range(8) for y in range(8)]:
self.square[i,j] = Button(self.f, command=lambda i=i, j=j: self.press(i,j), image=self.images[1 + self.board.square[i,j]])
self.square[i,j].image = 1 + self.board.square[i,j]
self.square[i,j].grid(column=i, row=j)
def cp_set(self, m):
self.pb, self.pw = m
return
def press(self, a, b):
# make it do something
return
class Board:
def __init__(self, parent=None):
self.parent = parent
if parent:
self.square = parent.square.copy()
self.black_next = not parent.black_next
self.game_over = parent.game_over
else:
self.square = defaultdict(int)
for square in [(x,y) for x in range(8) for y in range(8)]:
self.square[square] = 0
for square in [(3,3), (4,4)]:
self.square[square] = 1
for square in [(3,4), (4,3)]:
self.square[square] = -1
self.black_next = True
self.game_over = False
This gives a picture in which the top 4 rows of the game board line up with the radiobuttons but the bottom 4 rows get split up, with both text lines sitting in rows of their own, not opposite a row of the game board.
Seeing that this didn't work, I read something about the problem being grid not preserving column and rows between parent and child frames, so I tried a different approach in which the parent frame splits into 2 columns, and then the child frame sits underneath that with its own row/column definitions. But that didn't work either:
self.board = Board()
self.display = Tk()
self.f = Frame(self.display, width=1050, height=700)
self.f.grid(row=0, column=0, rowspan=1, columnspan=2)
self.frame1 = Frame(self.f, width=700, height=700)
self.frame1.grid(row=0, column=0, rowspan=8, columnspan=8)
self.f2 = Frame(self.f)
self.f2.grid(row=0, column=1, rowspan=2, columnspan=1)
self.frame2 = Frame(self.f2, width=350, height=350)
self.frame2.grid(row=0, column=0)
self.frame3 = Frame(self.f2, width=350, height=350)
self.frame3.grid(row=1, column=0)
self.text1 = Text(self.frame2)
self.text1.pack()
self.text2 = Text(self.frame3)
self.text2.pack()
# in this version, Radiobuttons are children of self.frame2,
# and Buttons in draw_board() are children of self.frame1
I really liked this second version until I saw the results in which the board has disappeared altogether. Any pointers would be much appreciated.
You might want to try something like this:
from tkinter import *
from collections import defaultdict
from PIL import Image as PIL_Image, ImageTk
class Master:
def __init__(self):
self.board = Board()
self.display = Tk()
self.left = Frame(self.display)
self.left.grid(row=0, column=0, sticky="new")
self.right = Frame(self.display)
self.right.grid(row=0, column=1)
self.right_top = Frame(self.right)
self.right_top.grid(row=0, column=0, sticky="nsw")
self.right_mid = Frame(self.right)
self.right_mid.grid(row=1, column=0)
self.right_bottom = Frame(self.right)
self.right_bottom.grid(row=2, column=0)
self.text1 = Text(self.right_mid)
self.text1.pack()
self.text2 = Text(self.right_bottom)
self.text2.pack()
self.square = defaultdict(Button)
self.images = [ImageTk.PhotoImage(PIL_Image.open(f)) for f in ['white.png', 'empty.png', 'black.png']]
modes = [('{} vs {}'.format(i,j), (i, j)) for i in ['human','computer'] for j in ['human', 'computer']]
v = StringVar()
v.set(modes[0][1])
for text, mode in modes:
b = Radiobutton(self.right_top, text=text, variable=v, value=mode, command=lambda mode=mode: self.cp_set(mode))
b.pack(anchor=W)
self.text1.insert(END, 'score')
self.text2.insert(END, 'evaluation')
self.draw_board()
self.display.mainloop()
def draw_board(self):
for i, j in [(x,y) for x in range(8) for y in range(8)]:
self.square[i,j] = Button(self.left, command=lambda i=i, j=j: self.press(i,j), image=self.images[1 + self.board.square[i,j]])
self.square[i,j].image = 1 + self.board.square[i,j]
self.square[i,j].grid(column=i, row=j)
The layout of the frames will be like this:
I am trying to write my first app using Tkinter. I can't understand at all how it is possible to pass the data on variables in the 2D array (entered by user) from one class to another. Tried to change something, but nothing turned out. I will be very grateful for any help or advice.
from Tkinter import *
date_index = [2017, 2018, 2019, 2020, 2021]
product_name = ['product 1', 'product 2', 'product 3', 'product 4', 'product 5']
class main:
def __init__(self, master):
self.master = master
self.master.title('revenue calc')
Button(self.master, text = 'quantity', command=self.q_button).pack()
Button(self.master, text = 'prices', command=self.p_button).pack()
self.master.mainloop()
def q_button(self):
q_child(self.master)
def p_button(self):
p_child(self.master)
class q_child:
def __init__(self, master):
self.slave = Toplevel(master)
self.slave.title('quantity')
self.corner_frame = Frame(self.slave)
self.corner_frame.grid(row=0, column=0)
self.left_frame = Frame(self.slave)
self.left_frame.grid(row=1, column=0)
self.head_frame = Frame(self.slave)
self.head_frame.grid(row=0, column=1)
self.main_frame = Frame(self.slave)
self.main_frame.grid(row=1, column=1)
self.button_frame = Frame(self.slave)
self.button_frame.grid(row=2, column=1)
for i in range(len(product_name)):
self.testlabel = Label(self.left_frame, text = product_name[i])
self.testlabel.grid(row=i, column=0)
for j in range(len(date_index)):
self.testlabel1 = Label(self.head_frame, width = 5, text = date_index[j])
self.testlabel1.grid(row=0, column=j)
self.q0 = []
for j in range(len(date_index)):
self.q0.append([])
for i in range(len(product_name)):
self.q0[j].append(Entry(self.slave, width = 5, text=""))
self.q0[j][i].grid(row=j, column=i, in_ = self.main_frame)
self.save_q_button = Button(self.button_frame, text = 'save', command = self.save_q_data)
self.save_q_button.pack()
def save_q_data(self):
self.q = []
for j in range(len(date_index)):
self.q.append([])
for i in range(len(product_name)):
self.q[j].append(float(self.q0[j][i].get()))
class p_child:
def __init__(self, master):
self.slave = Toplevel(master)
self.slave.title('prices')
self.corner_frame = Frame(self.slave)
self.corner_frame.grid(row=0, column=0)
self.left_frame = Frame(self.slave)
self.left_frame.grid(row=1, column=0)
self.head_frame = Frame(self.slave)
self.head_frame.grid(row=0, column=1)
self.main_frame = Frame(self.slave)
self.main_frame.grid(row=1, column=1)
self.button_frame = Frame(self.slave)
self.button_frame.grid(row=2, column=1)
for i in range(len(product_name)):
self.testlabel = Label(self.left_frame, text = product_name[i])
self.testlabel.grid(row=i, column=0)
for j in range(len(date_index)):
self.testlabel1 = Label(self.head_frame, width = 5, text = date_index[j])
self.testlabel1.grid(row=0, column=j)
self.p0 = []
for j in range(len(date_index)):
self.p0.append([])
for i in range(len(product_name)):
self.p0[j].append(Entry(self.slave, width = 5, text=""))
self.p0[j][i].grid(row=j, column=i, in_ = self.main_frame)
self.save_p_button = Button(self.button_frame, text = 'save', command = self.save_p_data)
self.save_p_button.pack()
def save_p_data(self):
self.rev = []
self.revall = []
self.p = []
for j in range(len(date_index)):
self.rev.append([])
self.p.append([])
self.s = 0
for i in range(len(product_name)):
self.p[j].append(float(self.p0[j][i].get()))
self.rev[j].append(self.p[j][i]*q[j][i]) # NameError: global name 'q' is not defined
self.s += self.rev[j][i]
self.revall.append(self.s)
root = Tk()
main(root)
See below a simplified version of your code which shows how to pass data (in this case the text of a single Entry box) from your TopLevel() window back to your main window.
Basically, in your q_child class, you store the data you want to return in an attribute called, for example, self.data, so that when you return to the main class, you can access it by calling q.data.
You can even store this data in the main window's master attribute under a name like q_data, so that it can be accessed in the p_child class, through master.q_data
import Tkinter as tk
class main:
def __init__(self, master):
self.master = master
self.master.q_data = "No data entered"
tk.Button(self.master, text='quantity', command=self.q_button).pack()
tk.Button(self.master, text='prices', command=self.p_button).pack()
self.master.mainloop()
def q_button(self):
# Create a TopLevel window to get user input
q = q_child(self.master)
# Wait for the user to close the TopLevel window
self.master.wait_window(q.slave)
# Store the data input by the user in the main window's "master" attribute
self.master.q_data = q.data
def p_button(self):
# Create a TopLevel window to use the user input data
p = p_child(self.master)
# Wait for the user to close the TopLevel window
self.master.wait_window(p.slave)
class q_child:
def __init__(self, master):
# Create a TopLevel window, and grab focus
self.slave = tk.Toplevel(master)
self.slave.grab_set()
# Add an Entry box and a button
self.q_entry = tk.Entry(self.slave, text="")
self.q_entry.pack()
tk.Button(self.slave, text='save', command=self.save_q_data).pack()
# Initialize the data to be returned
self.data = "No data entered"
def save_q_data(self):
# Update the data to be returned with the Entry box content
self.data = self.q_entry.get()
# Close the TopLevel window
self.slave.destroy()
class p_child:
def __init__(self, master):
# Create a TopLevel window, and grab focus
self.slave = tk.Toplevel(master)
self.slave.grab_set()
# Retrieve the user-input data from the "master"
q_data = master.q_data
# Show the data on a label
tk.Label(self.slave, text=q_data).pack()
# Add a button to go back
tk.Button(self.slave, text='back', command=self.slave.destroy).pack()
root = tk.Tk()
main(root)
Important: self.master.wait_window(q.slave) ensures that the main class waits for the TopLevel window to be closed before continuing to run.