Why do we store methods in a variable when using Tkinter in python? - python-3.x

I just started learning Tkinter and when thing that really stood out was
win=tkinter.Tk()
I mean from what it looks win is basically a reference for the main window we created but this is the first time I have seen something like this since tk() is a method and not a class . I know that everything is an object in python eh but I am kinda confused , help me senpais ....

Why do we store methods in a variable when using Tkinter in python?
This question doesn't make much sense. Most programs do not store methods.
Regardless, tkinter is no different than any other module in this regard: you save a reference to something when you need to refer to the object later or you need to prevent the garbage collector from collecting the object.
I mean from what it looks win is basically a reference for the main window we created
That is correct. In your code example win is a reference to the root window, an instance of the Tk class.
I have seen something like this since tk() is a method and not a class
That is not true. At least, not if you meant to say Tk(). Tk from the tkinter module is in fact a class. So in this case you are creating an instance of the Tk class.

tkinter.Tk() is an object of type tkinter.Tk, not a method.

Related

Can you have two instances of tkinter in Python?

Is it possible to have two instances of tkinter ?
import tkinter as tk
import tkinter as sk
root = tk.Tk()
root2 = sk.Tk()
....some window with tk
....some window with sk
root.mainloop()
root2.mainloop()
then have a Toplevel() in both instances.
You can, but the way it works likely won't be like what you expect. Importing it twice isn't the problem (but neither is it the solution). No matter how you import it or how often you import it, creating more than one instance of Tk is the problem. Each instance is backed by a separate internal interpreter. Images and variables and widgets created in one won't exist in the other.
If you need more than one window, it's usually best if second and subsequent windows are instances of Toplevel.
By creating 2 instances or tkinter I can close one instance and all top Levels of that instance. Leaving the second Instance open and running with it's Toplevel instances. This is useful in the sense where I use it for a User preference file. Where it checks for a userfile database for keeping track of variables. I use a csv file to save user variable using pandas. This in turn keeps all the form information in my app safe from being erased after closing the application or accidentally closing the window. Adding AtExit saves the info closing the addinfo window and continues to run the main application. That was my reasoning for having asked the question. I have since found that using multiple Toplevel(s) is a better choice as it will also produce the same result. So my menu items all have a separate instance definition and can be closed in the same manner ,making error checks with each closed window.
from tkinter import Tk,Label,Entry,Button,Toplevel
root=Tk()
def about():
ab=Toplevel()
# About stuff for this window
ab.mainloop()
def info():
inf=Toplevel()
# Information Stuff for this window.
inf.mainloop()
def getUserInfo():
# User info using pandas
getUserInfo.mainloop()
root.mainloop()

Reload UI, Rather Than Recreating

import sys
import webbrowser
import hou
from PySide2 import QtCore, QtUiTools, QtWidgets, QtGui
# Calling UI File & Some Modification
class someWidget(QtWidgets.QWidget):
def __init__(self):
super(someWidget,self).__init__()
ui_file = 'C:/Users/XY_Ab/Documents/houdini18.5/Folder_CGI/someUI.ui'
self.ui = QtUiTools.QUiLoader().load(ui_file, parentWidget=self)
self.setParent(hou.qt.mainWindow(), QtCore.Qt.Window)
self.setFixedSize(437, 42)
self.setWindowTitle("Requesting For Help")
window_C = someWidget()
window_C.show()
So, I have created this small script that shows the UI, I have connected this to Houdini Menu Bar. Now The Problem is if I click the menu item multiple times it will create another instance of the same UI & the previous one stays back, What I want is something called "If Window Exist Delete It, Crate New One" sort of thing.
Can someone guide me? I am fairly new to python in Houdini and Qt so a little explanation will be hugely helpful. Also, why can't I use from PySide6 import?? Why do I have to use from PySide2?? Because otherwise Houdini is throwing errors.
For the same thing what used to do in maya is
# Check To See If Window Exists
if cmds.window(winID, exists=True):
cmds.deleteUI(winID)
Trying to do the same thing inside Houdini.
I don't have Maya or Houdini, so I can't help you too much.
According to https://www.sidefx.com/docs/houdini/hom/cb/qt.html
It looks like you can access Houdini's main window. The main reason the window is duplicated or deleted is how python retains the reference to window_C. You might be able to retain the reference to just show the same widget over and over again by accessing the main Houdini window.
In the examples below we are using references a different way. You probably do not need your code that has
self.setParent(hou.qt.mainWindow(), QtCore.Qt.Window)
Create the widget once and keep showing the same widget over and over.
import hou
# Create the widget class
class someWidget(QtWidgets.QWidget):
def __init__(self, parent=None, flags=QtCore.Qt.Window): # Note: added parent as an option
super(someWidget,self).__init__(parent, flags)
...
MAIN_WINDOW = hou.ui.mainQtWindow()
try:
MAIN_WINDOW.window_C.show()
except AttributeError:
# Widget has not been created yet!
# Save the widget reference to an object that will always exist and is accessible
# parent shouldn't really matter, because we are saving the reference to an object
# that will exist the life of the application
MAIN_WINDOW.window_C = someWidget(parent=MAIN_WINDOW)
MAIN_WINDOW.window_C.show()
To delete the previous window and create a new window.
import hou
# Create the widget class
class someWidget(QtWidgets.QWidget):
def __init__(self, parent=None, flags=QtCore.Qt.Window): # Note: added parent as an option
super(someWidget,self).__init__(parent, flags)
...
MAIN_WINDOW = hou.ui.mainQtWindow()
# Hide the previous window
try:
MAIN_WINDOW.window_C.close()
MAIN_WINDOW.window_C.deleteLater() # This is needed if you parent the widget
except AttributeError:
pass
# Create the new Widget and override the previous widget's reference
# Python's garbage collection should automatically delete the previous widget.
# You do not need to have a parent!
# If you do have a parent then deleteLater above is needed!
MAIN_WINDOW.window_C = someWidget() # Note: We do not parent this widget!
MAIN_WINDOW.window_C.show()
Another resource shows you can access the previous widget from the page level variable. https://echopraxia.co.uk/blog/pyqt-in-houdinimaya-basic This is possible, but seems odd to me. The module should only be imported once, so the page level variable "my_window" should never exist. However, it sounds like the Houdini plugin system either reloads the python script or re-runs the import. If that is the case every time you show a new window from the import of the script, you are creating a new window. If the previous window is not closed and deleted properly, Houdini could have an ever growing memory issue.
try:
my_window.close()
except (NameError, Exception):
pass # Normal python would always throw a NameError, because my_window is never defined
my_window = MyWindow()
#This is optional you can resize the window if youd like.
my_window.resize(471,577)
my_window.show()
PySide6
https://www.sidefx.com/docs/houdini/hom/cb/qt.html
The bottom of the page shows how to use PyQt5. The same would apply for PySide6. Houdini just happens to come with PySide2.

