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.

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.

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?

Edited 5 Years Ago by pyguy62: n/a

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.

Edited 5 Years Ago by pyTony: n/a

This article has been dead for over six months. Start a new discussion instead.