Reset infinite JavaFX RotateTransition - javafx

Problem
In my application i use an infinite RotateTransition.
Button btnRotating = new Button("I'm rotating");
final RotateTransition rt = new RotateTransition(Duration.millis(2000), btnRotating);
rt.setFromAngle(0);
rt.setToAngle(180);
rt.setInterpolator(Interpolator.LINEAR);
rt.setCycleCount(Timeline.INDEFINITE);
When i stop this transition i want to reset the rotated node to its initial position.
rt.stop();
rt.getNode().setRotate(0);
Problem is that rt.stop() is asynchronous so the transition might not have stopped when i reset the rotation of the node. So occasionally the transition will overwrite my rotation reset.
Unsuccessful solutions
rt.onFinishedProperty().addListener((observable, oldValue, newValue) -> {
rt.getNode().setRotate(0);
});
Executing rt.getNode().setRotate(0) when the onFinishedProperty of the transition changes does not work as stopping the transition manually does not trigger the onFinishedProperty.
rt.statusProperty().addListener((observable, oldValue, newValue) -> {
if(newValue == Status.STOPPED){
rt.getNode().setRotate(0);
}
});
Listening for the statusProperty of the transition has the same problem as resetting the rotation manually. The status property gets set to STOPPED as soon as i call rt.stop() but it does not wait until the transition is terminated.
Question
So how can i safely reset my node to its initial position? How can i determine that the transition has really ended when i reset the node?
When i execute rt.getNode().setRotate(0) on the JavaFX application thread it seems to work reliably as my reset rotation gets queued after the last transition rotation. Is this the best solution? Should rt.stop() and rt.start also be executed on the FX thread?
Another thing i noticed is that in JavaDoc of the rt.stop() function it says: "Stops the animation and resets the play head to its initial position." So the play head gets reset but this reset is not transferred to the node as the transition is stopped. Is there a way to make use of that?

btnRotating.setOnAction(event -> {
if (rt.statusProperty().get() == Animation.Status.RUNNING){
rt.stop();
btnRotating.setRotate(0);
}else{
rt.playFromStart();
}
});
you can stop animation from background thred by use this
Platform.runLater(() -> {
rt.stop();
btnRotating.setRotate(0);
});

Related

Restore window to normal doesn't work after maximized in qt on ubuntu 18.04

I meet two problems, I searched, but they seems not so easy as I think.
I'm working on ubuntu-18.04 with qt-5.15.x. And my two problems are:
cannot restore my window after maximizing it.
cannot move my window out of my screen with mouse dragging
Can not restore
Firstly, I set my window frameless, and then using a button standing for maximize or restoreoperations to trigger maximization or normal
// set fremeless in contruction
setWindowFlags(Qt::FramelessWindowHint | windowFlags());
// maximaization slot or restore
void WindowTitle::onButtonMaxClicked()
{
QWidget *pWin = window();
if(pWin->isMaximized())
{
pWin->showNormal();
}
else
{
pWin->showMaximized();
}
}
What I expected is when I click button ,window will maximized, and that is true indeed. Then clicking on button again, window will return original position and size again, but that doesn't happen, instead, window's height is correct, but its width is equal screen(getting rid of applications docker bar), x-axis value is wrong too. And I have tried setGeometry after I have stored its value, but failed.
Can not move outside
I can make sure that I have set correct position with
void WindowTitle::mouseMoveEvent(QMouseEvent *event)
{
if (m_isPressed)
{
QPoint movePoint = event->globalPos() - m_startMovePos;
QPoint widgetPos = QApplication::activeWindow()->pos();
m_startMovePos = event->globalPos();
QApplication::activeWindow()->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y());
}
return QWidget::mouseMoveEvent(event);
}
function, but the result shows something wrong.
I have searched above two questions. But get nothing. And I'm curious that other applications can do just right both restore and move, suchlike Firefox web browser(which I'm typing on). So, there must be some way to make it work.

On mesh click, ArcRotateCamera focus on

