Re: DFL background tasks

2015-06-10 Thread thedeemon via Digitalmars-d-learn

On Wednesday, 10 June 2015 at 22:18:21 UTC, Scroph wrote:

client.perform;
while(!client.isStopped)


I don't think this will work as you expect. "perform" is a 
synchronous call, it will not return until the download finishes, 
as I understand, so your while loop is too late. I think you 
should insert message processing stuff inside onProgress, though 
it's also suboptimal (if no progress for long time - no joy). 
Proper design will require more thought...


Re: DFL background tasks

2015-06-10 Thread Scroph via Digitalmars-d-learn
Briliant, thanks a lot ! Looks like I misunderstood Adam's reply, 
sorry about that !


I tried different things but I didn't think of calling invoke 
from within the worker thread, that solved the freezing problem. 
I ended up using the Thread class; spawn complained about the 
mutability of the given arguments. I realize what I did may be an 
ugly solution for the problem at hand, so I'll try to modify the 
code in order to avoid giving the thread UI elements altogether, 
thus being able to take advantage of more.. modern techniques 
like std.concurrency.


I'm using the Timer trick on another part of the project. Right 
now I'm running instances of a "Downloader" class in the 
background, a class that extends Thread and provides some useful 
functionality : a way to track the progress, to set rate limits, 
to abort the download, etc. It seems to be working fine, I'm 
using the (incomplete) program to download Visual Studio as we 
speak.


I set up a timer to periodically retrieve the progress of a 
download from the instances then update the ListView accordingly. 
Right now I'm hesitating between continuing with this approach vs 
using std.concurrency or even std.parallelism since the downloads 
are supposed to run in parallel without communicating with one 
another, but the only way I see would be to place the message 
handling code inside a loop, which for some reason doesn't sit 
right with me. I'm still learning concurrency (courtesy of Ali 
Çehreli and his book), so maybe my doubts are unfounded. Here's 
what I'll probably end up using, minus the error handling :


void dlWorker(HTTP client, string local, string remote, bool 
resume = true)

{
auto fh = File(local, resume ? "a" : "w");
scope(exit)
fh.close;
size_t size;
size_t progress;
	client.onProgress = (size_t dlTotal, size_t dlNow, size_t 
ulTotal, size_t ulNow) {

size = dlTotal;
progress = dlNow;
};
client.onReceive = (ubyte[] data) {
fh.rawWrite(data);
return data.length;
};  
client.perform;
while(!client.isStopped)
{
auto msg = receiveOnly!(MsgType, int)();
switch(msg[0])
{
case MsgType.progress: send(ownerTid, progress); break;
case MsgType.size: send(ownerTid, size); break;
case MsgType.abort:
client.shutdown;
send(ownerTid, 1);
break;
default:

client.handle.set(CurlOption.recv_rate_speed_large, msg[1]);
send(ownerTid, 1);
break;
}
}
}

enum MsgType
{
progress, size, abort, limit;
}

Though I'm not entirely sure how I would tie the ListView rows to 
their respective workers. But I'll cross that bridge when I get 
to it.


Again, thanks for all the help. You guys are definitely going to 
be mentioned in the "About" section of the finished product.


Re: DFL background tasks

2015-06-08 Thread thedeemon via Digitalmars-d-learn

On Monday, 8 June 2015 at 07:45:28 UTC, Ali Çehreli wrote:


> receiveTimeout(dur!"msecs"(0).
UFCS makes durations more pleasant: :)
receiveTimeout(0.msecs)


Oh, thanks! I missed this bit. This is much nicer!


Re: DFL background tasks

2015-06-08 Thread Ali Çehreli via Digitalmars-d-learn

Just a couple of minor improvements:

On 06/08/2015 12:22 AM, thedeemon wrote:

> spawn() a thread and don't pass any GUI controls to it, just
> thisTid (identifier of your main UI thread)

The owner's thread id is available as 'ownerTid' as well.

> receiveTimeout(dur!"msecs"(0).

UFCS makes durations more pleasant: :)

receiveTimeout(0.msecs)

Ali



Re: DFL background tasks

2015-06-08 Thread thedeemon via Digitalmars-d-learn
A more general and proper approach is to use message passing from 
std.concurrency. With DFL it looks like this: you spawn() a 
thread and don't pass any GUI controls to it, just thisTid 
(identifier of your main UI thread). In that worker tread when 
you've got some data to show in the UI (be it end result of just 
some status update) you use tid.send(...) and send the data in 
appropriate messages (defined as separate structs or classes). In 
the main UI thread you've got a Timer running that checks whether 
there are any messages in the main thread's mailbox and process 
them there.


Here's an example from a real DFL project:
https://bitbucket.org/infognition/undup/src/e8d295b89bc76545860e38a8c9ee171c86f3c84c/newscan.d?at=default#cl-200

OnStart() is a button callback. It does some quick UI updates and 
spawns a thread, passing relevant data and thisTid:


  worker = spawn(&makeScan, fname, hdr, thisTid);

(makeScan is a function with some long running operation, it's 
defined in another module)


