Replacing widgets in a xrc created dialog - python

I would like to replace a dialog's object dynamically but didn't find a way. The dialog is created with the help of a xrc definition file.
All what I found is https://www.blog.pythonlibrary.org/2012/05/05/wxpython-adding-and-removing-widgets-dynamically/: needs to access a sizer, but xrc does not provide access to sizer objects as far as I know.
Can anybody help?
My system: python 2.7, wxpython 3.0.2.0, win7
Best regards
Humbalan

With the help of Goyo's post I found the following solution:
def exchange_control( self, control, coded ) :
"""
Exchanges a wx.TextCtrl (where wx.TE_PASSWORD is set) with a wx.TextCtrl (where
wx.TE_PASSWORD is not set) and vice versa. A similar code could be used to exchange
for exampe a DirPickerCtrl with a FilePickerCtrl.
:param wx.TextCtrl control: Contains a coded or encoded text dependend on `coded`.
:param bool coded: True if the text in `control` is coded, False if it is encoded.
:returns:
A new wx.TextCtrl with the coded (`coded` was False) or encoded Text (`coded` was
True) of `control`.
:rtype: wx.TextCtrl
"""
containing_sizer = control.GetContainingSizer() # get the sizer containing `control`
# Compute arguments for a new TextCtrl.
# - The value of style is arbitrary except the TE_PASSWORD part.
# - a_validator is a validator checking correctness of the value of `control`, is
# optional.
kwargs = { 'parent' : control.Parent,
'id' : wx.ID_ANY,
'value' : control.GetValue(),
'size' : control.Size,
'style' : wx.TE_NO_VSCROLL|wx.TE_PASSWORD if coded else wx.TE_NO_VSCROLL,
'name' : control.Name,
'validator': a_validator
}
# Setup a new TextCtrl with the arguments kwargs
new_text_ctrl = wx.TextCtrl( **kwargs )
containing_sizer.Replace( control, new_text_ctrl )
containing_sizer.Layout()
return new_text_ctrl
# In the calling code:
# The returned `the_text_ctrl` is the TextCtrl Field with the (en)coded Text and `coded`
# is the boolean value which shows if the text whithin this field is coded or encoded.
the_text_ctrl = self.exchange_control( the_text_ctrl, coded )
EDIT:
I changed the code since I found out that the method Replace() is much easier to use than my first attempt.

Related

How to Set and Get "comment" text using setText?

I am trying to store string data within a QAbstractButton.text().
Why?
I want to display the short name in the text() itself, but be able to call the long name via the text() "comment" through code.
You are able to write "comments" within QT Designer, but I have been unable to replicate this in Python.
Looking at the code in notepad, it appears the "comment" text is created within the text string itself:
<property name="text">
<string extracomment="toast">Select object and click here</string>
What I currently have in python is:
Xsl = cmds.ls(sl=1)[0]
Xbutton.setText(Xsl)
How can I also set and get the comment part of this text?
Any advice would be appreciated!
If you want to add extra data to a widget why not just subclass it and create your own?
class MyCustomButton(QtWidgets.QPushButton):
def __init__(self, parent=None):
super(MyCustomButton, self).__init__(parent)
self.my_variable = None
Now you can continue using MyCustomButton just like a normal button, and also add whatever you like to my_variable.
I have found that every object contains a variable for windowTitle. If this isn't the main window, the window title is generally left blank, therefore I can store data here.
Granted, this probably isn't the cleanest approach, but it'll serve for now.
Green Cell's subclassing is most likely the best way to resolve this issue. However, I am mainly building the UI using Qt Designer, and want to primarily keep any edits within that wrapper.
def store_selected_node_on_button(self):
"""
Changes the text of a given button to store an object's name
As the button isn't a window, I can set the window title to store the long name of the selected object.
:return: None
"""
button = self.sender()
sl = cmds.ls(sl=1, long=True)
if not sl:
button.setText("Select object and click here")
button.setWindowTitle("")
else:
button.setText(sl[0].split("|")[-1])
button.setWindowTitle(sl[0])
return

PyQt5 double spin box returning none value

I have this: (there are class methods, which inherience from QWizard)
def getForms(self):
return [
(
QtWidgets.QLabel("Name"),
QtWidgets.QLineEdit()
),
(
QtWidgets.QLabel("Roll"),
QtWidgets.QDoubleSpinBox()
)
]
def registerFields(self, page, forms):
page.registerField("name*", forms[0][1])
page.registerField("roll", forms[1][1])
And in other place in code
id = self.currentId()
if id == 1:
print self.field("name") # this rightly give me a name from LineEdit
print self.field("roll") # but this give me just None, why?
When I changed
QtWidgets.QDoubleSpinBox()
to
QtWidgets.QSpinBox()
Line:
print self.field("roll")
works fine.
Why do I get None instead double value?
EDIT
I've just noticed that when I'm trying make 'roll' field as a mandatory.
page.registerField("roll*", forms[1][1])
And I fill this 'spinbox' in program, I can not click 'next' (next is disabled). I have spinbox in my form in program. I can set the value there. But this looks like this field is not connected with QWizard(?)?
The QWizardPage class only has internal knowledge of a few widget types. When registering a widget it does not know about, you need to specify the property for reading the value, along with the signal that is emitted when a value is changed, as a string.
For QDoubleSpinBox this would be:
page.registerField("roll", forms[1][1], "value", "valueChanged")
The list of widget types QWizardPage knows about is listed in the c++ documentation here.
You can also register this information globally using a method of QWizard, so that you don't have to specify it each time you call registerField(). To do this, call:
my_wizard.setDefaultProperty("QDoubleSpinBox", "value", "valueChanged")
Note: This is a method of the wizard, not the page.

