The short of it is that I have a widget, with source, written in Tk, specifically an analogue gauge, that I would like to use in a TKinter gui. Is this even possible? If so, how?

Thanks for any insight you might have.

Recommended Answers

All 9 Replies

Why wouldn't you be able to? Post the source.

Check Tkinter.Tcl command.

Why wouldn't you be able to? Post the source.

Here is the source of the tcl/tk guage that I want to use in Python:

$Tk::Gauge::VERSION = '0.3';

package Tk::Gauge;

use 5.8.2;
use Tk 804.027;
use Tk::widgets qw/ Trace /;
use base qw/ Tk::Derived Tk::Canvas /;
use strict;

Construct Tk::Widget 'Gauge';

# Class global definitions.

my $id = 0;			# needle ID

my ( %band_defaults )   = (
    -arccolor   =>        'white',
    -minimum    =>              0,
    -maximum    =>            100,
    -piecolor   =>        'white',
    -tag        =>             '',
);				# default band options

my $d2r = 0.0174532;		# degrees to radians

my ( %needle_defaults ) = (
    -arrowshape =>  [ 12, 23, 6 ],
    -color      =>        'black',
    -command    =>          undef,
    -format     =>           '%d',
    -id         =>              0,
    -radius     =>             96,
    -showvalue  =>              0,
    -tag        =>             '',
    -title      =>             '',
    -titlecolor =>        'black',
    -titlefont  => 'Helvetica-12',
    -titleplace =>        'south',
    -variable   =>       \my $var,
    -width      =>              5,
);				# default needle options

