PhotoImage GIF won't appear (noob friendly please) - python-3.x

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

Related

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.

Add a QWidget inside a QFrame

I'm developing a desktop software using Python3 and QtDesigner for the Graphic User Interface.
My problem is the seguent: i'm trying to automate the creation of many QRadioButtons over a QFrame (The RadioButtons must stay inside the frame [as...children?]).
Now, i see that i can only create new widgets inside a Layout (e.g. "MyLayout.addWidget(QRadioButton")) and it's not possible to do something like "MyFrame.addWidget(QRadioButton)". I need these widgets inside the frame cause then i can place them in the correct position with "MyRB.move(X,Y)".
With QtDesigner is possible to place many Widgets (like RadioButtons) in a frame that has a 'broken layout' so i can choose X,Y coordinates but i need to create and place a variable number of those.
Is it possible to create Qwidgets inside a QFrame?
[EDIT]
according to musicamante's comment, i got that's a parent problem.
I tried to insert a Label and a RadioButton in the main window:
def __init__(self):
super().__init__()
uic.loadUi('DSS_GUI2.ui',self) # i load the GUI with QtDesigner
LB1 = QLabel('MyLabel',self)
RB1 = QRadioButton('MyRadioButton',self)
...
This very simple example works fine but when i try to add a Label through a function
def myFunction(self):
LB1 = QLabel('MyLabel')
LB1.setObjectName('LABEL_1')
LB1.setParent(self.myFrame)
the Widget is inserted but it is not visible, in fact adding this lines to check his presence
WidgetList = self.myFrame.findChildren(QLabel)
for item in WidgetList:
print(item.objectName())
i see in the console that the Label is there.
Do you know why it's not visible?
Try
def myFunction(self):
LB1 = self.sender()
LB1.QLabel('MyLabel')
LB1.setObjectName('LABEL_1')
LB1.setParent(self.myFrame)
You can call self.myFunction() in parent.
If you wanted to pass label, you could:
def myFunction(self, label):
LB1 = self.sender()
LB1.QLabel(label)
LB1.setObjectName(label)
LB1.setParent(self.myFrame)

Tkinter: pack()ing frames that use grid()

I am working on a UI for a data-display applet. I started with a tutorial and have since expanded it well beyond the scope of the tutorial, but some legacy bits remain from the tutorial that are now causing me difficulty. In particular relating to pack() and grid().
Following the tutorial I have defined a class Window(Frame) object, which I then declare as app = Window(root) where root = Tk(). Within the Window object is an initializing function def init_window(self), where my problems arise. Here is the relevant code in init_window():
def init_window(self):
self.master.title('Data Explorer') #changing the widget title
self.pack(fill=BOTH,expand=1) # allow widget to take full space of root
# Initializing a grid to place objects on
self.mainframe = Frame(root)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
self.mainframe.columnconfigure(0, weight = 1)
self.mainframe.rowconfigure(0, weight = 1)
self.mainframe.pack(pady = 10, padx = 10)
where the object self.mainframe contains a number of data selection dropdowns and buttons later on.
If I understand what this code is expected to do: it sets up the full window to be pack()ed with various frames. It then initializes a frame, self.mainframe, and within that frame initializes a grid(). Thus pack() and grid() do not collide. This setup was built by following the aforementioned tutorial.
This works correctly on my computer where I am developing the applet. However, when a collaborator compiles, they receive
_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack
on the line self.mainframe.grid(...). I have replaced the mainframe.pack() command with a mainframe.place() command, but this has not resolved the issue (since his compile does not reach that point); I have not figured out a way to remove the self.pack() command without causing all other elements of my UI to vanish.
Can anyone help us understand what is going wrong? For reference, we are both using MacOS, and compiling with Python3. I can provide additional information as requested, within limits.
The error is telling you exactly what is wrong. You can't use grid on a widget in the root window when you've already used pack to manage a widget in the root window.
You wrote:
It then initializes a frame, self.mainframe, and within that frame initializes a grid()
No, that is not what your code is doing. It is not setting up a grid within the frame, it's attempting to use grid to add the widget to the root window.
First you have this line of code which uses pack on a widget in the root window:
self.pack(fill=BOTH,expand=1)
Later, you try to use grid for another window in the root window:
self.mainframe = Frame(root)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
The above isn't setting up a grid within self.mainframe, it's using grid to add the widget to the root window.
You need to use one or the other, you can't use both for different windows that are both direct children of the root window.
In other words, you're doing this:
self.pack(fill=BOTH,expand=1)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
but since both self and self.mainframe are a direct child of the root window, you can't do that. You need to either use pack for both:
self.pack(fill=BOTH,expand=1)
self.mainframe.pack(...)
... or grid for both:
self.grid(...)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )

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.

how structure a python3/tkinter project

I'm developing a small application using tkinter and PAGE 4.7 for design UI.
I had designed my interface and generated python source code. I got two files:
gm_ui_support.py: here declare tk variables
gm_ui.py : here declare widget for UI
I'm wondering how this files are supposed to be use, one of my goals is to be able to change the UI as many times as I need recreating this files, so if I put my code inside any of this files will be overwritten each time.
So, my question is:
Where I have to put my own code? I have to extend gm_ui_support? I have to create a 3th class? I do directly at gm_ui_support?
Due the lack of answer I'm going to explain my solution:
It seems that is not possible to keep both files unmodified, so I edit gm_ui_support.py (declaration of tk variables and events callback). Each time I make a change that implies gm_ui_support.py I copy changes manually.
To minimize changes on gm_ui_support I create a new file called gm_control.py where I keep a status dict with all variables (logical and visual) and have all available actions.
Changes on gm_ui_support.py:
I create a generic function (sync_control) that fills my tk variables using a dict
At initialize time it creates my class and invoke sync_control (to get default values defined in control)
On each callback I get extract parameter from event and invoke logical action on control class (that changes state dict), after call to sync_control to show changes.
Sample:
gm_ui_support.py
def sync_control():
for k in current_gm_control.state:
gv = 'var_'+k
if gv in globals():
#print ('********** found '+gv)
if type(current_gm_control.state[k]) is list:
full="("
for v in current_gm_state.state[k]:
if len(full)>1: full=full+','
full=full+"'"+v+"'"
full=full+")"
eval("%s.set(%s)" % (gv, full))
else:
eval("%s.set('%s')" % (gv, current_gm_state.state[k]))
else:
pass
def set_Tk_var():
global current_gm_state
current_gm_control=gm_control.GM_Control()
global var_username
var_username = StringVar()
...
sync_control()
...
def on_select_project(event):
w = event.widget
index = int(w.curselection()[0])
value = w.get(index)
current_gm_control.select_project(value)
sync_state()
...

Resources