Add a QWidget inside a QFrame - python-3.x

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)

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.

Unable to display image label in second Tkinter frame

I am trying to build a simple gui using Tkinter. The application involves a smaller secondary frame opening up over the primary one upon pressing a button. This secondary frame must contain an image. Image labels appear easily on the primary frame, but on the secondary frame, the image label appears as an empty box the size of the image, with whatever background colour I set.
Here's how I'm doing it:
#send diagram page
def send_diagram():
send_diagram_frame=tk.Frame(frame, bg="#D4BAEC")
send_diagram_frame.place(relx=0.5, rely=0.5, relheight=0.7, relwidth=0.7, anchor="center")
send_diagram_entry_working_image=Image.open('/home/raghav/RemEdi/design/assets/generic_page_entry.png')
send_diagram_entry_image=ImageTk.PhotoImage(send_diagram_entry_working_image)
send_diagram_entry_label=tk.Label(send_diagram_frame, image=send_diagram_entry_image)
send_diagram_entry_label.place(relx=0.5, rely=0.5, anchor="center")
return
As visible, send_diagram() is the command for the button.
I have tried adding another smaller frame inside the secondary frame to contain the image, but that did not work either.
Any help would be greatly helpful. Thanks!
You are creating the new image inside a function, with it's own local namespace. When the function ends the reference to the image will be garbage collected.
You can fix this by saving a reference to the image in the Label widget. Put this line in the function after the image is created:
send_diagram_entry_label.image = send_diagram_entry_image
Here is working code. I have try it and it's working for me.
def make_label_image(parent, img):
label = tk.Label(parent, image=img)
label.place(relx=0.5, rely=0.5, anchor="center")
def send_diagram():
send_diagram_fram = tk.Frame(frame, bg="#D4BAEC")
send_diagram_frame.place(relx=0.5, rely=0.5, relheight=0.7, relwidth=0.7, anchor="center")
send_diagram_fram.pack_propagate(0)
send_diagram_fram.pack()
img = ImageTk.PhotoImage(Image.open('C:/Users/xxxx/Desktop/logo.jpg'))
make_label_image(send_diagram_fram, img)

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

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!

How do I set the minimum size of a Gtk ButtonBox child?

I'm trying to set the minimum size of the buttons in this GtkButtonBox. Currently they seem to be fixed - approx 85 pixels I think.
Is this possible?
If not, is there another way in Gtk to get two small sized buttons to snuggle together like in the above picture rather than having them appear to be two separate buttons? For example GtkStackSwitcher may be something I could use but there doesn't appear to be a way to respond to click events for a button.
I've used this test program to create the above (Ubuntu 14.04, Gtk+3.10 and Python3):
from gi.repository import Gtk
import sys
class MyWindow(Gtk.ApplicationWindow):
def __init__(self, app):
Gtk.Window.__init__(self, title="example", application=app)
self.set_default_size(350, 200)
self.set_border_width(10)
hbox = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL)
hbox.set_layout(Gtk.ButtonBoxStyle.EXPAND)
button = Gtk.Button(label="a")
hbox.add(button)
button2 = Gtk.Button(label="b")
hbox.add(button2)
self.add(hbox)
class MyApplication(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
def do_activate(self):
win = MyWindow(self)
win.show_all()
def do_startup(self):
Gtk.Application.do_startup(self)
app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
With regards to a question about the desktop environment I'm using.
I've tried Mate, Unity and Gnome-Shell. All work the same way. I've removed the title and those controls. Still the same thing happens. To me this looks more like a GTK issue.
I believe that GtkButtonBox imposes some layout constraints on its buttons that you may not want here. Try using buttons in just a regular GtkGrid, but give them the GTK_STYLE_CLASS_LINKED CSS class.
For each button, do:
button.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED)

Resources