How to make an unchangeable default text for a Tkinter Entry? - python

I've managed to find a post on SO on how to create a Tkinter entry with a default value (referring to this one). The code below demonstrates my use of it:
comCurrent_Label = tk.Entry(root, font = (16), bg = "black", fg = "white", bd = 3, relief = "sunken")
comCurrent_Label.insert(0, ">>> ")
comCurrent_Label.grid(row = 2, column = 0, ipady = 15, ipadx = 175)
But I'd want for the user to be unable to delete >>> by backspacing too far.
My question is: How to make that entry's default text unchangeable/undeletable?

You can use the entry widget's validation feature to prevent the user from deleting the leading characters. Simply require that any new value begin with the string ">>> ", and the entry will prevent the user from deleting those characters.
Here's an example:
import tkinter as tk
def validate(new_value):
return new_value.startswith(">>> ")
root = tk.Tk()
vcmd = root.register(validate)
entry = tk.Entry(root, validate="key", validatecommand=(vcmd, "%P"))
entry.pack(side="top", fill="x", padx=20, pady=20)
entry.insert(0, ">>> ")
root.mainloop()
For a more in-depth explanation of entry validation see Interactively validating Entry widget content in tkinter

The easiest solution to this is to just put the >>> in a different widget, like a Label:
import tkinter as tk
root = tk.Tk()
subframe = tk.Frame(root, bd = 3, relief = "sunken")
lbl = tk.Label(subframe, text=">>> ", font = (16), bg = "black", fg = "white", bd=0, highlightthickness=0)
lbl.pack(side=tk.LEFT)
comCurrent_Label = tk.Entry(subframe, font = (16), bg = "black", fg = "white", bd=0, highlightthickness=0)
comCurrent_Label.pack(side=tk.LEFT)
subframe.grid(row = 2, column = 0, pady = 15, padx = 175)
root.mainloop()
You should probably wrap that up in a neat little subclass.

Related

How to call local variable outside function?

