Where do I make a mistake with this tkinter focus? - python

I made a tool to add multiple order numbers in our system. The first time a row of entry cells is placed the focus is where it should be. But the second time the focus is not in the new left cell. First I thought it has to do with using the tab key. But if I understand the code correct, I first execute the moving of the tab key and then execute the code. So the command to focus on the new left cell is last.
Where am I going wrong?
import tkinter as tk
from tkinter import ttk
# Create variables for later use
order_list = []
date_list = []
row_number = 0
active_order_entry = None
active_date_entry = None
def add_a_row_of_entry_cells():
global row_number
global active_order_entry
global active_date_entry
row_number += 1
order_entry = ttk.Entry()
order_entry.grid(row=row_number, column=0)
order_entry.focus()
date_entry = ttk.Entry()
date_entry.grid(row=row_number, column=1)
# Make these entries the active ones
active_order_entry = order_entry
active_date_entry = date_entry
# Add entries to a list
order_list.append(order_entry)
date_list.append(date_entry)
def tab_pressed(event):
if active_order_entry.get() != "" and active_date_entry.get() != "":
add_a_row_of_entry_cells()
else:
print("Order, date or both are not filled yet")
def button_pressed():
print("Button pressed")
# Create window
window = tk.Tk()
# Add function to the Tab key
window.bind("<Tab>", tab_pressed)
# Labels on top of the columns
label_order_number = tk.Label(window, text="Order", fg="#22368C")
label_order_number.grid(row=row_number, column=0)
label_date = tk.Label(window, text="Date", fg="#22368C")
label_date.grid(row=row_number, column=1)
# Create empty row
empty_row = tk.Label(window)
empty_row.grid(row=87, column=0)
# Create button
button = tk.Button(window, text="Add orders", command=lambda: button_pressed())
button.grid(row=98, column=0, columnspan=3)
# Create empty row
empty_row = tk.Label(window)
empty_row.grid(row=99, column=0)
# Add the first row
add_a_row_of_entry_cells()
window.mainloop()

Related

How to display the label ( text) value dynamically based on combo box selection value ( List box) in Tkinter?

