Can anyone clarify this statement from the WatchKit Development Tips page?
Simplify controller scenes.
Reduce the number of hidden objects as much as possible to significantly improve load time. For example, five versions of a controller’s layout in a single controller scene will result in all objects being created before the controller is displayed
I've read it a few dozen times now and can't figure out what it's trying to say.
What is a "scene"? Is it referring to the Storyboard scene?
Are "hidden objects" referring to literally hidden UI elements like a hidden button?
How is it possible to have five versions of a controller's layout? This just does not compute.
When a storyboard is loaded, and there are, say, 10 different WKInterfaceControllers in the file, will that be very slow even if the initial controller is blank? Aren't these only loaded as-needed? Or, would it be better to have a single table with 10 prototype rows - each of which is actually a stand-alone interface - which are only instantiated one at a time?
Because we can't programmatically add interface elements to controllers in the current version of WatchKit, any interface elements that we might need to display must be included in a Storyboard scene. By including these initially-hidden elements, we can programmatically hide or unhide these elements as needed.
For example, it's common to include a full-screen label that is initially hidden. Then, if a full-screen message needs to be shown for some reason, the text is populated, the label is unhidden, and the rest of the elements on the screen are hidden. To make hiding a set of elements easier, they're typically included in a WKInterfaceGroup, so that only the top-level group element needs to be hidden.
So, to answer your questions:
Indeed, a "scene" is a standard Storyboard scene.
Yes, "hidden objects" is referring to literally hidden objects as I've described above.
Using the method I've described, you could create five top-level WKInterfaceGroup elements, each with its own set of controls and layout. Then, you'd likely unhide the one that makes sense to display and hide all the others.
I use these techniques in my own app, though I typically don't have more than three top-level groups.
So yes, because it takes time to initialize and layout all of these elements (even if they're hidden), the recommendation is to keep it to a minimum.
Regarding the loading of interface controllers in a storyboard, you're correct that only the interface controllers that are needed are loaded. However, if you have a set of five page-based controllers, they'll all be loaded and initialized before the first page is activated. Other controllers would then be loaded as appropriate.
Creating unique rows is another possibility, but whether you do that or simply hide/unhide top-level groups depends on your app's specific needs. As always, it's worth testing on actual hardware.
Related
I have a set of QML items distributed all over my UI. They display data from a remote device and their content needs to be updated regularly. The Items are spread on several tabs and hidden in nested ListView instances, so most of them won't be visible to the user all the time.
In order to keep the bandwidth low I want to update only those items that are currently visible to the user.
I am looking for the right hook to get the information which of these Items is currently displayed from within the Item, without relying on information from the parents. If they were all placed in ListView delegates I could use the delegate's Components onCompleted and onDestroyed signals. Since this is not the case I am stuck at finding out how to get this information.
Am I missing something here? Is there an onPaintFinished signal or something similar? My workaround would be to add that logic to the parent containers, but that would be tedious, since there are several kinds of container that can contains these display Items.
Instances that are on delegates of a ListView will not exist until they would be in the visible range or the cache range around the visual area of the list view. If the delegate moves outside of that range, it is destroyed. So, no need to worry about instances hidden there.
Furthermore, items are currently not visible are also not drawn. They are not entered into the scene graph, and hence, not rendered. So, instances of your items appearing on tabs that are currently not current will also not be drawn. However, these items do still exist of course.
Figuring out if an item is effectively visible or not is quite a hard problem though. QML delegates part of that to OpenGL (clipping for instance). There is not feedback on the result of that. You could in theory lift that information out of the renderer, but that would require customizing that and that is very hard. You could take a look at the heuristics that GammaRay uses to warn about items not being visible. Perhaps you can take some inspiration from that.
How do you use QStyledItemDelegate / QItemDelegate to permanently display a complex widget, i.e. to handle the Qt.DisplayRole, not just Qt.EditRole?
The documentation eludes to using paint()... but that's just way to complex! Let's take for example rendering a QTreeView or QTableVeiw inside of a QTableView cell.
There is QAbstractItemView.setIndexWidget(), but that is a bad idea as it only used to display static content (and whats the fun in static models?).
Note
I found part of the answer in another post, but it was only a small subset of the answer, so I thought it warranted a new post with proper question.
The key is to use QAbstractItemView.openPersistentEditor() to always keep the cell in edit mode.
Some additional key elements
The Qt.EditRole flag will need to be provided for the cells which use delegates.
QStyledItemDelegate.sizeHintChanged.emit(index) needs to be called anytime the size of the editor widget has changed.
Implementing QStyledItemDelegate.sizeHint() can be tricky and tedious (or you can do index.internalPointer().editor_widget.sizeHint() assuming you saved a reference of the editor to the internal pointer during QStyledItemDelegate.createEditor()
here is a good post on how to determine sizes: What are the mechanics of the default delegate for item views in Qt?
Caution
It should be mentioned that opening editors is costly, so if you have thousands of indexes and they are all loaded at once, it can take a while. There are many ways to mitigate this issue:
Load the model incrementally using a thread
Use Qt's fetchMore() mechanism
call openPersistentEditor incrementally (using a timer, or as they come into view for the first time)
call openPersistentEditor when the parent is expanded and closePersistentEditor when the parent is collapsed, and possibly restrict the use of expand-all on nodes with many children.
What would be the preferred (recommended) way to rearrange the components of a QML UI on an event such as a button click?
I do have designed a UI consisting of a bunch of custom components, each of them is bound to a (C++) model. I use anchors to position the components in the ApplicationWindow.
On a button click, I want to add an additional component which, due to its size, makes it necessary to rearrange the existing components. Actually, the button could be understood as switching into a different mode such as the Debug view in an IDE (think of Eclipse).
My goal is to keep the components in a consistent state between views and make the switch as fluent as possible.
I could think of these options:
Design two different views, and use a Loader or State to switch between them. As initializing the models takes some time, they should remain not be deleted during switching. I think, setting them as ContextProperty of the QMLApplicationEngine should take care of that.
Do a lot of rearranging in the onClicked()-Handler of the button. Probably the worst solution but does not require to reinitialize the components and models.
I think the most elegant solution would be to initialize the components in a some kind of container (or model) and then assign different layouts to this container on button click. But I have no idea, if this is possible and how to achieve that.
Edit: I followed approach 1. by using the StackLayout. It works basically, but it seems as if the invisible UI is still running in the background and consuming resources (e.g. requesting Images from my QQuickImageProvider).
Using a Loader eliminates this problem as it destroys the previous UI on every change of the source property. What I do like about the StackLayout however is that it preloads all UIs on app startup. So the transitions are smoother compared to the Loader. Is there a way to disable the invisible UIs?
Your option (1) is the one giving your the most flexibility, since you have two entirely separate UIs depending on state.
As you already discovered yourself this requires keeping all relevant state data in a way that is accessible by both UIs, e.g. in C++ or in globally accessible QML/Script objects.
I would go for that if you envision any more changes or tweaks than just rearranging elements.
If you are sure that rearranging elements will be sufficient, have a look at QML states
It is a bit like a combination of your options (2) and (3).
The state setup allows you very locally to define anchors and other properties for each element depending on a named state.
The button's signal handler then only needs to change which of the states is active, by assigning one of the names to the respective state property.
I have a spark List in which I am adding custom components manually (without an item renderer). Everything is working fine except when I add hundreds of items in the list (based on several calculations), the render time increases to several seconds.
My questions are:
Does flex automatically delay the rendition of components that are added to a list?
If not then, how can I tell the spark list to only render the components once everything is added.
I hope what I am asking makes sense. I am pretty sure the render time is increasing because I have quite a few components inside the custom component. I just need a simple answer to my questions.
One more thing to know is that,
ActionScript 3 runs on flash player based on even-driven. It means that if you run a chunk of code in blocking, anything else, including rendering, will not be executed until the code ends.
This design sometimes causes issues: If a developer adds too much job in one blocking function, the player looks frozen and unresponsive momentarily. (If the code is an infinite loop, it stops until the player detects and throws ScriptTimeoutError, or possibly forever.)
However, for your question, yes, anything will be delayed until the adding job is done. Flex components doesn't basically split the job automatically.
Flex components are rendered in the list according to the Flex lifecycle: http://help.adobe.com/en_US/flex/using/WS460ee381960520ad-2811830c121e9107ecb-7fff.html
There are two reasons your List may be running slow, your components may not be following the Flex lifecycle correctly and the second because virtual layouts are not enabled on your List.
First the hard one:
Your first step should be to ensure you are following the correct phases in your custom components for commitProperties(), measure() and updateDisplayList(unscaledWidth:Number, unscaledHeight:Number).
So long as you are not calling any method named validateNow() on either your UIComponent subclasses or the LayoutManager then your components should follow this just fine.
The second may be that your list layout is not using virtual layout. This is enabled by default in a Spark List component but if you're overriding the layout you should ensure that useVirtualLayout is set to true on whatever layout you're using.
This is the property to enable it:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/components/List.html#useVirtualLayout
This is what it does to speed up rendering many items in a Spark DataGroup component (which a List is):
http://help.adobe.com/en_US/flex/using/WSc2368ca491e3ff92-1483ec4a12136246e76-8000.html#WS685c4ccbb85d340c6617851e121d4b8abae-8000
I have an MXML component in a website that I am reusing in a few different screens.
Based on what the user clicks, the component initializes a few values and starts playing media.
Now if I click through the site, and I play media in screen 1, the component initializes fine and plays the media.
If I then go to screen 2 and play the media, the component initializes twice. Once for screen one, and once for screen 2.
When I then go to screen three, it will start initializing three times.. So it is creating a new instance of the MXML component for each screen!!
How can I make sure that the MXML component only initializes for the screen that I need it to initialize for?
What I really want is that this component always has just one instance throughout the whole application. Is it possible to make that MXML component into a Singleton, so that I always have one instance of that MXML in my application?
Can you explain this a little more indepth? What do you mean by "screens"? It sounds like you have your component nested in some sort of view stack, and that your screens are different sections in the view stack, but it's hard to tell here.
Regardless, I think the solution is to abstract the part of your component that you want to be a singleton from the view. Flex initializes objects in view stacks in a lazy manner by default, but this can be overridden using the creationPolicy property on the ViewStack object. It sounds like your creationPolicy is initializing children as you access them, and something in your component code is causing other instances of the same object to re-fire some initialization code when others are created (possibly in your experimenting for an MXML singleton.)
To truly achieve your desired effect, you should probably just write a bit of actionscript that intelligently re-parents the display object you only want to be created twice. The idea of a "singleton" doesn't make as much sense when we're talking about view objects on screens - to have it displayed in many places, you need many instances, and the process of re-parenting is slightly more complicated than the singleton pattern, so you'll need to do a bit of creative logic around that.
Hope this helps - again, please feel free to post some more source code if you want a more specific response.
Why don't you make the component into a module and use it that way. You Load and/or Unload a module and use it where ever you like! in just calling it as a single item! and you have very much less overheads in your application.