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
Related
I want to change a number from a matrix and then display it in the same tk window, but I find it hard to work with variables from an input. The r[][] should be the matrix formed with the user's input. And after all I have to display the matrix with the modification: r[0][1] += 5, in the same tk window.
from tkinter import *
import numpy as np
root = Tk()
def process():
values = [e1.get(),e2.get(),e3.get(),e4.get()]
a = np.zeros((2,2),dtype=np.int64)
for i in range(2):
for j in range(2):
a[i][j] = values[i*2+j]
print(a)
e1 = Entry(root)
e2 = Entry(root)
e3 = Entry(root)
e4 = Entry(root)
e1.grid(row=0,column=0,padx=10,pady=10)
e2.grid(row=0,column=1)
e3.grid(row=1,column=0,padx=10,pady=10)
e4.grid(row=1,column=1)
b = Button(root,text='Process',command=process)
b.grid(row=2,column=0,columnspan=4,sticky=E+W)
root.mainloop()
r=[[e1.get(),e2.get()],[e3.get(),e4.get()]]
r[0][1] += 5
Tkinter GUI programs are event-driven which requires using a different programming paradigm than the one you're probably familiar with which is called imperative programming. In other words, just about everything that happens is done in response to something the user has done, like typing on the keyboard, clicking on a graphical button, moving the mouse, etc.
I think the code below will give you a good idea of how to do what you want in a framework like that. It creates a StringVar for each Entry widget, which has the advantage what's displayed in each Entry will automatically be updated whenever the corresponding StringVar is changed (make that more-or-less automatic).
To determine which StringVar is associated with a given Entry, a separate dictionary is created which maps the internal tkinter variable name to corresponding Python variable. The internal tkinter variable name is obtained by using the universal cget() widget method.
import tkinter as tk
from tkinter.constants import *
ROWS, COLS = 2, 2
def process(entry_widgets, row, col):
var_name = entry_widgets[row][col].cget('textvariable')
var = root.variables[var_name]
try:
value = float(var.get())
except ValueError: # Something invalid (or nothing) was entered.
value = 0
var.set(value+5) # Update value.
root = tk.Tk()
# Create a grid of Entry widgets.
entries = []
root.variables = {} # To track StringVars.
for x in range(COLS):
row = []
for y in range(ROWS):
var = tk.StringVar(master=root) # Create variable.
root.variables[str(var)] = var # Track them by name.
entry = tk.Entry(root, textvariable=var)
entry.grid(row=x, column=y)
row.append(entry)
entries.append(row)
btn = tk.Button(root, text='Process', command=lambda: process(entries, 0, 1))
btn.grid(row=2, column=0, columnspan=COLS, sticky=E+W)
root.mainloop()
Would this be what you're looking for? I deleted a bunch of code that seems to do nothing in context -- you just want to replace the text in the corner box right?
from tkinter import *
def process():
replace(e4)
def replace(entry_loc):
temp = int(entry_loc.get())
temp += 5
entry_loc.delete(0,500)
entry_loc.insert(0, temp)
root = Tk()
var_e1 = StringVar
var_e2 = StringVar
var_e3 = StringVar
var_e4 = StringVar
e1 = Entry(root, textvariable=var_e1)
e2 = Entry(root, textvariable=var_e2)
e3 = Entry(root, textvariable=var_e3)
e4 = Entry(root, textvariable=var_e4)
e1.grid(row=0, column=0, padx=10, pady=10)
e2.grid(row=0, column=1)
e3.grid(row=1, column=0, padx=10, pady=10)
e4.grid(row=1, column=1)
b = Button(root, text='Process', command=process)
b.grid(row=2, column=0, columnspan=4, sticky=E + W)
root.mainloop()
I'm creating a GUI in python using tkinter and am having trouble when running it. I have an entry box widget, a radiobutton widget, and a button widget. When I press the button, what I want is the user to type a number into the entry box and select an option from the list of radiobuttons. When the user presses the button, I'd like the values to be retrieved and displayed in the other frame for testing. What I get instead is when the button gets pressed, I get the error 'NoneType' object has no attribute 'get'. The error is referring to the value inside of the entry box: self.tune_entry
The code I have is as follows:
SA_main.py
import os
import tkinter as tk
from tkinter import ttk
from tkinter import font
import SA_gui
def main():
x_vals = [0,1,2,3,4]
y_vals = [0,1,2,3,4]
root = SA_gui.tk.Tk()
UI = SA_gui.Window(root, x_vals, y_vals)
root.mainloop()
if __name__ == "__main__":
main()
SA_gui.py
import os
import tkinter as tk
from tkinter import ttk
from tkinter import font
# Class to define, setup, and build the GUI
class Window:
# Dimensions of the GUI
HEIGHT = 600
WIDTH = 1200
# Colors for main layout
bg_color = "#B0E0E6"
frame_color1 = "#73B1B7"
white_color = "#FFFFFF"
def __init__(self, master, x_vals, y_vals):
# Take in the lists of files for later use
self.x_vals = x_vals
self.y_vals = y_vals
#--------------------------------------------------------------
# Define and create the window
self.master = master
master.title("Signal Analysis")
master.geometry("{}x{}".format(Window.WIDTH, Window.HEIGHT))
# Create and place the background frame
self.bg_frame = tk.Frame(self.master, bg=Window.bg_color, bd=5)
self.bg_frame.place(relwidth=1, relheight=1)
# Create the main title
self.main_title = tk.Label(self.bg_frame, text="Software Defined Radio Signal Analysis",
bg=Window.bg_color, font=("Courier", 14))
self.main_title.pack(side="top")
#--------------------------------------------------------------
# Create and place the frame for tuning
self.tune_frame = tk.Frame(self.bg_frame, bg=Window.frame_color1, bd=4)
self.tune_frame.place(relx=0.05, rely=0.1, relwidth=0.2428, relheight=0.8)
# Create and place the title for the tuning frame
self.tune_title = tk.Label(self.tune_frame, text="Tune", bg=Window.frame_color1, font=
("Courier", 11))
self.tune_title.place(relwidth=1, anchor="nw")
# Create and place the contents of the tuning frame
self.tune_cont = tk.Frame(self.tune_frame, bg=Window.white_color, bd=4)
self.tune_cont.place(relx=0.05, rely=0.05, relwidth=0.9, relheight=0.925)
#Label for frequency entry
self.tune_label = tk.Label(self.tune_cont, text='Enter carrier frequency: (kHz)',
bg=Window.white_color)
self.tune_label.place(relx=0.025, rely=0)
#Entry Box for frequency entry
self.tune_entry = tk.Entry(self.tune_cont)
self.tune_entry.place(relx=0.025, rely=0.075, relwidth=0.95, relheight=0.05)
#Label for divider
self.tune_div = ttk.Separator(self.tune_cont, orient="horizontal")
self.tune_div.place(rely=0.175, relwidth=1)
#Label for display mode
self.disp_label = tk.Label(self.tune_cont, text='Select Display:', bg=Window.white_color)
self.disp_label.place(relx=0.025, rely=0.2)
#Variable for radiobuttons
self.var = tk.IntVar(self.tune_cont).set("1")
#Radio Button for Spectral Analysis
self.SA_select = tk.Radiobutton(self.tune_cont, text="Spectral
Analysis",bg=Window.white_color, padx=20, variable=self.var, value=1)
self.SA_select.place(relx=0.025, rely=0.275)
#Radio Button for Option 2
self.opt2_select = tk.Radiobutton(self.tune_cont, text="Option 2",bg=Window.white_color,
padx=20, variable=self.var, value=2)
self.opt2_select.place(relx=0.025, rely=0.35)
#Radio Button for Option 3
self.opt3_select = tk.Radiobutton(self.tune_cont, text="Option 3",bg=Window.white_color,
padx=20, variable=self.var, value=3)
self.opt3_select.place(relx=0.025, rely=0.425)
#Button for selection
self.tune_button = ttk.Button(self.tune_cont, text="Enter", command=lambda:
self.print_selected(self.var.get(), self.tune_entry.get()))
self.tune_button.place(relx= 0.775, rely=0.9, relwidth=0.2, relheight=0.075)
#-----------------------------------------------------------------
# Create and place the frame for the plot
self.plot_frame = tk.Frame(self.bg_frame, bg=Window.frame_color1, bd=4)
self.plot_frame.place(relx=0.3428, rely=0.1, relwidth=0.6071, relheight=0.8)
# Create and place the title for the plot frame
self.plot_title = tk.Label(self.plot_frame, text="Plot", bg=Window.frame_color1, font=
("Courier", 11))
self.plot_title.place(relwidth=1, anchor="nw")
# Create and place the contents of the plot frame
self.plot_cont = tk.Frame(self.plot_frame, bg=Window.white_color, bd=4)
self.plot_cont.place(relx=0.025, rely=0.05, relwidth=0.95, relheight=0.925)
def print_selected(self, disp, freq):
if disp == 1:
disp_mode = "Spectral Analysis"
elif disp == 2:
disp_mode = "Option 2"
else:
disp_mode = "Option 3"
#Label for this test
self.prnt_label = tk.Label(self.plot_cont, text="Display: " + disp_mode + ", Center Freq: " +
freq, bg=Window.white_color)
self.prnt_label.place(relx=0.025, rely=0.2)
Any help to resolve this issue is greatly appreciated!
Consider this code:
self.var = tk.IntVar(self.tune_cont).set("1")
Anytime you do x=y().z() python assigns the return value of z() to x. Thus, in your code you're assiging the result of .set("1") to self.var. The set method is returning None so self.var is None. Thus, when you later try to call self.var.get() it's the same as doing None.get().
If you want to initialize a variable at the time of creation, there is no need to call set. Also, while it works to pass a string, if you're setting an IntVar you really ought to be setting it to an integer.
self.var = tk.IntVar(value=1)
I am popping up a custom dialog box using Tkinter.
I am opening it from another Tkinter window.
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
self.sub_root = Tk()
self.sub_root.title("Intovex")
self.sub_root.iconbitmap("Icon.ico")
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar()
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
c=1
print(names)
for i in names:
print(i)
r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
r.pack(anchor=W)
c+=1
self.var.set(1)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
def ask(self):
self.sub_root.mainloop()
var2 = StringVar()
def get_choice():
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
var2.set(str(list.choice))
label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()
However, it works when it is run alone by directly instantiating the class and invoking ask() method.
You may have seen that I have print statements everywhere in the code(it is for debugging) and I found where isn't it working
The print statement to print the names list parameter is printing the whole list correctly.
Inside the for-loop also it prints the elements in the names list correctly
When I click on a radio button it invokes the end() method. There it always prints the default value.
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
self.sub_root = Tk()
self.sub_root.title("Intovex")
self.sub_root.iconbitmap("Icon.ico")
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar()
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
c=1
print(names)
for i in names:
print(i)
r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
r.pack(anchor=W)
c+=1
self.var.set(1)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
def ask(self):
self.sub_root.mainloop()
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
print(list.choice)
But it works if I open it as a TopLevel widget. But then the main window doesn't wait till the popup windows returns the value(choice).
The problem with the code in the first snippet is because you're calling Tk() more that once within the tkinter application — it confuses the interface code and can cause a variety of problems, as you're finding out.
If you replace the call inside the __init__() method of the ListDialog class, with one to tk.Toplevel() instead, then your code will start working.
I also streamlined the for loop that creates the Radiobuttons by changing it so to use the built-in enumerate() function to automatically keep a count of the names. In conjunction with that, I made the initial value of the IntVar zero which is not one of the values that selecting one the radiobuttons will assign it. This is so when the ListDialog is first displayed, none of them will be selected. It also ensures that the end() callback function will get called whenever the user presses one of them, so your app will always be informed when that happens.
Note that, although I didn't change it, you shouldn't name a variable list because that hides the name of the built-in class by that name. In general you should avoid naming anything something that conflicts with an existing standard Python name.
from tkinter import *
from tkinter.font import Font
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
# self.sub_root = Tk() # Wrong - don't call Tk() more than once.
root.withdraw() # Hide root window.
self.sub_root = Toplevel() # Create another top-level window.
self.sub_root.title("Intovex")
# self.sub_root.iconbitmap("Icon.ico") # I don't have this file...
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar(value=0) # Define and init value to one *not* produced by btns.
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
print(names)
for c, name in enumerate(names, start=1):
print(c)
r = Radiobutton(self.sub_root, text=c, variable=self.var, value=c,
command=self.end)
r.pack(anchor=W)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0",
fg="White", bd=0, width=12, pady=4, padx=4, height=1,
font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
root.deiconify() # Reshow root window.
def ask(self):
self.sub_root.mainloop()
var2 = StringVar()
def get_choice():
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
var2.set(str(list.choice))
label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()
I'm having a problem with the grid alignment when I add in a text box.
I've set up a simple GUI with a 3 column wide display. As part fo the GUI I have a text box for entering notes, and a spinbox for a defined number range.
If I remove the text box from the active code my spinbox aligns correctly with all other entries, but when I place the text box in the grid my spinbox is moved over to the right.
Doesn't seem to matter where the spinbox code is in relation to the text box, once the text box is present the spin box moves over to the right.
At this stage I just can't see what is causing the problem.
I'll admit I'm new to Tkinter (and stackoverflow).
This is the particular line of code that causes the problem:
self.newText.grid(row=display_row, column=0, columnspan=4, padx=10, pady=10).
Sample code below. If I comment out the above line the spin box aligns correctly, if its present then the spinbox is offset. Its only the spinbox that is affected, neither Label, Entry, RadioButton or Text widgets are affected, but Spinbox is. These are the only widgets needed for my GUI.
Any help appreciated.
#-------------------------------------------------------------------------------
# Name: MyGUI
# Purpose: Problem Sample
#
#-------------------------------------------------------------------------------
from Tkinter import *
#===============================
# GUI Class
#===============================
class GUIClass():
'''
GUI class
'''
def __init__(self):
self.displayframe = 0
self.NameVar = StringVar()
self.NumVar = StringVar() # shouldn't be but it works
self.ButtonVar = BooleanVar()
#===============================
# Function for standard button
def StandardEntry(self, display_row,labeltext,entryvar):
'''
Standard 'Entry' feature for GUI.
'''
Label(self.displayframe, text=labeltext).grid(row=display_row, column=0, sticky=W)
name = Entry(self.displayframe, textvariable=entryvar)
name.grid(row=display_row, column=1, sticky=W)
display_row+=1
def BooleanRadioButton(self, display_row, labeltext, entryvar):
'''
Boolean true/false radio button function
'''
Label(self.displayframe, text=labeltext).grid(row=display_row, column=0, sticky=W)
ButtonOn = Radiobutton(self.displayframe, text="Enabled", variable=entryvar, value=True)
ButtonOn.grid(row=display_row, column=1, sticky=W)
ButtonOn = Radiobutton(self.displayframe, text="Disabled", variable=entryvar, value=False)
ButtonOn.grid(row=display_row, column=2, sticky=W)
def StandardSpinBox(self, display_row, labeltext, min_value, max_value, variablename):
'''
Standard spinbox for this project.
'''
Label(self.displayframe, text=labeltext).grid(row=display_row, column=0, sticky=W)
spinboxwidget = Spinbox(self.displayframe, from_=min_value, to=max_value, textvariable=variablename)
spinboxwidget.grid(row=display_row, column=1)
def AddFreeTextForm(self, display_row, notes):
'''
Standard widget for free text entry
'''
self.newText = Text(self.displayframe, width=50, height=8, takefocus=0)
self.newText.grid(row=display_row, column=0, columnspan=4, padx=10, pady=10)
def movesettingstoclass(self, dataclass):
dataclass.NameVar = self.NameVar.get()
dataclass.NumVar = int(self.NumVar.get())
dataclass.ButtonVar = self.ButtonVar.get()
def populate(self, dataclass):
'''
Takes the data in the data structure and populates the GUI.
'''
self.NameVar.set(dataclass.NameVar)
self.NumVar.set(str(dataclass.NumVar))
self.ButtonVar.set(dataclass.ButtonVar)
#===============================
# Data Class
#===============================
class DataClass():
def __init__(self):
self.NameVar = 'SomeName'
self.NumVar = 11
self.ButtonVar = False
def showvalues(self):
'''
Debug function/method for checking values are set correctly.
'''
msg += 'NameVar : %s\n' %self.NameVar
msg += 'NumVar : %d\n' %self.NumVar
msg += 'KVVar : %d\n' %self.KVVar
if self.ButtonVar == True:
msg += 'ButtonVar: True\n'
else:
msg += 'ButtonVar: False\n'
print msg
#=============================================
# Main window
#=============================================
root = Tk()
MyData = DataClass()
mainGUI = GUIClass()
root.title('Problem Example')
menu = Menu(root)
root.config(menu=menu)
#==============================================================================
# text fields to be entered by the user...
display_row = 0
Notes = ''
mainGUI.displayframe = Frame(root)
#==============================================================================
# Some entry input parameter
mainGUI.StandardEntry(display_row, "Some Label", mainGUI.NameVar)
display_row+=1
#==============================================================================
# Some spinbox input parameter
mainGUI.StandardSpinBox(display_row, "Some Label", 3, 21, mainGUI.NumVar)
display_row+=1
#==============================================================================
# Some Radiobutton input parameter
SwitchOn = mainGUI.BooleanRadioButton(display_row, "Button Label", mainGUI.ButtonVar)
display_row+=1
Label(mainGUI.displayframe, text="Configuration Notes").grid(row=display_row, column=1, sticky=W)
display_row += 1
#notes = mainGUI.AddFreeTextForm(display_row, "Configuration Notes", MyData.Notes)
notes = mainGUI.AddFreeTextForm(display_row, Notes)
display_row+=1
mainGUI.displayframe.pack(anchor='w')
#==============================================================================
# and the magic all happens here
mainloop()
You forgot to set the sticky parameter when calling the spinbox' grid method.
spinboxwidget.grid(row=display_row, column=1, sticky=W)
I wrote a small farad converter to learn GUI programming. It works great, looks fine-ish. The only problem is I can't seem to figure out how to control this strange highlighting that comes up on my ttk.Combobox selections. I did use a ttk.Style(), but it only changed the colors of the ttk.Combobox background, entries, etc. I also tried changing openbox/gtk themes.
I'm talking about what's seen there on the text "microfarads (uF)".
It'd be fine, if it highlighted the entire box; but I'd rather have it gone completely.
How can I manipulate a ttk.Combobox's selection highlight?
# what the farad?
# thomas kirkpatrick (jtkiv)
from tkinter import *
from tkinter import ttk
# ze la programma.
def conversion(*args):
# this is the numerical value
inV = float(inValue.get())
# these two are the unit (farads, microfarads, etc.) values
inU = inUnitsValue.current()
outU = outUnitsValue.current()
# "mltplr" is multiplied times inValue (inV)
if inU == outU:
mltplr = 1
else:
mltplr = 10**((outU - inU)*3)
outValue.set(inV*mltplr)
# start of GUI code
root = Tk()
root.title("What the Farad?")
# frame
mainFrame = ttk.Frame(root, width="364", padding="4 4 8 8")
mainFrame.grid(column=0, row=0)
# input entry
inValue = StringVar()
inValueEntry = ttk.Entry(mainFrame, width="20", justify="right", textvariable=inValue)
inValueEntry.grid(column=1, row=1, sticky="W")
# input unit combobox
inUnitsValue = ttk.Combobox(mainFrame)
inUnitsValue['values'] = ('kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (uF)', 'nanofarads (nF)', 'picofarads (pF)')
inUnitsValue.grid(column=2, row=1, sticky="e")
inUnitsValue.state(['readonly'])
inUnitsValue.bind('<<ComboboxSelected>>', conversion)
# result label
outValue = StringVar()
resultLabel = ttk.Label(mainFrame, textvariable=outValue)
resultLabel.grid(column=1, row=2, sticky="e")
# output unit combobox
outUnitsValue = ttk.Combobox(mainFrame)
outUnitsValue['values'] = ('kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (uF)', 'nanofarads (nF)', 'picofarads (pF)')
outUnitsValue.grid(column=2, row=2, sticky="e")
outUnitsValue.state(['readonly'])
outUnitsValue.bind('<<ComboboxSelected>>', conversion)
# padding for widgets
for child in mainFrame.winfo_children(): child.grid_configure(padx=4, pady=4)
# focus
inValueEntry.focus()
# bind keys to convert (auto-update, no button)
root.bind('<KeyRelease>', conversion)
root.mainloop()
Could it be that with a readonly combobox the problem is not the selection but the relatively strong focus-indicator?
With this workarround you lose the ability to control your program by keyboard. To do it right you would have to change the style of the focus-highlighting.
from tkinter import *
from ttk import *
def defocus(event):
event.widget.master.focus_set()
root = Tk()
comboBox = Combobox(root, state="readonly", values=("a", "b", "c"))
comboBox.grid()
comboBox.set("a")
comboBox.bind("<FocusIn>", defocus)
mainloop()
You can use the Combobox's selection_clear() method to clear the selection whenever you want.
e.g
inUnitsValue.selection_clear()
Just refresh the selected value of the Combobox.
This will help for removing the highlight.
import tkinter.ttk
import tkinter
items = ["test1","test2","test3","test4"]
class TkCombobox(tkinter.ttk.Combobox):
def __init__(self, *arg, **kwarg):
super(TkCombobox, self).__init__(*arg, **kwarg)
self._strvar_ = tkinter.StringVar()
self._strvar_.set("")
self["textvariable"] = self._strvar_
self.bind("<<ComboboxSelected>>", self.highlight_clear)
def highlight_clear(self, event):
current = self._strvar_.get()
self.set("")
self.set(current)
master = tkinter.Tk();master.geometry("400x400")
c = TkCombobox(master, values=items, state="readonly")
c.pack()
master.mainloop()