Tkinter textbox not clickable when window first opened - python-3.x

So when I first open the window, the everything else is fine, I can click the buttons and all but when I try to click the textbox and type something inside, the cursor does not appear and I can't type anything.
However, if I switch to any other window(literally anything from chrome to notepad) and switch back, I'm able to normally type inside the textbox.
I'm not sure how to solve this. Could anyone help me? My Python version is 3.8.0.
Here's part of my code and 'top' is my tkinter window's variable name. inputtxt1 and inputtxt2 is the textboxes I am having a problem with.
load_txt.set("analyze")
a_bt = Button(top, text = "analyze", command = _analyze_)
a_bt.config(textvariable=load_txt)
a_bt.grid(row=4, column=0, sticky='E')
dir_bt = Button(top, text = "change dir", command = ch_dir)
dir_bt.grid(row=4, column=1, sticky='W')
save_button['command'] = save_change
save_button.grid(row=5, column=0, sticky='E')
top.geometry("600x1000")
top.resizable(width = True, height = True)
Label(top, text = "start_slab").grid(row=0, column=0, sticky='E')
inputtxt1 = Text(top, height = 1, width = 5, state='normal')
inputtxt1.grid(row=1, column=0, sticky='E')
Label(top, text = "end_slab").grid(row=2, column=0, sticky='E')
inputtxt2 = Text(top, height = 1, width = 5, state='normal')
inputtxt2.grid(row=3, column=0, sticky='E')
avg_bt = Button(top, text = "avg", command = show_avg)
prev_bt = Button(top, text = "prev", command = show_prev)
next_bt = Button(top, text = "next", command = show_next)
#panel.pack()
dir_name = askdirectory()
top.bind("<Return>", analyze)
top.grid_rowconfigure(0, weight=0)
top.mainloop()
Thank you!
===========Edit===========
Here is a short working version of the code
from tkinter.filedialog import askdirectory
from tkinter import Tk, Label, Button, Text
frame_idx = 0
dir_name = None
top = Tk()
save_button = Button(top, text = "save images")
a_bt = Button(top, text = "analyze")
a_bt.grid(row=4, column=0, sticky='E')
dir_bt = Button(top, text = "change dir")
dir_bt.grid(row=4, column=1, sticky='W')
save_button.grid(row=5, column=0, sticky='E')
top.geometry("600x1000")
top.resizable(width = True, height = True)
Label(top, text = "start_slab").grid(row=0, column=0, sticky='E')
inputtxt1 = Text(top, height = 1, width = 5, state='normal')
inputtxt1.grid(row=1, column=0, sticky='E')
Label(top, text = "end_slab").grid(row=2, column=0, sticky='E')
inputtxt2 = Text(top, height = 1, width = 5, state='normal')
inputtxt2.grid(row=3, column=0, sticky='E')
dir_name = askdirectory()
top.grid_rowconfigure(0, weight=0)
top.mainloop()
It seems that when I don't have the askdirectory function, this does not become a problem but if I do, the problem occurs.

Add top.update() or top.update_idletasks() just before dir_name = askdirectory(). I think that the askdirectory interferes with the entries so they stop handling events until they are redrawn on the screen (when you refocus the window). That might be a tcl bug? I have no idea how tcl handles events when askdirectory is called.

Related

All other widgets are affected when one of the widgets move in a GUI (using the tkinter library in python 3)