There is also a timer set up when a form is created, and in the 
timer function OnTimer() there is a check for new messages via 
receiveTimeout(dur!"msecs"(0).


while(receiveTimeout(dur!"msecs"(0), &RcvMsgNumOfDirs, 
&RcvMsgScanning, &RcvMsgDone)) {}


Important part here is to make it non-blocking, it should not sit 
here waiting for new messages, otherwise UI will not be 
responsive.


And RcvMsgNumOfDirs, RcvMsgScanning, RcvMsgDone are functions 
that react to corresponding messages sent from the worker thread. 
They work in the UI thread, of course.


Re: DFL background tasks

2015-06-08 Thread thedeemon via Digitalmars-d-learn

On Sunday, 7 June 2015 at 13:34:59 UTC, Scroph wrote:
I tried your code and it did work, however it still froze the 
UI while it was retrieving the data.


Sure, because now you do your long operation in the UI thread. 
What happens in the delegate passed to invoke() happens in the 
main UI thread.


You've got two steps: first some long operation producing some 
data, then updating the listview with this data. You need to do 
the long processing part in a worker thread and updating in the 
UI thread. This is how it's done:


in the button callback (which runs in UI thread) you spawn() a 
thread with some worker function. This worker function does your 
long operation, and it happens in separate thread so the UI keeps 
responding. After you finished this long operation you call 
listview.invoke() with a delegate that just updates the listview, 
it doesn't do any long operations. This update will happen in the 
UI thread.


Something like:

void loadDetailsCallback(Control sender, EventArgs ea)
{
  ...
  spawn(&loadDetails, qualityBox);  // quickly launch a thread
  // and go on responding to UI events
}

void loadDetails(ListView qualityBox) {
  //do the work, this happens in worker thread
  vid = ...

  qualityBox.invoke( { // this part happens in UI thread, quickly
qualityBox.beginUpdate(); // this belongs out of loop
foreach(q; vid.qualities) {
  qualityBox.addRow(q.itag.to!string, q.type, q.quality, 
q.link);			

}
qualityBox.endUpdate();
  });
}



Re: DFL background tasks

2015-06-07 Thread Scroph via Digitalmars-d-learn

On Sunday, 7 June 2015 at 12:30:40 UTC, Adam D. Ruppe wrote:
For the Invoke call, you should be able to just ignore most the 
arguments and write something like


listview.invoke( delegate Object(Object[]) {
listview.Add(whatever);
return null;
});

and it should work.


Hello Adam, thanks for the fast reply. I tried your code and it 
did work, however it still froze the UI while it was retrieving 
the data.


I'm not sure if I'm using it correctly, I put the invoke call 
inside a Button.click callback. Here's a stripped down version of 
the code : 
https://gist.github.com/Scroph/0f3191fcd8b99db04d4b#file-dfl-d-L14-L17


A few days ago I read abot the XY problem 
(http://xyproblem.info/), so maybe I'm not asking the right 
questions. Here's what I'm actually trying to do : after clicking 
the button, I would like for the UI to remain responsive while :


a) A worker thread retrieves the needed information and places it 
in the ListView or,
b) A worker thread (or an asynchronous task maybe ?) retrieves 
the information and messages the UI with the data when it 
finishes running.


The latter seems to be more idiomatic because it separates the 
"data retrieval" bit from the "UI modification" bit, but I'm not 
sure if DFL does things this way. I tried spawn()ing a worker 
thread from the Button.click callback and receiveOnly(VideoInfo) 
from it, but this too ended up freezing the UI.


Thanks in advance.


Re: DFL background tasks

2015-06-07 Thread Adam D. Ruppe via Digitalmars-d-learn
For the Invoke call, you should be able to just ignore most the 
arguments and write something like


listview.invoke( delegate Object(Object[]) {
listview.Add(whatever);
return null;
});

and it should work.


DFL background tasks

2015-06-07 Thread Scroph via Digitalmars-d-learn

Hello,

I couldn't find a DFL subforum so I decided to post here, I 
apologize in advance if this isn't the right place.


I'm currently using DFL to write a Youtube downloading program 
with a simple GUI. I chose DFL because I enjoy the Entice 
Designer WYSIWYG capabilities, but also because I found DFL to be 
easy to use after reading the sample codes, especially the 
Phonebook example. Unfortunately the project is more or less 
abandoned, and the documentation is incomplete.


To make a long story short, there is a ListView that I want to 
populate with information that could take a while to retrieve 
(video qualities and links), which temporarily freezes the 
program. The way I did things thus far was to simply extend the 
Thread class and pass it the UI elements and let it modify them 
directly once the information is retrieved, or by just running 
the task in a delegate that has access to the elements I wish to 
modify, then passing said delegate to an instance of the Thread 
class. It worked with TextBoxes and Buttons and other elements, 
but not with ListView. I did some reading and it turned out this 
was a frowned upon method, that the UI should only be modified by 
the UI thread, and that this could be achieved with the "Invoke" 
method according to some C#-tagged replies on stackoverflow. I 
did some more digging and found an Invoke method in the Control 
class, but its signature is hard for me to decipher. I also am 
not sure how to use this with an onClick event.


Control.invoke documentation : 
http://wiki.dprogramming.com/DflDoc/Control-Control-invoke


Any input is appreciated. Thanks in advance.