I am new to tkinter application. The below code is working fine. Please help how to implement mentioned features.
The dynamic value should be displayed above clear button or below the combo box ( Used pack is bottom )- Now working
Clear the label value on combo box selection.
import tkinter as tk
from tkinter import ttk
from tkinter import *
from datetime import datetime
# root window
root = tk.Tk()
root.geometry("500x350")
root.resizable(False, False)
root.title('Test')
# Log Generator in frame
Generator = tk.Frame(root)
Generator.pack(padx=10, pady=10, fill='x', expand=True)
def clear():
combo.set('')
# Function to print the index of selected option
# in Combobox
def get_log_file_name(*arg):
date_Value = datetime.now().strftime("%Y_%m_%d_%I%M%S")
output_file_name_value = "Log_"+date_Value
if var.get() == "apple":
Label(Generator, text="The value at index: "+output_file_name_value+".txt", font=('Helvetica 12')).pack()
else:
Label(Generator, text="The value at index: "+output_file_name_value+".html", font=('Helvetica 12')).pack()
# Define Tuple of months
months = ('apple','banana')
# Create a Combobox widget
label = ttk.Label(Generator, text="Selection_Option:",font=('Helvetica', 10, 'bold'))
label.pack(fill='x', expand=True)
var = StringVar()
combo = ttk.Combobox(Generator, textvariable=var)
combo['values'] = months
combo['state'] = 'readonly'
combo.pack(padx=5, pady=5)
# Set the tracing for the given variable
var.trace('w', get_log_file_name)
# Create a button to clear the selected combobox
# text value
button = Button(Generator, text="Clear", command=clear)
button.pack(side=left)
# Make infinite loop for displaying app on
# the screen
Generator.mainloop()
Clear the label value on combo box selection.
You need to capture the ComboboxSelect event to do that and the function to execute if captured
the function should be like this
What you want to do here, is to capture the combobox event, and then, do the label configuration when capturing it,
Below is the code to do the thing. and you can add code there.
def comboboxEventCapture(e=None):
label.configure(text='')
# Your code after resetting variables!
Here's the event capturing part
combo.bind("<<ComboboxSelect>>", comboboxEventCapture)
You can name the function whatever you want though.
Note that the arguement e is needed because if the event is captured, the event itself is passed as a parameter into the function, that is of no use here (unless you are going to do something with it, then use e.objname)
The dynamic value should be displayed above clear button
The second label could be outside of get_log_file_name() function.
And also configure inside function. So you don't do duplicate Label widget, naming Label2
Also the pack() must be split to prevent an error.
To clear Label2 use .configure(text='')
We will be using ttk. So don't do this from tkinter import *
Code:
import tkinter as tk
from tkinter import ttk
from datetime import datetime
root = tk.Tk()
root.geometry("500x350")
root.resizable(False, False)
root.title('Test')
Generator = tk.Frame(root)
Generator.pack(padx=10, pady=10, fill='x', expand=True)
def clear():
label2.configure(text='')
def get_log_file_name(*arg):
date_Value = datetime.now().strftime("%Y_%m_%d_%I%M%S")
output_file_name_value = "Log_"+date_Value
if var.get() == "apple":
label2.configure(text="The value at index: "+output_file_name_value+".txt", font=('Helvetica 12'))
else:
label2.configure(text="The value at index: "+output_file_name_value+".html", font=('Helvetica 12'))
# Define Tuple of months
months = ('apple','banana')
# Create a Combobox widget
label2 = ttk.Label(Generator)
label2.pack()
label = ttk.Label(Generator, text="Selection_Option:",font=('Helvetica', 10, 'bold'))
label.pack(fill='x', expand=True)
var = tk.StringVar()
combo = ttk.Combobox(Generator, textvariable=var)
combo['values'] = months
combo['state'] = 'readonly'
combo.pack(padx=5, pady=5)
# Set the tracing for the given variable
var.trace('w', get_log_file_name)
# Create a button to clear the selected combobox
# text value
button = ttk.Button(Generator, text="Clear", command=clear)
button.pack(side='left')
# Make infinite loop for displaying app on
# the screen
Generator.mainloop()
Screenshot for apple:
Screenshot for banana:
Screenshot to clear Label2:

ttk combobox selection synchronize the state of entry