How to see all attributes of a node in Maya?

I'm trying to update some attribute values in Maya via code and I'm having a really tough time accessing them.
I need the attributes displayed name, (I'm pretty sure this is supposed to be their 'Nice Name'), but I can't seem to get them in any way. Using listAttr or using OpenMaya's MFnAttribute both don't give me what I want - they pass back long names and short names and cleaned up 'Nice Names' but none of those are the displayed UI name to the attributes.
As an example my node contains an attribute with the name 'Horizontal Damping Factor' under a drop down titled 'Advanced Anchor Controls.' When I query the node for a list of attribute nice names I get the similar name 'Anchor HDamping Factor', but that is not the displayed name. This is true for 23 other attributes as well.
Do you have any ideas on what's going on?
(All of these attributes are located in fields two dropdowns deep as well, is that a problem?)
EDIT: It's definitely because the attributes are two dropdowns deep... I still have no idea what these dropdowns are called or how to access the attributes contained inside of them.
EDIT 2: Well, I was wrong, the name of the attribute IS different from the name that's displayed, (when I adjust a slider the editor window shows the name of the value I just changed, which is different from the displayed name, and it's not just a 'Nice' name version of it either.) Still trying to figure this out.
EDIT 3: Not sure how clear this is but it shows the discrepancy between the UI label of the attribute and the 'Nice Name.' There is no 'Horizontal Damping Factor' in the list of attribute names.
FINAL EDIT: Looks like it's not possible to change the value of an attribute by querying the UI name if the attribute was given a different UI name then the authoritative name on creation. Created my own mappings in code instead.
The authoritative names are the ones in listAttr; the names you see in the UI are not reliable because an AETemplate can override them in any way it wishes. The AE often presents fake attributes which are used to calculate the real values -- for example, cameras have an angleOfView in the attribute editor, but setting it actually changes the focalLength attribute; there is no angleOfView on the camera node.
So, you may need to do some detective work to figure out what the visible UI is really doing. Playing with the sliders and watching the history is the usual first step.
FWIW
dict(zip(cmds.listAttr(), cmds.listAttr(sn=True)))
will give you a dictionary mapping the long names to the short names, which can be handy for making things more readable.
If you knew the longName of the attribute in question and the name of the node it is part of, you can use attributeQuery with the longName, shortName or niceName to get the it's long name, short name and nice name respectively.
From what I understand from your question, you want to be able to look at ll of this information for all the attributes of the node so you can make the right decision of which attribute to choose to do what you are doing. Here is a quick script I wrote, a simple query of sorts, to give you just that: (Interspersed generously with explanation as comments)
import maya.cmds as cmds
def printAttributes(node, like=''):
''' Convinience function to print all the attributes of the given node.
:param: node - Name of the Maya object.
:param: like - Optional parameter to print only the attributes that have this
string in their nice names.
'''
heading = 'Node: %s' % node
# Let's print out the fancy heading
print
print '*' * (len(heading)+6)
print '** %s **' % heading
print '*' * (len(heading)+6)
# Let's get all the attributes of the node
attributes = cmds.listAttr(node)
# Let's loop over the attributes now and get their name info
for attribute in attributes:
# Some attributes will have children. (like publishedNodeInfo)
# We make sure we split out their parent and only use the child's name
# because attributeQuery cannot handle attributes with the parentName.childName format.
attribute = attribute.split('.')[-1]
# Let's now get the long name, short name
# and nice name (UI name) of the atribute.
longName = cmds.attributeQuery(attribute, node=node, longName=True)
shortName = cmds.attributeQuery(attribute, node=node, shortName=True)
niceName = cmds.attributeQuery(attribute, node=node, niceName=True)
# if we the 'like' parameter has been set, we'll check if
# the nice name has that string in it;
# else we skip this attribute.
if like and like.lower() not in niceName.lower():
continue
# Now that we have all the info we need, let's print them out nicely
heading = '\nAttribute: %s' % attribute
print heading
print '-' * len(heading)
print 'Long name: %s\nShort name: %s\nNice name: %s\n' % (longName,
shortName,
niceName)
if __name__ == '__main__':
# Let us get a list of selected node(s)
mySelectedNodes = cmds.ls(sl=True)
# Let us do the printAttributes() for every selected node(s)
for node in mySelectedNodes:
# Just for example, just printing out attributes that
# contain the word 'damping' in their nice (UI) name.
printAttributes(node, like='damping')
Note that we use the command listAttr() to get a list of all the attributes of the node. Note that we can also use attributeInfo() to get a list of the attributes also. The advantage of attributeInfo() over listAttr() is that attributeInfo() has more filter flags to filter out the list based on various parameters and properties of the attributes.
It is worth it to go check out the documentation for these:
attributeQuery
attributeInfo
listAttr