I was trying to call local variables outside far from a function that I made.
I was playing with Tkinter in python. Here is the sample code.
Is that possible to call variables outside function anywhere?
from tkinter import *
font1 = "Roboto 14"
window = Tk()
window.geometry("1200x800")
window.title("TEST")
canvas = Canvas(window, bg = "#FFFFFF", height = 800, width = 1200, bd = 0,
highlightthickness = 0)
canvas.place(x = 0, y = 0)
def load_auto_button_output ():
get_text_file = r"Links.txt"
read_text_file = open(get_text_file, 'r')
intermediate = read_text_file.read()
urlList = intermediate.split("\n")
s1 = urlList[0]
s2 = urlList[1]
txt_box.insert(0.0,
s1 + '\n' +
s2 + '\n'
)
def load_automatically_button ():
load_auto_button = Button(window, text = 'Automatically Load', font = font1 ,
width = 25, bd = 1, highlightthickness = 0,
relief = "ridge", command = load_auto_button_output)
load_auto_button.place(x = 540, y = 60)
load_automatically_button ()
txt_box = Text(window, bg = 'white', font = font1, height = 10, width = 70, bd = 3,
relief = "flat", wrap=WORD)
txt_box.place(x = 370, y = 500)
window.resizable(False, False)
window.mainloop()
Here you can see I made a button command function name load_auto_button_output
and I called all output inside text_box , via txt_box.insert.
Now, how do I call text_box.insert outside of that load_auto_button_output or how do I call s1, s2 variables outside that function?
I have tried global but it's not working by my side
global s1, s2
then I had to return the function load_auto_button_output (), then it's automatically print values from the button without a click and nothing happen when I press the Automatically Load button.
You can't access the local variables of a function outside the function. Sometimes you can pass their values to other functions if you call any from the function were they are being defined, but that doesn't help in this case.
You said you tried using global variables and it didn't work — but it should have, so the code below show how to do it that way.
I've also modified your code so it follows the PEP 8 - Style Guide for Python Code guidelines to a large degree so to make it more readable.
Update
I've changed the code to show how one might use the global variables after their values are set in one function in another. It also better illustrates how event-driven programming works.
Specifically, there's is now another button whose command= option is a new function that puts the values stored in the global variables into the text box.
import tkinter as tk # PEP 8 says to avoid 'import *'.
from tkinter.constants import * # However this is OK.
import tkinter.messagebox as messagebox
FONT1 = 'Roboto 14' # Global constant.
LINKS_FILE_PATH = r'Links.txt'
# Define global variables.
s1, s2 = None, None
window = tk.Tk()
window.geometry('1200x800')
window.title('Test')
canvas = tk.Canvas(window, bg='#FFFFFF', height=800, width=1200, bd=0,
highlightthickness=0)
canvas.place(x=0, y=0)
def load_links():
"""Load URLs from links file."""
global s1, s2 # Declare global variables being assigned to.
with open(LINKS_FILE_PATH, 'r') as read_text_file:
urlList = read_text_file.read().splitlines()
s1, s2 = urlList[:2] # Assign first two links read to the global variables.
messagebox.showinfo('Success!', 'URLs loaded from file')
put_in_text_box_btn.config(state=NORMAL) # Enable button.
def put_in_text_box():
"""Put current values of global variables s1 and s2 into text box."""
txt_box.delete('1.0', END) # Clear Text widget's contents.
txt_box.insert(0.0, s1+'\n' + s2+'\n') # Insert them into the Text widget.
load_links_btn = tk.Button(window, text='Load Links', font=FONT1, width=25, bd=1,
highlightthickness=0, relief='ridge', command=load_links)
load_links_btn.place(x=540, y=60)
put_in_text_box_btn = tk.Button(window, text='Put in text box', font=FONT1, width=25,
bd=1, highlightthickness=0, relief='ridge',
state=DISABLED, command=put_in_text_box)
put_in_text_box_btn.place(x=540, y=100)
quit_btn = tk.Button(window, text='Quit', font=FONT1, width=25, bd=1,
highlightthickness=0, relief='ridge', command=window.quit)
quit_btn.place(x=540, y=140)
txt_box = tk.Text(window, bg='white', font=FONT1, height=10, width=70, bd=3,
relief='flat', wrap=WORD)
txt_box.place(x=370, y=500)
window.resizable(False, False)
window.mainloop()

How can I pull a row through with grid_columnconfigure tkinter (Error)?