Whenever I try to move individual widgets or click a button that produces words effectively moving other widgets that had nothing to do those both specified actions above.
Here is my code:
import tkinter as tk
# Create the main window
window = tk.Tk()
window.title("Price Calculator")
window.geometry("800x600")
window.config(background = "#777474")
def calculate_price():
global input_field
input_field_value = input_field.get()
try:
input = int(input_field_value)
price = input* 0.1
answer.config(text = (f"Your price is ","%.3f"%price,"KWD"))
except ValueError as ve:
answer.config(text = 'What you have just entered are not numbers whatsoever, try again!', fg = "#CD5F66")
price_input = tk.Label(window, text = "Total Pages:", font = "Arial", bg = "#777474", fg = "#FEFCF2")
price_input.grid(column = 0, row = 0)
input_field = tk.Entry(window, font = "Arial", bg = "#FEFCF2")
input_field.grid(column = 1, row = 0, padx = 0,pady = 10)
answer = tk.Label(window, bg = "#777474")
answer.grid(pady = 20)
button_return = tk.Button(window, text = "Calculate Price", command = calculate_price).grid()
# Run the main loop
window.mainloop()
This is my GUI before I click on the button which is called "Calculate Price":
This is my GUI after I have clicked on the button:
The problem here is that I don't want the Total Pages and the entry field to move away from each other whenever I click on the button.
I would suggest to put price_input and input_field into a frame for easier layout management. I also use sticky="w" on the frame, answer and button_return so that they are aligned at the left.
Below is the modified code:
import tkinter as tk
FGCOLOR = "#FEFCF2"
BGCOLOR = "#777474"
ERRCOLOR = "#FF5F66"
# Create the main window
window = tk.Tk()
window.title("Price Calculator")
window.geometry("800x600")
window.config(background="#777474")
def calculate_price():
input_field_value = input_field.get()
try:
input = int(input_field_value)
price = input * 0.1
answer.config(text=f"Your price is {price:.3f} KWD", fg=FGCOLOR)
except ValueError as ve:
answer.config(text='What you have just entered are not numbers whatsoever, try again!', fg=ERRCOLOR)
# create a frame for price_input and input_field
frame = tk.Frame(window, bg=BGCOLOR)
frame.grid(column=0, row=0, padx=10, pady=10, sticky="w")
price_input = tk.Label(frame, text="Total Pages:", font="Arial", bg=BGCOLOR, fg=FGCOLOR)
price_input.grid(column=0, row=0)
input_field = tk.Entry(frame, font="Arial", bg=FGCOLOR)
input_field.grid(column=1, row=0)
answer = tk.Label(window, bg=BGCOLOR)
answer.grid(column=0, row=1, padx=10, pady=10, sticky="w")
button_return = tk.Button(window, text="Calculate Price", command=calculate_price)
button_return.grid(column=0, row=2, sticky="w", padx=10, pady=10)
# Run the main loop
window.mainloop()

Tkinter dialog's elements position

I am building custom Tkinter dialog window with Entry and Combobox. I am stuck with placing text and enter frames. Currently I am placing them manually. I am looking for the way to let tkinter do it automatically (maybe with pack() method). And also configure TopLevel size automatically.
My code:
def ask_unit_len():
values = ['millimeters', 'micrometers', 'nanometers']
top = Toplevel()
top.geometry('170x100')
top.resizable(False, False)
top.focus_set()
top.grab_set()
top.title('Enter length and units')
label_length = Label(top, text='Length:')
label_length.place(x=0, y=0)
units_type = StringVar()
length = StringVar()
answer_entry = Entry(top, textvariable=length, width=10)
answer_entry.place(x=55, y=0)
label_units = Label(top, text='Units:')
label_units.place(x=0, y=30)
combo = Combobox(top, width=10, textvariable=units_type,
values=values)
combo.place(x=50, y=30)
button = Button(top, text='Enter',
command=lambda:
mb.showwarning("Warning",
"Enter all parameters correctly")
if (units_type.get() == "" or not length.get().isdigit()
or int(length.get()) <= 0)
else top.destroy())
button.place(x=65, y=70)
top.wait_window(top)
return int(length.get()), units_type.get()
So, is there any way to perform this?

the Label Frame is getting wider that I don't

