When you use Qt_Designer or Qt_Creator to design a form, objectName for any given widget is always set to something. But if you create a widget in code AND you need the objectName later, you have to assign it explicitly. So then the widget assignment takes at least two lines of code. This seems very inelegant.
Example:
button1 = QPushButton('button caption') # at this point objectName is some kind of empty string
button1.setObjectName('button1')
If you need to find the widget later (i.e. with findChild), you must have objectName set, otherwise you're out of luck.
Is there some way to automatically set objectName without extra lines of code? I looked at the PyQt5 Reference Guide and could not find such a feature. A similar question was asked on Code Review, but got no answers. Two lines of code isn't the end of the world, but it seems redundant. Somehow I'm required to assign this name twice once for Python (first line) and again for Qt.
You can pass objectName as a keyword argument when creating the button.
button1 = QPushButton('button caption', objectName='button1')
This can extend this to any Qt property during initialization:
button1 = QPushButton(text='button caption', objectName='button1', icon=icon1)
Moreover, signals can be connected when constructing an object, too:
button1 = QPushButton(text='button caption', objectName='button1', clicked=someMethod)
The added named argument is equivalent to button1.clicked.connect(someMethod)
Related
I imagine this is a fairly basic Tkinter question - but I am a noob and I haven't seen this answer after some searching.
I would like to be able to check what the attribute of my canvas is in Tkinter.
So,
canvas = tk.Canvas(root, 200,200, bg="blue")
canvas2 = tk.Canvas(root, 200,200, bg="red")
canvases = [canvas, canvas2]
What I am looking for is something to check what the attribute is of the canvas. For example -
for canvas in canvases:
if canvas.get_color() == "red": # IS THERE SOMETHING LIKE get_color... or get_attr(bg)?
print("HECK YA")
else:
print("I'm feeling blue")
Thanks for the help!
you can call canvas.config('attribute') to obtain the value of a given attribute.
For instance canvas.config('bg') returns the value of the background.
Calling canvas.config() without arguments will return a dictionary of the current configuration
Universal Widget methods that relate to configuration of options:
The methods are defined on all widgets. In the descriptions, w can be any widget of any type.
w.cget(option): Returns the current value of option as a string. You can also get the value of an option for widget w as w[option].
w.config(option=value, ...)
Same as .configure().
w.configure(option=value, ...)
Set the values of one or more options. For the options whose names are Python reserved words (class, from, in), use a trailing underbar: 'class_', 'from_', 'in_'.
You can also set the value of an option for widget w with the statement w[option] = value
If you call the .config() method on a widget with no arguments, you'll get a dictionary of all the widget's current options. The keys are the option names (including aliases like bd for borderwidth). The value for each key is:
for most entries, a five-tuple: (option name, option database key, option database class, default value, current value); or,
for alias names (like 'fg'), a two-tuple: (alias name, equivalent standard name).
I'm writing a GUI application with tkinter and I have different classes that represent different pages of my application. I'm trying to call a variable from one class to another, so I used the repr() function to gain some insight into how to call that object from one class to antother.
I called print(repr(listbox))
my listbox is within a class named SelectionPage and is defined as listbox = tk.Listbox(self)
the representation printed as <tkinter.Listbox object .!selectionpage.!listbox>
I'm new to python and I'm confused as to what these exclamation marks mean. Is this even a good way to debugg?? Sorry if this is a duplicate question, I'm really confused and I couldn't find the answer elsewhere
What do exclamation marks mean in the representation of an object
They don't mean anything. Tkinter developers simply chose to give all of their autogenerated internal widget names a leading exclamation point.
Tkinter is just a python wrapper around a tcl/tk interpreter. In tcl/tk, a widget is represented as a hierarchy of widgets. For example, the root widget is .. A frame in the root might be named .frame. A listbox in that frame might be named .frame.lb. Tcl is very broad in what it will accept - essentially, a widget name can be any character other than "." so using an exclamation point is legal, though uncommon in the tcl/tk world.
When you create a Tkinter widget by instantiating one of its classes, tkinter must create a valid widget name to pass to the tcl interpreter in order to create the actual widget. In older versions of Tkinter it used unique numbers (eg: '.4368693152.4368765368'). In python3, tkinter chose to use more human-friendly names prefixed with an exclamation point.
As far as I know, the exclamation point has no special meaning, though I suppose it might be useful in determining whether a widget name was created by tkinter or through some other means.
I'm trying to call a variable from one class to another, so I used the repr() function to gain some insight into how to call that object from one class to antother.
There are almost certainly better ways to do that than to use the internal name of the widget. However, if you know the internal name of the widget (eg: ".!selectionpage.!listbox") and insist on doing it this way, you can convert the string name to the tkinter widget instance with the universal widget method nametowidget.
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
listbox = tk.Listbox(frame)
listbox_name = str(listbox)
lb = root.nametowidget(listbox_name)
assert lb is listbox
By the way, you can give widgets a name if you don't like the autogenerated name. This works for all widgets except the root widget.
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root, name="my_frame")
listbox = tk.Listbox(frame, name="my_listbox")
assert str(listbox) == ".my_frame.my_listbox"
I am learning Python w/ Tkinter and I recently learned the difference between the reference and the name/instance of a widget. The reference is the string you assign to a widget (which can be changed later on), but the name seems to be the actual identity of the widget (which is immutable). Essentially it seems as though the reference is a nickname of a widget because it can change overtime and be different depending on who you are talking to, while the actual name of the widget on the widget's drivers license is always the same. Specifically, in this line of code...
sample_frame = Frame(root, name = 'frame1', bg = 'blue', height = 50, width = '50')
"sample frame" is the reference, while 'frame1' is the name.
Unless I specifically assign the string 'frame1' as the name of this frame, python automatically generates a number sequence as its name. In order to view the name of any widget you just have to add...
print(str(sample_frame))
(the output in this example is .frame1)
So in Tkinter if I wanted to place this frame in my GUI i would have to pack/grid/place it in the following line like so...
sample_frame.pack()
But what I would like to do is call the pack method on this frame widget by its name rather than its reference. Something like this...
frame1.pack() #or
.frame1.pack() #because of that period
The problem is that Python claims frame1 was never defined, and .frame1 is invalid syntax. Does anybody know how to do something like this? Thanks.
For broader context I am doing this because I iterated the creation of 21 different frames and placed them in a 3x7 grid. Because of this all 21 frames have an identical reference. BUT, I made sure to make the name of each frame corresponds with its position.
The name= option sets the name of the widget within the Tcl environment that actually implements the GUI - it has no effect on the Python side. The only reason I can think of for doing this is that it might make Tcl error messages somewhat easier to read (the auto-generated widget name that you'd otherwise get is not particularly meaningful).
As always, the proper way to deal with multiple objects created in a loop is to store them in a container of some sort. In your case, it could be a 21 element list, a nested list (widget[row][column]), or perhaps a dict indexed by tuples (widget[row, column]).
While I fully agree with jasonharper's answer that you should keep a proper reference to the widgets and I do not recommend using what I'm about to explain, there actually is a way to achieve what you're asking. There's a widget method called nametowidget(), which returns the widget object when you give it a name. Note that you should call the method on the object (Tk, Toplevel, Frame) that contains the widget you're looking for.
So following your example, this works:
from tkinter import *
root = Tk()
sample_frame = Frame(root, name = 'frame1', bg = 'blue', height = 50, width = '50')
root.nametowidget('frame1').pack()
root.mainloop()
And if you would do the same with a button inside the frame you should do:
sample_button = Button(sample_frame, text='Button', name='button1')
sample_frame.nametowidget('button1').pack()
I have managed to get a widget to appear by calling a function, then make it disappear by using the destroy method.
Unfortunately, the only way I managed to do that is by making the object global, which I understand is a bad way of doing things. I have tried several times to destroy the object without using global, but none of them worked.
Here's the relevant code:
def hayHijos():
print("ii")
z=var9.get()
if z==1:
var10 = StringVar()
global numHijosMat
numHijosMat=OptionMenu(app,var10,1,2,3,4,5,6,7,8,9,10)
numHijosMat.grid(column=2)
elif z==0:
print("aa")
numHijosMat.destroy()
var9 = IntVar()
hijosEntrePartes = Checkbutton(app, text="Hijos entre las partes", variable=var9, command=hayHijos)
hijosEntrePartes.var=var9
hijosEntrePartes.grid()
Two general possibilities.
Either you create a class context to keep track of elements such as widgets using class variables (self.widget for example). Therefor have a look at the class documentation
You return and pass the widget to / from your function.
This is not very suitable for callbacks but a general approach
def function(var, widget=None):
""" Widget is an optional argument. If not passed, it is initialized with None """
if var.get()==1:
# some code to create your widget
# make sure to use 'widget = ...' to create it
# to have the reference for your return
# call at the end of the function
else:
# some code to destroy your widget
# e.g. widget.destroy()
return widget
Using this code makes it easy for you to use the widget without making it global. Another variable is used in a global behaviour in your code. "var9". You should also pass this one on.
You would need to adapt your callback using maybe a lambda to pass both.
More recommended would be the class approach over here as lambdas often lack in scope of readability of code and reusability of code. (Could call it also "bad habit" but IMHO this is highly influenced by use case)
If you want to reuse the widget and only want to make it appear / disappear as the use of a Checkbutton suggests, I would rather recommend grid_remove method instead of widget.destroy method
Would it be considered bad practice to use a widgets title attribute to refer it?
For example I have a number of custom radioBoxCtrls on a panel
I only ever need to get/set all the values at once
so the container class(a panel) for the radioBoxCtrls objects has the following methods
get_options()
set_options()
To set options for all the radioBoxCtrls a dictionary is passed to the set_options() method.
Each key, value pair in the dictionary is a title of a radioBoxCtrl and the title of the button on the radioBoxCtrl that should be set
def set_options(self, options={}):
"""
This method sets which radio button is selected
on each RadioBoxCtrl object
#param options: A dictionary
Each key is the title of a RadioBoxCtrl
each keys value is the button on the radio box that is to be selected
"""
for option_box in self.option_boxes:
if option_box.title in options.keys()
option_box.set_selected_button(options[option_box.title])
def get_options(self):
"""
This method returns a dictionary of the selected options
Each key is the title of a RadioBoxCtrl object
and each keys value is the name of the button selected on the radio box
"""
options = defaultdict(list)
for option_box in self.option_boxes:
options[option_box.title]=option_box.get_selected_btn()
return options
So (in an attempt to be clear) when I call the set method from my controller
I pass in a dictionary like so:
options = {"Name of radioBoxCtrl": "Option 2", ... }
self.gui.optionsPanel.set_options(options)
Why the hell do you want do that? (you may ask)
Short answer: mvc
I want to create a suitable layer of abstraction. All that my controller needs to know with
regard to the options is how to get them to pass to the model when some processing needs to be done and how set them when a config file is loaded...
I thought it would simplify things if I could just call one method to set and vice-versa -but Im not so sure now!
I realize that this is probably more of question on the acceptability of refering to objects by some string attribute they posses, which in my case just happens to be the title.. so feel free to answer it accordingly..
Please feel free to improve the title of question(I wasnt really sure how to word it) and add tags.
Thanks
I can't tell whether the gurus would call it bad practive. I just know I'd never do it - it is slower, more error-prone, possibly makes it very hard to change a title's name (admittedly, only if you hardcode it everywhere) and (arguably) very inelegant. At least you should associate te options with the widget's identity, which still smells imho, but at least has a few problems less. Maybe that whole part of the application should be refactored to be less centralized so the issue disappears completely.
Edit on how to refactor: Honestly, I can't tell - I know little about the application. The obvious approach would be subclassing all widgets and making them responsible for somehow getting the options and changing accordingly. But I can't tell whether that's really feasible.