QML: Is there any reason to set a context property instead of setting a property on the root object, when the latter way has advantages? - qt

Consider this setting of a context property:
engine.rootContext()->setContextProperty("text", "hey");
And this qml code:
Text {
text: text
}
This doesn't work as expected, because of the name shadowing.
One fix is to name the context property _text - then there's no problem. But I don't want to have to remember this naming convention (both to remember to use it, and to remember what it means when I read it).
Another fix is to change the context property into a property on the root object:
engine.rootObjects()[0]->setProperty("text", "hey"); // [1]
and in the QML:
text: window.text
Is there any reason to avoid the latter way when it seems so good?

Setting a context property has one disadvantage - it requires recompilation of the C++ code. Naturally, that's a non-issue if you are exposing a C++ object, which is the typical and only logical use case.
It may be seen as an advantage that the context property can be available before the root object is created, but in most cases it will work equally well if it is just a property of the root object. And I clarify that "most cases" here means that object properties are typically not being set from C++ and not being used in initial bindings, since obviously, if you do that, the initial binding will fail to resolve the property when the object is created, before the property is being set. Also note that when creating QML object form C++, you can use the two step process - prepare the object, then set any properties and so on, and only then complete the object creation, which will delay binding evaluations, so you won't have that problem.
Another possible advantage is that you can have more than one root object, thus if the property is in the root context, then all root objects and their subsequent children will be able to resolve it.
Another possible advantage - when you set a context property, you typically get assistance from the IDE, as that object is now known to be out there, so you will have auto-complete for that property name. It sometimes works for parent object properties as well, but it is a hit-or-miss.
As for the speed of access, I am not so sure that a context property would be faster than root object property. I haven't tested it, but it is just logical. Any object property is a de-facto context property, as each object has its own context. Considering that, as you recently discovered, context properties can be shadowed by object properties, it is thus logical to conclude that a context property lookup simply goes down the object contexts until it finds that property rather than going straight to it, so if anything, resolving context properties should be a tad slower than resolving root object properties, because they go one step deeper.

Until line [1] has been executed, window.text is undefined, so at first, you get an error message on the console (though once it gets to executing line [1], the text is displayed). But if you use a context property, you're free to create it before loading the QML file, which removes the problem.

Related

QML: scope of id's

Say I've created an object with an id foo. How far is the object visible by the name foo? Everywhere in the current file? Everywhere in the current directory? Or what?
I read the QML documentation on scope but I didn't find info on this there.
I found the answer in the id attribute documentation.
It says:
An object can be referred to by its id from anywhere within the component scope in which it is declared.
And "component scope" is defined in the scope documentation page
Using ids to reference objects in a single source is all fine and dandy, but there is more to the story. ids will also resolve down the object tree:
ApplicationWindow {
id: main
visible: true
width: 600
height: 300
property int test: 667
Obj { }
}
// Obj.qml
Item {
Obj2 {}
}
// Obj2.qml
QtObject {
Component.onCompleted: console.log(main.test)
}
As this example illustrates, the main id resolves from Obj2 that is created in Obj that is created in main.qml. In this aspect the id works very much like dynamic scope properties, with one notable exception, dynamic scope properties will only work if they are declared in the root object of a particular source, whereas the id will resolve regardless of which object it is declared in, as long as it is in that source and of course, as long as it is not shadowed. I guess internally they do end up sort of transferred to the root object, as they become attributes for that component. Common logic dictates lookup performance would be similar to that of dynamic properties. To further reinforce the similarity with dynamic scope properties, both are not possible to chain and only resolve from the particular qml file. You cannot someObject.someDynamicScopeProperty just like you cannot someObject.someId, even if both the property and the id are resolvable from within SomeObject.
This is actually a lot more useful than it may appear initially. It will allow to reference objects down a known tree branch without the need to go back and meticulously interface properties or aliases in order to expose the branch structure for lookups. This is also a very good reason to use meaningful and unique identifiers for ids, rather than using id: root for the root object of every qml file.

Multiple "default" properties/methods in a VB6 class?

