Tk::DropSite question
am 04.03.2007 22:10:11 von g_m
I have reduced the following script,
but it's runnable. It's a way
to make a Tk listbox that accepts
a selection of files or folders
from Windows Explorer.
The drop handler (my "OnSourceDrop")
is apparently called individually
on each file or folder in a multiple selection.
And my question is,
is there any way to know when to call
a group-drop-handler,
to be called after all the individual
calls to OnSourceDrop() are finished
after a group drop?
thank you,
~greg
~~~~~~~~~~~~~~~~~~~~~~
use strict;
use warnings;
$|=1;
use Tk;
use Tk::DropSite qw(Win32);
my $Main = MainWindow->new
(
-title=>'DND'
);
my %SourceList;
my $SourceListBox = $Main->Scrolled
(
"Listbox",
-scrollbars => "osoe",
)->pack
(
-fill => 'x',
);
$SourceListBox->DropSite
(
-dropcommand => [\&OnSourceDrop,$SourceListBox],
-droptypes => 'Win32',
);
sub SourceAdd
{
my $f = shift; # file or folder
return if exists $SourceList{$f};
return if ! -e $f;
$SourceList{$f} = 1;
my $slash = -d $f ? '\\' : '';
$SourceListBox->insert('end', "$f$slash" );
$SourceListBox->yviewMoveto(1);
}
sub OnSourceDrop
{
my($widget, $selection) = @_;
my $f = $widget->SelectionGet('-selection'=>$selection,'STRING');
return if ! defined $f; # is this really necessary?
SourceAdd($f);
}
MainLoop;
Re: Tk::DropSite question
am 05.03.2007 00:04:28 von Ch Lamprecht
~greg wrote:
> I have reduced the following script,
> but it's runnable. It's a way
> to make a Tk listbox that accepts
> a selection of files or folders
> from Windows Explorer.
>
> The drop handler (my "OnSourceDrop")
> is apparently called individually
> on each file or folder in a multiple selection.
>
> And my question is,
>
> is there any way to know when to call
> a group-drop-handler,
> to be called after all the individual
> calls to OnSourceDrop() are finished
> after a group drop?
One way is setting up a timer:
This one will trigger group_drop_handler 50ms after the last call to
OnSourceDrop. Are groups with one element considered being groups in your case?
Otherwise you would have to add a counter...
>
>
> use strict;
> use warnings;
> $|=1;
>
> use Tk;
> use Tk::DropSite qw(Win32);
>
> my $Main = MainWindow->new
> (
> -title=>'DND'
> );
>
> my %SourceList;
>
> my $SourceListBox = $Main->Scrolled
> (
> "Listbox",
> -scrollbars => "osoe",
> )->pack
> (
> -fill => 'x',
> );
>
> $SourceListBox->DropSite
> (
> -dropcommand => [\&OnSourceDrop,$SourceListBox],
> -droptypes => 'Win32',
> );
>
> sub SourceAdd
> {
> my $f = shift; # file or folder
> return if exists $SourceList{$f};
> return if ! -e $f;
> $SourceList{$f} = 1;
> my $slash = -d $f ? '\\' : '';
> $SourceListBox->insert('end', "$f$slash" );
> $SourceListBox->yviewMoveto(1);
> }
>
{
my $after_cb;
> sub OnSourceDrop
> {
> my($widget, $selection) = @_;
$widget->afterCancel($after_cb);
$after_cb = $widget->after(50,\&group_drop_handler);
> my $f = $widget->SelectionGet('-selection'=>$selection,'STRING');
> return if ! defined $f; # is this really necessary?
> SourceAdd($f);
> }
}
>
> MainLoop;
sub group_drop_handler{
print "group drop finished\n";
}
Christoph
--
use Tk;use Tk::GraphItems;$c=tkinit->Canvas->pack;push@i,Tk::GraphItems ->
TextBox(text=>$_,canvas=>$c,x=>$x+=70,y=>100)for(Just=>anoth er=>Perl=>Hacker);
Tk::GraphItems->Connector(source=>$i[$_],target=>$i[$_+1])fo r(0..2);
$c->repeat(30,sub{$_->move(0,4*cos($d+=3.16))for(@i)});MainL oop
Re: Tk::DropSite question
am 05.03.2007 02:25:40 von g_m
"Ch Lamprecht" > wrote ...
> One way is setting up a timer:
> This one will trigger group_drop_handler 50ms after the last call to OnSourceDrop. Are groups with one element considered being
> groups in your case?
> Otherwise you would have to add a counter...
- nah, singletons aren't special in this.
(- one thing that might come up later though
is how to block drops while the last drop
is still being processed - because the processing
could take a lot of time.
But I think I'm already thinking of a way to do this ....)
~
Thank you!
I understand it.
As long as the calls to OnSourceDrop
come within 50 milliseconds of each other,
then the delayed call to the group_drop_handler()
is continuously canceled.
But as soon as OnSourceDrop has not been
called within 50 milliseconds of the last time
it was called, then the call to the
group_drop_hander goes through.
It works!
And I'm sure it's robust enough.
But I was kinda hoping for something
that didn't depend on timing. :)
Following up on your suggestion,
I found, and then substituted, ->afterIdle()
for ->after().
And this works too.
And I also added a print in OnSourceDrop,
and (together with $|=1) this seems to show me
that it's all working in proper order.
I'm guessing that group-dropped items are all
put into the event queue at the same time,
and that that's why ->afterIdle() works,
and that it is ok if some other random event happens
to fall into the event queue before the ->afterIdle fires,
because that would simply delay the group_drop_handler
a bit longer, -- which is what your ->after(50,...) does too ...
So, would you agree that ->afterIdle() is better?
Or could there be some problem with it?
Thank you!
~greg
~~~~~~~
# changed to using ->afterIdle() instead of ->after()
# and in putting a print in OnSourceDrop()
# ...
use strict;
use warnings;
$|=1;
use Tk;
use Tk::DropSite qw(Win32);
my $Main = MainWindow->new
(
-title=>'DND'
);
my %SourceList;
my $SourceListBox = $Main->Scrolled
(
"Listbox",
-scrollbars => "osoe",
)->pack
(
-fill => 'x',
);
$SourceListBox->DropSite
(
-dropcommand => [\&OnSourceDrop,$SourceListBox],
-droptypes => 'Win32',
);
sub SourceAdd
{
my $f = shift; # file or folder
return if exists $SourceList{$f};
return if ! -e $f;
$SourceList{$f} = 1;
my $slash = -d $f ? '\\' : '';
$SourceListBox->insert('end', "$f$slash" );
$SourceListBox->yviewMoveto(1);
}
my $after_cb;
sub OnSourceDrop
{
print "OnSourceDrop\n";
my($widget, $selection) = @_;
$widget->afterCancel($after_cb);
#$after_cb = $widget->after(50,\&group_drop_handler);
$after_cb = $widget->afterIdle(\&group_drop_handler);
my $f = $widget->SelectionGet('-selection'=>$selection,'STRING');
return if ! defined $f; # is this really necessary?
SourceAdd($f);
}
MainLoop;
sub group_drop_handler
{
print "group drop finished\n";
}
Re: Tk::DropSite question
am 05.03.2007 23:44:22 von Ch Lamprecht
~greg wrote:
> "Ch Lamprecht" > wrote ...
>
>>One way is setting up a timer:
>
> But I was kinda hoping for something
> that didn't depend on timing. :)
>
> Following up on your suggestion,
> I found, and then substituted, ->afterIdle()
> for ->after().
>
> And this works too.
> So, would you agree that ->afterIdle() is better?
> Or could there be some problem with it?
afterIdle will work as long as no update- method is called by any of the
involved subs.
However, as I read the source of the module I found, that it provides a
mechanism, to do what you need: You can pass a Callback to the -entercommand
option. That will be called with either 0 or 1 as first arg to indicate the
beginning/end of a group_drop. See example below.
(And see another post for a second solution)
use strict;
use warnings;
use Data::Dumper;
use Tk;
use Tk::DropSite qw(Win32);
my $Main = MainWindow->new;
my %SourceList;
my $SourceListBox = $Main->Scrolled("Listbox",
-scrollbars => "osoe",
)->pack(-fill => 'both',
);
$SourceListBox->DropSite( -entercommand => \&drop_state,
-dropcommand => [\&OnSourceDrop,
$SourceListBox],
-droptypes => 'Win32',
);
sub SourceAdd{
my $f = shift; # file or folder
return if exists $SourceList{$f};
return if ! -e $f;
$SourceList{$f} = 1;
my $slash = -d $f ? '\\' : '';
$SourceListBox->insert('end', "$f$slash" );
$SourceListBox->yviewMoveto(1);
}
sub OnSourceDrop{
print "OnSourceDrop\n";
my($widget, $selection) = @_;
my $f = $widget->SelectionGet('-selection'=>$selection,'STRING');
return if ! defined $f; # is this really necessary?
SourceAdd($f);
}
MainLoop;
sub drop_state{
print 'group drop state: ',$_[0] ? 'started':'finished' , "\n";
}
--
use Tk;use Tk::GraphItems;$c=tkinit->Canvas->pack;push@i,Tk::GraphItems ->
TextBox(text=>$_,canvas=>$c,x=>$x+=70,y=>100)for(Just=>anoth er=>Perl=>Hacker);
Tk::GraphItems->Connector(source=>$i[$_],target=>$i[$_+1])fo r(0..2);
$c->repeat(30,sub{$_->move(0,4*cos($d+=3.16))for(@i)});MainL oop
Re: Tk::DropSite question
am 05.03.2007 23:57:15 von Ch Lamprecht
~greg wrote:
> "Ch Lamprecht" > wrote ...
>
>>One way is setting up a timer:
>>This one will trigger group_drop_handler 50ms after the last call to OnSourceDrop.
>
> It works!
> And I'm sure it's robust enough.
>
> But I was kinda hoping for something
> that didn't depend on timing. :)
>
> Following up on your suggestion,
> I found, and then substituted, ->afterIdle()
> for ->after().
>
> And this works too.
Reading the source I found, that it is not necessary to have the filenames
separated first, then appended to the clipboard, then read one by one by a
callback. A callback could as well get the filenames directly.
The code for the derived class is ugly because Tk::DragDrop::Win32Site is not
strictly OO - but it works:
(the code in main gets much shorter that way)
use strict;
use warnings;
#use Data::Dumper;
use Tk;
package Tk::DragDrop::Win32DirectSite;
require Tk::DragDrop::Win32Site;
our @ISA = ('Tk::DragDrop::Win32Site');
Tk::DropSite->Type('Win32Direct');
sub Win32Drop{
my ($w,$site,$msg,$wParam,$lParam) = @_;
my ($x,$y,@files) = Tk::DragDrop::Win32Site::DropInfo($wParam);
my $cb = $site->{'-dropcommand'};
#$site->Apply(-entercommand => $x, $y, 1);
if ($cb){
$cb->Call($w,\@files);# passing the target widget
# and an arrayref containing the filenames
}
#$site->Apply(-entercommand => $x, $y, 0);
return 0;
}
sub WM_DROPFILES () {563}
sub InitSite{
my ($class,$site) = @_;
my $w = $site->widget;
$w->BindClientMessage(WM_DROPFILES,[\&Win32Drop,$site]);
Tk::DragDrop::Win32Site::DragAcceptFiles($w,1);
}
package main;
require Tk::DropSite ;
my $mw = MainWindow->new();
my $lb = $mw->Scrolled('Listbox',
-scrollbars => "osoe",
)->pack(-expand => 1,
-fill => 'both',
);
$lb->DropSite(-droptype => 'Win32Direct',
-dropcommand => \&drop_handler,
);
MainLoop;
sub drop_handler{
my ($w,$files) = @_;# target widget, filenames
@$files = map {$_ .= -d $_ ? '\\':''} @$files;
$w->insert('end',@$files);
}
--
use Tk;use Tk::GraphItems;$c=tkinit->Canvas->pack;push@i,Tk::GraphItems ->
TextBox(text=>$_,canvas=>$c,x=>$x+=70,y=>100)for(Just=>anoth er=>Perl=>Hacker);
Tk::GraphItems->Connector(source=>$i[$_],target=>$i[$_+1])fo r(0..2);
$c->repeat(30,sub{$_->move(0,4*cos($d+=3.16))for(@i)});MainL oop
Re: Tk::DropSite question
am 06.03.2007 00:45:54 von g_m
"Ch Lamprecht" > wrote...
> Reading the source I found, ...
Thank you!
I am not very good at reading the source yet,
although I know that it's often the best or the only documentation.
Below is the entire pod for Tk::DropSite!
I once asked if there was a contest for the worst documented module.
This would have to be one of my first choices.
Thank you!
~greg
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tk::DropSite - Receive side of Drag & Drop abstraction
NAME
SYNOPSIS
DESCRIPTION
NAME
Tk::DropSite - Receive side of Drag & Drop abstraction
SYNOPSIS
use Tk::DropSite qw(...);
$widget->DropSite(-entercommand => ...,
-dropcommand => ...,
-motioncommand => ...,
-dropcommand => ...,
);
DESCRIPTION
DropSite creates an object which represents a site on which things may be "Dropped".
A DropSite provides the following methods:
$site->Enter($token,$event)
$site->Leave($token,$event)
$site->Motion($token,$event)
$site->Drop($token,$event)
Re: Tk::DropSite question
am 06.03.2007 01:04:12 von g_m
Ch Lamprecht , ...
I like this first way best. :)
Beautiful,
Thank you!
~greg