Silverlight databinding with IronPython and Datagrid - python

We've been successfully using clrtype with IronPython 2.6 and
Silverlight for databinding, based on the example provided by Lukás(:
http://gui-at.blogspot.com/2009/11/inotifypropertychanged-and-databinding.html
We create the binding when we create the datagrid columns programatically. Because we are using IronPython some of the static databinding techniques you would normally use with C# don't work.
I've been trying (and failing) to get a column in the grid show
different colors based on databinding.
I've got the colored bubble showing in the grid, but can't get
databinding to the color to work. First the basics.
This is the xaml for the bubble with a fixed color:
<DataTemplate xmlns='http://schemas.microsoft.com/client/2007'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Ellipse Stroke="#FF222222" Height="15" Width="15">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop x:Name="bubbleColor" Offset="0.694"
Color="#FF00FF40" />
<GradientStop Color="#FFE6E6E6"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
I can add a column based on this template very simply. The loadXaml function is a thin wrapper around XamlReader.Load:
from com_modules.loadxaml import loadXaml
from System.Windows.Controls import DataGridTemplateColumn
column = DataGridTemplateColumn()
column.CellTemplate = loadXaml('templatecolumn')
column.Header = 'Bubble'
grid.Columns.Add(column)
If I try to naively specify a binding in the xaml then I get a
PARSER_BAD_PROPERTY_VALUE when I attempt to load the xaml (so no hope of
setting up the binding after load):
<GradientStop x:Name="bubbleColor" Offset="0.694" Color="{Binding color}" />
One approach I tried was to create a ValueConverter. Here is the
skeleton of the class I created:
from System import Type
from System.Globalization import CultureInfo
from System.Windows.Data import IValueConverter
class ColorConverter(IValueConverter):
_clrnamespace = "Converters"
__metaclass__ = clrtype.ClrClass
#clrtype.accepts(object, Type, object, CultureInfo)
#clrtype.returns(object)
def Convert(self, value, targetType, parameter, culture):
pass
#clrtype.accepts(object, Type, object, CultureInfo)
#clrtype.returns(object)
def ConvertBack(self, value, targetType, parameter, culture):
pass
As there is a _clrnamespace specified I thought I might then be able to use this converter in xaml. Trying to reference the ColorConverter class in the Converters namespace in a resources dictionary again causes blow ups when loading the xaml.
Setting this up programatically would be ideal. Anyone got any ideas?

I don't know anything about IronPython, but I know that you cannot bind to a Color in Silverlight, regardless of the language used. This has caused me many grievances. In Silverlight 3 you can only bind properties on a FrameworkElement, and since GradientStop is a DependencyObject, it will not work. The good news is that Silverlight 4 will get rid of that limitation and allow you to bind properties on DependencyObject. I haven't tried it though, so I cannot say for sure. More info here:
http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind
At the moment, what you could do is to bind the Fill property on the Ellipse instead. But then you will have to create the entire LinearGradientBrush in your converter code, so it's a bit complicated.

Related

Setting value of a iterable property of a Python COM object

I am using pywin32 to automate some tasks in software that has an Automation Server technology interface (formerly OLE Automation Server).
This software comes with a somewhat detailed manual with code examples in VBA, C++ or Matlab but no Python. I have built a Python library that can do most of the functionalities built into the software but there are some parts I cannot do in Python.
I cannot change the value of a property if this property is contained in a iterable COM object.
What I can do:
[Documentation for Visibility property]
import win32com.client
app = win32com.client.Dispatch('NAME_OF_APP')
app.Visibility = True
As an example, with this code, I can change the visibility parameter of the software: if it runs with or without GUI.
What I cannot do:
[Documentation for getting and setting current device]
import win32com.client
app = win32com.client.Dispatch('NAME_OF_APP')
app.CurrentDevice(0) = 'NAME OF DEVICE'
I then get the following error:
SyntaxError: cannot assign to function call here. Maybe you meant '==' instead of '='?
This error makes sense to me but I cannot find a way to set any of these software properties when they come in the form of an iterable object. As soon as I have to specify an index, I don't know how to set the value.
From what I understand, in C++ we are able to change the value because of pointers but how can we achieve the same thing in Python? Is it possible or do I have to use some C++ code in parallel to my Python to run my library? I don't know anything in C++ so if I could avoid doing that, it would be good.
What I have tried
Of course, the 1st thing I tried was to change () to [] or {} which logically didn't work.
Then I used the Evaluate function in PyCharms to see what was hiding behind my app.CurrentDevice. I was hoping to find sub-attributes that I could then set but I don't see anything inside the object:
[Result of Evaluate on the CurrentDevice object]
Finally, I have tried the following:
import win32com.client
app = win32com.client.Dispatch('NAME_OF_APP')
curr_device = app.CurrentDevice(0)
curr_device = 'NAME OF DEVICE'
I wanted to affect the object to a variable and then change the value but of course, this only rewrites the variable curr-device with 'NAME OF DEVICE' but loses any link to COM Object.
I feel like my questions are similar to the following unanswered question:
How can I set the value of an indexed property of a COM object in Python?
It looks as if win32com is struggling to set the property if there is an additional argument to the put function, which is a little surprising.
First thing to do is to use
app = win32com.client.gencache.EnsureDispatch('NAME_OF_APP')
This creates a Python wrapper for the COM object (rather than just firing function calls at the object and hoping). This may in itself clear up your issue.
If not, here is a quite ugly way of working around. As you have identified, the relevant part of the type library is:
[id(0x00000018),propput, helpstring("property CurrentDevice")]
HRESULT CurrentDevice([in] long lAcq, [in] VARIANT pVal);
And you can use this to set the property at a low level.
win32com dispatch objects are a wrapper for the PyIDispatch object. All dispatch objects support the Invoke method, and you can use this to call the function yourself. NB. Since I don't have access to your COM object, I can't test, so this answer may need some tweaking (!).
The PyIDispatch documentation
Try:
import win32com.client as wc
import pythoncom
app = wc.gencache.EnsureDispatch('NAME OF APP')
app.Visibility=TRUE
newVal = wc.VARIANT(pythoncom.VT_VARIANT,'NAME OF DEVICE')
app._oleobj_.Invoke(24,0,pythoncom.INVOKE_PROPERTYPUT,0,0,newVal)
There are a lot of 'magic' numbers here, but basically:
24 = 0x00000018 in decimal: this is the Id of the property
0 = the LCID, the Locale Id ... I always set it to 0
pythoncom.INVOKE_PROPERTYPUT = the type of call.
0 = whether you care about the return type (you probably don't = False)
0 = first parameter, lAcq, as in CurrentDevice(0)
newVal = second paramter,pVal, the new device name as a VARIANT
I haven't tried this, but pythoncom is pretty good about converting VARIANT types, so you might not need the VARIANT creation, and can just use NAME OF DEVICE directly as the parameter.

Manipulating userforms using xlwings

I was trying to find out the methods of the pywin32 object of a userform ComboBox in Excel, but I honestly has no idea what I'm doing and got nowhere.
VBA Code (Sending combobox object to python):
Private Sub ComboBox1_Change()
s = test(ComboBox1)
End Sub
Python Code :
#xw.func
def test(obj):
print(obj._dict__)
So, the print above returned this :
{'_oleobj_': <PyIDispatch at 0x03957A90 with obj at 0x01218C8C>, '_username_': 'IMdcCombo', '_olerepr_': <win32com.client.build.LazyDispatchItem object at 0x03FB0FD0>, '_mapCachedItems_': {}, '_builtMethods_': {}, '_enum_': None, '_unicode_to_string_': None, '_lazydata_': (<PyITypeInfo at 0x03957B50 with obj at 0x0121919C>, <PyITypeComp at 0x03957B68 with obj at 0x012196F4>)}
I guess I was expecting to see the same methods/properties found in VBA, but I have no idea what to take from this.
Anyone knows a way to manipulate userform/controls directly from python using xlwings?
Specifically I'm looking for dynamically adding new controls to the userform, reading/modifying controls attributes, and ideally modifying their events, all through python.
I guess I was expecting to see the same methods/properties found in VBA, but I have no idea what to take from this.
You can take anything from this, but this isn't a real Combobox nor something from COM environment - it's just a "wrapper" object over a COM object, implemented via IDispatch interface, and it's possibily thanks to the win32com dependency.
Because of that there's no an "intellisense"-like feature, but you're still able to use properties/methods:
#xw.func
def test(obj):
# accesing method
obj.AddItem('Hello world!')
# accesing property
obj.Enabled = False
also you can pass an UserForm as obj to add a new control to it:
#xw.func
def test(obj):
# add new label
control = obj.Add('Forms.Label.1')
# accesing property
control.Caption = 'Hello world!'
When looking under the documentation for xlWings under the Shapethere does seem to be access to all properties.
Under missing features, you can find a workaround using .api to access all vba methods. Through this you could create and modify controls, just like you would do in VBA.
Also what you could do is using the macro(name)-function you could create functions in VBA to modify, create comboboxes and pass values to the function, i.e to create a combobox at position x, y , width, height and pass these parameters trough python.
As it seems, you cannot access events trough xlWings. But i've found IronPython, it uses the .NET interop facilities to access the excel object and events. As you can see under the documentation, you can work with the excel object as you would do in C#, VB.NETetc..
So as a conclusion, i would suggest you looking up the documentations of both. As they both reveal the excel object to python you should be able to do what you want with one of them.

Python-pptx: “Show legend overlapping the chart”

I am using Office 2007.
I found if I would like to show the legend overlapping the chart in office2007.
The XML should be as the following.
`-<c:legend>
<c:overlay val="1"/>`
But no matter I use the API from python-pptx 'chart.legend.include_in_layout = True' or I leave it as the default. The generated XML would always be as the following.
`-<c:legend>
<c:overlay/>`
Without the val=1, then office2007 won't show the format properly.
What can I do to force the python-pptx to write the val=1? thanks.
Explanation
In short, the True value is not explicitly set (in contrast to False) because True corresponds to the default value of overlay's val attribute.
To explain it in more detail - you can follow the python-pptx hierarchy as follows: overlay is mapped to CT_Boolean (all overlay oxml elements are instantiated from CT_Boolean). The actual val parameter is then mapped via OptionalAttribute and is defined with the default value of True:
class CT_Boolean(BaseOxmlElement):
"""
Common complex type used for elements having a True/False value.
"""
val = OptionalAttribute('val', XsdBoolean, default=True)
Now, when setting the optional attribute to its default value, it is actually skipped/deleted, as you can see here if value == self._default:
class OptionalAttribute(BaseAttribute):
"""
Defines an optional attribute on a custom element class. An optional
attribute returns a default value when not present for reading. When
assigned |None|, the attribute is removed.
"""
#property
def _setter(self):
def set_attr_value(obj, value):
if value == self._default:
if self._clark_name in obj.attrib:
del obj.attrib[self._clark_name]
return
str_value = self._simple_type.to_xml(value)
obj.set(self._clark_name, str_value)
return set_attr_value
Fix - provide custom CT_Boolean class
Add these lines somewhere before you need to use the overlay. It will overwrite python-pptx overlay mapping with the custom CT_Boolean_NoDefault class:
from pptx.oxml import register_element_cls
from pptx.oxml.xmlchemy import BaseOxmlElement, OptionalAttribute
from pptx.oxml.simpletypes import XsdBoolean
class CT_Boolean_NoDefault(BaseOxmlElement):
"""
Common complex type used for elements having a True/False value with no
default value.
"""
val = OptionalAttribute('val', XsdBoolean)
register_element_cls('c:overlay', CT_Boolean_NoDefault)
This worked for me and finally I got:
<c:legend>
<c:overlay val="1"/>
</c:legend>
Fix - modify python-pptx permanently
This is not recommended but you might want to modify python-pptx instead of adding the solution from above for each script you run.
First, add the following to pptx/oxml/chart/shared.py which defines a new bool class without a default value:
class CT_Boolean_NoDefault(BaseOxmlElement):
"""
Common complex type used for elements having a True/False value.
"""
val = OptionalAttribute('val', XsdBoolean)
Second, modify pptx/oxml/__init__.py to add the new bool class:
from .chart.shared import (
CT_Boolean, CT_Double, CT_Layout, CT_LayoutMode, CT_ManualLayout,
CT_NumFmt, CT_Tx, CT_UnsignedInt, CT_Boolean_NoDefault
)
Third, modify pptx/oxml/__init__.py to change the mapping of the overlay element to the new bool class:
register_element_cls('c:overlay', CT_Boolean_NoDefault)
Better solution
In case you have time, please submit a ticket here so it might become a permanent fix. In case #scanny finds some time, he will read this. Perhaps there is some better solution for this, too, and I've completely missed something.
#pansen 's analysis is spot-on. Here's an alternative way to get this working in your case that might be a little lighter weight:
def include_in_layout(legend):
legend_element = legend._element
overlay = legend_element.get_or_add_overlay()
overlay.set('val', '1')
This appears to be a localized non-conformance of that version of PowerPoint with the ISO/IEC 29500 spec. As pansen rightly points out, a missing val attribute is to be interpreted the same as val=1 (True). I'd be interested to discover how extensive this non-conformance goes, i.e. what other elements exhibit this same behavior. The CT_Boolean type is used quite frequently in PowerPoint, for things like bold, italic, varyColors, smooth, and on and on. So a "compensating" fix would need to be applied carefully to avoid reporting incorrect results for other elements.
I think I'll take pansen's cue and use a specialized element class for this element only. It will still report True for an element without the val attribute, which will be inconsistent with the observed behavior on this version of PowerPoint; but assuming other versions behave correctly (according to the spec), the inconsistency will be localized and at least assigning True to that property will make the legend show up the way you want.

Pythonic way to parse command line output into a container object

Please read this whole question before answering, as it's not what you think... I'm looking at creating python object wrappers that represent hardware devices on a system (trimmed example below).
class TPM(object):
#property
def attr1(self):
"""
Protects value from being accidentally modified after
constructor is called.
"""
return self._attr1
def __init__(self, attr1, ...):
self._attr1 = attr1
...
#classmethod
def scan(cls):
"""Calls Popen, parses to dict, and passes **dict to constructor"""
Most of the constructor inputs involve running command line outputs in subprocess.Popen and then parsing the output to fill in object attributes. I've come up with a few ways to handle these, but I'm unsatisfied with what I've put together just far and am trying to find a better solution. Here are the common catches that I've found. (Quick note: tool versions are tightly controlled, so parsed outputs don't change unexpectedly.)
Many tools produce variant outputs, sometimes including fields and sometimes not. This means that if you assemble a dict to be wrapped in a container object, the constructor is more or less forced to take **kwargs and not really have defined fields. I don't like this because it makes static analysis via pylint, etc less than useful. I'd prefer a defined interface so that sphinx documentation is clearer and errors can be more reliably detected.
In lieu of **kwargs, I've also tried setting default args to None for many of the fields, with what ends up as pretty ugly results. One thing I dislike strongly about this option is that optional fields don't always come at the end of the command line tool output. This makes it a little mind-bending to look at the constructor and match it up to tool output.
I'd greatly prefer to avoid constructing a dictionary in the first place, but using setattr to create attributes will make pylint unable to detect the _attr1, etc... and create warnings. Any ideas here are welcome...
Basically, I am looking for the proper Pythonic way to do this. My requirements, for a re-summary are the following:
Command line tool output parsed into a container object.
Container object protects attributes via properties post-construction.
Varying number of inputs to constructor, with working static analysis and error detection for missing required fields during runtime.
Is there a good way of doing this (hopefully without a ton of boilerplate code) in Python? If so, what is it?
EDIT:
Per some of the clarification requests, we can take a look at the tpm_version command. Here's the output for my laptop, but for this TPM it doesn't include every possible attribute. Sometimes, the command will return extra attributes that I also want to capture. This makes parsing to known attribute names on a container object fairly difficult.
TPM 1.2 Version Info:
Chip Version: 1.2.4.40
Spec Level: 2
Errata Revision: 3
TPM Vendor ID: IFX
Vendor Specific data: 04280077 0074706d 3631ffff ff
TPM Version: 01010000
Manufacturer Info: 49465800
Example code (ignore lack of sanity checks, please. trimmed for brevity):
def __init__(self, chip_version, spec_level, errata_revision,
tpm_vendor_id, vendor_specific_data, tpm_version,
manufacturer_info):
self._chip_version = chip_version
...
#classmethod
def scan(cls):
tpm_proc = Popen("/usr/sbin/tpm_version")
stdout, stderr = Popen.communicate()
tpm_dict = dict()
for line in tpm_proc.stdout.splitlines():
if "Version Info:" in line:
pass
else:
split_line = line.split(":")
attribute_name = (
split_line[0].strip().replace(' ', '_').lower())
tpm_dict[attribute_name] = split_line[1].strip()
return cls(**tpm_dict)
The problem here is that this (or a different one that I may not be able to review the source of to get every possible field) could add extra things that cause my parser to work, but my object to not capture the fields. That's what I'm really trying to solve in an elegant way.
I've been working on a more solid answer to this the last few months, as I basically work on hardware support libraries and have finally come up with a satisfactory (though pretty verbose) answer.
Parse the tool outputs, whatever they look like, into objects structures that match up to how the tool views the device. These can have very generic dict structures, but should be broken out as much as possible.
Create another container class on top of that that which uses attributes to access items in the tool-container-objects. This enforces an API and can return sane errors across multiple versions of the tool, and across differing tool outputs!

How do I use the GeometryConstraint class?

I've been trying to get this to work for so long now, I've read the docs here, but I can't seem to understand how to implement the GeometryConstraint.
Normally, the derivative version of this would be:
geometryConstraintNode = pm.geometryConstraint(target, object)
However, in Pymel, It looks a little nicer when setting attributes, which is why I want to use it, because it's much more readable.
I've tried this:
geometryConstraintNode = nt.GeometryConstraint(target, object).setName('geoConstraint')
But no luck, can someone take a look?
Shannon
this doesn't work for you?
import pymel.core as pm
const = pm.geometryConstraint('pSphere1', 'locator1', n='geoConstraint')
print const
const.rename('fred')
print const
output would be
geoConstraint
fred
and a constraint object named 'fred'.
The pymel node is the return value that comes back from the command defined in pm.animation.geometryConstraint. What it returns is a class wrapper for the actual in-scene constraint, which is defined in pm.nodetypes.GeometryConstraint. It's the class version where you get to do all the attribute setting, etc; the command version is a match for the same thing in maya.cmds with sometimes a little syntactic sugar added.
In this case, the pymel node is like any other pymel node, so things like renamimg use the same '.rename' functionality inherited from DagNode. You could also use functions inherited from Transform, like 'getChildren()' or 'setParent()' The docs make this clear in a round-about way by including the inheritance tree at the top of the nodetype's page. Basically all pynode returns will share at least DagNode (stuff like naming) and usually Transform (things like move, rotate, parent) or Shape (query components, etc)

Categories

Resources