TunesMan has asked for the wisdom of the Perl Monks concerning the following question:

I’m writing an Perl Tk app and want to provide a plugin option. I envision this as a column of predefined buttons and then optional user defined buttons added on the end. My thought is to use a ./PlugIn.pm file that subroutines can be added to and a configure script that would state the button properties and –command => \&do_something.

I’ve spent most of the day looking for how I could read in the do_something and get it to reference the subroutine defined in PlugIn.pm. I know I’ve read how to do something like this before (at least I think did). I’m at the banging head against monitor stage. Any pointers or help greatly appreciated.

Thanks TunesMan

Replies are listed 'Best First'.
Re: Dynamic Tk buttons from init file
by rjray (Chaplain) on Mar 19, 2003 at 23:42 UTC

    Probably the cleanest way of doing this would be to have a more formalized plug-in framework, and the PlugIn.pm file would have not just the new subroutines, but also calls into a "registry" of some sort, to make their presence known.

    There are a lot of ways of doing plug-ins, and how you choose to do them is mainly a function of how quickly you want results versus how elegant and maintainable you want the end product to be.

    Here's one suggestion: Have each plug-in use its own file, and mandate that each declare a package name, using a common prefix (such as Plugin:: and the file name, just like a "normal" Perl module. We'll call the example one "Print", so the plug-in is in Print.pm and it declares "package Plugin::Print;" near the top. Additionally, require that the plug-in have a common entry-point routine, much like Apache location handlers default to the name "handler". Call this one "run", for example. Now, we have a file with a known package name (it can be derived from the file name) and a known subroutine entry-point (Plugin::Print::run). Your configuration script now needs only to point to the Print.pm file, and you know that (a) you have to load it in, and (b) the button you create should have the "-command" option point to \&Plugin::Print::run.

    This is one way. It has benefits and drawbacks. I encourage you to look at other solutions, as well.

    --rjray

      Thanks rjray. Good idea. I think I'll steal it!

      For some reason I could not get the Plugin::Print::run to work.

      Test file "foo.pl"

      #!/usr/local/bin/perl -w Plugin::Print::run;
      Then in the Plugin directory I have Print.pm
      package Plugin::Print; sub run { print "Got print run!\n" } 1;
      When I run this I get Undefined subroutine &Plugin::Print::run called at foo.pl line 3.

      I expermented some and found that I could do this Test file "foo.pl"

      #!/usr/local/bin/perl -w use Plugin; Plugin::Print::run; Plugin::Edit::run;
      Then in Plugin.pm in same directory
      package Plugin::Print; sub run { print "Got print run!\n" } package Plugin::Edit; sub run { print "Got print run!\n" } 1;
      This works well enough but I'm curious why the first one didn't work?

      TunesMan

        For one thing, your first example doesn't seem to "use" the module before trying to call the routine.

        Another thing to watch for, is that if you plan on saying "use Plugin::Print", then Print.pm will have to be in a directory called "Plugin", and that directory in your search path. But there are other ways of reading in the code, besides "use" and "require".

        --rjray

Re: Dynamic Tk buttons from init file
by pg (Canon) on Mar 20, 2003 at 05:20 UTC
    Here is one solution. Let the user pass in:
    1. A hash uses button text as key, and coderef as value
    2. An array defines the sequence of user buttons (help the hash to maintain order)
    Also we use an array to store the button handlers.

    #!/usr/bin/perl use Tk; use strict; use constant BUTTON_WIDTH => 20; my $val; my %user_buttons = ("button1" => \&user_button1_func, "button2" => \&user_button2_func, "button3" => \&user_button3_func); my @user_button_sequence = ("button1", "button2", "button3"); my @user_buttons; my $mw = new MainWindow; my $entry = $mw->Entry(textvariable => \$val) ->pack(fill => "x"); my $default_button1 = $mw->Button(text => "Default Button 1", width => BUTTON_WIDTH, command => sub {$val = "default butt +on 1 clicked"}) ->pack(); my $default_button2 = $mw->Button(text => "Default Button 2", width => BUTTON_WIDTH, command => sub {$val = "default butt +on 2 clicked"}) ->pack(); foreach my $button_text (@user_button_sequence) { push @user_buttons, $mw->Button(text => $button_text, width => BUTTON_WIDTH, command => $user_buttons{$button_text}) ->pack(); } MainLoop; sub user_button1_func { $val = "user button 1 clicked"; } sub user_button2_func { $val = "user button 2 clicked"; } sub user_button3_func { $val = "user button 3 clicked"; }