sub Populate {

    my ($self, $args) = @_;

    $self->SUPER::Populate($args);
    
    $self->ConfigSpecs(
        -background           => [ 'SELF',    'background'          , 'Background'          ,              'white' ],
        -bands                => [ 'PASSIVE', 'bands'               , 'Bands'               ,                undef ],
        -bandplace            => [ 'PASSIVE', 'bandPlace'           , 'BandPlace'           ,         'underticks' ],
        -bandstyle            => [ 'PASSIVE', 'bandStyle'           , 'BandStyle'           ,               'band' ],
        -bandwidth            => [ 'PASSIVE', 'bandWidth'           , 'BandWidth'           ,                   10 ],
        -caption              => [ 'PASSIVE', 'caption'             , 'Caption'             ,                   '' ],
        -captioncolor         => [ 'PASSIVE', 'captionColor'        , 'CaptionColor'        ,              'black' ],
        -extent               => [ 'PASSIVE', 'extent'              , 'Extent'              ,                 -270 ],
        -fill                 => [ 'PASSIVE', 'fill'                , 'Fill'                ,              'white' ],
        -finetickcolor        => [ 'PASSIVE', 'fineTickColor'       , 'FineTickColor'       ,              'black' ],
        -finetickinterval     => [ 'PASSIVE', 'fineTickInterval'    , 'FineTickInterval'    ,                undef ],
        -fineticklength       => [ 'PASSIVE', 'fineTickLength'      , 'FineTickLength'      ,                    2 ],
        -finetickthickness    => [ 'PASSIVE', 'fineTickThickness'   , 'FineTickThickness'   ,                    1 ],
        -from                 => [ 'PASSIVE', 'from'                , 'From'                ,                    0 ],
        -hubcolor             => [ 'PASSIVE', 'hubColor'            , 'HubColor'            ,      '#ef5bef5bef5b' ],
        -huboutline           => [ 'PASSIVE', 'hubOutline'          , 'HubOutline'          ,      '#ef5bef5bef5b' ],
        -hubplace             => [ 'PASSIVE', 'hubPlace'            , 'Hubplace'            ,         'overneedle' ],
        -hubradius            => [ 'PASSIVE', 'hubRadius'           , 'HubRadius'           ,                    5 ],
        -majortickcolor       => [ 'PASSIVE', 'majorTickColor'      , 'MajorTickColor'      ,              'black' ],
        -majortickinterval    => [ 'PASSIVE', 'majorTickInterval'   , 'MajorTickInterval'   ,                   10 ],
        -majorticklabelcolor  => [ 'PASSIVE', 'majorTickLabelColor' , 'MajorTickLabelColor' ,              'black' ],
        -majorticklabelfont   => [ 'PASSIVE', 'majorTickLabelFont'  , 'MajorTickLabelFont'  ,       'Helvetica-12' ],
        -majorticklabelformat => [ 'PASSIVE', 'majorTickLabelFormat', 'MajorTickLabelFormat',                 '%d' ],
        -majorticklabelpad    => [ 'PASSIVE', 'majorTickLabelPad'   , 'MajorTickLabelPad'   ,                   10 ],
        -majorticklabelplace  => [ 'PASSIVE', 'majorTickLabelPlace' , 'MajorTickLabelPlace' ,             'inside' ],
        -majorticklabelscale  => [ 'PASSIVE', 'majorTickLabelScale' , 'MajorTickLabelScale' ,                    1 ],
        -majorticklabelskip   => [ 'PASSIVE', 'majorTickLabelSkip'  , 'MajorTickLabelSkip'  ,                undef ],
        -majorticklength      => [ 'PASSIVE', 'majorTickLength'     , 'MajorTickLength'     ,                   10 ],
        -majortickthickness   => [ 'PASSIVE', 'majorTickThickness'  , 'MajorTickThickness'  ,                    1 ],
        -margin               => [ 'PASSIVE', 'margin'              , 'Margin'              ,                   10 ],
        -minortickcolor       => [ 'PASSIVE', 'minorTickColor'      , 'MinorTickColor'      ,              'black' ],
        -minortickinterval    => [ 'PASSIVE', 'minorTickInterval'   , 'MinorTickInterval'   ,                undef ],
        -minorticklength      => [ 'PASSIVE', 'minorTickLength'     , 'MinorTickLength'     ,                    5 ],
        -minortickthickness   => [ 'PASSIVE', 'minorTickThickness'  , 'MinorTickThickness'  ,                    1 ],
        -needles              => [ 'PASSIVE', 'needles'             , 'Needles'             , [{%needle_defaults}] ],
        -needlepad            => [ 'PASSIVE', 'needlePad'           , 'NeedlePad'           ,                    0 ],
        -outline              => [ 'PASSIVE', 'outline'             , 'Outline'             ,              'black' ],
        -outlinewidth         => [ 'PASSIVE', 'outlineWidth'        , 'OutlineWidth'        ,                    2 ],
        -start                => [ 'PASSIVE', 'start'               , 'Start'               ,                  225 ],
        -style                => [ 'PASSIVE', 'style'               , 'Style'               ,              'chord' ],
        -to                   => [ 'PASSIVE', 'to'                  , 'To'                  ,                  100 ],
    );

    $self->OnDestroy( [ \&delete_traces, $self ] );

} # end Populate