I have a little problem with tkinter Display:
The Black and white bar aren't pulled trough. Why not?
I declared the white bar in White Info Bar, and the Black Info Bar in Black Bar.
But they stop at the second Side Bar (Frame f4).
I want both upper bars to go trough the whole Window, but don't know how to do it.
Please give me some help.
A left a few unimportant things, like the fonts away.
Please forgive the mess.
My code:
And an Image (Display)
from tkinter import *
import tkinter.font as tkFont
import tkinter as tk
root = Tk()
root.geometry("200x100")
# White Info Bar
f2 = tk.Frame(root, background = "white", width = 1, height = 30)
f2.grid(row = 1, column = 0, sticky = "ew")
# Side Bars
f3 = tk.Frame(root, bg = "black", width = 1, height = 1)
f3.grid(row = 3, column = 0, sticky = "nsw")
root.grid_rowconfigure(3, weight = 1)
f4 = tk.Frame(root, bg = "black", width = 1, height = 1)
f4.grid(row = 3, column = 1, sticky = "nse")
root.grid_rowconfigure(3, weight = 1)
# Window
root.title("THE FRIDGER")
root.geometry("500x500")
# Black Bar
f1 = tk.Frame(root, background="black", width=1, height=45)
f1.grid(row=0, column=0, sticky="ew")
root.grid_columnconfigure(0, weight=1)
# Black Bar Content
fridge = Label(f1, text = "Fridge", fg = "white", bg = "black")
fridge.grid(column = 0, row = 0, pady = 4, padx = 4)
recipes = Label(f1, text = "Recipes", fg = "white", bg = "black")
recipes.grid(column = 1, row = 0, pady = 4, padx = 4)
# Entry
content = Label(f3, text = "Content:", fg = "white", bg = "black")
content.grid(column = 0, row = 2, sticky = W)
quest = Entry(f3, width = 36, bg = "white", fg = "black", relief = FLAT)
quest.grid(column = 0, row = 3, sticky = W)
content1 = Label(f4, text = "Content:", fg = "white", bg = "black")
content1.grid(column = 0, row = 2, sticky = W)
quest1 = Entry(f4, width = 36, bg = "white", fg = "black", relief = FLAT)
quest1.grid(column = 0, row = 3, sticky = W)
root.bind("<Return>", analyse)
root.mainloop()
You have lots of issues/misunderstandings.
assigning width and height to a Frame does nothing unless you also call propagate(0) on the Frame. You don't need to worry about doing that though cause, your app is based on weight and content.
using a Frame as a separator bar is noobish. Use a ttk.Separator instead. Make it thicker by adding ipady (if necessary ... or ipadx if vertical)
all of your stuff has terrible names that don't mean or reveal anything. To illustrate this, try to describe your code and take note of how much sense it does not make ~ "First I put the fridge and recipes in the f1, and separate it from the content with f2...". Now explain my refactor ~ "First I put 2 anonymous labels in a header Frame and follow it with a separator..". Names matter!
your sticky values are confused. You are attempting to consider the entire app with your sticky, but tkinter is just considering the cell each widget is in. For instance: sticky='nse' does not make the cell stick to the right side. It makes the widget IN THE CELL stick to the right side of the cell.
your header frame and separator are intended to span both of the columns below them, but you never told either to do that
you store references to every Label even though you will likely never change or remove any of those Labels
you double import tkinter AND use both syntax possibilities in your code. For instance: some of your stuff is tk.Label and some of it is just Label, but both of these return the same kind of Label. It's very sloppy.
rows and columns are not global. In other words, if you use row=2 IN a Frame, you don't have to start with row=3 FOR the next Frame. Similarly, if you put a Frame on row 3, you don't have to start it's children on row 4. Every Frame you create has it's own grid and it will always start with 0,0 indexes for it's children.
arguments for tkinter classes/methods can already become ridiculous in length. Doing this: var = val, var2 = val2, var3 = val3 is not helping. Also, writing the exact same arguments across multiple widgets is not efficient. Prepare your common arguments for use as kwargs.
you start your app by configuring the root, but then you configure it a little more later on, and then a little more even later on. Doing this is a recipe for problems/errors/useless code. This is evidenced by the fact that you configured the same row twice, and assigned geometry twice. If you are working on a widget (including root), finish it (as much as possible) before going on to the next widget. Sometimes this is not entirely possible (like when attaching scrollbars to things), but it is generally mostly possible.
You can glean the rest from this refactor of your code.
import tkinter as tk, tkinter.ttk as ttk
root = tk.Tk()
root.title("THE FRIDGER")
root.geometry("500x500")
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(2, weight=1)
#prepared data
dflt = dict(fg="white", bg="black")
pads = dict(pady=4, padx=4)
#header frame
header = tk.Frame(root, bg="black")
header.grid(row=0, column=0, columnspan=2, sticky="nsew")
for i in range(2):
header.grid_columnconfigure(i, weight=1)
#header labels
tk.Label(header, text="Fridge", **dflt).grid(column=0, row=0, **pads)
tk.Label(header, text="Recipes", **dflt).grid(column=1, row=0, **pads)
#separator
s = ttk.Style()
s.configure('custom.TSeparator', background='white')
ttk.Separator(root, style='custom.TSeparator').grid(row=1, column=0, columnspan=2, sticky="ew")
#left side content
l_content = tk.Frame(root, bg="black")
l_content.grid(row=2, column=0, sticky="nsew")
tk.Label(l_content, text="Content:", **dflt).grid(column=0, row=0, sticky=tk.W)
l_query = tk.Entry(l_content, width=36, relief=tk.FLAT, **dflt)
l_query.grid(column=0, row=1, sticky=tk.W)
#right side content
r_content = tk.Frame(root, bg="black")
r_content.grid(row=2, column=1, sticky="nsew")
tk.Label(r_content, text="Content:", **dflt).grid(column=0, row=0, sticky=tk.W)
r_query = tk.Entry(r_content, width=36, relief=tk.FLAT, **dflt)
r_query.grid(column=0, row=1, sticky=tk.W)
#root.bind("<Return>", analyse)
root.mainloop()

