I've made an aplication with vala where at some point I have to process a lot of files. I've created a window to choose a folder and then I get the paths of files and make some proces on them.
I've added a progress bar to this window to show how many files have been processed but for some reason it remains always empty.
Code about window:
this.files_window = new Gtk.Window();
this.files_window.window_position = Gtk.WindowPosition.CENTER;
this.files_window.destroy.connect (Gtk.main_quit);
// VBox:
Gtk.Box vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
this.files_window.add (vbox);
// Buttons to open and close
Gtk.Button cancel = new Gtk.Button.with_label ("Cancel");
Gtk.Button select = new Gtk.Button.with_label ("Select");
vbox.add (select);
vbox.add (cancel);
// proogress bar
this.progress_bar = new Gtk.ProgressBar();
vbox.add(this.progress_bar);
// conect select to method do_stuff
select.clicked.connect (do_stuff);
this.files_window.show_all ();
As you can see, I connect the button "select" to the method "do_stuff" where I get the paths of selected files and make some process.
I update correctlly the fraction of the progres bar because I've added some prints to know if the value is correct and it is. It's just that the windows is not refreshing, possibly because all the work it is doing with the process of the files. Here is the code about do_stuff() method:
// some proces to get paths of files in the list sfiles
double fraction = 0.0;
this.progress_bar.set_fraction (fraction);
int processed_files = 0;
foreach (string sfile in sfiles) {
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
stdout.printf("Real fraction: %f\n", this.progress_bar.get_fraction());
}
The printf shows that the value of the progres bar is being updated but in the window the bar is always empty.
Am I missing something? Is it the correct way to do the progres bar? Should I made another thread to do the stuff?
As #nemequ says, your code is blocking the main loop thread (which handles both user input and scheduling/drawing widget updates), hence it the progress bar is not updated until the method completes.
Using a thread is one way solve the problem, however using threads can lead to a lot of bugs however since it can be difficult to make even simple interactions between threads safe.
An async method avoids this by interleaving the code with the other work being done by the main loop. An async version of your do_stuff() would be pretty straight-forward to write, simply declare it async and put a yield in the for loop somewhere:
public async void do_stuff() {
...
foreach (string sfile in sfiles) {
// all of this is as before
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
// Schedule the method to resume when idle, then
// yield control back to the caller
Idle.add(do_stuff.callback);
yield;
}
}
You can then kick it off from your click handler by calling: do_stuff.begin().
Unless there is some relevant code you're not showing, you're blocking the main loop. One option would be to do everything in a thread, and use an idle callback to update the UI. The basic idea is something like:
new GLib.Thread<void*>("file-processor", () => {
foreach (string sfile in sfiles) {
/* do stuff */
GLib.Idle.add(() => {
/* Update progress */
return false;
});
}
return null;
});
Depending on your application you may need to add a mutex to avoid race conditions. You may also need to add some logic for canceling the operation.
A better option might be to use a GLib.ThreadPool. You'd still want to update the UI from an idle callback, but this would allow each task to execute in parallel, which could provide a significant speed-up.
If I were you I'd probably wrap it all up in an async function to keep the API tidy, but you don't really have to.
Related
It seems that if Find.Execute finds a result inside a ContentControl, it will cause the ContentControlOnEnter and ContentControlOnExit events to fire. It's particularly annoying because the exit event fires even if the selection is still in the content control, so any code which sets the states of buttons dependent upon a content control being active will appear to be in the incorrect state.
Given a document containing a single content control with the word "test", and the following code:
// In setup
Application.ActiveDocument.ContentControlOnEnter += ActiveDocument_ContentControlOnEnter;
private void ActiveDocument_ContentControlOnEnter(Word.ContentControl ContentControl)
{
var selRange = _Application.Selection.Range;
_logger.Debug(m => m("Selection: {0}-{1}", selRange.Start, selRange.End));
}
//Later in another method
var finder = _Application.ActiveDocument.Range().Find;
_logger.Debug("Find.Execute start");
finder.Execute("test);
_logger.Debug("Find.Execute end");
The following gets logged:
38137 [VSTA_Main] DEBUG - Find.Execute start
38141 [VSTA_Main] DEBUG - Selection: 1-5
38149 [VSTA_Main] DEBUG - Find.Execute end
We have a lot of code that handles ContentControlOnEnter and ContentControlOnExit events, and having the find operation cause them to be called is really causing problems!
Is there any way to use Find.Execute without having it trigger these events? Failing that, is there a good way to distinguish between the Find-triggered ones and the genuine user ones? I have tried using the time between the enter and exit events, but this is not reliable.
I had similar problems in Word, though it was about the Selection event. I tried many solutions, but only one helped. In your case, make a new field bool _skipEnterAndExitEvents and set it true before calling
finder.Execute("test) and false after calling. And in the enter and exit event handlers check this field, if the field is true then just skip. This solutions is not beautiful, looks like a hack, but other solutions are even uglier and don't really work.
I think I found a decent solution:
private bool _doIgnoreNextExit = false;
private void ActiveDocument_ContentControlOnEnter(Word.ContentControl ContentControl)
{
if (Application.Selection.Find.Found)
{
_logger.Debug("Ignoring CC enter event caused by Find operation");
_doIgnoreNextExit = true;
return;
}
// Do things
}
private void ActiveDocument_ContentControlOnExit(Word.ContentControl ContentControl)
{
if(_doIgnoreNextExit)
{
_logger.Debug("Ignoring fake exit");
_doIgnoreNextExit = false;
return;
}
// Do things
}
I'm implementing a document editor with JavaFX8 and e(fx)clipse and want to user to be informed when the export (write to disc) is ongoing. I'm using the main (GUI) Thread for this as I want to block the gui during this operation (which takes 2-3 seconds). During this operation I want to show a small popup to inform the user that the export is ongoing, nothing fancy.
#FXML
public void export() {
Dialog dialog = new Dialog();
dialog.setContentText("exporting ...");
dialog.show();
// some lenghty methods come here, ~equivalent to Thread.sleep(3000);
dialog.hide();
}
When I press the corresponding Button which invokes the export method, I get somehow two dialogs, one of them NOT closing and remaining open after the method has finished.
Does somebody has an idea what's happening here? I'm really interested in a simple solution, I don't need to have a progress bar etc..
Another possibility would be to show a wait-cursor before the operation starts and switching back to the default cursor after that. Unfortunately, this does also not seem to work. I understand that the UI is blocked during the "lengthty" operation, but I don't udnerstand why I cant change the UI before and after that operation....
Your example isn't very complete - however I would recommend using one of two approaches. However, you aren't putting the long process on a background thread which will FREEZE your app. You want to offload that process.
1) Use the ControlsFX Dialog which has a Progess Alert. Tie your work to either a Task or a Service and provide that to the alert. This will pop the alert up while the thread is active, and will automatically close it when done. If you have intermediary progress values, it can be used to update the progress bar.
Or if you don't want to use this dialog, you could do something like this:
Alert progressAlert = displayProgressDialog(message, stage);
Executors.newSingleThreadExecutor().execute(() -> {
try {
//Do you work here....
Platform.runLater(() ->forcefullyHideDialog(progressAlert));
} catch (Exception e) {
//Do what ever handling you need here....
Platform.runLater(() ->forcefullyHideDialog(progressAlert));
}
});
private Alert displayProgressDialog(String message, Stage stage) {
Alert progressAlert = new Alert(AlertType.NONE);
final ProgressBar progressBar = new ProgressBar();
progressBar.setMaxWidth(Double.MAX_VALUE);
progressBar.setPrefHeight(30);
final Label progressLabel = new Label(message);
progressAlert.setTitle("Please wait....");
progressAlert.setGraphic(progressBar);
progressAlert.setHeaderText("This will take a moment...");
VBox vbox = new VBox(20, progressLabel, progressBar);
vbox.setMaxWidth(Double.MAX_VALUE);
vbox.setPrefSize(300, 100);
progressAlert.getDialogPane().setContent(vbox);
progressAlert.initModality(Modality.WINDOW_MODAL);
progressAlert.initOwner(stage);
progressAlert.show();
return progressAlert;
}
private void forcefullyHideDialog(javafx.scene.control.Dialog<?> dialog) {
// for the dialog to be able to hide, we need a cancel button,
// so lets put one in now and then immediately call hide, and then
// remove the button again (if necessary).
DialogPane dialogPane = dialog.getDialogPane();
dialogPane.getButtonTypes().add(ButtonType.CANCEL);
dialog.hide();
dialogPane.getButtonTypes().remove(ButtonType.CANCEL);
}
I am writing a program in Vala using GTK+. It has a function that creates a ListBox that contains a lot of EventBox objects. There is one issue: there is one function that downloads the image and it takes a lot of time, so the main window didn't show up unless all downloads are finished. This is not what I wanted, I wanted main window to appear and then images to download and to be shown. So I separated image load to separate function, but main window still doesn't show unless all downloads are finished. What am I doing wrong?
Here is the function I'm using:
foreach (MediaInfo post in feedPosts)
feedList.prepend(post);
foreach (PostBox box in feedList.boxes)
box.loadImage();
("feedList" is a class inherited from Gtk.ListBox and "boxes" is a list containing all of PostBox (which is inherited from Gtk.EventBox) objects)
This is feedList.prepend function:
public void append(MediaInfo post)
{
Gtk.Separator separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
base.prepend(separator);
PostBox box = new PostBox(post);
base.prepend(box);
boxes.append(box);
}
And this is the constructor and loadImage functions of PostBox class:
public PostBox(MediaInfo post)
{
box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
this.add(box);
this.post = post;
userToolbar = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
userNameLabel = new Gtk.Label("#" + post.postedUser.username);
this.userNameLabel.set_markup(
"<span underline='none' font_weight='bold' size='large'>" +
post.postedUser.username + "</span>"
);
userToolbar.add(userNameLabel);
box.pack_start(userToolbar, false, true);
image = new Gtk.Image();
box.add(image);
box.add(new Gtk.Label(post.title));
box.add(new Gtk.Label( post.likesCount.to_string() + " likes."));
print("finished.\n");
return;
}
public void loadImage()
{
var imageFileName = PhotoStream.App.CACHE_URL + getFileName(post.image.url);
downloadFile(post.image.url, imageFileName);
Pixbuf imagePixbuf = new Pixbuf.from_file(imageFileName);
imagePixbuf = imagePixbuf.scale_simple(IMAGE_SIZE, IMAGE_SIZE, Gdk.InterpType.BILINEAR);
image.set_from_pixbuf(imagePixbuf);
}
You have written the download operations in another method, however the operations are still synchronous, i.e. they block the thread. You never want to do computationaly or otherwise expensive things in the GUI thread, because that makes the GUI unresponsive.
You should start your downloads asynchronously, and trigger a callback method when the download is complete. In the callback, then you may for example change the image placeholders to actual images.
I replaced all my async methods with multithreading and now it is working the way I want it to work.
I have a working ProgressMonitorDialog, but I want to make sure that I am setting it up correctly.
First the Code:
Method to create Dialog
public void startProgressBar() {
try {
new ProgressMonitorDialog(getShell()).run(true, true,
new ProgressBarThread());
}
catch (InvocationTargetException e) {
MessageDialog.openError(getShell(), "Error", e.getMessage());
}
catch (InterruptedException e) {
MessageDialog.openInformation(getShell(), "Cancelled", e.getMessage());
}
}
Class File
class ProgressBarThread implements IRunnableWithProgress {
private static final int TOTAL_TIME = 1000;
public ProgressBarThread() {
}
public void run(IProgressMonitor monitor) throws InvocationTargetException,InterruptedException {
monitor.beginTask("Creating PDF File(s): Please wait.....", IProgressMonitor.UNKNOWN);
for (int total = 0; total < TOTAL_TIME ; total++) {
Thread.sleep(total);
monitor.worked(total);
if (total == TOTAL_TIME / 2) monitor.subTask("Please be patient... Operation should finish soon.");
}
monitor.done();
}
}
Method that calls the ProgressBar and runs a Pdf file creation Operation
private void startSavePdfOperation() {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
startProgressBar();
}
});
saveOp = new AplotSaveOperation(appReg.getString("aplot.message.SAVETOPDF"), "PDF", session);
saveOp.addOperationListener(new MyOperationListener(this) {
public void endOperationImpl() {
java.io.File zipFile = null;
try {
AplotSaveResultsParser.SaveResult saveResults = saveOp.getSaveResults();
if (saveResults != null) {
ETC..... ETC......
Questions:
Being the ProgressMonitorDialog is a GUI, it needs to be executed in a
Display.getDefault().asyncExec?
If the ProgressMonitorDialog is running in a separate thread, how does it know to close when the operation is finsihed?
Is there any relationship between the progressbar and the operation?
I am correct in assuming that the for loop in the ProgressBarThread class is basically the timer that keeps the monitor open?
Is there a way to increase the speed of the ProgressMonitorDialog's indicator, also can you remove the cancel button?
This is what I am thinking is happening currently.
I am starting the progress bar just before I start the PDF Operation Listener
See startSavePdfOperation() Above
The progress bar is running as unknown, but using a for loop to keep the progress bar dialog open, while the operation is running on a thread in the background.
See Class ProgressBarThread above
When the PDF operation completes the listener operation class closes the base GUI dialog.
public void endOperation() {
try {
endOperationImpl();
}
finally {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
w.getShell().setCursor(new Cursor(Display.getCurrent(), SWT.CURSOR_ARROW));
w.recursiveSetEnabled(getShell(), true);
w.getShell().setEnabled(!getShell().getEnabled());
w.close();
}
});
}
}
I am not sure what is happening to the ProgressBarThread monitor?
Is this Possible?
When the PDF Operation starts, the ProgressMonitorDialog opens and starts the indicator. OK with keeping it unknown.
When the PDF Operation completes, the monitor closes, then the base Dialog
I am just wanting to open progress bar dialog that will inform the user that their request is working in the background.
As stated the above code works, but I am afraid by letting the closing of Base GUI, destroy my Progress Thread and Monitor is not good practice.
First of all, in your ProgressBarThread#run() you should use monitor.worked(1). You don't need to set the total worked but increment it by the amount of work done, since the last time it was called.
Q1. Yes it needs to be executed in the display thread
Q2. Normally the work that needs to be done is actually performed in the runnable that is passed to the progress monitor dialog so that you can accurately report the amount of progress made. So your operation (if it is a synchronous call) should be called from within ProgressBarThread#run() so that you call monitor.worked(1) only when one file processing is complete.
Q3. What kind of operation are you running, perhaps it already supports showing progress bar, and you just need to invoke the right API. Is it an IUndoableOperation?
Q4. As I said this approach is problematic because you can never accurately report the progress and close the dialog only when the operation is completed. But if this is the only choice you have, then you can just save the monitor reference somewhere so that it is accessible to the other thread. Once monitor.done() is called, your ProgressBarThread#run() should return, the dialog will close.
Q5. You can remove the cancel button by passing the correct parameter to ProgressMonitorDialog#run(..):
new ProgressMonitorDialog(getShell()).run(true, false, new ProgressBarThread());
For the rest of the questions I can better answer if I know what kind of operation (what API) you are using.
Assume button A in an HTML5 webapp built with jQuery Mobile.
If someone taps button A, we call foo(). Foo() should get called once even if the user double taps button A.
We tried using event.preventDefault(), but that didn't stop the second tap from invoking foo(). event.stopImmediatePropagation() might work, but it also stops other methods further up the stack and may not lead to clean code maintenance.
Other suggestions? Maintaining a tracking variable seems like an awfully ugly solution and is undesirable.
You can set a flag and check if it's OK to run the foo() function or unbind the event for the time you don't want the user to be able to use it and then re-bind the event handler after a delay (just a couple options).
Here's what I would do. I would use a timeout to exclude the subsequent events:
$(document).delegate('#my-page-id', 'pageinit', function () {
//setup a flag to determine if it's OK to run the event handler
var okFlag = true;
//bind event handler to the element in question for the `click` event
$('#my-button-id').bind('click', function () {
//check to see if the flag is set to `true`, do nothing if it's not
if (okFlag) {
//set the flag to `false` so the event handler will be disabled until the timeout resolves
okFlag = false;
//set a timeout to set the flag back to `true` which enables the event handler once again
//you can change the delay for the timeout to whatever you may need, note that units are in milliseconds
setTimeout(function () {
okFlag = true;
}, 300);
//and now, finally, run your original event handler
foo();
}
});
});
I've created a sample here http://jsfiddle.net/kiliman/kH924/
If you're using <a data-role="button"> type buttons, there is no 'disabled' status, but you can add the appropriate class to give it the disabled look.
In your event handler, check to see if the button has the ui-disabled class, and if so, you can return right away. If it doesn't, add the ui-disabled class, then call foo()
If you want to re-enable the button, simply remove the class.
$(function() {
$('#page').bind('pageinit', function(e, data) {
// initialize page
$('#dofoo').click(function() {
var $btn = $(this),
isDisabled = $btn.hasClass('ui-disabled');
if (isDisabled) {
e.preventDefault();
return;
}
$btn.addClass('ui-disabled');
foo();
});
});
function foo() {
alert('I did foo');
}
});