sub ConfigChanged {

    my( $self, $args ) = @_;

    $self->delete( 'gauge', 'hub', 'ticks', 'caption' );

    my $radius = $self->maxradius;
    $self->{ -maxradius } = $radius;

    my( $center_x, $center_y ) = $self->centerpoint;
    $self->configure( -width => 2 * $center_x, -height => 2 * $center_y - $self->cget( -margin ) / 2 );

    # Create the main gauge.

    $self->createArc(
        ( $center_x - $radius, $center_y - $radius ),
        ( $center_x + $radius, $center_y + $radius ),
        -extent  => $self->cget( -extent ),
        -fill    => $self->cget( -fill ),
        -outline => $self->cget( -outline ),
        -start   => $self->cget( -start ),
        -style   => $self->cget( -style ),
        -tags    => 'gauge',
        -width   => $self->cget( -outlinewidth ),
    );

    # Creat the hub.

    my $hub_place = $self->cget( -hubplace );
    die "Invalid -hubplace '$hub_place': must be 'overneedle', 'underneedle or 'hide'." unless $hub_place =~
	/^overneedle|underneedle|hide$/;
    if( $hub_place ne 'hide' ) {
	$self->createOval(
            ( $center_x - $self->cget( -hubradius ), $center_y - $self->cget( -hubradius ) ),
            ( $center_x + $self->cget( -hubradius ), $center_y + $self->cget( -hubradius ) ),
            -fill    => $self->cget( -hubcolor ),
            -outline => $self->cget( -huboutline ),
            -tags    => 'hub',
        );
    }

    # Draw bands.
    
    my $from  = $self->cget( -from );
    my $to    = $self->cget( -to );
    my $bands = $self->cget( -bands );
    if( $bands ) {
	foreach my $band ( @$bands ) {
	    my(@margs, %ahsh, $args, @args);        # fill in default band options not supplied by the user
	    @margs = grep ! defined $band->{$_}, keys %band_defaults;
	    %ahsh = %$band;                         # argument hash
	    @ahsh{@margs} = @band_defaults{@margs}; # fill in missing values
	    my( $arccolor, $min, $max, $piecolor, $tag ) =
		@ahsh{ qw/-arccolor -minimum -maximum -piecolor -tag / };
	    my $gext    = $self->cget( -extent ); # gauge -extent
	    my $gstart  = $self->cget( -start ); # gauge -start
	    my $ext     = - ( $max - $min  ) * ( $gext / ( $to - $from ) );
	    my $start   = $gstart + $gext * ( $max - $from ) / ( $to - $from );
            my $radius4 = $radius - ( $self->cget( -bandwidth ) / 2 );
	    my $style   = $self->cget( -bandstyle );
	    die "Invalid -bandstyle '$style': must be 'band' or 'pieslice'." unless $style =~ /^band|pieslice$/;
	    $style = 'arc' if $style eq 'band';
            $self->createArc(
                ( $center_x - $radius4, $center_y - $radius4 ),
                ( $center_x + $radius4, $center_y + $radius4 ),
                -extent  => $ext,
                -fill    => $piecolor,
                -outline => $arccolor,
                -start   => $start,
                -style   => $style,
                -tags    => [ 'bands', $tag ],
                -width   => $self->cget( -bandwidth ),
            );
	} # forend all bands
    } # ifend bands

    # Draw tick marks.

    my $start  = $self->cget( -start );
    my $extent = $self->cget( -extent );
    my $tincr  =  ( $extent / ( $to - $from ) );
    my $angle  = $start - $tincr;

    for( my $gvalue = $from; $gvalue <= $to; $gvalue ++ ) {

	$angle += $tincr;
	my $theta = -$angle * $d2r;

	my( $x, $y ) = ( cos( $theta ) * $radius, sin( $theta ) * $radius );
	$x += $center_x;
	$y += $center_y;

	my $major = 0;
	   $major  = ! ( $gvalue % $self->cget( -majortickinterval ) );
	my $minor  = 0;
	   $minor  = ! ( $gvalue % $self->cget( -minortickinterval ) ) if defined $self->cget( -minortickinterval );
	my $fine   = 0;
	   $fine   = ! ( $gvalue % $self->cget( -finetickinterval  ) ) if defined $self->cget( -finetickinterval );

	next unless $major or $minor or $fine;

	my $format = $self->cget( -majorticklabelformat );
	my $scale  = $self->cget( -majorticklabelscale );

	my $place = $self->cget( -majorticklabelplace );
	die "Invalid -majorticklabelplace '$place': must be 'inside', 'outside' or 'hide'." unless $place =~
	    /^inside|outside|hide$/;
	my $skip = 0;
	my $skipref = $self->cget( -majorticklabelskip );
	if( defined $skipref ) {
	    die "Invalid -majorticklabelskip '$skipref': must be an array reference." unless ref $skipref eq 'ARRAY';
	    foreach my $skipval ( @$skipref ) {
		next unless $skipval == $gvalue;
		$skip = 1;
		last;
	    }
	}

	if( $major and $place ne 'hide' and not $skip ) {
	    my $radius2;
	    my $fw = $self->fontMeasure( $self->cget( '-majorticklabelfont' ), '0' );
	    if( $place eq 'outside' ) {
		$radius2 = $radius + ( length( $to * $scale ) / 2 * $fw ) + $self->cget( -majorticklabelpad ) ;
	    } elsif( $place eq 'inside' ) {
		$radius2 = $radius - ( length( $to * $scale ) / 2 * $fw ) - $self->cget( -majorticklabelpad ) - $self->cget( -majorticklength );
	    }
	    my( $x2, $y2 ) = ( cos( $theta ) * $radius2, sin( $theta ) * $radius2 );
	    $x2 += $center_x;
	    $y2 += $center_y;
	    my $tangle = sprintf( $format, $gvalue * $scale );
	    $self->createText( $x2, $y2, -text => $tangle, -fill => $self->cget( -majorticklabelcolor ), -tags => 'ticklabel' );
	}

	if( $major or $minor or $fine ) {
	    my $radius3;	# order is important
	    my $color;
	    my $tag;
	    my $width;
	    if( $fine ) {
		$radius3  = $radius - $self->cget( -fineticklength );
		$color    = $self->cget( -finetickcolor );
		$tag      = 'finetick';
		$width    = $self->cget( -finetickthickness );
	    } 
	    if( $minor ) {
		$radius3  = $radius - $self->cget( -minorticklength );
		$color    = $self->cget( -minortickcolor );
		$tag      = 'minortick';
		$width    = $self->cget( -minortickthickness );
	    } 
	    if( $major ) {
		$radius3  = $radius - $self->cget( -majorticklength );
		$color    = $self->cget( -majortickcolor );
		$tag      = 'majortick';
		$width    = $self->cget( -majortickthickness );
	    }
	    my( $x3, $y3 ) = ( cos( $theta ) * $radius3, sin( $theta ) * $radius3 );
	    $x3 += $center_x;
	    $y3 += $center_y;
	    $self->createLine( $x, $y, $x3, $y3,
                -fill  => $color,
                -tags  => $tag,
                -width => $width,
            );
	}
	
    } # forend all ticks

    # Add the caption.

    $self->createText( $center_x, $self->cget( -margin ) + 2 * $radius,
        -fill => $self->cget( -captioncolor ),
        -text => $self->cget( -caption ),
        -tags => 'caption',
    );

    # Trace the variables and setup callbacks associated with all the needles.

    my $needles = $self->cget( -needles );
    if( $needles ) {
	$id = 'needle000000';
	$self->delete_traces;
	foreach my $needle ( @$needles ) {
	    $needle->{ -id } = $id++;
	    $self->delete( $needle->{ -id } );
	    $self->traceVariable ( $needle->{ -variable } , 'w', [ \&tracew => $self, $needle ] );
	    if( defined $needle->{ -command } ) {
		$needle->{ -command_callback } = Tk::Callback->new( $needle->{ -command } );
	    }
	}
    } 

} # end ConfigChanged