How to use an OptionMenu in Python Tkinter to set the justify option in a text box

I am creating a word editor in which I would like a taskbar at the top which has an OptionMenu widget with 3 possible choices - "right", "left", and "center". When one of these choices are chosen, it should take the value of that choice and set a text box window to each of those values using .tag_add, .insert, and .tag_config. Here is my code so far. All of it is inside of a frame called Task1, and the text box itself is inside a frame called label_frame. Next, the taskbar and the OptionMenu widget is inside a frame called Taskbar1. Here is my full code, which makes the GUI work.
from tkinter import *
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText
import tkinter as tk
from tkinter import Menu, filedialog
root = Tk()
class ToDoList(tk.Frame):
def __init__(self, master):
root.columnconfigure(2, weight=1)
root.rowconfigure(1, weight=1)
root.title("To - Do List")
root.geometry("1200x600")
root.configure(background = "white")
# Variable list:
style = ttk.Style()
current_theme =style.theme_use()
style.theme_settings(current_theme, {"TNotebook.Tab": {"configure": {"padding": [20, 5], "background" : "white"}}})
style.theme_settings(current_theme, {"TNotebook" : {"configure" : {"tabposition" : "wn", "padding" : (0, 5)}}})
style.theme_settings(current_theme, {"TNotebook.Window" : {"configure" : {"width" : 500}}})
TasksList = ttk.Notebook(root)
Task1 = tk.Frame(TasksList, bg='white', height = 1000, width = 3000)
Taskbar1 = tk.Frame(Task1, bg="white", width=176)
Taskbar1.pack()
Button(Taskbar1, text="Hello", highlightthickness=0, borderwidth=0, highlightbackground = "white").pack(pady=[4, 5], padx=[3,3], ipadx = [2], ipady = [2], side = LEFT)
JustifyOptionList = ["right", "center", "left"]
JustifyDefaultOption=StringVar(Taskbar1)
JustifyDefaultOption.set(JustifyOptionList[0]) # default choice
JustifyOption= OptionMenu(Taskbar1, JustifyDefaultOption, *JustifyOptionList)
JustifyOption.pack(side = LEFT)
JustifyDefaultOption
entry1 = Entry(Task1, width = 60, font = "Calibri 20", highlightthickness = 0, justify = "center", selectborderwidth = 0, bd = 1, borderwidth = 0, relief = FLAT)
entry1.pack()
label_frame = tk.Frame(Task1, width=1000,height=550,bg="blue")
label_frame.pack()
label_frame.columnconfigure(0, weight=2)
label_frame.rowconfigure(0, weight = 1)
label_frame.pack_propagate(0)
# create a Text widget
root.txt = tk.Text(label_frame)
root.txt.config(font=("TkMenuFont"), undo=True, wrap='word', highlightthickness=0, borderwidth=0, bd = 1, highlightbackground = "white", spacing1 = 5, spacing2 = 5, spacing3 = 5)
root.txt.tag_config(JustifyDefaultOption.get(), justify = JustifyDefaultOption.get())
root.txt.insert("1.0", "Please enter your notes here")
root.txt.tag_add(JustifyDefaultOption.get(), "1.0", "end")
root.txt.pack(expand=TRUE, fill = "both", side = LEFT)
# create a Scrollbar and associate it with txt
scrollb = tk.Scrollbar(label_frame, command=root.txt.yview, width = 16, bg = "white", troughcolor = "white", highlightbackground = "white")
scrollb.pack(fill = Y, side = RIGHT)
root.txt['yscrollcommand'] = scrollb.set
Task2 = tk.Frame(TasksList, bg='white')
text=ScrolledText(Task2, width = 176, height = 120, font = "TkMenuFont")
text.grid(row = 2, column = 0)
entry2 = Entry(Task2, width = 179, font = "TkMenuFont")
entry2.grid(row=0, column=0, sticky = W)
Task3 = tk.Frame(TasksList, bg = "white")
text=ScrolledText(Task3, width = 176, height = 120, font = "TkMenuFont")
text.grid(row = 2, column = 0)
entry3 = Entry(Task3, width = 179, font = "TkMenuFont")
entry3.grid(row=0, column=0, sticky = W)
TasksList.add(Task1,text = 'Click Here In Order To Read The Instructions')
TasksList.add(Task2, text = 'Two Two Two Two Two Two'[0: 40] + '...')
TasksList.add(Task3, text = "Three Three Three Three Three Three Three Extra"[0 : 40] + '...')
TasksList.grid(row=1, column=0, sticky=N+W, columnspan=3)
Button(root, text = "WELCOME", borderwidth=0, highlightthickness=0).grid(row=0, column=1, sticky=E, ipady = [5])
Label(text="HELLO", borderwidth=0, highlightthickness=0).grid(row=0, column=0, sticky=W, ipady = [5])
root.mainloop()
The part that I am confused about regarding this is the fact that even though I have a OptionList that records the option that the user selects, this option is not set in the justify settings even though I am using a .get function to take the user's justify setting and apply it to the text box.
The problem is that you do not change the justify setting each time the option changes. Initializing with .get does not make the value update when the StringVar value changes.
One way of applying the new justify setting to the text is to use the command option of the OptionMenu to do it:
import tkinter as tk
def justify_text(option):
"""Change text justify setting."""
text.tag_configure('justify', justify=option)
# make sure all text has the tag
text.tag_add('justify', '1.0', 'end')
root = tk.Tk()
options = ["right", "center", "left"]
var = tk.StringVar(root, options[0])
menu = tk.OptionMenu(root, var, *options, command=justify_text)
menu.pack()
text = tk.Text(root)
text.insert('1.0', "Please enter your notes here", 'justify')
text.tag_configure('justify', justify=options[0])
text.pack()
root.mainloop()

