I'm trying to override my animation player functions like this:
tool
extends AnimationPlayer
func seek (seconds:float,update:bool=false ) -> void:
print("seek =>",seconds)
.seek(seconds,update)
func advance (delta:float) -> void:
print("advanced=>",delta)
.advance(delta)
func play (name:String="", custom_blend:float=-1, custom_speed:float=1.0,from_end:bool=false) -> void:
print("play")
.play(name,custom_blend,custom_speed,from_end)
but I have no idea why it isn't working, nothing is being printed when I test it
I'm particularly interested in seek(), when I move the time bar/when the animation is playing, it should be simultaneously print the seconds of where it is
Basically I'm trying to track the time bar
These methods are not virtual.
Some people "override" non-virtual Godot methods (e.g. add_child) but the instances where people report that working, it is because of the type information (or lack thereof). And I should emphasize it is not overriding, but hiding the original method from late binding.
If the code that is calling seek knows it is an AnimationPlayer, it will call seek from the AnimationPlayer class. And since seek from the AnimationPlayer class is not virtual, your code does not have a chance to run. If it were doing late binding, then it might, but you should not rely on that working.
The case we are talking about, is the animation panel in the editor. Which is written in C++ and thus would know it is dealing with an AnimationPlayer.
Hacking the animation panel
First of all, make an EditorPlugin. You can refer to the documentation on Making Plugins. But, to cover the basics:
Go to Project -> Project Settings… -> Plugins and then click on Create.
Godot gives you a form to fill information about the Plugin.
Once you fill the form, it will create an EditorPlugin script on the sub folders of the "addons" folder you specified and with the name you specified.
You got your EditorPlugin script? Good.
The first steps are as I described in another answer.
You can get the current AnimationPlayer like this:
tool
extends EditorPlugin
var edited_animation_player:AnimationPlayer
func handles(object:Object) -> bool:
return object is AnimationPlayer
func edit(object:Object) -> void:
edited_animation_player = object
print(edited_animation_player)
And you can get a reference to the AnimationToolsPanel like this:
var animation_tools_panel:AnimationToolsPanel
func _enter_tree() -> void:
var control:= Control.new()
var animation_player_editor:Control
add_control_to_bottom_panel(control, "probe")
for sibling in control.get_parent().get_children():
if (sibling as Control).get_class() == "AnimationPlayerEditor":
animation_player_editor = sibling
break
remove_control_from_bottom_panel(control)
if animation_player_editor == null:
push_error("Unable to find animation player editor")
Alright, now that we got that, we can extract some stuff from it. In particular we want:
The control that holds the name of the current animation.
The control that holds the current time.
So, let us have variables for those two:
var animation_name_input:OptionButton
var animation_time_input:SpinBox
Now, the first child of the AnimationToolsPanel is the top bar. And the top bar contains the controls we want. So we are going to iterate over the children of the top bar to find them:
var found_option_button:OptionButton
var found_spin_box:SpinBox
for child in animation_player_editor.get_child(0).get_children():
if child is OptionButton:
found_option_button = child as OptionButton
elif child is SpinBox:
found_spin_box = child as SpinBox
if (
is_instance_valid(found_option_button)
and is_instance_valid(found_spin_box)
):
break
animation_name_input = found_option_button
animation_time_input = found_spin_box
Finally, in your _process if the references you got (the animation player, the editor panel, and the controls) are valid, you can get the animation name and time:
var index := animation_name_input.selected
var animation_name = "" if index == -1 else animation_name_input.get_item_text(index)
var animation_time := animation_time_input.value
You want to do that in _process so it updates in real time when an animation is playing from the animation panel.
Of course, the issue is that you want to subclass AnimationPlayer. Thus, in your plugin you can check if the current AnimationPlayer is one of yours, and if it isn't disable this mechanism. But if it is, you can tell your custom AnimationPlayer what is the current animation and time is according to the panel from the plugin code by calling a custom method that takes those values as parameters, and does whatever you need to do with them.
Related
I'm looking for a way to detect whether a Qt widget's ToolTip is visible at the moment when a particular key combination is pressed. If it is, I want to copy the ToolTip's text to the clipboard.
Specifically, I have a QListView containing abbreviated strings, which is set up (via the Qt::ToolTipRole of the associated model) to show the full string of the appropriate list item when the mouse is hovered over it. The behaviour I'm looking for is that if the user presses CTRL-C (as detected by a QShortcut) while the tooltip is visible, then the tooltip text is copied to the clipboard.
My original idea was to use the children() method of the QListView widget to see if there was a tooltip preset among them:
// Inisde the slot connected to QShortcut::activated...
auto children = _ui -> myListView -> children();
QString selectionText;
for (const auto & child : children)
{
if (qobject_cast<QToolTip *>(child))
{
selectionText = qobject_cast<QToolTip *>(child) -> text();
break;
}
}
...but this failed because it turns out that QToolTip does not inherit from QObject.
I've also thought of screening for QEvent::QToolTip events in the ListView's main event handler, and while I could probably get this to work it seems excessively low-level; I'd need to use screen co-ordinates to determine which item in the list was being hovered over and look for the widget's timeout to check that the tooltip hadn't disappeared again by the time that the QShortcut was fired. I'd be disappointed if there weren't a simpler way.
Is there an obvious way forward that I've failed to see?
There are probably several possible solutions, but I am afraid none of them is simple. What I would do is to use the implementation detail that the tooltip actual widget is called QTipLabel. See https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qtooltip.cpp.html#QTipLabel and it inherits from QLabel so you can easily get the text from it.
I am afraid the following solution is just a savage hack. I have not tested it, but it should work.
I would override the data model for your view, specifically override method data() which would call the data() method of the original model class but cache the last value which was returned when this method is called with role == Qt::ToolTipRole.
Then you need to catch the shortcut you are interested in. After it is caught, you get all qApp->topLevelWidgets() https://doc.qt.io/qt-5/qapplication.html#topLevelWidgets` and go through them and check if any of them has class name equal to QTipLabel (use QMetaObject::className()) and is visible, i.e. isVisible() == true.
If you get this visible QTipLabel widget (you hold it via QWidget*), qobject_cast it to QLabel* (you cannot cast it to QTipLabel beause you do not have access to the definition of QTipLabel class because it is in private Qt source file) and get the text with QLabel::text(). If the text is the same as the text which you stored in step 1, then yes, this is the text you are looking for and you can copy it to clipboard or do whatever yo want with it.
Nasty, isn't it? But it is the simplest what I can think of.
PS: I believe that step 1 can be implemented also by catching QEvent::QToolTip for your view and then do some magic to get the text, but I think that overriding data() for model can be a bit easier.
PPS: One obvious drawback is that Qt can rename QTipLabel class in the future. But I would not be worry about it. That won't happen becaus ethey do not change QtWidgets module any more. And if it happens, then you just rename the class in your code. No problem.
PPPS: Another potential corner-case is that some other widget (whose tooltip you do NOT want to capture with that shortcut) actually has the same tooltip text as any of the items in your list view (which you DO want to capture). Then if you display tooltip for your list item, then you move your mouse over to that other widget and hover so that its tooltip gets shown (but you do NOT want to capture it) and then you press that shortcut... But I guess that in reality this will not be your case. I doubt there will be this unlikely clash of tooltips.
With thanks to #V.K., this is what worked:
auto candidates = qApp->topLevelWidgets();
QString selectionText;
for (const auto & candidate : candidates)
{
if (strcmp(candidate->metaObject()->className(), "QTipLabel") == 0)
{
QLabel * label = qobject_cast<QLabel *>(candidate);
if (label->isVisible())
{
selectionText = label -> text();
break;
}
}
}
if (!selectionText.isEmpty())
QGuiApplication::clipboard() -> setText(selectionText);
I have a browser view that on its call method has something like this:
def __call__(self):
context = aq_inner(self.context)
parent = aq_parent(context).
...
Putting a pdb at the beginning and playing with it seems that, for Dexterity there is no need to use it, is that right?
ipdb> self.context, id(self.context), self.context.__class__
(<Container at /plone/ausgaben>, 4651890160, <class 'plone.dexterity.content.Container'>)
ipdb> aq_inner(self.context), id(aq_inner(self.context)), aq_inner(self.context).__class__
(<Container at /plone/ausgaben>, 4651890160, <class 'plone.dexterity.content.Container'>)
So the result is the same using aq_inner or not.
So the question is: does Dexterity (as self.context and in our project actually everything is Dexterity based) prevent us from having to wrap everything with aq_inner and aq_parent and so on, and instead use directly the objects or __parent__ pointers?
Like AT contenttypes, DX contenttypes are also aq-wrapped. So you're going to face the same behaviour (issues :-)) as with AT.
As sdupton said in his aq_parent(instance) == instance.__parent__. The parent pointer ist still implemented thru acquisition.
But there is a small differences to AT.
If you create a new DX obj the following happens:
createContent will be called which creates the DX obj - At this point the content is not yet aq-wrapped. So if you subscripe the ObjectCreatedEvent you gonna have a not aq-wrapped obj.
addContentToContainer will be called, which adds the created DX content to the container. In container._setObject the ObjectAddedEvent will be fired. If you subscribe this event you will have a aq-wrapped dx content.
This is different in AT, of course other events are fired for this case, but the AT content is always aq-wrapped (also in the factory, while adding a new AT obj)
Please let me know, if I misunderstood something.
In paper js I've created a compound path and now I want to implement drag and drop on it. The compound path has two children (two circles).
Here's the issue: when I attach a mouse event to the compound path (using hittest to get the whole path), and click on it, I get the reference only to the first child path. If I click on the second child path, the hittest returns undefined, while I'd like to get the compound path.
Any idea?
Thanks.
Francesco R.
I had the same problem, but there are few ways to make it work.
I assume you use Tool. If so, you can try to get the item not by hit testing, but using ToolEvent's item object
example:
mousedown: function (event) {
if (event.item) {
// event.item is what you need
}
},
If you know what item you'll be hit testing - for example you want to check if user clicked on a specific item, you can call hitTest() from this item
example:
mousedown: function (event) {
var hit = yourItem.hitTest(event.point, { tolerance: 10 });
if (hit) {
// now you can use hit.item or yourItem ('cos you know the item)
}
},
You can try hitTest options. There you can specify what class of items are you looking for:
options.class: Only hit test again a certain item class and its sub-classes: Group, Layer, Path, CompoundPath, Shape, Raster, PlacedSymbol, PointText, etc.
Tell me if that helps or provide some test case. You can create a paperscript sketch here and provide us the link you get in your adress bar after running your sketch.
EDIT: Actually I've created a sketch for you, showing those methods: look here
Assume that I have a workflow with 3 Custom Activities which are placed in a Sequence Activity. And I created a Boolean variable (name it as “FinalResult”) at Sequence Activity level (Root) to hold the Result. My Intention is, I want to assign each Custom Activity Result to Root level variable (“FinalResult”) within the Custom Activity Execute method after finishing the activity.
I can get this by declaring the output argument in Custom Activity and placing the variable name at design time in the properties window of activity manually while designing the policy.
But I don’t want to do this by the end user. I want just the end user drag and drop the activities and write conditions on the” FinalResult” variable. Internally I have to maintain the Activity Result in “FinalResult” Variable through programmatically.
Finally I want to maintain the workflow state in “FinalResult” variable and access it anytime and anywhere in the workflow.
I tried like this below getting error "Property does not exist".
WorkflowDataContext dataContext = context.DataContext;
PropertyDescriptorCollection propertyDescriptorCollection = dataContext.GetProperties();
foreach (PropertyDescriptor propertyDesc in propertyDescriptorCollection)
{
if (propertyDesc.Name == "FinalResult")
{
object data = propertyDesc.GetValue(dataContext);// as WorkUnitSchema;
propertyDesc.SetValue(dataContext, "anil");
break;
}
}
Please let us know the possible solutions for the same.
I do this all the time.
Simply implement IActivityTemplateFactory in your activity. When dragged and dropped onto the design surface, the designer will determine if your activity (or whatever is being dropped) implements this interface. If it does, it will construct an instance and call the Create method.
Within this method you can 1) instantiate your Activity and 2) configure it. Part of configuring it is binding your Activities' properties to other Activities' arguments and/or variables within the workflow.
There are a few ways to do this. Most simply, require these arguments/variables have well known names. In this case, you can simply bind to them via
return new MyActivity
{
MyInArgument = new VisualBasicValue<object>(MyActivity.MyInArgumentDefaultName),
};
where MyActivity.MyInArgumentDefaultName is the name of the argument or variable you are binding to.
Alternatively, if that variable/argument is named by the user... you're in for a world of hurt. Essentially, you have to
Cast the DependencyObject target passed to the Create method to an ActivityDesigner
Get the ModelItem from that AD
Walk up the ModelItem tree until you find the argument/value of the proper type
Use its name to create your VisualBasicValue
Walking up the ModelItem tree is super duper hard. Its kind of like reflecting up an object graph, but worse. You can expect, if you must do this, that you'll have to fully learn how the ModelItem works, and do lots of debugging (write everything down--hell, video it) in order to see how you must travel up the graph, what types you encounter along the way, and how to get their "names" (hint--it often isn't the Name property on the ModelItem!). I've had to develop a lot of custom code to walk the ModelItem tree looking for args/vars in order to implement a drag-drop-forget user experience. Its not fun, and its not perfect. And I can't release that code, sorry.
i have a Flex tree control and im trying to select a tree node 3 levels down right after the dataProvider is assigned with a collection object like the following.
basically treeItem1, treeItem2, treeItem3 are the nodes in the tree and treeitem3 is a child of treeItem2 which is a child of treeItem1. Assume these treeItem(1,2,3) are referenced correctly from the collection items.
my problem is that if i wait for the whole component to load completely then select the nodes, it open/select/scrolltoIndex correctly. However, if i were to select the node right after the dataProvider is assigned, then it doesn't even open or select (basically the this.treeService.selectedItem is always null).
can anyone point out what i did wrong? is there anything needs to happen after the dataProvider is assigned?
thanks
this.treeService.dataProvider = oPricingHelper.getCurrentPricingSercicesTreeSource();
this.treeService.expandItem(treeItem1, true);
this.treeService.expandItem(treeItem2, true);
this.treeService.selectedItem = treeItem3;
this.treeService.scrollToIndex(this.treeService.selectedIndex);
I have used the updateComplete event to know when a component (such as a DataGroup or List) has completed rendering after performing a simple task (such as updating the dataProvider reference). Of course, you have to be careful and remove listening to updateComplete because it can run a lot, unless you have a need for it to run.
Something like:
//...some function...
this.treeService.addEventListener(FlexEvent.UPDATE_COMPLETE, onTreeUpdateComplete);
this.treeService.dataProvider = oPricingHelper.getCurrentPricingSercicesTreeSource();
//...rest of some function...
private function onTreeUpdateComplete(event:FlexEvent):void {
this.treeService.removeEventListener(FlexEvent.UPDATE_COMPLETE, onTreeUpdateComplete);
this.treeService.expandItem(treeItem1, true);
this.treeService.expandItem(treeItem2, true);
this.treeService.selectedItem = treeItem3;
this.treeService.scrollToIndex(this.treeService.selectedIndex);
}
I'm not positive your experiencing the same issue but I seem to have the same type of problem with using the advanced data grid, it appears in these cases where the dataprovider is acceptable as multiple types, the components do some extra work in the background to wrap things up into something Hierarchical (HierarchicalData or HierarchicalCollectionView) and in doing so the dataprovider setter call is not synchronous (so it will return before actually having assigned the internal property storing the dataprovider). I've used callLater in this case with moderate success, callLater is generally a bad practice but basically adds a function to a list of functions to call once background processing is done, so this is assuming that something in the dataprovider setter called UIComponent.suspendBackgroundProcessing() and that it will subsequently call UIComponent.resumeBackgroundProcessing() and then it will execute the list of functions added by using callLater. Alternatively you could use setTimeout(someFunction,1000).
These are both "hacks" the real solution is to dig into the framework code and see what it's really doing when you tell it to set the dataprovider. Wherever you see that it actually has set the dataprovider you could extend that class and dispatch an event that you could listen for to run the function to do the selections after this point.
If anyone has a better solution please by all means correct me (I would love to have a better answer than this)