I'm using ArcRotateCamera, when I click on mesh, I have to focus camera on
var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 2, 300, BABYLON.Vector3.Zero(), scene);
camera.setTarget(BABYLON.Vector3.Zero());
// on mesh click, focus in
var i = 2;
var pickInfo = scene.pick(scene.pointerX, scene.pointerY);
if (pickInfo.hit) {
pickInfo.pickedMesh.actionManager = new BABYLON.ActionManager(scene);
pickInfo.pickedMesh.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger,
function (event) {
camera.position = (new BABYLON.Vector3(pickInfo.pickedPoint.x, pickInfo.pickedPoint.y, camera.position.z + i));
i += 2;
})
);
}
this code changes mesh's z position but don't makes it in the center of screen
There are a few things that can be changed in your code.
1st - what you are doing is executing a code action after a click, instead of simply running the code in the callback after a pick has occurred. You are registering a pick action (technically user click) on right on the first frame, but only if the mouse was found in the right location at the right moment. My guess is that it didn't work every time (unless you scene is covered with meshes :-) )
2nd - you are changing the camera's position, instead of change the position to which it is looking. Changing the camera's position won't result in what you want (to see the selected mesh), it will move the camera to a new position while still focusing on the old position.
There are a few ways to solve this. The first is this:
scene.onPointerDown = function(evt, pickInfo) {
if(pickInfo.hit) {
camera.focusOn([pickInfo.pickedMesh], true);
}
}
The ArcRotate camera provides focusOn function that focuses on a group of meshes, while fixing the orientation of the camera. this is very helpful. You can see a demo here:
https://playground.babylonjs.com/#A1210C#51
Another solution would be to use the setTarget function:
https://playground.babylonjs.com/#A1210C#52
Which works a bit differently (notice the orientation change of the camera).
Another thing - use the pointer events integrated in Babylon, as they are saving you the extra call for a scene pick. pointer down is executed with the pickinfo integrated in the function, so you can get the picking info of the current pointer down / up / move each frame.
**** EDIT ****
After a new comment - since you want to animate the values, all you need to do is store the current values, calculate the new ones, and animate the values using the internal animation system (documentation here - https://doc.babylonjs.com/babylon101/animations#basic-animation) . There are many ways to achieve this, I took an old function and modernized it :-)
Here is the demo - https://playground.babylonjs.com/#A1210C#53

Problems changing the flags of a QWidget

I have a QWidget as the child of another within my application. The task consists of putting the internal widget in full screen mode and being able to see it again in normal mode with the same button. This partly I have managed to do it in the following way:
if(!isFullScreen())
{
setWindowFlags(windowFlags() | Qt::Window);
showFullScreen();
}
else
{
setWindowFlags(windowFlags() & ~Qt::Window);
showNormal();
activateWindow();
}
The problem arises when you return to see the widget in normal mode. Things that happen:
The mouse cursor stays with pointing hand cursor.
The button to change mode remains in hover state (the background color is changed when the mouse is over)
Passing the mouse through other widget controls does not change its appearance
I have to click on the widget to fix the behavior. It's as if the widget did not receive events of any kind or something like that. I tried calling setFocus () and it did not work. I have also tried to send an event by hand in the following way but it has not worked either:
QMouseEvent my_event(QEvent::MouseButtonPress, QPointF (0, 0), Qt :: NoButton, 0, 0);
QApplication::sendEvent(this, & my_event);
Any ideas?
Thanks.
Cannot reproduce your issue on Xubuntu with Qt5.9.4.
However I did the same in my last job, and it worked correctly on all platforms. It was something like this, I think:
if(!isFullScreen())
{
setParent(0);
showFullScreen();
}
else
{
orignalParentLayout->addWidget(this); // better: insert at correct position
showNormal();
}
For this you have to add some knowledge about the parent's layout though. You could try to detect that info before going into full screen mode, but it is probably not worth the effort.
You could also try:
if(!isFullScreen())
{
logicalParent = parentWidget();
setParent(0);
showFullScreen();
}
else
{
setParent(logicalParent);
showNormal();
}
If you have no layout. You might also want to store the geometry before going to full screen.

javafx setOpacity method doesn't work when repeated for some times

I use a Thread to change the opacity of an AnchorPane.
I add an onMouseEnter method to raise opacity gradually,and an onMouseExited method to decrease opacity gradually.
I have two panes, if my mouse enter any one of them,both two panes will change opacity.
Then I start my application, I find that after my mouse enter and exit the pane for some times, one will stop at a specific opacity,while the other still change opacity gradually.
I am confused why this happen...
My codes are like:
private double base = 0.5;
private void changeOpacity(boolean increase){
new Thread(()-> {
while((increase && base < 0.9) || ((!increase)&&base > 0.5)){
base += increase?0.01:-0.01;
leftPane.setOpacity(base);
rightPane.setOpacity(base);
Thread.sleep(5);
}
}
}
I am sorry for my poor English.
Thanks.!
You are only allowed to change values of JavaFX components while you are on the JavaFX-Thread (something you are clearly not).
You need to sync yourself back using Platform.runLater(Runnable) but in your case you'd swap the event loop with Runnables which an equally bad idea.
The correct way is:
Use a Timeline with a KeyFrame
Use the already predefined FadeTransition foreach Node (together with a ParallelTransition)

fragments, android:zAdjustment (z order) and animations

I'm using the support library. Now, I want to have a fragment shifting in from the bottom, moving OVER the previous one.
For this I use this to keep the previous fragment (the one that is being slided over) visible until the new fragment is in its place:
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0" android:toAlpha="1.0"
android:duration="2500"
android:zAdjustment="bottom" />
this is animation used for the new fragment to slide in from bottom:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="100%p" android:toYDelta="0"
android:duration="#android:integer/config_mediumAnimTime"
android:zAdjustment="top"/>
I've put the z adjustment to bottom and top for both, but still the 'bottom' animation is still on top of the new fragment! I have put the duration to 2500 for testing and it stays on top for the whole time.
Does zAdjustment not work for fragment animations?
According to this google group thread Z adjustment only works for window animations.
"The Z adjustment only works for window animations. I thought this was documented, but apparently not."
-- Dianne Hackborn (Android framework engineer)
You can override the onCreateAnimation method and for any animations you can check what animation is currently running and if you need it to be on top, set the Z-axis from there.
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
if (nextAnim == R.anim.enter_from_right || nextAnim == R.anim.exit_to_right) {
ViewCompat.setTranslationZ(view, 1f)
} else {
ViewCompat.setTranslationZ(view, 0f)
}
return super.onCreateAnimation(transit, enter, nextAnim)
}
Recommend implementing this as a base Fragment class for all your fragments.
I've also got stuck with that problem. So instead of using transaction.replace(containerId, newFragment) I've created two containers for fragments and now my code looks like this one
Add first fragment:
transaction.add(containerId1, firstFragment).commit();
Add second fragment with animation over the first one:
findViewById(containerId2).bringToFront();
transaction.setCustomAnimations(R.anim.slide_in_up,
R.anim.stay).remove(oldFragment).add(containerId2, newFragment).commit()
You can use androidx.fragment.app.FragmentContainerView as a fragments container. It automatically handles z order for animations specified in setCustomAnimations()

Resources