Tkinter - How to display image when clicking a button?

First time here so forgive me as this is my FIRST attempt at making a silly GUI game (if you want to call it that). I'm trying to get the user to click a button and the image of their selection pops up. I can't seem to figure out how to get the image to pop up though.
Image does show if I run it separately.
My code:
from Tkinter import *
root = Tk()
class PokemonClass(object):
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.WelcomeLabel = Label(root, text="Welcome! Pick your Pokemon!",
bg="Black", fg="White")
self.WelcomeLabel.pack(fill=X)
self.CharButton = Button(root, text="Charmander", bg="RED", fg="White",
command=self.CharClick)
self.CharButton.pack(side=LEFT, fill=X)
self.SquirtButton = Button(root, text="Squirtle", bg="Blue", fg="White")
self.SquirtButton.pack(side=LEFT, fill=X)
self.BulbButton = Button(root, text="Bulbasaur", bg="Dark Green",
fg="White")
self.BulbButton.pack(side=LEFT, fill=X)
def CharClick(self):
print "You like Charmander!"
global CharSwitch
CharSwitch = 'Yes'
CharSwitch = 'No'
if CharSwitch == 'Yes':
CharPhoto = PhotoImage(file="Charmander.gif")
ChLabel = Label(root, image=CharPhoto)
ChLabel.pack()
k = PokemonClass(root)
root.mainloop()
This works, but the actual image no longer shows, if I keep the PhotoImage OUT of the class it will print but I want to have it print IF they click the specific button:
from Tkinter import *
root = Tk()
class PokemonClass(object):
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.WelcomeLabel = Label(root, text = "Welcome! Pick your Pokemon!", bg = "Black", fg = "White")
self.WelcomeLabel.pack(fill = X)
self.CharButton = Button(root, text = "Charmander", bg = "RED", fg = "White", command = CharClick)
self.CharButton.pack(side = LEFT, fill = X)
self.SquirtButton = Button(root, text = "Squirtle", bg = "Blue", fg = "White")
self.SquirtButton.pack(side = LEFT, fill = X)
self.BulbButton = Button(root, text = "Bulbasaur", bg = "Dark Green", fg = "White")
self.BulbButton.pack(side = LEFT, fill = X)
def CharClick():
print "You like Charmander!"
CharPhoto = PhotoImage(file = "Charmander.gif")
ChLabel = Label(root, image = CharPhoto)
ChLabel.pack()
k = PokemonClass(root)
root.mainloop()
You need to maintain a reference to your PhotoImage object. Unfortunately there is an inconsistency in tkinter in that attaching a Button to a parent widget increments the reference count, but adding an image to a widget does not increment the reference count. As a consequence at the moment the CharPhoto variable goes out of scope at the end of the function CharClick, the number of reference to the PhotoImage falls to zero and the object is made available for garbage collection.
If you keep a reference to the image somewhere, it will appear. When you kept it globally it remained in scope for the entire script and hence appeared.
You can keep a reference to it in the PokemonClass object or in the Label widget.
Below is the later of those options
from Tkinter import *
root = Tk()
class PokemonClass(object):
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.WelcomeLabel = Label(root, text="Welcome! Pick your Pokemon!",
bg="Black", fg="White")
self.WelcomeLabel.pack(fill=X)
self.CharButton = Button(root, text="Charmander", bg="RED", fg="White",
command=self.CharClick)
self.CharButton.pack(side=LEFT, fill=X)
self.SquirtButton = Button(root, text="Squirtle", bg="Blue", fg="White")
self.SquirtButton.pack(side=LEFT, fill=X)
self.BulbButton = Button(root, text="Bulbasaur", bg="Dark Green",
fg="White")
self.BulbButton.pack(side=LEFT, fill=X)
def CharClick(self):
print "You like Charmander!"
global CharSwitch
CharSwitch = 'Yes'
CharPhoto = PhotoImage(file="Charmander.gif")
ChLabel = Label(root, image=CharPhoto)
ChLabel.img = CharPhoto
ChLabel.pack()
CharSwitch = 'No'
k = PokemonClass(root)
root.mainloop()
The solution which helped me is just simply declaring all the image variables on the next line after 'root = Tk()'. Doing so won't spoil your code or anything.