PhotoImage GIF won't appear (noob friendly please)

Alright, so I want to make a gif display when a function has been called, but the gif will go invisible and not show up. I searched for possible answers but all of them mention "create a reference to (insert code here)" and I don't really get it because:
1. 99% of them use objects and classes in which I have 0 experience
2. Some say to make a reference with "self.img = PhotoImage(...)" which I'm pretty sure its connected to objects and classes.
3. Others only say to create a reference.
Sorry for being somewhat rude. I'm just really fed up, I searched for answers for 2 hours now.
I tried to assign the variable to global, place the variable in the function and tried to remake the gif and rename the file
This is what I tried to do
def red_flicker():
global root
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(image=red_btn_flicker)
label_red.place(x=red_btn_place_x, y=red_btn_place_y)
the gif is invisible.
Please be noob friendly.
Any stuff about python 2.7 and using objects/classes will be ignored
Ok so first things first.
Your function is adding a new label every time it is call so you probably should generate the label in the global namespace once and then just apply the image to the label in the function. This way you can call the function all you want without adding more labels.
I would also move your PhotoImage to the global so you do not need to keep reopening the image each time you load the function.
By making this change we do not even need to use global as the widget creating and image loading happens in the global already.
Make sure to save the reference to the image so its not garbage collected.
import tkinter as tk
root = tk.Tk()
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(root)
label_red.pack()
def red_flicker():
label_red.config(image=red_btn_flicker)
label_red.image = red_btn_flicker # saving reference to image.
red_flicker()
root.mainloop()
You must save a reference, as mentioned in the answer to this question: Why does Tkinter image not show up if created in a function?
Since you aren't using classes, you can use a global variable. For example:
def red_flicker():
global red_btn_flicker
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(image=red_btn_flicker)
label_red.place(x=red_btn_place_x, y=red_btn_place_y)
Another simple technique is to attach the image as an attribute of the label itself. This works because python lets you create custom attributes on an object. However, you must make sure that the reference to the label itself isn't lost
def red_flicker():
global label_red
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(image=red_btn_flicker)
label_red.place(x=red_btn_place_x, y=red_btn_place_y)
label_red.image = red_btn_flicker

Print Current Time In Tkinter