I would like to realize a special case, when i select the value in combobox, the entry state changes correspondingly, e.g. when i select 1 in levels, the entry of level-1 is active, while the entry of level-2 is disabled, can anyone give me some suggestion. when i select 2 in levels, both of the state of entry are enabled.
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import ttk
class MainGui(tk.Tk):
def __init__(self):
super().__init__()
self.create_widgets()
def create_widgets(self):
label_1 = tk.Label(self,
text = "Levels:",
anchor = 'w')
label_1.grid(row=0, column=0)
ComboBox1 = ttk.Combobox(self, width = 10, state = 'readonly')
ComboBox1["values"] = ("1", "2")
ComboBox1.current(0)
ComboBox1.grid(row=0, column=1)
label1 = tk.Label(self,
text = "Level-1:",
anchor = 'w')
label1.grid(row=1, column=0)
entry1 = tk.Entry(self,
width = 10,
)
entry1.grid(row=1, column=1)
label2 = tk.Label(self,
text = "Level-2:",
anchor = 'w')
label2.grid(row=2, column=0)
entry2 = tk.Entry(self,
width = 10,
)
entry2.grid(row=2, column=1)
def main():
# gui
root = MainGui()
root.mainloop()
if __name__ == '__main__':
main()
You can bind the virtual event <<ComboboxSelected>> on ComboBox1 and update the state of Entry2 in the event callback which will be executed whenever the selection of ComboBox1 is changed. But you need to change ComboBox1 and entry2 to instance variables in order to access them inside the event callback:
def create_widgets(self):
...
# changed ComboBox1 to instance variable self.ComboBox1
self.ComboBox1 = ttk.Combobox(self, width = 10, state = 'readonly')
self.ComboBox1["values"] = ("1", "2")
self.ComboBox1.current(0)
self.ComboBox1.grid(row=0, column=1)
self.ComboBox1.bind("<<ComboboxSelected>>", self.on_select)
...
# changed entry2 to instance variable self.entry2
self.entry2 = tk.Entry(self,
width = 10,
state = "disabled" # initially disabled
)
self.entry2.grid(row=2, column=1)
def on_select(self, event=None):
# set state of entry2 based on selected value
self.entry2.config(state="normal" if self.ComboBox1.get()=="2" else "disabled")
Here is a solution (sample):
from tkinter import Tk, Label, Entry, Frame
from tkinter.ttk import Combobox
entry_list = []
def activate_entries(event=None):
to = int(combobox.get())
for entry in entry_list[:to]:
entry.config(state='normal')
for entry in entry_list[to:]:
entry.config(state='disabled')
root = Tk()
values = []
for i in range(5):
(frame := Frame(root)).grid(row=i + 1, column=0)
Label(frame, text=f'Level-{i + 1}').pack()
(e := Entry(frame, state='disabled' if i != 0 else 'normal',
disabledbackground='grey80')).pack()
entry_list.append(e)
values.append(i + 1)
combobox = Combobox(root, values=values, state='readonly')
combobox.current(0)
combobox.grid(row=0, column=0)
combobox.bind('<<ComboboxSelected>>', activate_entries)
root.mainloop()
The main part here is the activate_entries() function, it simply gets the value from combobox and uses a list of the entries to set their state based on their index in the list which is given by the combobox. (Also some parts require a Python version of 3.8 or higher, can be adjusted for older versions too), also note the <<ComboboxSelected>> event that calls the activate_entries() function when user selects something from the combobox.
EDIT: added disabledbackground='grey80' to visually indicate that the entry is disabled
EDIT2: you can obviously set the range to two which will create two entries, their reference is saved in the entry_list so you can access them through that list to get their value for example

How to get values of checkbuttons from for loop tkinter python

I am new to programming in tkinter and am very stuck on using checkbuttons. I have created multiple checkbuttons in one go, all with different text for each one and a different grid position. However I have no idea how to get the value of each button or how to even set it. I want to be able to get the state/value for each button and if it is checked, then another function is called. How do I set and call the value/state of each button? Can this be done in a for loop or do I have to create them individually?
def CheckIfValid(self, window):
Class = self.ClassChosen.get()
Unit = self.UnitChosen.get()
Topic = self.TopicChosen.get()
if Class == '' or Unit == '' or Topic == '':
tm.showinfo("Error", "Please fill in all boxes")
else:
QuestionData = OpenFile()
QuestionsList = []
for x in range (len(QuestionData)):
#if QuestionData[x][2] == Topic:
QuestionsList.append(QuestionData[x][0])
for y in range(len(QuestionsList)):
self.ButtonVal[y] = IntVar()
Checkbutton(window, text = QuestionsList[y], padx = 20, variable = self.ButtonVal[y]).grid(row = 12 + y, column = 2)
ConfirmSelection = Button(window, text = "Set Homework", command = lambda: SetHomeworkClass.ConfirmHomework(self)).grid()
print(variable.get()) #here I would like to be able to get the value of all checkbuttons but don't know how
You use the list of IntVars either called from a command= in the Checkbutton or in the Button. Don't know why you are calling another class's object, SetHomeworkClass.objectConfirmHomework(self). It doesn't look like that will work as you have it programmed, as that is another name space and the list of IntVars is in this name space, but that is another topic for another thread.
try:
import Tkinter as tk # Python2
except ImportError:
import tkinter as tk # Python3
def cb_checked():
# remove text from label
label['text'] = ''
for ctr, int_var in enumerate(cb_intvar):
if int_var.get(): ## IntVar not zero==checked
label['text'] += '%s is checked' % cb_list[ctr] + '\n'
root = tk.Tk()
cb_list = [
'apple',
'orange',
'banana',
'pear',
'apricot'
]
# list of IntVar for each button
cb_intvar = []
for this_row, text in enumerate(cb_list):
cb_intvar.append(tk.IntVar())
tk.Checkbutton(root, text=text, variable=cb_intvar[-1],
command=cb_checked).grid(row=this_row,
column=0, sticky='w')
label = tk.Label(root, width=20)
label.grid(row=20, column=0, sticky='w')
# you can preset check buttons (1=checked, 0=unchecked)
cb_intvar[3].set(1)
# show what is initially checked
cb_checked()
root.mainloop()

