We need to be able to handle a "playable" (play/pause/seek) effect in which the nature of the effect cannot be determined at compile time.
The problem we are running into is resetting the target(s) state after the effect has completed. If we manually drag the seek slider back to the beginning, everything works fine. However, if we set the playheadTime of the composite effect back to 0, the effected targets retain their original value until the playheadTime gets to the correct position to effect the target.
Here is a simplified (as much as I could) test case with view source enabled:
http://www.openbaseinteractive.com/_tmp/PlayableEffectTest/
The problem is demonstrated if you let it play to the end, and then hit the play button to start it over.
What is the best way to go about manually resetting the target values given that the exact nature of the effect is unknown?
Many thanks for your time!
edit
I forgot to mention we are using Flex 4.5 preview release.
Have you tried:
effect.reverse()
More info
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/effects/IEffect.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/effects/IEffect.html#reverse()
Well it's a little kludgy, but I was able to accomplish this by calling some internal methods on the effect to capture the start values, then assigned those values to the targets on a reset.
import mx.core.mx_internal;
use namespace mx_internal;
private var _propertyChangesArray:Array;
protected function captureStartValues(effect:Object):void
{
effect.captureStartValues();
_propertyChangesArray = effect.propertyChangesArray;
}
protected function reset(effect:Object):void
{
for each(var change:PropertyChanges in _propertyChangesArray)
{
var target:Object = change.target;
for(var p:String in change.start)
{
if(target.hasOwnProperty(p))
{
var startVal:* = change.start[p];
var endVal:* = target[p];
if(!isNaN(startVal) && startVal != endVal)
{
target[p] = startVal;
}
}
}
}
effect.playheadTime = 0;
}
I don't know if this is the best way to accomplish this, but it seems to be working so far. I am absolutely open to suggestions for a better method.
Cheers!
Related
I'm working with QML and Python3.6 + PySide2 and I'm trying to write script in QML that takes two integers from a connection in python and compares them to decide what image background to use for the window.
There are a few things I'm struggling with. First, I am unsure how to compare my numeric (sunset and sunrise) variables. Second, I don't know how to write an if statement-esque part that produces a background image conditionally. Third, I don't think it's best to do this under Connections, and maybe even in my QML, but I'm not sure how to move my variables somewhere else.
I really appreciate any pointers or help!!
The data I'm drawing from looks like this:
"sunrise":1592565499,"sunset":1592617094
The QML pseudo-ish code:
Connections {
target: weather
function onDataChanged(){
if(!weather.hasError()){
var sunrise = weather.data['dt']['sunrise']
var sunset = weather.data['dt']['sunset']
if (sunrise <= sunset)
Image {
source: "night.png"}
else
Image {
course: "day.png"}
}
You cannot create QML-items from if-statements like that (neither from State's). You can call Qt.createComponent if you like, however, it is rather overkill in this example. You should directly set the source property of your image:
Image {
id: image_tod
}
Connections {
target: weather
function onDataChanged(){
if(!weather.hasError()){
var sunrise = weather.data['dt']['sunrise']
var sunset = weather.data['dt']['sunset']
if (sunrise <= sunset)
image_tod.source = "night.png"
else
image_tod.source = "day.png"
}
}
}
Looking at the code, you might actually be able to bind it directly to the source property (not sure what your model exactly looks like):
Image {
source: {
if(weather.data['dt']['sunrise'] <= weather.data['dt']['sunset'])
return "night.png"
else
return "day.png"
}
}
This works because when compiling the QML, the engine creates a dependency from every referenced variable (weather and data in this case), and re-evaluates the whole binding if any of them signals a change. To make fully use of this, you should also expose hasError as a property rather than a function (and emit whenever it changes).
Follow-up update
Yes, you can make it as wild as you want. I think you mean this:
Image {
source: {
if(weather.data['dt']['sunrise'] <= weather.data['dt']['sunset'])
return "night.png"
else if(weather.data['dt']['sunrise'] > weather.data['dt']['sunset'])
return "day.png"
else
return "" //means no image
}
}
I've been messing around with CM conventions trying to understand how they work but i haven't found a decent article somewhere explaining step-by-step how and why.
However I've found a few code snippets that i've been working with with some success.
In this case, however, i don't understand what is going on.
I'm trying to bind a NumericUpDown Value and Maximum to a corresponding ViewModel property. I was able to do it with the following code:
Value
ConventionManager.AddElementConvention<NumericUpDown>(NumericUpDown.ValueProperty, "Value", "ValueChanged");
Maximum
ConventionManager.AddElementConvention<NumericUpDown>(NumericUpDown.MaximumProperty, "Maximum", "MaximumChanged");
var baseBindProperties = ViewModelBinder.BindProperties;
ViewModelBinder.BindProperties =
(frameWorkElements, viewModels) =>
{
foreach (var frameworkElement in frameWorkElements)
{
var propertyName = frameworkElement.Name + "Max";
var property = viewModels.GetPropertyCaseInsensitive(propertyName);
if (property != null)
{
var convention = ConventionManager.GetElementConvention(typeof(NumericUpDown));
ConventionManager.SetBindingWithoutBindingOverwrite(
viewModels,
propertyName,
property,
frameworkElement,
convention,
convention.GetBindableProperty(frameworkElement));
}
}
return baseBindProperties(frameWorkElements, viewModels);
};
However, an here comes the weird part, i can only make one of them to work. That makes me believe that i'm doing some noob mistake somewhere. It almost seems i can only call AddElementConvention and therefor only the last call is executed.
I would appreciate either a help with this piece of code or a reference to some good documentation that could help me with it.
Best Regards
i found out somewhere that CM only allows one convention per item so that's the reason of this behavior...
However since items like ComboBox allows binding for multiple properties (SelectedItem, ItemSource and so on...) i'm not completed convinced...
i wonder if it is possible to pre-configurate Flex Elements on the Server. I have the Problem with a custom ItemRenderer which turns out to be very slow. It would be very cool to pre-process such an element on the server instead in the clients browser... somehow? Maybe it is possible to produce the MXML dynamically on the server for that.
This is it basically. I create a Label for each data entry in an array list. This entry is added to a BorderContainer and this goes to the containing element as a whole here. Sometimes i add 200 - 300 items this way which is costing very high computing cost at client side. So i wonderd if i could just pass this as a whole dynamic mxml element to the client.
override public function set data(value:Object):void {
_data = value as WordResultObject;
var data:WordResultObject = _data as WordResultObject;
this.removeAllElements();
if(_data!=null)
{
_l.text = data.wordform;
_l.setStyle("fontSize", data.fontSize);
_l.setStyle("color", data.color);
_l.toolTip = "Frequency: " + data.freq;
if(data.date != null)
{
_l.toolTip += "\nDate: " + AppUtils.TimeString(data.date as Date);
_l.addClickEvent(data.id as int, data.date as Date);
}
_border.addElement(_l);
this.addElement(_border);
}
}
Thank you
Andreas
I wonder if it is possible to
pre-configurate Flex Elements on the
Server.
Not that I know of. Perhaps if you go back to Flex 1 / 1.5 which was primarily a server based platform. I do not expect rolling your code back to an "old" server would improve efficiency at all, though. How would you expect this work? What benefit are you expecting to receive.
I have the Problem with a custom
ItemRenderer which turns out to be
very slow.
Show your code; and perhaps we can help you with writing your renderer to be more efficient.
In flex component life cycle, after we make some change in a components property, invalidation methods schedules a call to methods like commitProperties, updateDisplayList, etc for some later time. I need to call the updateDisplayList instantaneously. Is there some direct way to do this.
Currently, both the labels are changed simultaneously after completion of the loop. Instead I need it to work like this, to first render the updated 'myButton1' label then enter the loop and then update myButton2's label. I know, it is elastic race track issue, but isn't there some way to achieve this ?
myButton1.label = 'New Label1' ;
// Some logic to forcibly make the screen reflect it
for (var i:int = 0; i < 500 ; i ++){
//a dummy loop
}
myButton2.label = 'New Label2' ;
You can use myButton1.validateNow() but it's use is discouraged, for you may end up having the same component update itself multiple times on the same frame.
Use validateNow() . But, I would use it sparingly. using invalidateDisplayList() will force updateDisplayList() to run on the next renderer event.
A render event happens on each frame. 24 frames happen each second by default for Flex. Are you sure need to change these values quicker?
I would set the label for myButton1, then put the remaining code into a separate method and call that method using callLater:
private function foo():void {
myButton1.label = 'New Label1';
this.callLater(bar);
}
private function bar():void {
for (var i:int = 0; i < 500 ; i ++){ //a dummy loop
}
myButton2.label = 'New Label2';
}
This way myButton1 will update to show the new label before going into your loop since callLater doesn't call bar until after the event queue is cleared, giving myButton1 a chance to update.
invalidateDisplayList() or valdiateNow() will do it for you however excess of using these will end up memory leaks.
I have problem on deleting component which is created on runtime. Please help me.
heres my code in creating component
var oh: ObjectHandles = new ObjectHandles;
oh.x = event.localX-xOff;
oh.y = event.localY-yOff;
Canvas(event.target).addChild(oh);
oh.addEventListener(KeyboardEvent.KEY_DOWN,deleteSel);
oh.width=270;
oh.height=200;
oh.mouseChildren = true;
var vdo:FXVideo = new FXVideo;
vdo.source = "http://thehq.tv/wp-content/uploads/flv/funny-people-trailer.flv";
vdo.percentHeight = 100;
vdo.percentWidth = 100;
oh.addChild(vdo);
code in keyboard delete event
private function deleteSel(event:KeyboardEvent):void
{
if(event.charCode == 127)
{
FXVideo(ObjectHandles(event.target).getChildAt(0)).stop();
delete(ObjectHandles(event.target).getChildAt(0));
ObjectHandles(event.target).removeAllChildren();
ObjectHandles(event.target).parent.removeChild(ObjectHandles(event.target));
delete ObjectHandles(event.target);
}
}
after I delete the Object Handles Component(inside is FxVideo Component) the memory usage is still there. How to remove the memory allocation of component after deletion?
You need to remove the event listener, or you can add the event listener with a weak reference:
oh.addEventListener(KeyboardEvent.KEY_DOWN,deleteSel,false,0,true)
I would not recommend calling delete. Calling removeAllChildren should take care of it. Although, by looking at your code, that probably is not necessary either. Once you remove the event listener it should get cleaned up.
delete only works with dynamic objects and won't have an affect here. I personally recommend explicitly removing the event listener:
event.target.removeEventListener(KeyboardEvent.KEY_DOWN,deleteSel);
as well as using the weak reference suggested by Osman.