Using Entry box with Tkinter in Grid manager?

I'm trying to make a basic GUI using Tkinter and have an entry box next to my label using a Grid manager, but the window is not showing when I run my program if I use .grid() with my Entry object.
It does work when I use .pack(), which is strange because I heard not to use .pack() when I have other things using .grid() in the same widget. But I do want to use .grid() because I want to be able to organize it how I want to.
Code is below, I'm having trouble with Entry object showName. The commented out .pack() statement is the one that does work, the .grid() statement is the one that does not work.
Does anybody know what's wrong with this?
from Tkinter import *
class RenamerGUI():
def __init__(self, master):
frame = Frame(master)
frame.pack() #Make frame visible
self.exit = Button(frame, text = "Exit", fg = "red", command = frame.quit)
self.csv2tsv = Button(frame, text = "csv2tsv", fg = "green", bg = "black", command=self.csv2tsv)
self.epguidestsvFormatter = Button(frame, text = "epguidestsvFormatter", fg = "green", bg = "black", command = self.epguidestsvFormatter)
self.epNamesList = Button(frame, text = "epNamesList", fg = "green", bg = "black", command = self.epNamesList)
self.SeasonSplitter = Button(frame, text = "SeasonSplitter", fg = "green", bg = "black", command = self.SeasonSplitter)
self.Renamer = Button(frame, text = "Renamer", fg = "green", bg = "black", command = self.Renamer)
self.showLabel = Label(frame, text = "Show: ")
self.showName = Entry(master)
self.get = Button(frame, text = "Get", command = self.textgetter)
self.exit.grid(row=3, column=4)
self.csv2tsv.grid(row=1, column = 0)
self.epguidestsvFormatter.grid(row=1, column=1)
self.epNamesList.grid(row=1, column=2)
self.SeasonSplitter.grid(row=1, column=3)
self.Renamer.grid(row=1, column=4)
self.showLabel.grid(row=2)
self.showName.grid(row=2, column=1)
#self.showName.pack(side=BOTTOM)
The entry has the wrong parent:
self.showName = Entry(master)
should be
self.showName = Entry(frame)

Categories

Resources