How to call local variable outside function? - python

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()

Related

Only open 1 window when button clicked multiple times

I am trying to create a basic invoicing system. However i have encountered an issue as you can tell from my the title, is there any way to achieve this. I have been using a counter to determine if the window should open or not but i dont think it is right.
from tkinter import *
window = Tk()
count = 0
def openNewWindow():
global count
count = count + 1
if count == 1:
newWindow = Toplevel(window)
newWindow.title("New Window")
newWindow.geometry("800x800")
newWindow.title('test ©') # Frame title
newWindow.iconbitmap('icon4.ico') # Frame logo
if 'normal' == newWindow.state():
count = 2
else:
count = 0
width = window.winfo_screenwidth()
height = window.winfo_screenheight()
window.geometry("%dx%d" % (width, height))
bg = PhotoImage(file="bsor.gif")
label_image = Label(window, image=bg)
label_image.place(x=0, y=0)
title_label = Label(window, text="Job Management System", bg="black", fg="white")
title_label.config(font=("Courier", 70))
title_label.place(x=65, y=3)
customer_database_button = Button(window, text="Customer Database", width="23", height="2",
font=('Courier', 13, 'bold'), command=openNewWindow)
customer_database_button.grid(row=3, column=0, pady=185, padx=(110, 0))
employee_database_button = Button(window, text="Employee Database", width="23", height="2",
font=('Courier', 13, 'bold'))
employee_database_button.grid(row=3, column=1, pady=10, padx=(50, 0))
job_category_button = Button(window, text="Job Category (Pricing)", width="23", height="2",
font=('Courier', 13, 'bold'))
job_category_button.grid(row=3, column=2, pady=10, padx=(50, 0))
quote_sale_button = Button(window, text="Quotes / Sales", width="23", height="2", font=
('Courier', 13, 'bold'))
quote_sale_button.grid(row=3, column=3, pady=10, padx=(50, 0))
cash_management_button = Button(window, text="Cash Management", width="23", height="2", font=
('Courier', 13, 'bold'))
cash_management_button.grid(row=3, column=4, pady=10, padx=(50, 0))
analysis_mode_button = Button(window, text="Analysis Mode", width="23", height="2", font=
('Courier', 13, 'bold'))
analysis_mode_button.grid(row=3, column=5, pady=10, padx=(50, 0))
window.title('test') # Frame title
window.iconbitmap('icon4.ico') # Frame logo
window.mainloop()
Here is a minimal example on how to do it (works best with only one additional allowed window):
from tkinter import Tk, Toplevel, Button
def open_window(button):
button.config(state='disabled')
top = Toplevel(root)
top.transient(root)
top.focus_set()
top.bind('<Destroy>', lambda _: btn.config(state='normal'))
root = Tk()
root.geometry('300x200')
btn = Button(root, text='Open new window!', command=lambda: open_window(btn))
btn.pack(expand=True)
root.mainloop()
Just have the function disable the button and bind a <Destroy> event to the Toplevel to set the button's state back to normal. (Also you may want to use .transient on the Toplevel to make it appear above its master so that people don't forget that they haven't closed the window and wonder why they can't press the button (it will also not display additional icon in the taskbar))
Also:
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but have space around = if it is used for assigning a value (variable = 'some value'). Have space around operators (+-/ etc.: value = x + y(except here value += x + y)). Have two blank lines around function and class declarations.

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

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.

Adding images to toplevel window

I'm trying to add an image in a Top Level window in python, however I can't seem to get it working.
I've tried using canvases and following some other steps on stack overflow. One thing I haven't tried is using class, init and self, but I don't really understand that, and hoping I can acheive my thing without the use of self. and stuff like that :D.
def pika():
def close():
win.destroy()
win = Toplevel()
win.geometry("150x150")
win.title("I CHOOSE U")
yellowb = Label(win, bg = 'yellow', fg = 'yellow', padx = 100, pady = 100)
yellowb.pack()
poke = PhotoImage(file = "pika.gif")
pika = Label(image = poke) # REEEEEEEEEEEEEEEEEE IT NO WORKEY
pika.grid(row = 0, column=1, padx = 10, pady = 10)
button = Button(win, text = "Close", command = close)
button.place(x=49, y=130)
Heres the main windows code
win = Tk()
win.geometry("290x200")
win.title('Error')
text1 = Label(win, bg= "Cyan", fg = 'Teal', padx = 50, pady = 75, text = '#hash error',
font = ('Times', '40', 'bold'))
text1.place(x=win.winfo_width() / 2, y=win.winfo_width() / 2)
button1 = Button(win, text = 'green', command = green)
button1.place(x=40, y=160)
button2 = Button(win, text = 'blue', command = blue)
button2.place(x=90, y=160)
button3 = Button(win, text = "red", command = red)
button3.place(x=130, y=160)
button4 = Button(win, text = 'yellow', command = yellow)
button4.place(x=170, y=160)
button5 = Button(win, text = "orange", command = orange)
button5.place(x=229, y=160)
eggs = PhotoImage(file = "pika.gif")
eggpic = Button(win, image = eggs, bg = 'Cyan', fg = 'Cyan', command = pika)
eggpic.place(x=100, y=20)
def close():
win.destroy()
closebut = Button(win, text='close', command = close)
closebut.place(x=0, y=0)
When I click a button and it opens a new windows the bg of the windows is yellow and everything as intended, however the image doesn't appear on the windows, and a white box the size of the image appears in the main tk window.
https://imgur.com/a/XU5m3Se
You can have two problems:
First: Every widget needs parent. If you don't use it then tkinter assigs widget to main window.
And you have this problem in Label(image = poke) because you forgot win in
pika = Label(win, image=poke)
This is why you see rectangle in main window instead of toplevel window.
Second: There is bug in PhotoImage. Garbage Collector removes it from memory when it is created in function and assigned to local variable. And then you can see empty image.
You have to assign PhotoImage to global variable or assign to other widget in function.
Popular solution with assigning to other widget:
poke = PhotoImage(file = "pika.gif")
pika = Label(win, image=poke)
pika.photo = poke # <-- assign PhotoImage to other widget too
More: PhotoImage

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.

Categories

Resources