sub delete_traces {

    my( $self ) = @_;

    my $needles = $self->cget( -needles );
    return unless defined $needles;
    foreach my $needle ( @$needles  ) {
	$self->traceVdelete ( $needle->{ -variable } ) if defined $needle->{ -variable };
    }

} # end delete_traces

sub setvalue {			# draw needle(s)

    my( $self, $value, $needle ) = @_;

    # Fill in default needle options not supplied by the user.

    my(@margs, %ahsh, $args, @args);
    @margs = grep ! defined $needle->{$_}, keys %needle_defaults;
    %ahsh = %$needle;                         # argument hash
    @ahsh{@margs} = @needle_defaults{@margs}; # fill in missing values

    # Get needle options.

    my( $radius, $color, $cmd, $format, $vref, $width, $arrowshape, $showvalue, $title, $tcolor, $tfont, $tplace, $tag ) =
	@ahsh{ qw/ -radius -color -command -format -variable -width -arrowshape
		   -showvalue -title -titlecolor -titlefont -titleplace -tag / };

    my( $center_x, $center_y ) = $self->centerpoint;

    if( $value > $self->cget( -to ) ) {
	$value = $self->cget( -to );
    } elsif( $value < $self->cget( -from ) ) {
	$value = $self->cget( -from );
    }

    my( $x, $y ) = $self->radialpoint( $value, $radius );
    $self->delete( $needle->{ -id } );
    $self->createLine( $x, $y, $center_x, $center_y,
        -arrow      => 'first',
        -arrowshape => $arrowshape,
        -fill       => $color,
        -tags       => [ 'needle', $needle->{ -id }, $tag ],
        -width      => $width,
    );
    my $hub_place = $self->cget( -hubplace );
    $self->raise( 'hub', 'needle' ) if $hub_place eq 'overneedle';
    $self->lower( 'hub', 'needle' ) if $hub_place eq 'underneedle';
    
    if( $showvalue ) {
	my $fw = $self->fontMeasure( $self->cget( -majorticklabelfont ), '0' );
	my $radius2 = $self->{ -maxradius } + $self->cget( -majorticklabelpad ) + ( $fw * length( $self->cget( -to ) ) );
	( $x, $y ) = $self->radialpoint( $value, $radius2 );
	$value = sprintf( $format, $value );
	$self->createText( $x, $y, -text => $value, -fill => $color, -tags => [ 'needlevalue', $needle->{ -id } ] );
    }

    # Needle title.

    $x = $center_x;
    die "Invalid -titleplace '$tplace': must be 'north' or 'south'." unless $tplace =~ /^north|south$/;
    if( $tplace eq 'south' ) {
	$y = $center_y + $radius / 2;
    } elsif( $tplace eq 'north' ) {
	$y = $center_y - $radius / 2;
    }

    $self->createText( $x, $y, -text => $title, -fill => $tcolor, -font => $tfont , -tags => [ 'title', $needle->{ -id } ] );

    my $bplace = $self->cget( -bandplace );
    die "Invalid -bandplace '$bplace': must be 'underticks' or 'overticks'." unless $bplace =~
	/^overticks|underticks$/;
    $self->raise( 'bands' ) if $bplace eq 'overticks';

} # end setvalue

