Too many globals

Hello everyone.

I am doing an API using Tkinter. Now I have found some widgets I want to use.
For any widget I can open via a menu I also got a button to close it. To got it working the way I wanted I had to put in two globals for every close function. I have six of them so far. When googling about globals I came to this discussion on Stack Exchange.

  • So now I wonder if all the globals I have put in is bad for any reason? Security? Performance? Other reasons?
  • Is there a better way to work around the issue (if it is)?

Here is my code to test functions and design (can be improved :slight_smile: )

from tkinter import Button, Label, Entry, Tk, Menu, INSERT, scrolledtext
# 2nd edit. Only necessary imports for this code.

root = Tk()
root.title("My First API")
root.geometry('450x500')

#  -----------------------------------

def load_label():
    global lab1
    global but1
    lab1 = Label(root, text="A label",)
    lab1.grid(row=0, column=0)
    but1 = Button(root, text="Delete label", command=del_label)
    but1.grid(row=0, column=1)

def del_label():
    lab1.destroy()
    but1.destroy()

#  -----------------------------------

def input_field():
    global inp_f
    global but2
    inp_f = Entry(root)
    inp_f.grid(row=0, column=0)
    but2 = Button(root, text="Delete label", command=del_input)
    but2.grid(row=0, column=1)

def del_input():
    inp_f.destroy()
    but2.destroy()

#  -----------------------------------

def archlinux():
    global arch_text
    global but3
    b = open('arch_linux.txt', 'r')
    arch_text = scrolledtext.ScrolledText(root, width=40, height=10)
    arch_text.insert(INSERT, b.read())
    arch_text.grid(row=0, column=0)
    but3 = Button(root, text="Remove text box", command=del_arch)
    but3.grid(row=0, column=1)

def del_arch():
    arch_text.grid_forget()
    but3.destroy()

#  -----------------------------------

menu = Menu(root)
new_item=Menu(menu)
new_item.add_command(label="Label", command=load_label)
new_item.add_separator()
new_item.add_command(label="Input", command=input_field)
new_item.add_separator()
new_item.add_command(label="Linux", command=archlinux)
new_item.add_separator()
menu.add_cascade(label='Menu', menu=new_item)
root.config(menu=menu)

root.mainloop()

Not sure I can help with your specific query but just out of interest, why are you importing the entire library on the first statement, but then specifics in the second? Surely it’s one or the other?

I guess I put them in as I saw them in examples I looked up and never tested if it was necessary or not.
Now I have :slight_smile:
However I have to keep “from tkinter import scrolledtext” to got that one working.

Thanks for pointing that out

No worries. It’s always a call really whether you need the entire library or just specifics. I know PEP8 always moans when you import everything and if you are worried about efficiency then it make sense to only import what you actually need.

I had a further check what I actually needed in my code and how to put it in according to PEP8.
PEP8 imports

from tkinter import Button, Label, Entry, Tk, Menu, INSERT, scrolledtext

I wonder if you wouldn’t normally encapsulate your UI in a class? Then you could use instance attributes instead of globals?

Hello @florian

Thanks for input. As the hobby pythonista I still am I have not come across everything one can do or should do. Is UI a better name for what I am trying to do? As a non native english speaker and not professional coder I don´t now all the correct names for the parts in a “system” (application?).

I will of course try to refactor this into a class. And I will take your suggestion (instance attributes) as a hint what to go for and see how long I can get on my own.

You will probably hear from me soon :slight_smile:

To all friends in the forum.
Have a nice weekend.

1 Like

Sorry, UI means user interface.

Hello again.
I managed to do this refactoring this weekend. And it was not a 48 hour marathon.
More like 45-60 minutes at the time with coffee and some long walk with the dog in between (and I did slept all night :slight_smile: ).
Not bad for a hobby pythonista?
Here is my new code with the same functions as I had before plus some more.
Globals are not necessary anymore. It is still just my basic needs.
I have to connect this to the code that read the log. I have to do some work on the the design when I see how much space the different results need to be viewed clearly.

I am glad for any input on this. I am NOT a designer. It is not my strongest side. And I guess there is something to say about the code too. I am here to learn.


import tkinter as tk
from tkinter import Menu, Button
# from tkinter import Frame, Button, Label, Entry, Tk, ttk, Menu, INSERT, scrolledtext

# back ground colors
bg_blue = "blue"
bg_green = "green"
bg_red = "red"
bg_blue = "blue"
# text colors
fg_blue = "blue"
fg_green = "green"
fg_red = "red"
fg_blue = "blue"
# To use for future improvement in design.

