I have a GUI built using PyQt4 where I use matplotlib (FigureCanvas) to plot several plots using multiple canvases (this is intentional rather than using subplots). for each canvas I use method:
self.canvas_xx.mpl_connect('scroll_event', self.on_mouse_scroll)
where xx represents the iteration of the canvas to get a signal to perform some action. I want to be able to reference the canvas by its name rather than using:
ylabel = event.inaxes.axes.get_ylabel().split(' ')[0]
where I use the longer method of referncing the ylabel name of each graph.
I looked under the event method using: dir(event) and there is a method called "canvas", but there is no apparent method to get the name of the canvas.
Any ideas?
I am not quite sure what you mean by name, but you can get a a reference to the canvas object via
event_canvas = event.inaxes.axes.figure.canvas
and canvas events are hashable, so you can use them as keys in a dict
other_thing = pre_populated_dict[event_canvas]
to keep track of what ever other data you want to about a given canvas in your code.
In python there is, in general, not a clean way to get the names of references to an object (it can be done, but you shouldn't).
Related
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()
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)
I am trying to animate an existing model I have made in Maya by using a Python script. However, I can't figure out how to access it or its polygons in order to animate them in the script. I know how to select objects beforehand but I just want to write things like this
cmds.setKeyframe( objectName, time = startTime, attribute ='rotateY', value = 0 )
where objectName is either my entire model or a specific polygon in the model
If you want to set attribute values inside your setKeyFrame call like you have shown in your code, you will have to set the attribute string appropriately. Egs. To set the y transform of a vertex attribute and keyframe it you'd do:
objectName = 'pSphere1.vtx[297]'
cmds.setKeyFrame(objectName, attribute='pnty', value=0.7)
# Where 'pnty' stands for Point Y, which is the y transform value for vertices.
Another way would be to perform all the transforms before the call to cmds.setKeyFrame() and call it with the controlPoints=True so it catches vertex and control point changes, as #theodox suggested.
Every Item of Tkinter Canvas widget has a unique item id. I can delete the item by using
canvas.delete(itemId)
Now how do I get back the item on the Canvas assuming I have kept the itemId intact?
canvas.add(itemId) # something like this but add is not a canvas method
I read that it can be done in Tcl/Tk but did not find any equivalent reference for tkinter.
The Canvas widget has no add method, but the different create_ methods. If you have already deleted an item and you want to "restore" it, you should get the options of the item before that and then create a new one with the values you have retrieved.
When you delete it, it's gone. You can't get it back. The item id is just an identifier, not an actual object.
I made a GUI with TKinter that reads a scope trace from a agilent scope. I want the x axis to update when I change time/div. To update the x and y data I use set_xdata and set_ydata. Is there a similar method for updating the x limits?
You need to understand a bit of the object hierarchy. You are calling set_xdata on a Line2D object, which is an Artist which is associated with an Axes object (which handles things like log vs linear, the x/y limits, axis label, tick location and labels) which is associated with a Figure object (which groups together a bunch of axes objects, deals with the window manager (for gui), ect) and a canvas object (which actually deals with translating all of the other objects to a picture on the screen).
If you are using Tkinter, I assume that you have an axes object, (that I will call ax).
ax = fig.subplot(111) # or where ever you want to get you `Axes` object from.
my_line = ax.plot(data_x, data_y)
# whole bunch of code
#
# more other code
# update your line object
my_line.set_xdata(new_x_data)
my_line.set_ydata(new_y_data)
# update the limits of the axes object that you line is drawn on.
ax.set_xlim([top, bottom])
ax.set_ylim([left, right])
so to update the data in the line, you need to update my_line, to update the axes limits, you need to update ax.
set_xlim doc and set_ylim doc
The pyplot.xlim() function changes the range of the x axis (of the current Axes).
The set_xticks() method of Axes sets the ticks. The current Axes can be obtained for instance with gca().