I am using python 3.11.0a5. How do I get actually LabelFrame to draw actually size in column 1? Unfortunately, I can't figure out to work around columnspan, rowspan, etc Here is pics
Unfortunately, I don't want the LabelFrame to draw wider. Here is pics.
Here is code:
from tkinter import *
from tkinter import ttk
#Create an instance of tkinter frame
win= Tk()
#Define the geometry of the window
win.geometry("400x160")
lblChannelName = ttk.Label(win, text='Channel Name')
lblChannelName.grid(row=0, column=0, sticky='W')
entry_channelname = ttk.Entry(win, width=45)
entry_channelname.grid(row=0, column=1, sticky='W')
lblChannelTopic = ttk.Label(win, text = 'Channel Topic')
lblChannelTopic.grid(row=1, column=0, pady=2, sticky='W')
entry_channelTopic = ttk.Entry(win, width=45)
entry_channelTopic.grid(row=1, column=1, pady=2, sticky='W')
#Initialize a LabelFrame Widget
labelframe = LabelFrame(win, width=400, height=200, bd=5)
labelframe.grid(row=3, padx=2, pady=5, columnspan=18, sticky='WE')
#Checkbutton Invite
invite = IntVar()
ck1 = ttk.Checkbutton(labelframe, text ='Invite Only[+i]', variable=invite)
ck1.grid(row=0, column=0, padx=0, sticky='W')
#Checkbutton Moderated
moderated = IntVar()
ck2 = ttk.Checkbutton(labelframe, text ='Moderated[+m]', variable=moderated)
ck2.grid(row=0, padx=170, columnspan=3, sticky='W')
#Checkbutton Message
message = IntVar()
ck3 = ttk.Checkbutton(labelframe, text ='No Outside Message[+n]', variable=message)
ck3.grid(row=1, column=0, sticky='W')
#Checkbutton Private
private = IntVar()
ck4 = ttk.Checkbutton(labelframe, text ='Private[+p]', variable=private)
ck4.grid(row=1, padx=170, columnspan=3, sticky='W')
#Checkbutton Only ops
topics = IntVar()
ck5 = ttk.Checkbutton(labelframe, text ='Only ops set topics[+t]', variable=topics)
ck5.grid(row=2, column=0, sticky='W')
#Checkbutton Secret Channel
secret = IntVar()
ck6 = ttk.Checkbutton(labelframe, text ='Secret Channel[+s]', variable=secret)
ck6.grid(row=2, padx=170, columnspan=3, sticky='W')
#Checkbutton Keyed
keyed = IntVar()
ck7 = ttk.Checkbutton(labelframe, text ='Channel is keyed[+k]', variable=keyed)
ck7.grid(row=3, column=0, sticky='W')
lblkeyed = ttk.Label(labelframe, text='Key:')
lblkeyed.grid(row=3, padx=135, columnspan=2, sticky='W')
entry_keyed = ttk.Entry(labelframe, width=20)
entry_keyed.grid(row=3, padx=170, columnspan=3, sticky='W')
#Checkbutton limit
limit = IntVar()
ck8 = ttk.Checkbutton(labelframe, text ='Limit channel members[+l]', variable=limit)
ck8.grid(row=4, column=0, sticky='W')
win.mainloop()
How can I fix the Labe Frame in pics 1?
I appreciate your help.
In your code, when you grid labelframe, change the columnspan to 2. Line 24 will look like this:
labelframe.grid(row=3, padx=2, pady=5, columnspan=2, sticky='WE')
That will set your LabelFrame to the right width.
If you want the entries to align with the LabelFrame, you can make them sticky='WE'. Lines 14 and 20 would look like this:
# Line 14
entry_channelname.grid(row=0, column=1, sticky='WE')
# Line 20
entry_channelTopic.grid(row=1, column=1, pady=2, sticky='WE')

Scrollbar for Dynamically Created Widgets - Python Tkinter [duplicate]