sub tracew {

    my ( $index, $value, $op, $self, $needle ) = @_;

    # Invoke the -command callback, if any, then move the needle.

    return unless defined $self; # if app is being destroyed
    return if $self->{_busy};

    if ( $op eq 'w' ) {
	my $rc = 1;
	if( defined $needle->{ -command_callback } ) {
	    $rc = $needle->{ -command_callback }->Call;
	}
	$self->setvalue( $value, $needle ) if $rc;
	return $value;
    } elsif ( $op eq 'r' ) {
    } elsif ( $op eq 'u' ) {
	$self->traceVdelete ( $needle->{-variable} );
    }

} # end tracew

# Public methods.

sub centerpoint {            # coordinates of center of gauge

    my( $self ) = @_;

    $self->{ -maxradius } = $self->maxradius;;
    return ( $self->cget( -margin ) + $self->{ -maxradius } ) x 2;

} # end centerpoint

sub maxradius {		     # maximum needle radius including padding

    my( $self ) = @_;

    my $radius = 0;
    foreach my $needle ( @{ $self->cget( -needles ) } ) {
	$radius = $needle->{ -radius } if $needle->{ -radius } > $radius;
    }
    return $radius += $self->cget( -needlepad );

} # end maxradius

sub radialpoint {            # coordinates of a needle value relative to the gauge centerpoint

    my( $self, $value, $radius ) = @_;

    my $from     = $self->cget( -from );
    my $to       = $self->cget( -to );
    my $start    = $self->cget( -start );
    my $extent   = $self->cget( -extent );
    my $tincr    = $extent / ( $to - $from );
    my $angle    = $start + ( ( $value - $from ) * $tincr );
    $angle       = -$angle * $d2r;
    my( $x, $y ) = ( cos( $angle ) * $radius, sin( $angle ) * $radius );
    my( $center_x, $center_y ) = $self->centerpoint;
    $x += $center_x;
    $y += $center_y;
    return ( $x, $y );

} # end radialpoint

1;

__END__

pyTony suggests using Tkinter.Tcl, but that seems to do just the opposite of what I need by calling a python function from tcl, unless I am misunderstanding its usage.

did you review this http://docs.python.org/library/tk.html?

Yes, I have spent some time reviewing that and numerous other documents prior to posting my question! I didn't even find a reference there for to.tcl or tk.eval.

