I have a function that looks like the following
void MainWindow::CreateEnvironment()
{
MdiWindow* sub = createSubWindow();
MyQTWidget* widget = CreateWidget();
..
..
}
I want that during this function a progress bar will be shown at the beggining and will close at the end of this function.
However adding
void MainWindow::CreateEnvironment()
{
**progressBarDialog->show();**
MdiWindow* sub = createSubWindow();
MyQTWidget* widget = CreateWidget();
..
..
**progressBarDialog->hide();**
}
Does not work, maybe because the function needs to exit first or something.
What is the best way to do this?
I assume that you use QProgressDialog?
You need to first setup the dialog with the correct number of steps you expect, how long you want to wait before it actually shows and more importantly: you need to call setValue() to update the progress bar.
Here is an example of how I would solve that (as far as I understand it)
void MainWindow::CreateEnvironment()
{
auto bar = new QProgressBarDialog(this);
bar->setLabelText(tr("Creating Environment..."));
bar->setCancelButton(nullptr); // No cancel button
bar->setRange(0, 10); // Let's say you have 10 steps
bar->setMinimumDuration(100); // If it takes more than 0.1 sec -> show the dialog
bar->setValue(0);
MdiWindow* sub = createSubWindow();
bar->setValue(1);
MyQTWidget* widget = CreateWidget();
..
..
bar->setValue(10);
MyLastWidget* last = CreateLastWidget();
bar->deleteLater(); // Not needed anymore, let's get rid of it
}
And don't worry too much if the dialog never shows. Unless you're doing really heavy computation (such as allocating / initialising huge portion of memory), creating widgets is very fast and would finish before the 100ms times out.
EDIT: Another thing to be aware of: QProgressDialog is meant to work after the main event loop started. (That is after the call to app.exec() in your main())
If you plan to show call this function in the constructor of your MainWindow, the dialog might even never show up because the window itself is not fully created and operational.
If you intended to call this function later, when the main window is already displayed on screen and the user hit a New Document button of some sort: you can ignore this part of the answer.
Related
I'm trying to create a simple memory match game using Java 11 and JavaFX.
I have a scenario where two cards don't match. Before the non-matching cards are flipped back I'd like to have 800 milliseconds delay, so the user can see what was the second selected card. I'm using this code and it works fine:
CompletableFuture.delayedExecutor(800, TimeUnit.MILLISECONDS).execute(() -> {
firstRevealedCard.setFaceDown(); // sets an ImageView's setImage() method
secondRevealedCard.setFaceDown(); // sets an ImageView's setImage() method
firstRevealedCard = null; // sets instance variable to null
secondRevealedCcard = null; // sets instance variable to null
});
All the cards in the game are in a 4 column and 2 row GridPane.
Each cell in the grid is an ImageView set to an Image.
Each card object has a Mouse (click) event attached to it:
// Add all cards to the deck
for ( int card_ID = 1; card_ID < unique_cards+1; card_ID++ ) {
Card card1 = new Card( card_ID );
card1.setOnMouseClicked( (MouseEvent event) -> { onMouseClicked(card1); });
addCard(card1);
Card card2 = new Card( card_ID );
card2.setOnMouseClicked( (MouseEvent event) -> { onMouseClicked(card2); });
addCard( card2 );
In the code above card1 and card2 are two cards having the same picture. They're matching cards.
The cards are stored in an ArrayList. After all the cards has been added to the ArrayList, the ArrayList is shuffled.
The ArrayList store Card objects and each object has its own mouse event listener attached.
THE ISSUE: When two cards don't match, the non-matching cards need to flip back and the 800 milliseconds timeout works well using the CompletableFuture.delayedExecutor, but I want the "mouse event" to be blocked or locked until the Executor fininshes, otherwise, let's say the user clicks an other card, but the non-matching cards didn't have the chance to flip back.
First card selected - Apple
Second card selected - Banana
Two non-matching cards
Executor starts
100 milliseconds
100 milliseconds
100 milliseconds
100 milliseconds
the user clicks another card (throws an exception)
just because user clicked another card before executor finishes
100 millisecond
...
...
at 800 milliseconds
Executor finishes, cards are flipped back
Exception in thread "ForkJoinPool.commonPool-worker-3" java.lang.NullPointerException
at memorygamefx.CardDeck.lambda$onMouseClicked$0(CardDeck.java:85)
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
I would be grateful for any suggestion. Thank you!
Don't use delayedExecutor for this. If you continue to do so, wrap the delayed code you invoke in Platform.runLater, so that it runs on the JavaFX thread otherwise weird stuff might happen.
Instead use a PauseTransition such as this:
PauseTransiton pause = new PauseTransition(Duration.millis(800));
Node disabledNode = scene.getRoot();
disabledNode.setDisable(true);
pause.setOnFinished(e -> {
disabledNode.setDisable(false);
// delayed code that you would like to run.
});
pause.play();
The example disables the entire scene when the pause is executing.
A binding can also be used instead, but manually setting and unsetting the disable property is probably better:
scene.getRoot().disableProperty().bind(
Bindings.equal(pause.statusProperty(), Animation.Status.RUNNING)
);
Disabling the scene will, by default, grey it out to give feedback that it is disabled. If you don't want that, you could instead add or remove a mouse event filter (please read attached link to understand this if you don't know what it is) on a scene or a hierarchy or nodes to prevent input that you don't want the app to process during the pause.
Another thing you could do to disable mouse input, is to set the root node to mouseTransparent while the pause is running, that way mouse input will be ignored. That would be similar to defining your own filters to ignore mouse events, but much easier to implement.
Some advantage of a PauseTransition are:
Everything runs on the JavaFX thread, so you don't need to deal with multi-threading issues.
It has a rich API so it is quite flexible.
It is reusable.
My Dialog is a simple Frame with an Image, a label to display a question and two more labels (Yes / No) with TapCommand.
I've set up the container with the DialogPage.xaml and DialogPageViewModel and injected in the ViewModel I want to open the dialog.
Here is the code I'm using to call the Dialog:
public void ShowDialog()
{
_dialogService.ShowDialog("DiscardPopup", CloseDialogCallback);
}
void CloseDialogCallback(IDialogResult dialogResult)
{
var goBack = dialogResult.Parameters.GetValue<bool>("GoBack");
if (goBack)
NavigationService.GoBackAsync();
}
If the user taps over the "Yes label", I execute this command:
YesCommand = new DelegateCommand(() => YesTapped());
private void YesTapped()
{
IDialogParameters pa = new DialogParameters();
pa.Add("GoBack", true);
RequestClose(pa);
}
If the user taps over the "No label", I simply call:
NoCommand = new DelegateCommand(() => RequestClose(null));
The "problem" is when the ShowDialog is fired, the DiscardPopup is taking up to 3 seconds to show up.
Is there a way to make it faster?
The same happens with the TapCommands, 2 - 3 seconds when the RequestClose is invoked.
Without actual code telling you exactly what the issue is, is going to be best guess. Based on your feedback to my comments above I would suggest the following:
Try displaying the dialog on a test page that doesn't have a complex layout. My guess is that you won't see such a long load time. If that's the case this would point to your layout being overly complex and that the lag time is due to the device struggling to re-render the View
Try using Prism.Plugin.Popups. You'll need to initialize Rg.Plugins.Popup and register the DialogService. You can see docs on that at http://popups.prismplugins.com
Fist of all, I'm really sorry for my bad English and I pretty new to game maker .
I have 2 object in the game : obj_apple and obj_door( I unchecked visible box)
my question is
how can I make an obj_door visible in the room when all the obj_apple are destroyed?
Object obj_door, Step event, place code (Add event -> Step -> Step -> tab Control -> section Code, first icon (Execute code)):
if !instance_exists(obj_apple) visible = true;
Another option, so you aren't making a check in every step event, is to put the check for the number of obj_apple in the destroy event of obj_apple.
For example, in the destroy event of obj_apple you would have:
if (instance_number(object_index) == 0) {
with (obj_door) {
visible = true;
}
}
When I click on a empty time slot on fullCalendar, it draws a rectangle on that empty cell. So, If my slotDuration is 30min, the block represents 30 min. I also can drag the cursor over multiple cells and select a custom range. But what I need to do is, when the user click (not drag) on a cell, select and draw the rectangle on 2 cells (representing 1 hour). Is this possible? I cannot change the slotDuration.
If I change the snapDuration to 1 hour, it works, but sadly, I cannot change it also.
What I was looking for is a way to override the event.end but that did not work.
Update 1:
I was able to do this exposing the cellDuration property:
on fullCalendar.js:
t.setCellDuration = function (minutes) {
var duration = moment.duration(minutes, 'minutes');
var view = t.getView();
view.timeGrid.cellDuration = duration;
}
now on the renderEvent handler, I can call
element.fullCalendar("setCellDuration", 60);
It works but if there is an alternative that does not involve change fullCalendar code, it would be nice.
I think you cannot do it just modifying the properties of the calendar, but you could do it modifying the fullCalendar.js file. Yes, I know you specify it on your question, but I think there is not alternative.
Exactly the listenStop function, which resides at line 4527 at version 2.3.3
listenStop check an array call dates
dates[
{FCMoment}, //start
{FCMoment} //end
]
So, before that check, you can modify your end time as you prefer. In addition, you have to render it.
In your code, now listenStop() function should be something like:
listenStop: function(ev) {
if (dates) { // started and ended on a cell?
if (dates[0].isSame(dates[1])) {
dates[1] = dates[0].clone().add(1, 'hours'); //Now we modify the end
_this.renderSelection(dates[0], dates[1]); //And render the modified selection
view.trigger('dayClick', dayEl[0], start, ev);
}
if (isSelectable) {
// the selection will already have been rendered. just report it
view.reportSelection(start, end, ev);
}
}
}
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.