class LoggApplication(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.master.geometry('600x600')
        self.master.title("My first API")
        self.grid()
        self.about()
        self.menyn()

    # Not in use for now
    def those(self):
        self.about["state"] = 'normal'
        self.about["state"] = 'disabled'

    def about(self):
        self.about = tk.Button(self)
        self.about["text"] = """Welcome to this application.\n \
        Use the meny to run and for search options.\n
        >Load a file.\n
        >Get a list of events in the file.\n
        >Search for specific events with keywords\n
        >Set a date/time filter. \n
        (To close window, click on it.)"""
        self.about["fg"] = fg_blue
        self.about["command"] = self.close_about
        self.about.grid(row=0, column=0, sticky='ew')
    def close_about(self):
        self.about.destroy()

    def load_file(self):
        self.etikett = tk.Label(self)
        self.etikett["text"] = "Load log file: >"
        self.etikett.grid(row=0, column=0)
        # self.etikett["bg"] = bg_green
        self.but_eti = tk.Button(self, text="Open", command=self.close_loadfile)
        self.but_eti.grid(row=0, column=2)
    def close_loadfile(self):
        self.etikett.destroy()
        self.but_eti.grid_forget()

    def events(self):
        self.event = tk.Button(self)
        self.event["text"] = "Show events"
        self.event["command"] = self.close_events
        self.event.grid(row=0, column=1)
        # scrolledtext gets here soon
    def close_events(self):
        self.event.grid_forget()

    def kw_entry(self):
        self.logfile = tk.Entry(self)
        self.logfile.grid(row=0, column=0)
        self.call_button = Button(self, text = "Keyword search", \
            command=self.close_kw)
        self.call_button.grid(row=0, column=1)
    def kw_search(self):
        pass
    def close_kw(self):
        # self.search_kw()
        self.logfile.destroy()
        self.call_button.destroy()

    def date_time_filter(self):
        self.date_filter = tk.Entry(self)
        self.date_filter.grid(row=0, column=0)
        self.time_filter = tk.Entry(self)
        self.time_filter.grid(row=0, column=1)
        self.filter_button = tk.Button(self)
        self.filter_button['text'] = "Filter date and time"
        self.filter_button['command'] = self
        self.filter_button.grid(row=0, column=2)
        self.filter_button_close = tk.Button(self)
        self.filter_button_close['text'] = "Close filter."
        self.filter_button_close['command'] = self.close_filter
        self.filter_button_close.grid(row=0, column=3)
    def get_datetime_filter(self):
        print("result..")
        self.filter_button['text'] = "close"
        self.filter_button['command'] = self.close_filter
    def close_filter(self):
        self.date_filter.destroy()
        self.time_filter.destroy()
        self.filter_button.destroy()
        self.filter_button_close.destroy()

    def menyn(self):
        menu = Menu(root)
        new_item=Menu(menu)
        new_item.add_command(label="About", command=self.about)
        new_item.add_separator()
        new_item.add_command(label="Load file", command=self.load_file)
        new_item.add_separator()
        new_item.add_command(label="Events in log" , command=self.events)
        new_item.add_separator()
        new_item.add_command(label="Word search" , command=self.kw_entry)
        new_item.add_separator()
        new_item.add_command(label="Date/time filter" , command=self.date_time_filter)
        new_item.add_separator()
        new_item.add_command(label="Shutdown", command=self.master.quit)
        new_item.add_separator()
        menu.add_cascade(label='Menu', menu=new_item)
        root.config(menu=menu)


root = tk.Tk()
app = LoggApplication(master=root)
app.mainloop()

Nice!

I don’t know Tkinter at all so I can’t say much about this.

One thing I’m noticing is that this will crash when you don’t pass a master to your app object. You have a default value of None, but then you set attributes on it. I think you should remove the default value so it can’t ever be instantiated without a master.

Hello @florian

Thanks again for your input. I have started to understand python classes and can do some basic stuff whit them. But when it gets advanced I quite soon get lost. As you perhaps understand I have not written this code out my knowledge but followed an example on tkinter — Python interface to Tcl/Tk — Python 3.9.5 documentation and got my code working in the class using their examples. There was a lot of other examples I looked at, but I decided to go for this one.

Please point out where this is in my code. I belive you. I have (so far) just not experienced any crashes.
But I am very interested in how this can be written to avoid problems to come.

Expect for the code in here I have put up and are intending to work from Github(ulfsjodin / my-tkinter-api)

It’s here:

When self.master is None, which is possible because of the default value, Python won’t find self.master.geometry and self.master.title.

To see if it does crash as I’d expect, try changing the last but one line to app = LogApplication() and see what happens.

It might actually not crash if the parent class sets the master attribute in the super call. You’ll see. :slight_smile:

Hello.

Yes of course it crashed when changing “app=LogApplication()” like you said.
I have no idea what so ever why the code is like so. I followed the examples and got it working.
It is not so good I do not fully understand the code I am writing. I can see that. But I am the only one suffering from this. I think I will get it soon.
Now I have a project to work on with a purpose. I have use for this in my work.
Beside learning tkinter as an API I also am learning to use Git. You are welcome to contribute if you want to. Like give an example on Github. And I can also put in exercises for pytest here.
I think I have a project for some years to come.

Can I play as well? Tk is on my list of things to play with, so seeing something already working would be a kickstart.

You are most welcome

1 Like