I have written some code in python for a live time in tkinter.
Whenever I run the code it comes up with some numbers on the tkinter window like 14342816time. Is there a way to fix this?
import tkinter
import datetime
window = tkinter.Tk()
def time():
datetime.datetime.now().time()
datetime.time(17, 3,)
print(datetime.datetime.now().time())
tkinter.Label(window, text = time).pack()
window.mainloop()
After some fixes to your code, I came up with the following, which should at least get you started toward what you want:
import datetime
import tkinter
def get_time():
return datetime.datetime.now().time()
root = tkinter.Tk()
tkinter.Label(root, text = get_time()).pack()
root.mainloop()
The imports are needed so that your program knows about the contents of the datetime and tkinter modules - you may be importing them already, however, I can't tell that for certain from what you posted. You need to create a window into which you put your label, which wasn't happening; following convention, I called that parent (and only) window "root". Then I put the Label into root. I changed the name of your time() function to get_time(), since it's best to avoid confusing fellow programmers (and maybe yourself) with a function that shares its name with another (the time() function in time). I removed two lines in get_time() that don't actually accomplish anything. Finally, I changed the print you had to a return, so that the value can be used by the code calling the function.
There are other improvements possible here. If you're content with the time as it is, you could eliminate the get_time function and just use datetime.datetime.now().time() instead of calling get_time(). However, I suspect you might want to do something to clean up that time before it is displayed, so I left it there. You might want to research the datetime and time modules some more, to see how to clean things up.

deconstructing basic Tk inter script

I need help in understanding how Tk inter works.I'm using the first example from the documents page which creates a simple window with 2 buttons.
Introduction to GUI programming with tkinter
Code:
from tkinter import Tk, Label, Button
class MyFirstGUI:
def __init__(self, master):
self.master = master
master.title("A simple GUI")
self.label = Label(master, text="This is our first GUI!")
self.label.pack()
self.greet_button = Button(master, text="Greet", command=self.greet)
self.greet_button.pack()
self.close_button = Button(master, text="Close", command=master.quit)
self.close_button.pack()
def greet(self):
print("Greetings!")
root = Tk()
my_gui = MyFirstGUI(root)
root.mainloop()
Questions:
MyFirstGUI does not inherit from TK or Frame so how does it know of all the parameters (self.label,self.greet etc) one might find in the Tk class
We are passing a TK object to the variable root ( root = Tk() )
and passing that into MyFirstGUI class (my_gui = MyFirst GUI(root) )
.The only plausible explanation then, is that self.label and self.greet_button are "indeed" class variables to begin with and "become" labels ( and buttons ) once they are bound with functions such as Label(master,text="This is our first GUI!")
is my understanding correct ?
behram
So tkinter is just a library with classes inside of it, in this code you are importing the TK class, the Label class, and the Button class. When you use import statements at the top of your code, you are telling the computer to go fetch those files/functions and read them into the program. For example, if the TK class is say 100 lines of code, the statement
from tkinter import TK is equivalent to those 100 lines of code being in front of what you have written.
Now jumping to the creation of the UI itself, you create an instance of the TK class, and assign it to the variable root.
This root creates the outer window you will see when you run your UI, it holds the title, the min/max/close buttons in the top right corner, and determines the size of the window, along with a bunch of other features.
"root" is just a conventional name, it could be "potato" but as long as you know that is the foundation of your User Interface, that's what matters.
It won't be called too much outside of that first time you pass it into your class unless you're doing a lot of window manipulation.
From there, you are passing that root (window) to your MyFirstGUI object.
The __init__ function will run at the time of creation for any python class and the parameters for that function will be the parameters required to call the class. In this case, there are two parameters self, and master.
"self" is required as the first parameter of all functions inside a class so that the function knows the object it belongs to and so that it can access the class level variables available to it.
"master" has a default value of None so you could theoretically call MyFirstGUI() but in this case we are passing the root (window) as the master for the object MyFirstGUI(root).
At that point, because we are creating an instance of MyFirstGUI, __init__ fires and the first thing it does is sets self.master equal to the input of master so that it can be referenced anywhere in the object, expanding the scope beyond just the __init__ function.
From there, the function is arbitrarily defining variables that are scoped at the class level by using self. again so that these variables can be called from any part of this class.
self.label is just a name the original writer decided on, once again it could be self.macaroni as long as it makes sense to you that this is your label.
Then, because you have imported the Label class from tkinter, you are able to just refer to it as if it was already in your code. When you call these classes, they return the objects that are being set to your self.label so that you can refer to them later in the program.
If you check out the docs, you can see the Label and Button classes each have their own set of parameters available to them.
Common parameters include as you see "text" to show text that you'd like it to have, command to let a button know which function it should fire when clicked, "width" to determine the width of the widget on screen, and so on.
With the use of Default values, you don't always need to provide every single possible parameter to a call to create these objects.
Deciding what parameters to give comes down to practice and knowledge of the capabilities of each class in order to know which values you want to set and what are appropriate settings for them.
tldr:
Your import statements at the top allow you to use the classes at will, and your knowledge determines which parameters to send to each class. The docs are your friend!
I believe your understanding is correct. self.label is defined in the __init__ function by the programmer, and then assigned the widget object of a tkLabel by calling the class. From that point on, self.label is available anywhere inside the MyFirstGUI class to be manipulated as you see fit. For example, instead of the greet function printing out "Greetings!" you could change that print statement to self.label.set("Greetings!") so now your button click will change the label's text instead.
I hope this helped!

Resources