http://www.perlmonks.org?node_id=210349

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

I have a script that can run with a GUI or not, depending on if the user specifies a -g option. The GUI is Tk based. Some of my users don't have Tk installed - they should still be able to run the script in console mode.
First, I tried:
if ($opt_g) { use Tk; <Tk code> }
but the 'use' would complain even of not finding the Tk module (for those without it installed) when the -g option was missing.
Then I tried:
if ($opt_g) { eval "use Tk; 1" or die "Tk not installed. Can't run GUI.\n"; <Tk code> }
This lets the script work in console mode, but with -g specified, the script fails to bring up the GUI, the script just exits back to the command prompt - no errors or anything.

Any help would be appreciated.

Replies are listed 'Best First'.
Re: using 'use' conditionally (require instead of use)
by ybiC (Prior) on Nov 05, 2002 at 00:50 UTC
    If I'm not mistaken, you want to require instead of use in your eval block.   Here's how I did similarly a while back:
    my $have_SWE; BEGIN { $have_SWE = 0; eval { require Spreadsheet::WriteExcel }; unless ($@) { Spreadsheet::WriteExcel->import(); $have_SWE = 1; } } if ($have_SWE == 1) { ... }
    If memory serves, I picked that up from something merlyn once said or posted.
        cheers,
        Don
        striving toward Perl Adept
        (it's pronounced "why-bick")

    Update:
    Super Search for "module installed" is what lead me to this thread, where I learnt of BEGIN, eval, and require.

    Update:
    After discussing with brother BUU, the lone advantage I can see of BEGIN... eval is that it allows you to include a more user-friendly message in the event that Tk isn't installed.   As always, Wiser Monks Than I may very well rightly say that I'm fullabeans.   {grin}

    Untested, but perhaps a wee bit cleaner than my original code example:

    my $have_Tk; BEGIN { $have_Tk = 0; eval { require Tk }; $have_Tk = 1 unless ($@); } if ($have_Tk == 1) { ... }

      Why bother evaling? Just do if($opt_g){require Tk;}

        What if the require fails?

      When I switch the 'use' to a 'require', I can indeed determine if the module is not installed and catch that.

      However, if the module *is* installed, the Tk code fails to function. There are no errors or anything - the script just exits. I actually placed a 'print' after the MainLoop call, and I see the print output.

      It seems to make no difference whether I call Tk->import;
      It seems to make no difference if I place Tk:: before the tk subroutines.

      bizarre...
Re: using 'use' conditionally
by LTjake (Prior) on Nov 05, 2002 at 01:22 UTC
    There's also the lovely if module.
    Example:
    use constant DEBUG => 1; use if (DEBUG), 'CGI::Carp' => 'fatalsToBrowser';


    --
    Rock is dead. Long live paper and scissors!
Re: using 'use' conditionally
by petral (Curate) on Nov 05, 2002 at 01:41 UTC
Re: using 'use' conditionally
by broquaint (Abbot) on Nov 05, 2002 at 11:04 UTC
    Although not directly applicable to the problem at hand (which can be solved with a require) there is the if module (core in 5.8.0) which will use a Perl module if a condition holds. The only problem with this is that it happens at compile time, so couldn't be directly applied in your particular case.
    HTH

    _________
    broquaint

    update: doh, just noticed LTjake's node above. note to self - recaffeinate head before posting

Re: using 'use' conditionally
by Anonymous Monk on Nov 05, 2002 at 10:34 UTC
    Have a look in the Perl Cookbook : "Recipe 12.2 Trapping Errors in require or use". It propose to trap problems with require using eval. It also says to do it in a BEGIN block (compile time versus run time).
Re: using 'use' conditionally
by dilbert (Novice) on Nov 05, 2002 at 11:03 UTC
    The error output of the eval block is caught in the $@ variable, that's why you don't see any error message. Leave out the 'die', and try this:
    if ($@){ die "Tk not installed. Can't run GUI. Error returned: $@\n"; }
Re: using 'use' conditionally
by blssu (Pilgrim) on Nov 05, 2002 at 16:59 UTC

    You can't always replace 'use' with 'require' because they work slightly differently. 'use' works at compile time and also imports stuff into your code. 'require' works at run time and never imports stuff.

    This example isn't exactly like your problem, but I think it might be useful anyways.

    BEGIN { if ($^O eq 'MSWin32') { eval "use APDM::ConfigWin32"; print $@ if ($@); } else { eval "use APDM::ConfigUnix"; print $@ if ($@); } }

      If you care that much about importing from the module:
      if($opt_g) { require Tk; Tk->import; }
      Difference goes away! wow.
        Difference goes away! wow.

        BUU is right. Additionally, blssu, you don't need to import if you're going to use the OO interface of whichever module you required... because good OO interfaces don't export their methods.

        blyman
        setenv EXINIT 'set noai ts=2'

        Sure, that's pretty close to what 'use' does now, but the 'eval' helps future-proof your code. The hand-rolled import isn't clever, it's just extra work. Laziness is one of the three pillars...

        The common efficiency and security disadvantages of 'eval' aren't issues here because (1) run-time 'require' must do an 'eval' eventually anyway, and (2) the 'eval' uses a string constant that is easy to understand and verify.

whatif { Re: using 'use' conditionally} ifonly { true; }
by mattr (Curate) on Nov 05, 2002 at 13:35 UTC
    I was intrigued by the Whatif modules with its patented Guardian Angel (TM) technology. This is neat!

    Of course Switch.pm's bad mojo got me into trouble recently so I am not going to be the first to implement in a production environment.. unless my first step is to surround the entire program with a whatif {} ifonly {} clause!

Re: using 'use' conditionally
by mousey (Scribe) on Nov 05, 2002 at 16:44 UTC
    require Tk if $opt_g;
Re: using 'use' conditionally
by dash2 (Hermit) on Nov 06, 2002 at 17:31 UTC
    Lots of people have pointed out BEGIN, "use if" and "require".

    Another point is that (unless your program is very simple) you probably don't want to include the Tk interface and the command line interface in the same packages.

    Instead do something like this:

    my $iface = $opt_g ? new MyInterface::Tk : new MyInterface::CommandLin +e;

    The two MyInterface subclasses provide different ways of doing the same thing... so then you can write:

    $iface->notify("Your flies are down"); $iface->ask("Do you want to zip them up?", 'Y', 'N');

    This separates your interface code nice and cleanly from the rest of your program.

    Of course, you still have to be careful what you require, because if Tk isn't installed, then MyInterface::Tk will presumably die on compilation.

    dave hj~

Re: using 'use' conditionally
by BUU (Prior) on Nov 06, 2002 at 02:12 UTC
    This is basically a reply to everyone who suggested something like:
    eval { use Tk; } if($@){ goto &cli } else { MainLoop(); }
    And the problem is that it takes control away from the user. What if i have Tk, but i don't want to use the bloody tk interface?

      BUU asks :

      What if i have Tk, but i don't want to use the bloody tk interface?
      Then you probably wouldn't be specifying it using the -g option, as given in the original question.
Re: using 'use' conditionally
by xafwodahs (Scribe) on Dec 02, 2002 at 19:04 UTC
    Here is a test script. Run it with a -g as the first parameter.

    On a machine without Tk, it will correctly die.
    On a machine with Tk, it *should* bring up a simple Tk window, but it exits instead. If I replace the 'require' and 'import' with the 'use', then it works.

    if ($ARGV[0] =~ /-g/) { # do gui die "You need Tk." unless (eval("require Tk;")); #use Tk; require Tk; Tk->import; $mw = MainWindow->new; $mw->Button(-text=>"EXIT", -command=>sub{exit})->pack(-side=>"bottom", -fill=>"both"); MainLoop; print "gui done.\n"; } else { # do non-gui print "non-gui stuff\n"; }
Re: using 'use' conditionally
by pizza_milkshake (Monk) on Nov 07, 2002 at 03:09 UTC
    use eval and check $@... there's an example in The Perl Cookbook by O'Reilly

    perl -e'$_=q#: 13_2: 12/"{>: 8_4) (_4: 6/2"-2; 3;-2"\2: 5/7\_/\7: 12m m::#;s#:#\n#g;s#(\D)(\d+)#$1x$2#ge;print'