Change label text on button using values from tuple click tkinter

I have a list of strings sorted in a tuple like this:
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
My simple code is:
from tkinter import *
root = Tk()
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
ru = Button(root,
text="Next",
)
ru.grid(column=0,row=0)
lab = Label(root,
text=values[0])
lab.grid(column=1,row=0)
ru2 = Button(root,
text="Previous"
)
ru2.grid(column=2,row=0)
root.mainloop()
I have two tkinter buttons "next" and "previous", the text value of the Label is directly taken from the tuple (text=value[0]), however I would want to know how to show the next string from the tuple when the next button is pressed, and how to change it to the previous values when the "previous" button is pressed. I know it can be done using for-loop but I cannot figure out how to implement that. I am new to python.
Use Button(..., command=callback) to assign function which will change text in label lab["text"] = "new text"
callback means function name without ()
You will have to use global inside function to inform function to assign current += 1 to external variable, not search local one.
import tkinter as tk
# --- functions ---
def set_next():
global current
if current < len(values)-1:
current += 1
lab["text"] = values[current]
def set_prev():
global current
if current > 0:
current -= 1
lab["text"] = values[current]
# --- main ---
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
current = 0
root = tk.Tk()
ru = tk.Button(root, text="Next", command=set_next)
ru.grid(column=0, row=0)
lab = tk.Label(root, text=values[current])
lab.grid(column=1, row=0)
ru2 = tk.Button(root, text="Previous", command=set_prev)
ru2.grid(column=2, row=0)
root.mainloop()
BTW: if Next has to show first element after last one
def set_next():
global current
current = (current + 1) % len(values)
lab["text"] = values[current]
def set_prev():
global current
current = (current - 1) % len(values)
lab["text"] = values[current]

In tkinter how do I generate a new row every time a button is pressed?

I've written this code which makes one entry box appear on a new row when the button is pressed, but I'm not sure how to get it to make a new entry on a new row underneath the last one. My main problems are how to increment a row_number variable within the function without re-setting it every time the function is called, and how to make the function give a new name to the "new_entry" object each time it is called so that it doesn't overwrite itself.
def new_row():
#Create widgets
new_entry = ttk.Entry(root, width=7)
#Put widgets in grid
new_entry.grid(column=0, row=2, sticky=(W, E))
root = Tk()
createRow_button = ttk.Button(root, text='New Row', command=new_row)
createRow_button.grid()
root.mainloop
This is the first time I've asked a question on Stack Overflow so sorry if I've got anything wrong.
Use a class and store the number of rows as class attribute, self.num_rows:
import Tkinter as tk
class App(object):
def new_row(self):
# Create widgets
new_entry = tk.Entry(root, width=7)
# Put widgets in grid
self.num_rows += 1
new_entry.grid(column=0, row=self.num_rows, sticky='WE')
def __init__(self):
self.num_rows = 1
createRow_button = tk.Button(
root, text='New Row', command=self.new_row)
createRow_button.grid()
root = tk.Tk()
app = App()
root.mainloop()

Categories