This question already has answers here:
Adding a scrollbar to a group of widgets in Tkinter
(3 answers)
Closed 3 years ago.
I have a GUI that dynamically generates widgets based on the user selected number of systems:
GUI
These widgets are generated using a Callback function like the below sample of code:
class Window():
def __init__(self, master):
master.title('Production Analysis Tool')
# callback function to create entry boxes based on number of systems
self.L0 = Label(root, text="Equipment Parameters:", font = ('TKDefaultFont', 9, 'bold'))
self.L0.grid(row=3,column=0, sticky=W)
inverter_file = r'F:\CORP\PROJECTS\07599-A_Solar Development\Screening\_Production Analysis Tool\User Inputs\Inverter_Data.csv'
module_file = r'F:\CORP\PROJECTS\07599-A_Solar Development\Screening\_Production Analysis Tool\User Inputs\Module_Data.csv'
def update_scroll_region(event):
canvas.configure(scrollregion=canvas.bbox("all"))
def callback(*args):
dynamic_widgets = Frame(canvas)
canvas.create_window(0,0, anchor='nw', window = dynamic_widgets)
self.system_size = int(self.system_size_raw.get())
# Inverter Type
self.Lblank = Label(dynamic_widgets, text = "").grid(row=8, column=1, sticky=W)
self.L3 = Label(dynamic_widgets, text = "Inverter Type")
self.L3.grid(row=9, column=1, sticky=W)
global inverter_types # declare array as global parameter so it can be accessed outside function
inverter_types = []
for i in range(self.system_size):
inverter_list = get_inverter_list(inverter_file)
inverter_list = ["Select"] + inverter_list
self.inverter_types_raw = StringVar()
self.L3a = Label(dynamic_widgets, text = "System {}".format(i+1), font = ('Calibri', 10,'italic'))
self.L3a.grid(row=10+i, column=1, sticky=E)
self.widget = OptionMenu(dynamic_widgets, self.inverter_types_raw, *inverter_list, command = get_values_0)
self.widget.grid(row=10+i, column=2,sticky=EW)
inverter_types.append(self.widget)
dynamic_widgets.bind("<Configure>", update_scroll_region)
global inv_type
inv_type = []
def get_values_0(value):
inv_type.append(value)
button = tk.Button(root, text = "Store Values", font=('Calibri', 10,'italic'), bg = "SlateGray3",command = lambda:[gget_values_0()])
button.grid(row = 61, column = 2, columnspan=8, sticky = 'nesw')
# System Type
self.L1 = Label(root, text = "System Type")
self.L1.grid(row=4, column=1, sticky=W)
self.sys_type_raw = StringVar(root)
types = ['Select', 'Central Inverter', 'String Inverters']
self.popupMenu6 = OptionMenu(root, self.sys_type_raw, *types)
self.popupMenu6.grid(row=4, column=2, sticky=EW)
# Number of Systems
self.L2 = Label(root, text = "Number of Systems")
self.L2.grid(row=6, column=1, sticky=W)
self.system_size_raw = IntVar(root)
choices = list(range(1,50))
self.popupMenu2 = OptionMenu(root, self.system_size_raw, *choices)
self.popupMenu2.grid(row=6, column=2, sticky=EW)
self.system_size_raw.trace("w", callback)
vsb = Scrollbar(root, orient="vertical")
vsb.grid(row=8, column=6, sticky = 'ns')
canvas = Canvas(root, width = 600, height = 200)
vsb.config(command = canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
canvas.grid(row=8,column=0)
# SITE ORIENTATION
self.L12 = Label(root, text="Site Orientation:", font = ('TKDefaultFont', 9, 'bold'))
self.L12.grid(row=66, column=0, sticky=W)
self.L13 = Label(root, text = "Module Tilt Angle (degrees)")
self.L13.grid(row=67, column=1, sticky=W)
self.modtilt_raw = Entry(master)
self.modtilt_raw.grid(row=67, column=2, sticky=EW)
self.L14 = Label(root, text = "Array Azimuth (degrees)")
self.L14.grid(row=68, column=1, sticky=W)
self.arraytilt_raw = Entry(master)
self.arraytilt_raw.grid(row=68, column=2, sticky=EW)
# SUBMIT INFORMATION
self.L27 = Label(root, text=" ").grid(row=84,column=1) # Add row of space
self.cbutton = tk.Button(root, text="SUBMIT",command = self.store_user_inputs, bg = "SlateGray3")
self.cbutton.grid(row=85, column = 0, columnspan=8, sticky = 'ew')
# STORE USER INPUT
def store_user_inputs(self):
self.system_size = np.float(self.system_size_raw.get())
# save all inputs as global parameters so they can be accessed as variables outside of GUI
global params
params = [self.system_type]
root = Tk()
root.configure()
window = Window(root)
root.mainloop()
I would like to place the dynamically generated widgets (Inverter Type, Modules per String, Strings per Inverter, Inverters per System, Module Type, and Max Current per System) into a scrollable frame.
I can post more code if needed.
A scrollable frame can be created by creating a Frame widget on a Canvas. An example is as follows:
vsb = Scrollbar(root, orient = VERTICAL)
vsb.pack(fill=Y, side = RIGHT, expand = FALSE)
canvas = Canvas(root, yscrollcommand=vsb.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vsb.config(command = canvas.yview)
canvas.config(scrollregion = canvas.bbox("all"))
InverterType = Frame(canvas)
canvas.create_window(0, 0, anchor = NW, window = InverterType)
Now make sure to add all the widgets created in the callback function to this InverterType frame.
(TIP - replace root with InverterType)

tkinter user input widget to xlsx

How do I write user input from tkinter entry widget to xlsx?
Should I be using a different module?
from tkinter import *
import xlsxwriter
def output():
c = Toplevel(root)
c.title =("Main")
c.geometry =('')
e= Entry(c, width=20).grid(row=0, column=1, sticky=W, padx=10)
l=Label(c, text="Current Value").grid(row = 0, column=0, sticky=W, padx=10)
e2 = Entry(c, width=20).grid(row=1, column=1, sticky=W, padx=10)
l2= Label(c, text="New Value").grid(row = 1, column=0, sticky=W, padx=10)
b=Button(c, text="Submit",command= write_to_xlsx).grid(row= 1, column=2, sticky=E, padx=10)
def write_to_xlsx():
workbook =xlsxwriter.Workbook('tkintertest18.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write_string('C1', 'e2.get()')
workbook.close()
root = Tk()
root.title("Main Menu")
root.geometry('400x400+230+130')
Button(root, text="1", command=output).grid(row =0, column= 2)
root.mainloop()
You should almost always use a class when tkinter is involved. So here is a working prototype of your code above. There were multiple problems. One is that when you slap grid next to your widgets instead of putting them on a new line, grid returns None, not the object. Implementing the app as a class enables the other functions to access the GUI components (in this case self.e2). Another error was you had single-quoted the 'e2.get()' call, which is technically a string. So removing the quotes fixes that. There may have been other things...
import xlsxwriter
class XLwriter:
def __init__(self):
c = Toplevel(root)
c.title =("Main")
c.geometry =('')
self.e= Entry(c, width=20).grid(row=0, column=1, sticky=W, padx=10)
self.l=Label(c, text="Current Value").grid(row = 0, column=0, sticky=W, padx=10)
self.e2 = Entry(c, width=20)
self.e2.grid(row=1, column=1, sticky=W, padx=10)
self.l2= Label(c, text="New Value").grid(row = 1, column=0, sticky=W, padx=10)
self.b=Button(c, text="Submit",command=self.write_to_xlsx).grid(row= 1, column=2, sticky=E, padx=10)
def write_to_xlsx(self):
workbook =xlsxwriter.Workbook('tkintertest18.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write_string('C1', self.e2.get())
workbook.close()
root = Tk()
root.title("Main Menu")
root.geometry('400x400+230+130')
app = XLwriter()
#Button(root, text="1", command=output).grid(row =0, column= 2)
root.mainloop()
Seeing that Ron already provided you with a class to perform this task I will respond with the non-class method of doing this to more closely reflect the current code you have.
Keep in mind that with xlsxwriter there is not a way to read from the spreadsheet so for the entry field you have that is labeled as "Current Value" you will need to use a different excel library to fill that field with data from the spreadsheet.
Make sure if you are going to interact with a widget that you do not use the geometry manager directly with the creation of the widget. This will return None to anything trying to interact with it. Instead on the next line after you have created the widget you can use the widget variable name and then your geometry manager of choice(grid(), pack(), place()). This will allow us to interact with the widget without the issue of None being returned by the geometry manager.
In order for you to be able to interact with e2 outside of the output() function you will need to assign e2 to the global namespace. You can do this by simply adding global e2 inside your output() function.
Note: The use of global is best avoided whenever possible this is one compelling reason to use a more Object Oriented Programing method (class) as the use of global is not needed when using class attributes.
from tkinter import *
import xlsxwriter
root = Tk()
root.title("Main Menu")
#root.geometry('400x400+230+130')
def output():
global e2
c = Toplevel(root)
c.title = ("Main")
e = Entry(c, width = 20)
e.grid(row = 0, column = 1, sticky = W, padx = 10)
l = Label(c, text = "Current Value")
l.grid(row = 0, column = 0, sticky = W, padx = 10)
e2 = Entry(c, width=20)
e2.grid(row = 1, column = 1, sticky = W, padx = 10)
l2 = Label(c, text = "New Value")
l2.grid(row = 1, column = 0, sticky = W, padx = 10)
b = Button(c, text = "Submit",command = write_to_xlsx)
b.grid(row = 1, column = 2, sticky = E, padx = 10)
def write_to_xlsx():
workbook = xlsxwriter.Workbook('tkintertest18.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write_string('C1', e2.get())
workbook.close()
btn = Button(root, text = "1", command = output)
btn.grid(row = 0, column = 2)
root.mainloop()

Resources