I am trying to make a replacement VB6 class for the Scripting.Dictionary class from SCRRUN.DLL. Scripting.Dictionary has (among other things) a "Keys" method that returns an array of keys, and a read/write "Item" property that returns the item associated with a key. I am confused about this, because both of them seem to be defaults for the class. That is:
For Each X In MyDict
Is equivalent to:
For Each X In MyDict.Keys
Which to me implies that "Keys" is the default operation for the class, but:
MyDict("MyKey") = "MyValue"
MsgBox MyDict("MyKey")
Is equivalent to:
MyDict.Item("MyKey") = "MyValue"
MsgBox MyDict.Item("MyKey")
Which to me implies that "Item" is the default operation for the class.
I've never before created a VB6 class that had a default operation, so upon realizing this, I thought perhaps I could define multiple default operations as long as they all have different signatures, which they do: Keys is nullary, the Item getter takes a Variant, and the Item setter takes two Variants. But this doesn't seem to be allowed: When I use "Tools/Procedure Attributes" to set the Keys function to be the default, and then I use it to set the Item property to be the default, the IDE complains that a default has already been set.
So I think I'm misunderstanding something fundamental here. What is going on in the Scripting.Dictionary object that makes it able to act as if "Keys" is the default in some contexts, but as if "Item" is the default in others? And whatever it is, can I accomplish the same thing in VB6?
OK, answering my own question: I haven't tried this yet, but I gather that "Item" should be made the default, and that I should add an entirely new function called "NewEnum" that looks something like the following (slightly modified from an example in Francesco Balena's "Programming Microsoft Visual Basic 6.0" book):
Public Function NewEnum() As IUnknown
Set NewEnum = m_Keys.[_NewEnum]
End Function
(where "m_Keys" is a Collection containing the keys), and then use Tools/Procedure Attributes to hide NewEnum and to set its ProcID to -4.
What you are observing is the difference between the default member and a collection enumerator. A COM object (including VB6 classes) can have both.
You can identify the default property of a class by looking in the Object Browser for the tiny blue globe or the words "default member of" in the description (see Contents of the Object Browser). The Object Browser will not identify an enumerator method, but if you look at the class's interface definition using OLE View or TypeLib Browser (free but registration required) it's DispId will be 0xfffffffc or -4.
In your own class, you can mark the default property by setting the Procedure ID to "(default)" in the Procedure Attributes dialog (see Making a Property or Method the Default). You already listed the steps for setting up the collection enumerator in your own answer, but you can find this listed as well in the Programmer's Guide topic Creating Your Own Collection Class: The House of Bricks.
Scripting.Dictionary has a dirty secret:
It does not handle enumeration at all, it returns big ugly Variant arrays and your For Each loops iterate over those.
This is one of the reasons why a Dictionary can actually be far less efficient than a standard VB6 Collection.

Prevent deletion of folder (type) in Plone unless parent is being deleted

I want to prevent deletion of instance of a folderish type in Plone, excepting when a parent folder is being deleted. I have a hunch that an event handler can prevent deletion (raise an exception in an IObjectRemovedEvent handler bound to a marker interface), but implementing the exception to this rule seems harder to pin down: I want to allow deletion of the item when its parent folder is being deleted.
My initial hunch was that I could traverse __parent__ pointers and check if an attribute was set on the parent (or some distant indirect container) that it was being deleted, but I am not sure how I would set this attribute on deletion of the parent prior to an attempt to delete the contained child (otherwise not allowing deletion). So now, I'm questioning whether I am approaching this the right way? Am I misunderstanding the problem constraints for deleting objects in Plone? Should I look to plone.app.linkintegrity source to borrow tricks?
My use case: a "settings folder" inside a "project" folder -- intrinsically, the settings folder should never be allowed to be deleted unless the project folder itself is being deleted. I guess I'm asking for referential integrity for a very-specific containment relationship.
How would you prevent deletion of an object except in the case it was being deleted as a direct result of its parent being deleted?
After a fair bit of time, I have finally found a solution to this general problem (but in a different context):
Create a subscriber on the parent for the parent's interface and OFS.interfaces.IObjectWillBeMovedEvent.
That handler should use zope.globalrequest.getRequest() to get the request object, and adapt that to IAnnotations to get an annotations object on the request (a global for the duration of the request, in practical terms).
The IObjectWillBeMovedEvent handler will save a tuple form of the physical path tuple(context.getPhysicalPath()) on the annotation object.
The event handler for IObjectRemovedEvent attempting to block deletion of the child item will pass if its path shares a base path of the parent (see example).
By consequence, an exception is only raised if an attempt is made to delete the object itself, but not when its parent is deleted.
See example: http://bazaar.launchpad.net/~upiq-dev/upiq/dev-uu.formlibrary/revision/113

Set HttpContext.Current.Request.Browser.Type in ASP.NET