How to change Operator's label in Blender 2.63 depending on the context?

I'm writing an exporter for a game my friend and I are making and it involves setting custom properties and tags to objects which are then recognized in the game and dealt with accordingly. Our engine, which is written in C/C++ has been successfully tested with my current version of the export script, and I''m currently working on tidying it up.
The script uses Blender's feature of custom properties to write custom tags to output file. The model typically consists of multiple 'parts' (Blender mesh objects that are parented to form a tree, with one 'parent' and multiple 'child' objects) and some of those parts are simple Blender Empty objects (for only it's X, Y and Z coordinates are needed) with custom properties that mark where things like ship's propulsion (it's a 3D shooter) are placed, or where the flames/explosions appear when ship's been shot. Those empty parts are also parented to either 'root' object or any of it's children. So far it's been working good, I have written a generic Operator class and some extended classes that reside in a panel which set part's properties (pretty handy since you don't have to add those custom properties by hand).
Now I want to speed thing up even more, that is to be able to simply click on an operator of desired type, and it should automatically add it to the scene and parent it to the active/selected object. I know how to do that, but I can't get those operators to change their labels. Basically, what I want is to operator to say 'Bullet point' when an existing empty is selected (I've got that part done), and when there's a mesh object selected to say 'Add bullet point'. So I just need a way to dynamically change operators' labels depending on the context (as the title of the question states clearly :))
This is what I got so far:
class OBJECT_OT_tg_generic (bpy.types.Operator):
bl_label = "Sets Generic Part Type"
bl_idname = "rbm.set_generic_part_type"
OB_TYPE = None
#classmethod
def poll (cls, context):
act = context.active_object
if 'Type' in act.keys ():
if act['Type'] == cls.OB_TYPE:
cls.bl_label = 'foo'
print (cls.bl_label)
# this prints foo but doesn't change the label
return False
return True
def execute (self, context):
# TODO: add code to automatically place empties and parent them to active object
bpy.context.active_object['Type'] = self.OB_TYPE
return{"FINISHED"}
And an example of a subclass:
class OBJECT_OT_tg_bullet_point (OBJECT_OT_tg_generic):
bl_label = "Bullet Point"
bl_idname = "rbm.set_bullet_point"
OB_TYPE = OT_BULLET_POINT
Here's how it looks in Blender:
http://i.imgur.com/46RAS.png
Guess I solved it. When you're adding an operator to a panel, you can do something like this:
def draw (self, context):
layout = self.layout
row = layout.row()
row.operator("foo.bar", text="Whatever you want")
and the "Whatever you want" is going to be your button's label. But what I did was something else. I didn't change the operators' labels, but instead gave them a different icons depending on whether it's a mesh or an empty currently selected/active:
def draw (self, context):
# (...) we're skipping some code here, obviously
act = context.active_object
if act.type == 'MESH':
op_icon = 'ZOOMIN'
else:
op_icon = 'EMPTY_DATA'
row = layout.column(align=True)
row.operator('rbm.set_bullet_point', icon=op_icon)
row.operator('rbm.set_rocket_point', icon=op_icon)
# (...) rest of the code

Access gtk.TreeIter inside render() Function of gtk.CellRenderer -- pygtk

I am trying to code the following: Two Columns. One contains a itemId, the other one contains a typeId. I want to render the itemId only when the typeId equals a specific value.
class IDRenderer(gtk.CellRendererText):
def __init__(self):
gtk.CellRendererText.__init__(self)
def do_render(self,window, widget, background_area, cell_area, expose_area, flags):
if ----} Condition to ask for value of the typeId - Cell {-----:
gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area,
expose_area, flags)
gobject.type_register(IDRenderer)
I don't know how to get the iter of the currently rendered row which i need to determine the value of the typeId. Is this even possible?
I now found out, thanks to a nice guy on #pygtk on gimpIRC:
You can do that, with binding so called cell data functions to the corresponding gtk.TreeViewColumn as done here in this example
def renderId(celllayout, cell, model, iter):
if model.get_value(iter,1) == 3:
cell.set_property('visible',True)
else:
cell.set_property('visible',False)
treeviewcolumn = gtk.TreeViewColumn()
renderer = gtk.CellRendererText()
treeviewcolumn.add_attribute(renderer,'text',0)
treeviewcolumn.set_cell_data_func(renderer,renderId)
I ommited some code relevant to render a complete treeview, but i think it shows what i wanted to do and how to do it.
The column renderes the value in the first column (0) of the model only if the value in the second modelcolumn (1) equals 3
I hope this could help someone some time.
It's not possible as far as I know. You need to use properties of the custom renderer which will be set automatically by the code calling the rendering function. (Like the text property of CellRendererText -- the rendering code doesn't get the text from the tree model, but the tree model sets the text property of the renderer before calling the rendering code.)

Categories