Despite using the config() method, my canvas's background color won't change. I've made sure the if statement is correct by using some print statements, I've done some research and this is the only way to change the color of an existing canvas-
google search results for how to change the canvas background color
screenshot of program when executed (the canvas is the white thing with the question text, the score is a label, the check mark and X buttons are buttons, and I've used the grid() methods to make them display)
In addition, after browsing some old questions, one of the potential causes for this was that a new canvas was being created after each iteration, and since I've defined the cavas in the innit() method, this isn't the case.
So what exactly should I do?
QuizBrain Class-
import html
class QuizBrain:
def __init__(self, q_list):
self.question_number = 0
self.score = 0
self.question_list = q_list
self.current_question = None
def still_has_questions(self):
return self.question_number < len(self.question_list)
def next_question(self):
self.current_question = self.question_list[self.question_number]
self.question_number += 1
q_text = html.unescape(self.current_question.text)
return f"Q.{self.question_number}: {q_text}"
def check_answer(self, user_answer):
correct_answer = self.current_question.answer
if user_answer.lower() == correct_answer.lower():
self.score += 1
return True
else:
return False
QuizInterface Class- look at the def give_feedback(self, is_right: bool) method, it's responsible for changing the canvas background which tells the user if they got it right(green) or wrong(red). I've also shared the other classes (above and below) for context and incase the problem is there.
from tkinter import *
from quiz_brain import QuizBrain
THEME_COLOR = "#375362"
class QuizInterface:
def __init__(self, quiz_brain: QuizBrain):
self.quiz = quiz_brain
self.window = Tk()
self.window.config(background=THEME_COLOR, padx=20, pady=20)
self.window.title("Quiz")
self.score_label = Label(text="score: 0", font=("Arial", 20, "italic"), padx=20, pady=20, bg=THEME_COLOR,
fg="white")
self.score_label.grid(row=0, column=1)
self.canvas = Canvas(width=300, height=250, background="white")
self.question_text = self.canvas.create_text(150, 125, text="SAMPLE",
font=("Arial", 20, "italic"), fill="black", width=250)
self.canvas.grid(column=0, row=1, columnspan=2, pady=40)
true_image = PhotoImage(file="images/true.png")
false_image = PhotoImage(file="images/false.png")
self.true_button = Button(image=true_image, command=self.true_pressed)
self.true_button.grid(row=2, column=0)
self.false_button = Button(image=false_image, command=self.false_pressed)
self.false_button.grid(row=2, column=1)
self.get_next_question()
self.window.mainloop()
def get_next_question(self):
question_text = self.quiz.next_question()
self.canvas.itemconfig(self.question_text, text=question_text)
def true_pressed(self):
is_right = self.quiz.check_answer("True")
self.give_feedback(is_right)
def false_pressed(self):
is_right = self.quiz.check_answer("False")
self.give_feedback(is_right)
def give_feedback(self, is_right: bool):
print("Called")
if is_right:
print("Called-2")
self.canvas.configure(bg="green")
print("Executed")
elif not is_right:
print("called-3")
self.canvas.configure(bg="red")
print("Executed")
self.window.after(3000, self.get_next_question)
self.canvas.config(background="white")
Question Class-
class Question:
def __init__(self, q_text, q_answer):
self.text = q_text
self.answer = q_answer
How I get my questions-
import requests
parameters = {
"amount": 10,
"type": "boolean"
}
quiz_data = requests.get(url="https://opentdb.com/api.php", params=parameters)
quiz_data.raise_for_status()
quiz_questions = quiz_data.json()
question_data = quiz_questions["results"]
Main.py-
from question_model import Question
from data import question_data
from quiz_brain import QuizBrain
from ui import QuizInterface
question_bank = []
for question in question_data:
question_text = question["question"]
question_answer = question["correct_answer"]
new_question = Question(question_text, question_answer)
question_bank.append(new_question)
quiz = QuizBrain(question_bank)
quiz_interface = QuizInterface(quiz)
#
# while quiz.still_has_questions():
# quiz.next_question()
#
# print("You've completed the quiz")
# print(f"Your final score was: {quiz.score}/{quiz.question_number}")
Move the line
self.canvas.config(background="white")
from give_feedback function to get_next_question function.
Related
Hello i try to write a desktop app with tkinter for typing speed test i search but i cant find my answer is the any way to check the value of user entry in Textbox while app is running ? i need to compare user entry with test text i try to put the function before main loop when i init class but its just check the entry one time and also i try to use window.after()
this is my full code and problem is running last function
def run_test():
random_number = random.randint(1, 5)
test = session.query(data).filter(database.Database.id == random_number).first()
return test
class UserInterFace:
def __init__(self):
customtkinter.set_appearance_mode("dark")
customtkinter.set_default_color_theme("green")
self.window = CTk()
self.window.title("Typing Speed Test Application")
self.window.resizable(False, False)
self.window.config(padx=50, pady=50)
self.window.geometry("1000x700")
self.test = run_test()
self.wrong_entry = 0
self.correct_entry = 0
self.test_running = True
self.start_count = 5
self.start_test_btn = None
self.timer = None
self.start_test_time = None
self.end_test_time = None
self.user_input = []
self.get_ready_label = CTkLabel(master=self.window,
font=timer_font,
text="Get ready Test will be start in :",
text_color="#609EA2")
self.count_down_label = CTkLabel(master=self.window,
font=count_down_font,
text=str(self.start_count),
text_color="#820000")
self.label_test = CTkLabel(master=self.window,
font=my_font2,
text="",
wraplength=900,
)
self.best_score_record = CTkLabel(master=self.window,
font=my_font1,
text_color="#609EA2",
text="")
self.type_area = CTkTextbox(master=self.window,
width=900,
height=200)
self.main()
self.tset_user_text(self.test.text, self.user_input)
self.window.mainloop()
def start_test(self, test):
self.start_test_time = time.time()
self.get_ready_label.grid_forget()
self.count_down_label.grid_forget()
test_id = test.id
test_text = test.text
test_record = test.record
self.label_test.configure(text=test_text)
if test_record == 0:
test_record = "No Record Yet"
else:
test_record = f"Best Record : {test_record}"
self.best_score_record.configure(text=test_record)
self.best_score_record.grid(row=0, column=0)
self.label_test.grid(row=1, column=0, pady=(20, 0))
self.type_area.grid(row=2, column=0, pady=(20, 0))
self.type_area.focus_set()
self.user_input = self.type_area.get("1.0", END)
def main(self):
self.start_test_btn = CTkButton(master=self.window,
text="Start Test",
command=lambda: self.start_time(),
height=100,
width=300,
font=my_font1)
self.start_test_btn.grid(row=0, column=0, padx=(300, 0), pady=(250, 0))
def start_time(self):
if self.start_count >= 0:
self.start_test_btn.grid_forget()
self.get_ready_label.grid(row=0, column=0, pady=(50, 100), padx=(200, 0))
self.count_down_label.grid(row=1, column=0, padx=(200, 0))
self.count_down_label.configure(text=self.start_count)
self.start_count -= 1
self.timer = self.window.after(1000, self.start_time)
else:
self.window.after_cancel(self.timer)
self.start_test(self.test)
def tset_user_text(self, test_text, user_text):
print("test")
test_text_list = list(test_text)
user_text_list = list(user_text)
if len(test_text_list) <= len(user_text_list):
self.test_running = False
self.end_test_time = time.time()
for n in range(len(test_text_list)):
if test_text_list[n] == user_text_list[n]:
self.correct_entry += 1
else:
self.wrong_entry += 1
Okay, so take this with a grain of salt because I can't test this on CustomTkinter, but you should be able to bind an event to your textbox like so:
# call a method called 'on_type' whenever a key is released in the text area
self.type_area.bind('<KeyRelease>', self.on_type)
You can define a callback method to handle this event. Thanks to the binding above, the event parameter is passed to your callback automatically when the event is triggered
def on_type(self, event):
print(event) # do whatever you need to do
FYI: binding to '<KeyRelease>' avoids (some) issues with key repeat caused by held keys
I have a question. I have this code:
import tkinter as tk
class new_f:
def __init__(self,root,num):
self.new_frame=tk.Frame(root,width=100,height=100,bg='white',bd=3,relief=tk.GROOVE)
self.new_frame.pack(side=tk.LEFT,fill=tk.X,expand=True)
self.num=num
def add_label(self,t):
self.l1=tk.Label(self.new_frame,bg='white',text=t)
self.l1.pack()
def return_instance(self):
return self.num
class Main_win:
def __init__(self,root):
self.root=root
self.bind_number=0
self.current_index=0
self.instance_list=[]
self.b1=tk.Button(self.root,text='Add Frame',command=self.add_frame_win)
self.b1.pack(side=tk.BOTTOM)
self.b2=tk.Button(self.root,text='Add text',command=self.add_text_frame)
self.b2.pack(side=tk.BOTTOM)
def return_instance_num(self,num,*args):
self.current_index=num
def add_frame_win(self):
new_in=new_f(self.root,self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>',lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index=new_in.return_instance()
self.bind_number+=1
def add_text_frame(self):
instance=self.instance_list[self.current_index]
instance.add_label('Hello World')
root=tk.Tk()
ob=Main_win(root)
root.mainloop()
What I a trying to achieve is that I want to detect on which frame was the left mouse-button clicked so as to make that Frame active and add the labels to that particular Frame. However, I am stuck on how would I go about writing the code. I need a new class Because I don't know how many frames will the user need.
This is a short example of the code I will be implementing later. So my question is:
How will I go to detect which frame was picked so as to make it active to add the labels?
In this approach I have label l1 bound to Button-1
This was achieved by passing self to new_f instead of root
and binding self.l1 to Button-1
import tkinter as tk
class new_f:
def __init__(self, prog, num):
self.prog = prog
self.new_frame = tk.Frame(prog.root, width = 100, height = 100, bg = 'white', bd = 3, relief = tk.GROOVE)
self.new_frame.pack(side = tk.LEFT, fill = tk.X, expand = True)
self.num = num
def add_label(self, t):
self.l1 = tk.Label(self.new_frame, bg = 'white', text = t)
self.l1.pack()
# binding button-1 press to label
self.l1.bind("<Button-1>", lambda evnt: self.prog.return_instance_num(self.return_instance()))
def return_instance(self):
return self.num
class Main_win:
def __init__(self, root):
self.root = root
self.bind_number = 0
self.current_index = 0
self.instance_list = []
self.b1 = tk.Button(self.root, text = 'Add Frame', command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(self.root, text = 'Add text', command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def return_instance_num(self, num, *args):
self.current_index = num
def add_frame_win(self):
# note passing self not root
new_in = new_f(self, self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>', lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index = new_in.return_instance()
self.bind_number = self.bind_number + 1
def add_text_frame(self):
instance = self.instance_list[self.current_index]
instance.add_label('Hello World')
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
Here is an alternative method that uses dictionaries to store l1 and new_frame objects as keys and new_f instances as values.
This method can be used for other tkinter objects (Entry, Listbox, Text, Canvas)
import tkinter as tk
class new_f:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(
parent.root, width = 100, height = 100,
bg = "white", bd = 3, relief = tk.GROOVE)
self.frame.pack(
side = tk.LEFT, fill = tk.X, expand = True)
self.frame.bind("<Button-1>", parent.get_current_frame)
def add_label(self, t):
self.label = tk.Label(self.frame, bg = "white", text = t)
self.label.pack(fill = tk.BOTH, expand = True)
# bind button-1 to label, set instance_label and current to self
self.label.bind("<Button-1>", self.parent.get_current_label)
self.parent.instance_label[self.label] = self.parent.current = self
class Main_win:
instance_label = dict() # This method can be expanded for other objects
instance_frame = dict() # that you may want to create in frames
def __init__(self, root):
self.root = root
self.b1 = tk.Button(
self.root, text = "Add Frame", command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(
self.root, text = "Add text", command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def get_current_label(self, ev):
self.current = self.instance_label[ev.widget]
def get_current_frame(self, ev):
self.current = self.instance_frame[ev.widget]
def add_frame_win(self):
# note passing self not root
self.new_in = new_f(self)
self.instance_frame[self.new_in.frame] = self.current = self.new_in
def add_text_frame(self):
# Change message with entry tool?
self.current.add_label("Hello World")
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
I have a Python Tkinter Windows program with many buttons. I need a button to change its background color forth and back when the pointer is on it and off it. This issue has been dicussed here before, and I tried to use the code snippets given to solve my problem but did not succeed. The best solution for me would be such that the method would be on such a level that it is needed only once. In my program the user can define the background color for the buttons, however similar to all, and the pointer-on color should be able to be affected by the choice.
Below a minimal code where I have tried to use bind. randint simulates user choice. But, as said, it does not work. What changes do I require? I am new with Python and Tkinter, so please give your answer as clear changes to the code below.
import tkinter as tk
from random import randint
class PointerOnOff:
def __init__ (self, root):
root.geometry ("200x140+100+100")
color = randint (0, 2)
if color == 0:
off_color = "#aaffaa"
on_color = "#99ff99"
elif color == 1:
off_color = "#ffffaa"
on_color = "#ffff99"
else:
off_color = "#ffaaaa"
on_color = "#ff9999"
self.OK = tk.Button (root, text = "OK", bg = off_color, command = self.OKPush)
self.OK.place (x = 50, y = 20, width = 100, height = 30)
self.Cancel = tk.Button (root, text = "Cancel", bg = off_color, command = self.CancelPush)
self.Cancel.place (x = 50, y = 60, width = 100, height = 30)
self.PushedButton = tk.Label (root, text = "")
self.PushedButton.place (x = 20, y = 100, width = 160, height = 30)
def on_enter (anybutton):
anybutton.widget.config (bg = on_color)
def on_leave (anybutton):
anybutton.widget.config (bg = off_color)
self.OK.bind("<Enter>", on_enter)
self.OK.bind("<Leave>", on_leave)
self.Cancel.bind("<Enter>", on_enter)
self.Cancel.bind("<Leave>", on_leave)
def OKPush (self):
self.PushedButton.config (text = "You pushed OK button")
def CancelPush (self):
self.PushedButton.config (text = "You pushed Cancel button")
root = tk.Tk ()
master = PointerOnOff (root)
root.mainloop ()
I'm python beginner and I'm not sure of my answer but the code for changing the background color is this in my opinion:
from tkinter import *
from random import randint
t = Tk()
t.geometry('200x200')
def change_bg():
color = ("#" + str(randint(100000, 999999)))
f = Frame(t, bg=color)
f.place(x=0, y=0, width=200, height=200)
The issue is due to incorrect indentation of the two functions: on_enter() and on_leave(). They need to be inner functions inside __init__():
class PointerOnOff:
def __init__ (self, root):
...
self.PushedButton = tk.Label (root, text = "")
self.PushedButton.place (x = 20, y = 100, width = 160, height = 30)
def on_enter (anybutton):
anybutton.widget.config (bg = on_color)
def on_leave (anybutton):
anybutton.widget.config (bg = off_color)
self.OK.bind("<Enter>", on_enter)
self.OK.bind("<Leave>", on_leave)
self.Cancel.bind("<Enter>", on_enter)
self.Cancel.bind("<Leave>", on_leave)
If you don't want to call the two bindings for every button, you better create a custom button class to embed the hover feature:
class HoverButton(tk.Button):
_colors = [
# off # on
('#aaffaa', '#99ff99'),
('#ffffaa', '#ffff99'),
('#ffaaaa', '#ff9999'),
]
def __init__(self, master=None, *args, **kw):
# if "colors" option not provided, use random choice from internal colors
self._off_color, self._on_color = kw.pop("colors", self._colors[randint(0, 2)])
super().__init__(master, *args, **kw)
self["bg"] = self._off_color
self.bind("<Enter>", lambda e: self.config(bg=self._on_color))
self.bind("<Leave>", lambda e: self.config(bg=self._off_color))
Then use this custom button class for those buttons you want to have hover effect:
def class PointOnOff:
def __init___(self, root):
...
self.OK = HoverButton(root, text="OK", colors=("orange", "gold"), command=self.OKPush)
self.OK.place(x=50, y=20, width=100, height=30)
self.Cancel = HoverButton(root, text="Cancel", command=self.CancelPush)
self.Cancel.place(x=50, y=60, width=100, height=30)
...
I'm aware that this is a question that has been asked before on this site. However, I've made an honest attempt to implement the solutions put forward in those answers, and I'm still running into the same problem: Python seems to keep garbage-collecting my image, and I get an empty slot in my window where the image ought to be, as shown in the attached screenshot.
This is the part of my code where I attempt to import an image:
def make_top_title(self):
result = Frame(self.top)
text_lbl = Label(result, text="Proserpine",
font=(main_font, title_pt, "bold"))
arms_image = PhotoImage("icon.png")
arms_lbl = Label(result, image=arms_image, height=200)
arms_lbl.image = arms_image
arms_lbl.pack()
text_lbl.pack()
return result
You'll notice that I've already attempted to use the trick of preserving the image by anchoring it onto a property of a label object. You may also notice, from the attached screenshot, that I have no problem importing a custom icon - using the same image file - for this window.
I'm happy to share any more code from this program, as required.
A couple of people have suggested I share a bit more of my code. This is the whole file in which the code above is located:
### This code holds a class which allows the user to inspect a the coldstore
### object attached to its parent.
# GUI imports.
from tkinter import *
# Custom imports.
from sibstructures import data_storage
from sibstructures.numerals import index_to_label_column, \
index_to_label_row, \
index_to_label_layer
# Imports
import time
from threading import Thread
# Local constants.
main_font = "Arial"
title_pt = 60
subtitle_pt = 30
big_pt = 20
standard_pt = 15
diddy_pt = 10
standard_pad = 10
inner_pad = 5
tile_width = 15
details_height = 10
details_width = 30
grid_border = 5
spot_stack_width = tile_width+5
##############
# MAIN CLASS #
##############
# The class in question.
class CS_Viewer:
def __init__(self, parent):
self.parent = parent
self.parent_window = self.parent.get_top()
self.code = self.parent.coldstore.code
self.parent.coldstore.reconstruct()
self.max_max_layers = self.parent.coldstore.get_max_max_layers()
self.layer = 0
self.top = Frame(self.parent_window)
self.top_title = self.make_top_title()
self.subtitle = Label(self.top, text="code="+self.code,
font=(main_font, subtitle_pt, "bold"))
self.main_container = Frame(self.top)
self.spot_grid = Spot_Grid(self, self.main_container)
self.box_details = Text(self.main_container,
height=details_height, width=details_width,
state=DISABLED)
self.spot_stack = Spot_Stack(self.main_container, None,
self.box_details)
self.add_headings()
self.arrange()
# Ronseal.
def make_top_title(self):
result = Frame(self.top)
text_lbl = Label(result, text="Proserpine",
font=(main_font, title_pt, "bold"))
arms_image = PhotoImage("icon.png")
arms_lbl = Label(result, image=arms_image, height=200)
arms_lbl.image = arms_image
arms_lbl.pack()
text_lbl.pack()
return result
# Add headings to the holster widgets.
def add_headings(self):
spot_grid_label = Label(self.main_container, text="Coldstore",
font=(main_font, big_pt, "bold"))
spot_stack_label = Label(self.main_container, text="Spot",
font=(main_font, big_pt, "bold"),
width=spot_stack_width)
box_details_label = Label(self.main_container, text="Box",
font=(main_font, big_pt, "bold"))
spot_grid_label.grid(column=0, row=0)
spot_stack_label.grid(column=1, row=0)
box_details_label.grid(column=2, row=0)
# Ronseal.
def place_spot_stack(self):
self.spot_stack.get_top().grid(column=1, row=1,
padx=standard_pad, pady=standard_pad)
# Arrange the object's elements.
def arrange(self):
self.top_title.pack()
self.subtitle.pack()
self.spot_grid.get_top().grid(column=0, row=1, sticky=N,
padx=standard_pad, pady=standard_pad,
ipadx=inner_pad, ipady=inner_pad)
self.place_spot_stack()
self.box_details.grid(column=2, row=1, sticky=N,
padx=standard_pad, pady=standard_pad)
self.main_container.pack()
# Replace the spot stack widget.
def replace_spot_stack(self):
self.spot_stack.get_top().grid_forget()
self.place_spot_stack()
# Ronseal.
def get_top(self):
return self.top
################################
# HELPER CLASSES AND FUNCTIONS #
################################
# A class which holds the grid of spots.
class Spot_Grid:
def __init__(self, parent, parent_window):
self.parent = parent
self.parent_window = parent_window
self.top = Frame(self.parent_window, borderwidth=grid_border,
relief="solid")
Thread(target=self.make_grid).start()
# Fill the grid with boxes.
def make_grid(self):
cs = self.parent.parent.coldstore
for i in range(len(cs.columns)):
column_label = Label(self.top, text=str(index_to_label_column(i)),
font=(main_font, big_pt, "bold"))
column_label.grid(column=(i+1), row=0, padx=standard_pad)
for j in range(len(cs.columns[0].spots)):
if i == 0:
row_label = Label(self.top, text=str(index_to_label_row(j)),
font=(main_font, big_pt, "bold"))
row_label.grid(column=0, row=(j+1), padx=standard_pad)
tile = Spot_Tile(self, self.parent, cs.columns[i].spots[j],
self.parent.box_details)
tile.get_top().grid(column=(i+1), row=(j+1))
# Ronseal.
def get_top(self):
return self.top
# A class which holds a clickable representation of a spot.
class Spot_Tile:
def __init__(self, parent, main_ref, spot_obj, box_details_ref):
self.parent = parent
self.main_ref = main_ref
self.spot_obj = spot_obj
self.box_details_ref = box_details_ref
self.parent_window = self.parent.get_top()
self.top = Frame(self.parent_window)
Thread(target=self.make_filling).start()
# Fill the object with either a tile or a label.
def make_filling(self):
if self.spot_obj.max_layers == 0:
filling = Label(self.top, text="VOID", font=(main_font, diddy_pt),
width=tile_width)
elif self.spot_obj.layers() == 0:
filling = Button(self.top, text="free", command=None,
font=(main_font, diddy_pt, "italic"),
width=tile_width, state=DISABLED)
else:
filling = self.make_filling_button()
filling.pack()
# Make the filling object if it is a button.
def make_filling_button(self):
result = Button(self.top, text=self.make_filling_button_text(),
command=self.inspect,
font=(main_font, diddy_pt), width=tile_width)
return result
# Make the text portion of the filling button.
def make_filling_button_text(self):
growers = set()
varieties = set()
fields = set()
for box in self.spot_obj.boxes:
current_epc = box.epc
current_data = data_storage.fetch_most_recent_crop(current_epc)
growers.add(current_data["grower"])
varieties.add(current_data["variety"])
fields.add(current_data["field"])
result = (set_to_string(growers)+"\n"+set_to_string(varieties)+"\n"+
set_to_string(fields)+"\n"+str(len(self.spot_obj.boxes)))
return result
# Inspect a given spot.
def inspect(self):
self.main_ref.spot_stack = Spot_Stack(self.main_ref.main_container,
self.spot_obj,
self.box_details_ref)
self.main_ref.replace_spot_stack()
# Ronseal.
def get_top(self):
return self.top
# A class which holds a representation of the boxes on a given spot.
class Spot_Stack:
def __init__(self, parent_window, spot_obj, box_details_ref):
self.parent_window = parent_window
self.spot_obj = spot_obj
self.box_details_ref = box_details_ref
self.top = Frame(self.parent_window)
if self.spot_obj is None:
self.fill_empty()
else:
Thread(target=self.add_boxes).start()
# "Fill in" the representation if the spot object is empty.
def fill_empty(self):
label = Label(self.top, text="Select spot",
font=(main_font, standard_pt, "italic"), width=tile_width)
label.pack()
# Add representations of the spot's boxes.
def add_boxes(self):
no_of_boxes = len(self.spot_obj.boxes)
if no_of_boxes == 0:
empty_label = Label(self.top, text="Empty spot",
font=(main_font, standard_pt, "italic"),
width=tile_width)
empty_label.pack()
else:
for i in range(no_of_boxes):
backwards_index = (no_of_boxes-1)-i
box_tile = Box_Tile(self.top, self.spot_obj.boxes[backwards_index],
backwards_index, self.box_details_ref)
box_tile.get_top().pack()
# Ronseal.
def get_top(self):
return self.top
# A class which holds a clickable representation of a box.
class Box_Tile:
def __init__(self, parent_window, box, index, box_details_ref):
self.parent_window = parent_window
self.box = box
self.index = index
self.box_details_ref = box_details_ref
self.top = Frame(self.parent_window)
self.make_filling()
# Fill the object with either a tile or a label.
def make_filling(self):
label = Label(self.top, text=str(index_to_label_layer(self.index)),
font=(main_font, standard_pt))
filling = Button(self.top, text=self.box.epc, command=self.inspect,
font=(main_font, standard_pt), width=tile_width)
label.grid(column=0, row=0, padx=standard_pad)
filling.grid(column=1, row=0)
# Ronseal.
def get_top(self):
return self.top
# Inspect the data for this particular box in more detail.
def inspect(self):
text_to_insert = data_storage.fetch_most_recent_crop(self.box.epc)
self.box_details_ref.config(state=NORMAL)
self.box_details_ref.delete("1.0", END)
self.box_details_ref.insert(END, text_to_insert)
self.box_details_ref.config(state=DISABLED)
# Turns a set into a string, with items thereof separated by commas.
def set_to_string(the_set):
result = ""
the_list = list(the_set)
# Reversal is necessary, since .add() seems to add items to the FRONT of
# the set.
the_list.reverse()
for item in the_list:
if the_list.index(item) == 0:
result = item
else:
result = result+", "+item
return result
This is the other file, which, with its fellow, makes up the whole program:
### This code holds a class which manages transitions between windows, and
### also oversees their interactions with the Coldstore object.
# Imports.
from pathlib import Path
# GUI imports.
from tkinter import *
# Custom imports.
from sibstructures.coldstore import Coldstore
# Local imports.
from cs_viewer import CS_Viewer
# Constants.
path_to_db = str(Path.home())+"/cseye/source/proserpine/data.db"
##############
# MAIN CLASS #
##############
# The class in question.
class Comptroller:
def __init__(self):
self.coldstore = Coldstore(proserpine_mode=True,
proserpine_path=path_to_db)
self.gui = Tk()
self.top = Frame(self.gui)
self.window = CS_Viewer(self)
self.arrange()
# Return the top-level GUI object.
def get_top(self):
return self.top
# Arrange the widgets.
def arrange(self):
self.window.get_top().pack()
self.top.pack()
# Run the "mainloop" method on the GUI object.
def run_me(self):
self.gui.title("Proserpine")
self.gui.iconphoto(True, PhotoImage(file="icon.png"))
self.gui.mainloop()
###################
# RUN AND WRAP UP #
###################
def run():
comptroller = Comptroller()
comptroller.run_me()
if __name__ == "__main__":
run()
The argument to PhotoImage("...") is wrong. It should be PhotoImage(file="...").
I I have 2 functions and the second function should run after the button in the first one has been clicked. This works fine, however I need the number that has been entered to go into a variable and so far the .get() function is not working and im not sure what to do.
I have looked at a lot of different examples, including login and sign up gui's however none of them have been able to help.
from tkinter import *
import tkinter.messagebox as box
def enter_rle_1():
enter_rle = Tk() #do not remove
enter_rle.title('Enter RLE') #do not remove
frame = Frame(enter_rle) #do not remove
label_linecount = Label(enter_rle,text = 'Linecount:')
label_linecount.pack(padx=15,pady= 5)
linecount = Entry(enter_rle,bd =5)
linecount.pack(padx=15, pady=5)
ok_button = Button(enter_rle, text="Next", command = linecount_button_clicked)
ok_button.pack(side = RIGHT , padx =5)
frame.pack(padx=100,pady = 19)
enter_rle.mainloop()
def linecount_button_clicked():
Linecount = linecount.get()
if int(Linecount) < 3:
tm.showinfo("Error", "Enter a number over 3")
elif int(Linecount) > 1000000000:
tm.showinfo("Error", "Enter a number under 1,000,000,000")
else:
print("fdsfd")
enter_rle_1()
I expect there to be a popup telling me the number is too big or too small, depending on wether the number is or not, and if it is a good number, i just have it set as a print as some code to test to see if it works before i move on.
Define a String variable for the Entry widget (Make sure it is global defined):
global linecount_STR
linecount_STR = StringVar()
linecount_STR.set('Enter value here')
linecount = Entry(enter_rle,bd =5,textvariable=linecount_STR)
linecount.pack(padx=15, pady=5)
The number entered there can then be read with int(linecount_STR.get()).
i suggest an OO approach, look this code
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.vcmd = (self.register(self.validate), '%d', '%P', '%S')
self.linecount = tk.IntVar()
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1)
f = ttk.Frame()
ttk.Label(f, text = "Linecount").pack()
self.txTest = ttk.Entry(f,
validate='key',
validatecommand=self.vcmd,
textvariable=self.linecount).pack()
w = ttk.Frame()
ttk.Button(w, text="Next", command=self.on_callback).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
def on_callback(self,):
#print ("self.text = {}".format(self.linecount.get()))
x = self.linecount.get()
if x < 3:
msg = "Enter a number over 3"
elif x > 1000000000:
msg = "Enter a number under 1,000,000,000"
else:
msg = "You ahve enter {0}".format(x)
messagebox.showinfo(self.parent.title(), msg, parent=self)
def on_close(self):
self.parent.on_exit()
def validate(self, action, value, text,):
# action=1 -> insert
if action == '1':
if text in '0123456789':
try:
int(value)
return True
except ValueError:
return False
else:
return False
else:
return True
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_title()
self.set_style()
frame = Main(self,)
frame.pack(fill=tk.BOTH, expand=1)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Enter RLE')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel(self.title(), "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()
notice this line at the begin....
self.linecount = tk.IntVar()# declare an integer variable
self.vcmd = (self.register(self.validate), '%d', '%P', '%S')#register a function to allow only integer in your text widget.