Is there a way to set HttpContext.Current.Request.Browser.Type in ASP.NET. That is a read-only, string property. So you cannot really just assign a string to it. Tried initializing Browser property which is of type HttpBrowserCapabilities, but it has only one constructor and does not take any parameters and browser Type is still null. The reason why I want to be able to set Type for browser is that my unit test is failing as Type property is null.
Edit per null check comments:
I could definitely modify code to check for null, but that will be just for my unit test as when the requests come from browsers, that value is never null. Hence not quite excited about doing that. But it can be my last resort.
You can define your own browser definition files which ASP.net will then use. Check out http://forums.asp.net/p/955969/1176101.aspx.
So if you know what browser it's failing on you could setup a browser file for it. However, I agree that checking for null values makes more sense as it accounts for a lot more possiblities that way.
You might want to think about refactoring your code to use HttpContextBase instead of relying directly on the concrete type.
By doing so you could simply create a stub object that provides the behavior you want. Eventually implementing your own HttpBrowserCapabilitiesBase object.
You would then have full control to use your mock types during unit testing. Indeed that is what they were created for.

React to change on a static property

I'm re-writing an MXML item renderer in pure AS. A problem I can't seem to get past is how to have each item renderer react to a change on a static property on the item renderer class. In the MXML version, I have the following binding set up on the item renderer:
instanceProperty={callInstanceFunction(ItemRenderer.staticProperty)}
What would be the equivalent way of setting this up in AS (using BindingUtils, I assume)?
UPDATE:
So I thought the following wasn't working, but it appears as if Flex is suppressing errors thrown in the instanceFunction, making it appear as if the binding itself is bad.
BindingUtils.bindSetter(instanceFunction, ItemRenderer, "staticProperty");
However, when instanceFunction is called, already initialized variables on the given instance are all null, which was the cause of the errors referenced above. Any ideas why this is?
You have 2 options that I am aware of:
Option 1
You can dig into the code that the flex compiler builds based on your MXML to see how it handles binding to static properties. There is a compiler directive called -keep-generated-actionscript that will cause generated files to stick around. Sleuthing through these can give you an idea what happens. This option will involve instantiating Binding objects and StaticPropertyWatcher objects.
Option 2
There is staticEventDispatcher object that gets added at build time to classes containing static variables see this post http://thecomcor.blogspot.com/2008/07/adobe-flex-undocumented-buildin.html. According to the post, this object only gets added based on the presence of static variables and not getter functions.
Example of Option 2
Say we have a class named MyClassContainingStaticVariable with a static variable named MyStaticVariable and another variable someobject.somearrayproperty that we want to get updated whenever MyStaticVariable changes.
Class(MyClassContainingStaticVariable).staticEventDispatcher.addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
function(event:PropertyChangeEvent):void
{
if(event.property == "MyStaticVariable")
{
someobject.somearrayproperty = event.newValue as Array;
}
});
I think you need to respond to the "PropertyChanged" event.
If you're going to do that, use a singleton instead of static. I don't think it will work on a static. (If you have to do it that way at all, there are probably a couple ways you could reapproach this that would be better).
var instance:ItemRenderer = ItemRenderer.getInstance();
BindingUtils.bindProperty(this, "myProperty", instance, "theirProperty");
After fiddling with this for a while, I have concluded that this currently isn't possible in ActionScript, not even with bindSetter. It seems there are some MXML-only features of data bindings judging by the following excerpt from the Adobe docs (though isn't it all compiled to AS code anyways)?
You cannot include functions or array
elements in property chains in a data
binding expression defined by the
bindProperty() or bindSetter() method.
For more information on property
chains, see Working with bindable
property chains.
Source: http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_7.html
You can create a HostProxy class to stand in for the funciton call. Sort of like a HostFunctionProxy class which extends from proxy, and has a getProperty("functionInvokeStringWithParameters") which will invoke the function remotely from the host, and dispatch a "change" event to trigger the binding in typical [Bindable("change")] Proxy class.
You than let the HostProxy class act as the host, and use the property to remotely trigger the function call. Of course, it'd be cooler to have some TypeHelperUtil to allow converting raw string values to serialized type values at runtime for method parameters (splitted by commas usually).
Example:
eg.
var standInHost:Object = new HostFunctionProxy(someModelClassWithMethod, "theMethodToCall(20,11)");
// With BindingUtils.....
// bind host: standInHost
// bind property: "theMethodToCall(20,11)"
Of course, you nee to create such a utlity to help support such functionality beyond the basic Flex prescription. It seems many of such (more advanced) Flex bindings are usually done at compile time, but now you have to create code to do this at runtime in a completely cross-platform Actionscript manner without relying on the Flex framework.

Resources