Yeah, I often get very frustrated with the lack of documentation, or at least quality documentation on things. It seems like this would be pretty easy to write in python, but I guess the answer to whether or not you could directly run the tcl code in the python interpreter is no. However, and I have no experience with it, there is a jacl (javatcl) to jython converter I found on bytes:http://www-1.ibm.com/support/docview...id=swg24012144, unfortunately I don't know of any other direct converter/translator. Just out of curiosity, why did you write something in tcl?

Yeah, I often get very frustrated with the lack of documentation, or at least quality documentation on things. It seems like this would be pretty easy to write in python, but I guess the answer to whether or not you could directly run the tcl code in the python interpreter is no. However, and I have no experience with it, there is a jacl (javatcl) to jython converter I found on bytes:http://www-1.ibm.com/support/docview...id=swg24012144, unfortunately I don't know of any other direct converter/translator. Just out of curiosity, why did you write something in tcl?

I didn't write it but found it in a search. Couldn't find a python tk widget similar. New to python. I could port it but being new would take some time.

Maybe you should check Power MegaWidgets (easy_install pmw), here is one gauge written with those:
http://www.java2s.com/Code/Python/GUI-Pmw/GaugemadefromPmwMegaWidget.htm:

#Pmw copyright

#Copyright 1997-1999 Telstra Corporation Limited, Australia 
#Copyright 2000-2002 Really Good Software Pty Ltd, Australia

#Permission is hereby granted, free of charge, to any person obtaining a copy 
#of this software and associated documentation files (the "Software"), to deal 
#in the Software without restriction, including without limitation the rights 
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
#copies of the Software, and to permit persons to whom the Software is furnished 
#to do so, subject to the following conditions:

#The above copyright notice and this permission notice shall be included in all 
#copies or substantial portions of the Software.

#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 


from Tkinter import *
import Pmw

class Gauge(Pmw.MegaWidget):
    def __init__(self, parent=None, **kw):

        # Define the options for the megawidget
        optiondefs = (
            ('min',           0,          Pmw.INITOPT),
            ('max',           100,        Pmw.INITOPT),
            ('fill',          'red',      None),
            ('size',          30,         Pmw.INITOPT),
            ('value',         0,          None),
            ('showvalue',     1,          None),
        )
        self.defineoptions(kw, optiondefs)

        # Initialize the base class
        Pmw.MegaWidget.__init__(self, parent)

        interior = self.interior()

        # Create the gauge component
        self.gauge = self.createcomponent('gauge',
                             (), None,
                             Frame, (interior,),
                             borderwidth=0)
        self.canvas = Canvas(self.gauge,
                             width=self['size'], height=self['size'],
                             background=interior.cget('background'))
        self.canvas.pack(side=TOP, expand=1, fill=BOTH, anchor=CENTER)
        self.gauge.grid()

        # Create the scale component
        self.scale = self.createcomponent('scale',
                             (), None,
                             Scale, (interior,),
                             command=self._setGauge,
                             length=200,
                             from_ = self['min'],
                             to    = self['max'],
                             showvalue=self['showvalue'])
        self.scale.grid()

        value=self['value']
        if value is not None:
            self.scale.set(value)

        # Check keywords and initialize options
        self.initialiseoptions(Gauge)

    def _setGauge(self, value):
        self.canvas.delete('gauge')
        ival = self.scale.get()
        ticks = self['max'] - self['min']
        arc = (360.0/ticks) * ival
        xy = 3,3,self['size'],self['size']
        start = 90-arc
        if start < 0:
            start = 360 + start
        self.canvas.create_arc(xy, start=start, extent=arc-.001,
                               fill=self['fill'], tags=('gauge',))

Pmw.forwardmethods(Gauge, Scale, 'scale')

root = Tk()
#root.option_readfile('optionDB')
root.title('Gauge')
Pmw.initialise()

gauges = tuple(Gauge(root, fill=color, value=10*v, min=0, max=255)
          for v, color in enumerate(('red', 'blue', 'black', 'yellow', 'pink', 'gray')))
for g in gauges:
    g.pack(side=LEFT, padx=1, pady=10)

root.mainloop()

I did little more clever creation for Gauge objects and packing them.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.