--- /dev/null
+#@ MODIF A_AU_PLUS_UN Accas DATE 28/01/2005 AUTEUR VABHHTS J.PELLET
+# -*- coding: iso-8859-1 -*-
+# CONFIGURATION MANAGEMENT OF EDF VERSION
+# ======================================================================
+# COPYRIGHT (C) 1991 - 2005 EDF R&D WWW.CODE-ASTER.ORG
+# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
+# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
+# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
+# (AT YOUR OPTION) ANY LATER VERSION.
+#
+# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
+# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
+# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
+#
+# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
+# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
+# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
+# ======================================================================
+
+from Noyau import N_REGLE
+from Validation import V_AU_PLUS_UN
+
+class AU_PLUS_UN(V_AU_PLUS_UN.AU_PLUS_UN,N_REGLE.REGLE):
+ """
+ La classe utilise l'initialiseur de REGLE. Il n'est pas
+ nécessaire d'expliciter son initialiseur car
+ V_AU_PLUS_UN.AU_PLUS_UN n'en a pas
+ """
# Les règles
from A_AU_MOINS_UN import AU_MOINS_UN
+from A_AU_PLUS_UN import AU_PLUS_UN
from A_UN_PARMI import UN_PARMI
from A_PRESENT_PRESENT import PRESENT_PRESENT
from A_PRESENT_ABSENT import PRESENT_ABSENT
#
#
# ======================================================================
+print 80
+import traceback
+traceback.print_stack()
--- /dev/null
+#@ MODIF calc_fonction_ops Macro DATE 12/05/2005 AUTEUR DURAND C.DURAND
+# -*- coding: iso-8859-1 -*-
+# CONFIGURATION MANAGEMENT OF EDF VERSION
+# ======================================================================
+# COPYRIGHT (C) 1991 - 2005 EDF R&D WWW.CODE-ASTER.ORG
+# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
+# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
+# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
+# (AT YOUR OPTION) ANY LATER VERSION.
+#
+# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
+# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
+# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
+#
+# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
+# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
+# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
+# ======================================================================
+
+
+def tocomplex(arg):
+ if arg[0]=='RI' : return complex(arg[1],arg[2])
+ if arg[0]=='MP' : return complex(arg[1]*cos(arg[2]),arg[1]*sin(arg[2]))
+
+def calc_fonction_ops(self,FFT,DERIVE,INTEGRE,LISS_ENVELOP,
+ SPEC_OSCI,ABS,COMB,COMB_C,COMPOSE,EXTRACTION,
+ ENVELOPPE,ASSE,CORR_ACCE,PUISSANCE,INVERSE,
+ NOM_PARA,NOM_RESU,INTERPOL,PROL_DROITE,
+ PROL_GAUCHE,NOM_PARA_FONC,INTERPOL_FONC,PROL_DROITE_FONC,
+ PROL_GAUCHE_FONC,**args):
+ """
+ Ecriture de la macro CALC_FONCTION
+ """
+ ier=0
+ import types
+ import string
+ import copy
+ from math import pi
+ from Utilitai.t_fonction import t_fonction,t_fonction_c,t_nappe
+ from Accas import _F
+ from Cata.cata import nappe_sdaster,fonction_sdaster,fonction_c
+ from Utilitai.Utmess import UTMESS
+ from Numeric import alltrue,less,array,reshape,cos,sin,exp,sqrt
+ from Numeric import choose,zeros,Float
+ import aster_fonctions
+
+ ### On importe les definitions des commandes a utiliser dans la macro
+ DEFI_FONCTION = self.get_cmd('DEFI_FONCTION')
+ DEFI_NAPPE = self.get_cmd('DEFI_NAPPE')
+
+ ### Comptage commandes + déclaration concept sortant
+ self.set_icmd(1)
+ self.DeclareOut('C_out',self.sd)
+
+ ### type de traitement
+ ###
+ if (INTEGRE != None):
+ __ff=INTEGRE['FONCTION'].convert()
+ if INTEGRE['METHODE']=='TRAPEZE' : __ex=__ff.trapeze(INTEGRE['COEF'])
+ if INTEGRE['METHODE']=='SIMPSON' : __ex=__ff.simpson(INTEGRE['COEF'])
+ ###
+ if (DERIVE != None):
+ __ff=DERIVE['FONCTION'].convert()
+ __ex=__ff.derive()
+ ###
+ if (INVERSE != None):
+ __ff=INVERSE['FONCTION'].convert()
+ __ex=__ff.inverse()
+ ###
+ if (ABS != None):
+ __ff=ABS['FONCTION'].convert()
+ __ex=__ff.abs()
+ ###
+ if (COMPOSE != None):
+ __ff=COMPOSE['FONC_RESU'].convert()
+ __fg=COMPOSE['FONC_PARA'].convert()
+ __ex=__ff[__fg]
+ ###
+ if (ASSE != None):
+ __f0=ASSE['FONCTION'][0].convert()
+ __f1=ASSE['FONCTION'][1].convert()
+ __ex=__f0.cat(__f1,ASSE['SURCHARGE'])
+ ###
+ if (COMB != None):
+ if args['LIST_PARA']!=None : vale_x=args['LIST_PARA'].Valeurs()
+ else :
+ vale_x=[]
+ for mcfact in COMB :
+ vale_x=vale_x+mcfact['FONCTION'].Absc()
+ vale_x=dict([(i,0) for i in vale_x]).keys()
+ vale_x.sort()
+ list_fonc=[]
+ if isinstance(self.sd,nappe_sdaster):
+ for mcfact in COMB :
+ list_fonc.append(mcfact['FONCTION'].convert())
+ list_fonch=[]
+ for f in list_fonc :
+ __ex=f
+ for g in list_fonc :
+ __ex=__ex.homo_support(g)
+ list_fonch.append(__ex)
+ list_fonc=list_fonch
+ elif isinstance(self.sd,fonction_sdaster):
+ for mcfact in COMB :
+ __ex=mcfact['FONCTION'].convert()
+ list_fonc.append(__ex.evalfonc(vale_x))
+
+ __ex=list_fonc[0]
+ __ex=__ex*COMB[0]['COEF']
+ i=1
+ for item in list_fonc[1:] :
+ item=item*COMB[i]['COEF']
+ __ex=__ex+item
+ i=i+1
+ ###
+ if (COMB_C != None):
+ if args['LIST_PARA']!=None : vale_x=args['LIST_PARA'].Valeurs()
+ else :
+ vale_x=[]
+ for mcfact in COMB_C :
+ vale_x=vale_x+mcfact['FONCTION'].Absc()
+ vale_x=dict([(i,0) for i in vale_x]).keys()
+ vale_x.sort()
+ list_fonc=[]
+ if isinstance(self.sd,nappe_sdaster):
+ for mcfact in COMB_C :
+ list_fonc.append(mcfact['FONCTION'].convert())
+ list_fonch=[]
+ for f in list_fonc :
+ __ex=f
+ for g in list_fonc :
+ __ex=__ex.homo_support(g)
+ list_fonch.appen(__ex)
+ list_fonc=list_fonch
+ elif isinstance(self.sd,fonction_sdaster) or isinstance(self.sd,fonction_c):
+ for mcfact in COMB_C :
+ __ex=mcfact['FONCTION'].convert(arg='complex')
+ list_fonc.append(__ex.evalfonc(vale_x))
+
+ __ex=list_fonc[0]
+ if COMB_C[0]['COEF_R']!=None: __ex=__ex*complex(COMB_C[0]['COEF_R'])
+ if COMB_C[0]['COEF_C']!=None: __ex=__ex*tocomplex(COMB_C[0]['COEF_C'])
+ i=1
+ for item in list_fonc[1:] :
+ if COMB_C[i]['COEF_R']!=None: coef=complex(COMB_C[i]['COEF_R'])
+ if COMB_C[i]['COEF_C']!=None: coef=tocomplex(COMB_C[i]['COEF_C'])
+ item=item*coef
+ __ex=__ex+item
+ i=i+1
+ ###
+ if (PUISSANCE != None):
+ __ff=PUISSANCE['FONCTION'].convert()
+ __ex=__ff
+ for i in range(PUISSANCE['EXPOSANT']-1) : __ex=__ex*__ff
+ ###
+ if (EXTRACTION != None):
+ if EXTRACTION['PARTIE']=='REEL' : __ex=EXTRACTION['FONCTION'].convert(arg='real')
+ if EXTRACTION['PARTIE']=='IMAG' : __ex=EXTRACTION['FONCTION'].convert(arg='imag')
+ if EXTRACTION['PARTIE']=='MODULE' : __ex=EXTRACTION['FONCTION'].convert(arg='modul')
+ if EXTRACTION['PARTIE']=='PHASE' : __ex=EXTRACTION['FONCTION'].convert(arg='phase')
+ ###
+ if (ENVELOPPE != None):
+ list_fonc=[]
+ if isinstance(self.sd,nappe_sdaster):
+ for f in ENVELOPPE['FONCTION'] : list_fonc.append(f.convert())
+ list_fonch=[]
+ for f in list_fonc :
+ __ff=f
+ for g in list_fonc :
+ __ff=__ff.homo_support(g)
+ list_fonch.append(__ff)
+ list_fonc=list_fonch
+ vale_para=list_fonc[0].vale_para
+ para =list_fonc[0].para
+ l_fonc_f =[]
+ for i in range(len(vale_para)):
+ __ff=list_fonc[0].l_fonc[i]
+ if ENVELOPPE['CRITERE']=='SUP' :
+ for f in list_fonc[1:] : __ff=__ff.sup(f.l_fonc[i])
+ if ENVELOPPE['CRITERE']=='INF' :
+ for f in list_fonc[1:] : __ff=__ff.inf(f.l_fonc[i])
+ l_fonc_f.append(__ff)
+ __ex=t_nappe(vale_para,l_fonc_f,para)
+ elif isinstance(self.sd,fonction_sdaster):
+ for f in ENVELOPPE['FONCTION'] : list_fonc.append(f.convert())
+ __ex=list_fonc[0]
+ if ENVELOPPE['CRITERE']=='SUP' :
+ for f in list_fonc[1:] : __ex=__ex.sup(f)
+ if ENVELOPPE['CRITERE']=='INF' :
+ for f in list_fonc[1:] : __ex=__ex.inf(f)
+ ###
+ if (CORR_ACCE != None):
+ __ex=CORR_ACCE['FONCTION'].convert()
+ para=copy.copy(__ex.para)
+ # suppression de la tendance de l accelero
+ __ex=__ex.suppr_tend()
+ # calcul de la vitesse
+ __ex=__ex.trapeze(0.)
+ # calcul de la tendance de la vitesse : y = a1*x +a0
+ __ex=__ex.suppr_tend()
+ if CORR_ACCE['CORR_DEPL']=='OUI':
+ # suppression de la tendance deplacement
+ # calcul du deplacement : integration
+ __ex=__ex.trapeze(0.)
+ # calcul de la tendance du déplacement : y = a1*x +a0
+ __ex=__ex.suppr_tend()
+ # regeneration de la vitesse : derivation
+ __ex=__ex.derive()
+ # regeneration de l accelero : derivation
+ __ex=__ex.derive()
+ __ex.para=para
+ ###
+ if (FFT != None):
+ if isinstance(self.sd,fonction_c):
+ __ff=FFT['FONCTION'].convert()
+ __ex=__ff.fft(FFT['METHODE'])
+ if isinstance(self.sd,fonction_sdaster):
+ __ff=FFT['FONCTION'].convert(arg='complex')
+ __ex=__ff.fft(FFT['METHODE'],FFT['SYME'])
+ ###
+ if (SPEC_OSCI != None):
+ if SPEC_OSCI['AMOR_REDUIT']==None :
+ l_amor=[0.02,0.05,0.1]
+ UTMESS('I','CALC_FONCTION',' : génération par défaut de 3 amortissements :'+str(l_amor))
+ else :
+ if type(SPEC_OSCI['AMOR_REDUIT']) not in (types.ListType,types.TupleType):
+ l_amor=[SPEC_OSCI['AMOR_REDUIT'],]
+ else : l_amor= SPEC_OSCI['AMOR_REDUIT']
+ if SPEC_OSCI['FREQ']==None and SPEC_OSCI['LIST_FREQ']==None:
+ l_freq=[]
+ for i in range(56) : l_freq.append( 0.2+0.050*i)
+ for i in range( 8) : l_freq.append( 3.0+0.075*i)
+ for i in range(14) : l_freq.append( 3.6+0.100*i)
+ for i in range(24) : l_freq.append( 5.0+0.125*i)
+ for i in range(28) : l_freq.append( 8.0+0.250*i)
+ for i in range( 6) : l_freq.append(15.0+0.500*i)
+ for i in range( 4) : l_freq.append(18.0+1.000*i)
+ for i in range(10) : l_freq.append(22.0+1.500*i)
+ texte=[]
+ for i in range(len(l_freq)/5) :
+ texte.append(' %f %f %f %f %f' %tuple(l_freq[i*5:i*5+5]))
+ UTMESS('I','CALC_FONCTION',' : génération par défaut de 150 fréquences :\n'+'\n'.join(texte))
+ elif SPEC_OSCI['LIST_FREQ']!=None:
+ l_freq=SPEC_OSCI['LIST_FREQ'].Valeurs()
+ elif SPEC_OSCI['FREQ']!=None:
+ if type(SPEC_OSCI['FREQ']) not in (types.ListType,types.TupleType):
+ l_freq=[SPEC_OSCI['FREQ'],]
+ else : l_freq= SPEC_OSCI['FREQ']
+ if abs(SPEC_OSCI['NORME'])<1.E-10 :
+ UTMESS('S','CALC_FONCTION',' : SPEC_OSCI, la norme ne peut etre nulle')
+ if SPEC_OSCI['NATURE_FONC']!='ACCE' :
+ UTMESS('S','CALC_FONCTION',' : SPEC_OSCI, le type de la fonction doit etre ACCE')
+ if SPEC_OSCI['METHODE']!='NIGAM' :
+ UTMESS('S','CALC_FONCTION',' : SPEC_OSCI, seule la méthode NIGAM est codée')
+ eps=1.e-6
+ for amor in l_amor :
+ if amor>(1-eps) :
+ UTMESS('S','CALC_FONCTION',' : SPEC_OSCI, la méthode choisie '\
+ 'suppose des amortissements sous-critiques, amor<1.')
+
+ __ff=SPEC_OSCI['FONCTION'].convert()
+
+ # appel à SPEC_OSCI
+ spectr = aster_fonctions.SPEC_OSCI(__ff.vale_x, __ff.vale_y, l_freq, l_amor)
+
+ # construction de la nappe
+ vale_para = l_amor
+ para = { 'INTERPOL' : ['LIN','LOG'],
+ 'NOM_PARA_FONC' : 'FREQ',
+ 'NOM_PARA' : 'AMOR',
+ 'PROL_DROITE' : 'EXCLU',
+ 'PROL_GAUCHE' : 'EXCLU',
+ 'NOM_RESU' : SPEC_OSCI['NATURE'] }
+ para_fonc = { 'INTERPOL' : ['LOG','LOG'],
+ 'NOM_PARA' : 'FREQ',
+ 'PROL_DROITE' : 'CONSTANT',
+ 'PROL_GAUCHE' : 'EXCLU',
+ 'NOM_RESU' : SPEC_OSCI['NATURE'] }
+ if SPEC_OSCI['NATURE']=='DEPL' : ideb = 0
+ elif SPEC_OSCI['NATURE']=='VITE' : ideb = 1
+ else : ideb = 2
+ l_fonc = []
+ for iamor in range(len(l_amor)) :
+ l_fonc.append(t_fonction(l_freq,spectr[iamor,ideb,:]/SPEC_OSCI['NORME'],para_fonc))
+ __ex=t_nappe(vale_para,l_fonc,para)
+ ###
+ if (LISS_ENVELOP!= None): return
+
+ ### creation de la fonction produite par appel à DEFI_FONCTION
+ ### on récupère les paramètres issus du calcul de __ex
+ ### et on les surcharge par ceux imposés par l'utilisateur
+
+ if isinstance(__ex,t_fonction) or isinstance(__ex,t_fonction_c):
+ para=__ex.para
+ if NOM_PARA !=None : para['NOM_PARA'] =NOM_PARA
+ if NOM_RESU !=None : para['NOM_RESU'] =NOM_RESU
+ if PROL_DROITE!=None : para['PROL_DROITE']=PROL_DROITE
+ if PROL_GAUCHE!=None : para['PROL_GAUCHE']=PROL_GAUCHE
+ if INTERPOL !=None : para['INTERPOL'] =INTERPOL
+ if isinstance(__ex,t_fonction_c): para['VALE_C'] = __ex.tabul()
+ elif isinstance(__ex,t_fonction) : para['VALE'] = __ex.tabul()
+ C_out=DEFI_FONCTION(**para)
+ elif isinstance(__ex,t_nappe):
+ def_fonc=[]
+ for f in __ex.l_fonc :
+ para=f.para
+ def_fonc.append(_F(VALE =f.tabul(),
+ INTERPOL =f.para['INTERPOL'],
+ PROL_DROITE=f.para['PROL_DROITE'],
+ PROL_GAUCHE=f.para['PROL_GAUCHE'],)
+ )
+ para=__ex.para
+ if NOM_PARA !=None : para['NOM_PARA'] =NOM_PARA
+ if NOM_RESU !=None : para['NOM_RESU'] =NOM_RESU
+ if PROL_DROITE !=None : para['PROL_DROITE']=PROL_DROITE
+ if PROL_GAUCHE !=None : para['PROL_GAUCHE']=PROL_GAUCHE
+ if NOM_PARA_FONC !=None : para['NOM_PARA_FONC'] =INTERPOL
+ if INTERPOL_FONC !=None : para['INTERPOL'] =INTERPOL
+ C_out=DEFI_NAPPE(PARA=__ex.vale_para.tolist(),DEFI_FONCTION=def_fonc,**para)
+ return ier
+
-#@ MODIF calc_precont_ops Macro DATE 22/11/2004 AUTEUR LEBOUVIE F.LEBOUVIER
+#@ MODIF calc_precont_ops Macro DATE 07/03/2005 AUTEUR DURAND C.DURAND
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
# 1.6 Blocage de tous les noeuds des cables actifs
# --------------------------------------------------
- __B_CA=AFFE_CHAR_MECA(MODELE=__M_CA,
+ _B_CA=AFFE_CHAR_MECA(MODELE=__M_CA,
DDL_IMPO= _F( GROUP_MA = __GROUP_MA_A,
DX = 0.,
DY = 0.,
# 1.7 Chargements concernant les cables
# -------------------------------------
- __C_CN=AFFE_CHAR_MECA(MODELE=__M_CA,**motscles)
- __C_CA=AFFE_CHAR_MECA(MODELE=MODELE,**motscle2)
- __C_CT=AFFE_CHAR_MECA(MODELE=MODELE,**motscle3)
+ _C_CN=AFFE_CHAR_MECA(MODELE=__M_CA,**motscles)
+ _C_CA=AFFE_CHAR_MECA(MODELE=MODELE,**motscle2)
+ _C_CT=AFFE_CHAR_MECA(MODELE=MODELE,**motscle3)
if CABLE_BP_INACTIF:
- __C_CI=AFFE_CHAR_MECA(MODELE=MODELE,**motscle6)
+ _C_CI=AFFE_CHAR_MECA(MODELE=MODELE,**motscle6)
#-------------------------------------------------------------------
# 2.1 Premiere etape : calcul sur le(s) cable(s) et
- # recuperation des __F_CAs aux noeuds
+ # recuperation des _F_CAs aux noeuds
# on travaile entre tmin et tmax
#-------------------------------------------------------------------
MODELE = __M_CA,
CHAM_MATER = CHAM_MATER,
CARA_ELEM = CARA_ELEM,
- EXCIT =(_F(CHARGE = __B_CA),
- _F(CHARGE = __C_CN),),
+ EXCIT =(_F(CHARGE = _B_CA),
+ _F(CHARGE = _C_CN),),
COMP_INCR =_F( RELATION = 'ELAS',
DEFORMATION = 'PETIT',
TOUT = 'OUI'),
CHAM_GD=__REA,
COEF_R = -1.), )
- __F_CA=AFFE_CHAR_MECA(MODELE=__M_CA,
+ _F_CA=AFFE_CHAR_MECA(MODELE=__M_CA,
VECT_ASSE = __REAC )
if dExcit[-1][i]==None : del dExcit[-1][i]
if CABLE_BP_INACTIF:
- dExcit.append(_F(CHARGE=__C_CI),)
+ dExcit.append(_F(CHARGE=_C_CI),)
# Creation du mots-cle EXCIT pour le STAT_NON_LINE
dExcit1=copy.copy(dExcit)
- dExcit1.append(_F(CHARGE=__C_CA),)
- dExcit1.append(_F(CHARGE = __F_CA,
+ dExcit1.append(_F(CHARGE=_C_CA),)
+ dExcit1.append(_F(CHARGE = _F_CA,
FONC_MULT=__FCT ),)
RES=STAT_NON_LINE(
# Creation du mots-cles EXCIT pour le STAT_NON_LINE
dExcit2=copy.copy(dExcit)
- dExcit2.append(_F(CHARGE=__C_CT,) )
+ dExcit2.append(_F(CHARGE=_C_CT,) )
# Calcul sur un seul pas (de __TINT a __TMAX)
RES=STAT_NON_LINE( reuse = RES,
-#@ MODIF impr_fonction_ops Macro DATE 30/11/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF impr_fonction_ops Macro DATE 11/05/2005 AUTEUR MCOURTOI M.COURTOIS
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
"""
macro='IMPR_FONCTION'
import aster
- from Accas import _F
- from Utilitai import Graph
- from Utilitai.Utmess import UTMESS
+ from Accas import _F
+ from Utilitai import Graph
+ from Utilitai.Utmess import UTMESS
+ from Utilitai.UniteAster import UniteAster
ier=0
# La macro compte pour 1 dans la numerotation des commandes
self.set_icmd(1)
# Le nom de la variable doit etre obligatoirement le nom de la commande
CALC_FONC_INTERP = self.get_cmd('CALC_FONC_INTERP')
DEFI_LIST_REEL = self.get_cmd('DEFI_LIST_REEL')
- DEFI_FICHIER = self.get_cmd('DEFI_FICHIER')
DETRUIRE = self.get_cmd('DETRUIRE')
#----------------------------------------------
# 0. Traitement des arguments, initialisations
# unité logique des fichiers réservés
ul_reserve=(8,)
+ UL = UniteAster()
# 0.1. Fichier
nomfich=None
if args['UNITE'] and args['UNITE']<>6:
- nomfich='fort.'+str(args['UNITE'])
+ nomfich=UL.Nom(args['UNITE'])
if INFO==2:
print ' Nom du fichier :',nomfich
- if nomfich and os.path.exists(nomfich):
+ if nomfich and os.path.exists(nomfich) and os.stat(nomfich).st_size<>0:
if FORMAT=='XMGRACE':
niv='A'
else:
for Ci in COURBE:
iocc+=1
dC = Ci.cree_dict_valeurs(Ci.mc_liste)
- if dC.has_key('LIST_PARA') and i0==0: i0=iocc
+ if dC.has_key('LIST_PARA') and dC['LIST_PARA']!=None and i0==0:
+ i0=iocc
for mc in dC.keys():
if dC[mc]==None: del dC[mc]
Courbe.append(dC)
if typ=='nappe_sdaster':
lpar,lval=obj.Valeurs()
dico,ldicf=obj.Parametres()
+ Leg=dCi['LEGENDE']
for i in range(len(lpar)):
p=lpar[i]
lx=lval[i][0]
lx=lv2[0][0]
ly=lv2[0][1]
# on stocke les données dans le Graph
+ nomresu=dic['NOM_RESU'].strip()+'_'+str(len(graph.Legendes))
dicC={
'Val' : [lx,ly],
- 'Lab' : [dic['NOM_PARA_FONC'],dic['NOM_RESU']]
+ 'Lab' : [dic['NOM_PARA_FONC'],nomresu]
}
+ # ajoute la valeur du paramètre
+ dCi['LEGENDE'] = '%s %s=%g' % (Leg,dic['NOM_PARA'].strip(),p)
Graph.AjoutParaCourbe(dicC, args=dCi)
graph.AjoutCourbe(**dicC)
+ DETRUIRE(CONCEPT=_F(NOM=('li__','ftmp__'),),ALARME='NON',INFO=1)
else:
ftmp__=obj
dpar=ftmp__.Parametres()
lx=lval[0]
lr=lval[1]
if typ=='fonction_c' and dCi.has_key('PARTIE'):
- if dCi['PARTIE']=='COMPLEXE' : lr=lval[2]
+ if dCi['PARTIE']=='IMAG' : lr=lval[2]
# on stocke les données dans le Graph
if typ=='fonction_c' and not dCi.has_key('PARTIE'):
+ nomresu=dpar['NOM_RESU'].strip()+'_'+str(len(graph.Legendes))
dicC={
'Val' : lval,
- 'Lab' : [dpar['NOM_PARA'],dpar['NOM_RESU']+'_R',dpar['NOM_RESU']+'_I']
+ 'Lab' : [dpar['NOM_PARA'],nomresu+'_R',nomresu+'_I']
}
else:
+ nomresu=dpar['NOM_RESU'].strip()+'_'+str(len(graph.Legendes))
dicC={
'Val' : [lx,lr],
- 'Lab' : [dpar['NOM_PARA'],dpar['NOM_RESU']]
+ 'Lab' : [dpar['NOM_PARA'],nomresu]
}
Graph.AjoutParaCourbe(dicC, args=dCi)
graph.AjoutCourbe(**dicC)
# on stocke les données dans le Graph
# on imprime la liste des paramètres seulement si LIST_PARA
if intloc:
+ nomresur=dpar['NOM_RESU'].strip()+'_'+str(len(graph.Legendes))
+ nomresu2=dpa2['NOM_RESU'].strip()+'_'+str(len(graph.Legendes)+1)
dicC={
'Val' : [lt,lx,ly],
- 'Lab' : [dpar['NOM_PARA'],dpar['NOM_RESU'],dpa2['NOM_RESU']]
+ 'Lab' : [dpar['NOM_PARA'],nomresur,nomresu2]
}
else:
+ nomresur=dpar['NOM_RESU'].strip()+'_'+str(len(graph.Legendes))
+ nomresu2=dpa2['NOM_RESU'].strip()+'_'+str(len(graph.Legendes)+1)
dicC={
'Val' : [lx,ly],
- 'Lab' : [dpar['NOM_RESU'],dpa2['NOM_RESU']]
+ 'Lab' : [nomresur,nomresu2]
}
Graph.AjoutParaCourbe(dicC, args=dCi)
graph.AjoutCourbe(**dicC)
elif FORMAT=='AGRAF':
nomdigr=None
if args['UNITE_DIGR']<>6:
- nomdigr='fort.'+str(args['UNITE_DIGR'])
+ nomdigr=UL.Nom(args['UNITE_DIGR'])
kargs['FICHIER']=[nomfich, nomdigr]
kargs['dform']={ 'formR' : '%12.5E' }
# Traiter le cas des UL réservées
if args['UNITE'] and args['UNITE'] in ul_reserve:
- DEFI_FICHIER( ACTION='LIBERER', UNITE=args['UNITE'], )
+ UL.Etat(args['UNITE'], etat='F')
if FORMAT=='AGRAF' and args['UNITE_DIGR']<>args['UNITE'] \
and args['UNITE_DIGR'] in ul_reserve:
- DEFI_FICHIER( ACTION='LIBERER', UNITE=args['UNITE_DIGR'], )
+ UL.Etat(args['UNITE_DIGR'], etat='F')
# 2.4. On trace !
graph.Trace(**kargs)
# 99. Traiter le cas des UL réservées
- if args['UNITE'] and args['UNITE'] in ul_reserve:
- DEFI_FICHIER( ACTION='ASSOCIER', UNITE=args['UNITE'],
- TYPE='ASCII', ACCES='APPEND' )
- if FORMAT=='AGRAF' and args['UNITE_DIGR']<>args['UNITE'] \
- and args['UNITE_DIGR'] in ul_reserve:
- DEFI_FICHIER( ACTION='ASSOCIER', UNITE=args['UNITE_DIGR'],
- TYPE='ASCII', ACCES='APPEND' )
+ UL.EtatInit()
return ier
-#@ MODIF impr_table_ops Macro DATE 30/11/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF impr_table_ops Macro DATE 11/05/2005 AUTEUR MCOURTOI M.COURTOIS
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
"""
macro='IMPR_TABLE'
import aster
- from Accas import _F
- from Cata.cata import table_jeveux
- from Utilitai.Utmess import UTMESS
+ from Accas import _F
+ from Cata.cata import table_jeveux
+ from Utilitai.Utmess import UTMESS
+ from Utilitai.UniteAster import UniteAster
ier=0
# La macro compte pour 1 dans la numerotation des commandes
self.set_icmd(1)
# On importe les definitions des commandes a utiliser dans la macro
# Le nom de la variable doit etre obligatoirement le nom de la commande
DETRUIRE = self.get_cmd('DETRUIRE')
- DEFI_FICHIER = self.get_cmd('DEFI_FICHIER')
RECU_FONCTION = self.get_cmd('RECU_FONCTION')
#----------------------------------------------
# 0. Traitement des arguments, initialisations
# unité logique des fichiers réservés
ul_reserve=(8,)
+ UL = UniteAster()
# 0.1. Fichier
nomfich=None
if args['UNITE'] and args['UNITE']<>6:
- nomfich='fort.'+str(args['UNITE'])
- if nomfich and os.path.exists(nomfich):
+ nomfich=UL.Nom(args['UNITE'])
+ if nomfich and os.path.exists(nomfich) and os.stat(nomfich).st_size<>0:
if FORMAT=='XMGRACE':
UTMESS('A',macro,'Le fichier '+nomfich+' existe déjà, on écrit ' \
'à la suite.')
# 0.4.2. Traiter le cas des UL réservées
if args['UNITE'] and args['UNITE'] in ul_reserve:
- DEFI_FICHIER( ACTION='LIBERER', UNITE=args['UNITE'], )
+ UL.Etat(args['UNITE'], etat='F')
#----------------------------------------------
# Boucle sur les tables
DETRUIRE(CONCEPT=_F(NOM=('__fonc',),), ALARME='NON', INFO=1,)
# 99. Traiter le cas des UL réservées
- if args['UNITE'] and args['UNITE'] in ul_reserve:
- DEFI_FICHIER( ACTION='ASSOCIER', UNITE=args['UNITE'],
- TYPE='ASCII', ACCES='APPEND' )
+ UL.EtatInit()
return ier
--- /dev/null
+#@ MODIF info_fonction_ops Macro DATE 12/05/2005 AUTEUR DURAND C.DURAND
+# -*- coding: iso-8859-1 -*-
+# CONFIGURATION MANAGEMENT OF EDF VERSION
+# ======================================================================
+# COPYRIGHT (C) 1991 - 2005 EDF R&D WWW.CODE-ASTER.ORG
+# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
+# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
+# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
+# (AT YOUR OPTION) ANY LATER VERSION.
+#
+# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
+# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
+# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
+#
+# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
+# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
+# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
+# ======================================================================
+def info_fonction_ops(self,RMS,NOCI_SEISME,MAX,NORME,ECART_TYPE,**args):
+ """
+ Ecriture de la macro INFO_FONCTION
+ """
+ ier=0
+ import string
+ from Utilitai.t_fonction import t_fonction,t_fonction_c,t_nappe
+ import math
+ from Accas import _F
+ from Utilitai.Utmess import UTMESS
+
+ ### On importe les definitions des commandes a utiliser dans la macro
+ CREA_TABLE = self.get_cmd('CREA_TABLE')
+ CALC_FONCTION = self.get_cmd('CALC_FONCTION')
+
+ ### Comptage commandes + déclaration concept sortant
+ self.set_icmd(1)
+ self.DeclareOut('C_out',self.sd)
+
+ ### type de traitement
+
+ ###
+ if (MAX != None):
+ __ff=MAX['FONCTION'].convert()
+ __ex=__ff.extreme()
+ n_mini=len(__ex['min'])
+ n_maxi=len(__ex['max'])
+ listeMCF=[_F(LISTE_K=[MAX['FONCTION'].nom]*(n_mini+n_maxi),PARA='FONCTION'),
+ _F(LISTE_K=['MINI',]*n_mini+['MAXI',]*n_maxi,PARA='TYPE'),]
+ if isinstance(__ff,t_nappe) :
+ listeMCF=listeMCF+[\
+ _F(LISTE_R=[i[0] for i in __ex['min']]+[i[0] for i in __ex['max']],PARA=__ff.para['NOM_PARA']),\
+ _F(LISTE_R=[i[1] for i in __ex['min']]+[i[1] for i in __ex['max']],PARA=__ff.para['NOM_PARA_FONC']),\
+ _F(LISTE_R=[i[2] for i in __ex['min']]+[i[2] for i in __ex['max']],PARA=__ff.para['NOM_RESU'])]
+ else :
+ listeMCF=listeMCF+[\
+ _F(LISTE_R=[i[0] for i in __ex['min']]+[i[0] for i in __ex['max']],PARA=__ff.para['NOM_PARA']),\
+ _F(LISTE_R=[i[1] for i in __ex['min']]+[i[1] for i in __ex['max']],PARA=__ff.para['NOM_RESU'])]
+ C_out=CREA_TABLE(LISTE=listeMCF)
+
+ ###
+ if (ECART_TYPE != None):
+ __ff=ECART_TYPE['FONCTION'].convert()
+ if ECART_TYPE['INST_INIT']!=None : tini=ECART_TYPE['INST_INIT']
+ else : tini=__ff.vale_x[0]
+ if ECART_TYPE['INST_FIN' ]!=None : tfin=ECART_TYPE['INST_FIN' ]
+ else : tfin=__ff.vale_x[-1]
+ __ff=__ff.cut(tini,__ff.vale_x[-1],ECART_TYPE['PRECISION'],ECART_TYPE['CRITERE'])
+ __ff=__ff.cut(__ff.vale_x[0],tfin,ECART_TYPE['PRECISION'],ECART_TYPE['CRITERE'])
+ if ECART_TYPE['METHODE' ]=='SIMPSON' : __ex=__ff.simpson(0.)
+ if ECART_TYPE['METHODE' ]=='TRAPEZE' : __ex=__ff.trapeze(0.)
+ fmoy=__ex.vale_y[-1]/(__ff.vale_x[-1]-__ff.vale_x[0])
+ __ff=__ff+(-1*fmoy)
+ __ff=__ff*__ff
+ if ECART_TYPE['METHODE' ]=='SIMPSON' : __ez=__ff.simpson(0.)
+ if ECART_TYPE['METHODE' ]=='TRAPEZE' : __ez=__ff.trapeze(0.)
+ sigma=math.sqrt(__ez.vale_y[-1]/(__ff.vale_x[-1]-__ff.vale_x[0]))
+ C_out=CREA_TABLE(LISTE=(_F(LISTE_K=ECART_TYPE['FONCTION'].nom,PARA='FONCTION'),
+ _F(LISTE_K=ECART_TYPE['METHODE'] ,PARA='METHODE'),
+ _F(LISTE_R=fmoy ,PARA='MOYENNE'),
+ _F(LISTE_R=sigma ,PARA='ECART_TYPE'),
+ _F(LISTE_R=tini ,PARA='INST_INIT'),
+ _F(LISTE_R=tfin ,PARA='INST_FIN'),)
+ )
+
+ ###
+ if (RMS != None):
+ RMS =list(RMS)
+ sigm =[]
+ tmpi =[]
+ tmpf =[]
+ nomf =[]
+ meth =[]
+ for i_rms in RMS :
+ __ff=i_rms['FONCTION'].convert()
+ if i_rms['INST_INIT']!=None : tini=i_rms['INST_INIT']
+ else : tini=__ff.vale_x[0]
+ if i_rms['INST_FIN' ]!=None : tfin=i_rms['INST_FIN' ]
+ else : tfin=__ff.vale_x[-1]
+ __ff=__ff.cut(tini,__ff.vale_x[-1],i_rms['PRECISION'],i_rms['CRITERE'])
+ __ff=__ff.cut(__ff.vale_x[0],tfin,i_rms['PRECISION'],i_rms['CRITERE'])
+ __ff=__ff*__ff
+ if i_rms['METHODE' ]=='SIMPSON' : __ez=__ff.simpson(0.)
+ if i_rms['METHODE' ]=='TRAPEZE' :
+ __ez=__ff.trapeze(0.)
+ sigm.append(math.sqrt(__ez.vale_y[-1]/(__ff.vale_x[-1]-__ff.vale_x[0])))
+ tmpi.append(tini)
+ tmpf.append(tfin)
+ nomf.append(i_rms['FONCTION'].nom)
+ meth.append(i_rms['METHODE'])
+ C_out=CREA_TABLE(LISTE=(_F(LISTE_K=nomf ,PARA='FONCTION'),
+ _F(LISTE_K=meth ,PARA='METHODE'),
+ _F(LISTE_R=tmpi ,PARA='INST_INIT'),
+ _F(LISTE_R=tmpf ,PARA='INST_FIN'),
+ _F(LISTE_R=sigm ,PARA='RMS'), )
+ )
+
+ ###
+ if (NORME != None):
+ __ff=NORME['FONCTION'].convert()
+ norme=[]
+ for __fi in __ff.l_fonc :
+ norme.append(__fi.normel2())
+ nom=[NORME['FONCTION'].nom,]*len(norme)
+ C_out=CREA_TABLE(LISTE=(_F(LISTE_R=norme ,PARA='NORME'),
+ _F(LISTE_K=nom ,PARA='FONCTION'), )
+ )
+
+ ###
+ if (NOCI_SEISME != None):
+ l_table=[]
+ if NOCI_SEISME['SPEC_OSCI'] !=None :
+ ### cas intensité spectrale d'une nappe de SRO
+ ### la seule option licite est INTE_SPEC
+ UTMESS('I','INFO_FONCTION',''' : intensite spectrale, avant de calculer l'\
+intensite spectrale, il est prudent de verifier la norme de la nappe sur laquelle \
+porte le calcul, ceci peut etre une source d erreurs.''')
+ amor=NOCI_SEISME['AMOR_REDUIT']
+ fini=NOCI_SEISME['FREQ_INIT' ]
+ ffin=NOCI_SEISME['FREQ_FIN' ]
+ __sp =NOCI_SEISME['SPEC_OSCI'].convert()
+ vale_x=__sp.l_fonc[0].vale_x
+ vale_y=[__sp(amor,f) for f in vale_x]
+ para =__sp.l_fonc[0].para
+ __srov=t_fonction(vale_x,vale_y,para)
+ if NOCI_SEISME['NATURE']=='DEPL' :
+ __srov.vale_y=(__srov.vale_y/__srov.vale_x)*2.*math.pi
+ elif NOCI_SEISME['NATURE']=='VITE' :
+ __srov.vale_y=__srov.vale_y/__srov.vale_x/__srov.vale_x
+ elif NOCI_SEISME['NATURE']=='ACCE' :
+ __srov.vale_y=__srov.vale_y/__srov.vale_x/__srov.vale_x
+ __srov.vale_y=__srov.vale_y/__srov.vale_x/2./math.pi
+ __srov=__srov.cut(fini,ffin,NOCI_SEISME['PRECISION'],NOCI_SEISME['CRITERE'])
+ insp=__srov.trapeze(0.).vale_y[-1]
+ l_table.append(_F(LISTE_R=fini ,PARA='FREQ_INIT' ))
+ l_table.append(_F(LISTE_R=ffin ,PARA='FREQ_FIN' ))
+ l_table.append(_F(LISTE_R=amor ,PARA='AMOR_REDUIT'))
+ l_table.append(_F(LISTE_R=insp ,PARA='INTE_SPECT' ))
+ if NOCI_SEISME['FONCTION'] !=None :
+ ### cas fonction
+ l_table.append(_F(LISTE_K=NOCI_SEISME['FONCTION'].nom,PARA='FONCTION'))
+ __ac=NOCI_SEISME['FONCTION'].convert()
+ option= NOCI_SEISME['OPTION']
+ if NOCI_SEISME['INST_INIT']!=None : tdeb=NOCI_SEISME['INST_INIT']
+ else : tdeb=__ac.vale_x[0]
+ if NOCI_SEISME['INST_FIN' ]!=None : tfin=NOCI_SEISME['INST_FIN' ]
+ else : tfin=__ac.vale_x[-1]
+ # calcul de la vitesse :
+ __vi=__ac.trapeze(NOCI_SEISME['COEF'])
+ # calcul du déplacement :
+ __de=__vi.trapeze(NOCI_SEISME['COEF'])
+ # calcul de |acceleration| :
+ __aa=__ac.abs()
+ # calcul de integrale(|acceleration|) :
+ ### on "coupe" la fonction entre tdeb et tfin
+ __ac=__ac.cut(tdeb,tfin,NOCI_SEISME['PRECISION'],NOCI_SEISME['CRITERE'])
+ __vi=__vi.cut(tdeb,tfin,NOCI_SEISME['PRECISION'],NOCI_SEISME['CRITERE'])
+ __de=__de.cut(tdeb,tfin,NOCI_SEISME['PRECISION'],NOCI_SEISME['CRITERE'])
+ __aa=__aa.cut(tdeb,tfin,NOCI_SEISME['PRECISION'],NOCI_SEISME['CRITERE'])
+ if NOCI_SEISME['FREQ' ]!=None : l_freq=NOCI_SEISME['FREQ']
+ elif NOCI_SEISME['LIST_FREQ']!=None : l_freq=NOCI_SEISME['LIST_FREQ'].Valeurs()
+ else :
+ # fréquences par défaut
+ l_freq=[]
+ for i in range(56) : l_freq.append( 0.2+0.050*i)
+ for i in range( 8) : l_freq.append( 3.0+0.075*i)
+ for i in range(14) : l_freq.append( 3.6+0.100*i)
+ for i in range(24) : l_freq.append( 5.0+0.125*i)
+ for i in range(28) : l_freq.append( 8.0+0.250*i)
+ for i in range( 6) : l_freq.append(15.0+0.500*i)
+ for i in range( 4) : l_freq.append(18.0+1.000*i)
+ for i in range(10) : l_freq.append(22.0+1.500*i)
+ if option in('TOUT','MAXI','ACCE_SUR_VITE') :
+ # calcul du max des valeurs absolues
+ maxa_ac=__ac.abs().extreme()['max'][0][1]
+ maxa_vi=__vi.abs().extreme()['max'][0][1]
+ maxa_de=__de.abs().extreme()['max'][0][1]
+ l_table.append(_F(LISTE_R=maxa_ac,PARA='ACCE_MAX'))
+ l_table.append(_F(LISTE_R=maxa_vi,PARA='VITE_MAX'))
+ l_table.append(_F(LISTE_R=maxa_de,PARA='DEPL_MAX'))
+ l_table.append(_F(LISTE_R=maxa_ac/maxa_vi,PARA='ACCE_SUR_VITE'))
+ if option in('TOUT','INTE_ARIAS') :
+ __a2=__ac*__ac
+ inte_arias=__a2.trapeze(0.).vale_y[-1]
+ inte_arias=inte_arias*math.pi/NOCI_SEISME['PESANTEUR']/2.
+ l_table.append(_F(LISTE_R=inte_arias,PARA='INTE_ARIAS'))
+ if option in('TOUT','POUV_DEST') :
+ __v2=__vi*__vi
+ pouv_dest=__v2.trapeze(0.).vale_y[-1]
+ pouv_dest=pouv_dest*(math.pi)**3/NOCI_SEISME['PESANTEUR']/2.
+ l_table.append(_F(LISTE_R=pouv_dest,PARA='POUV_DEST'))
+ if option in('TOUT','VITE_ABSO_CUMU') :
+ __vc=__aa.trapeze(0.)
+ vite_abso=__vc.vale_y[-1]
+ l_table.append(_F(LISTE_R=vite_abso,PARA='VITE_ABSO_CUMU'))
+ if option in('TOUT','INTE_SPEC') :
+ amor=NOCI_SEISME['AMOR_REDUIT']
+ fini=NOCI_SEISME['FREQ_INIT' ]
+ ffin=NOCI_SEISME['FREQ_FIN' ]
+ __so= CALC_FONCTION(SPEC_OSCI=_F(
+ NATURE ='VITE',
+ NATURE_FONC='ACCE',
+ FONCTION =NOCI_SEISME['FONCTION'],
+ METHODE ='NIGAM',
+ NORME =NOCI_SEISME['NORME'],
+ FREQ =l_freq,
+ AMOR_REDUIT=(amor,)
+ ), )
+ __srov=__so.convert().l_fonc[0]
+ __srov=__srov.cut(fini,ffin,NOCI_SEISME['PRECISION'],NOCI_SEISME['CRITERE'])
+ __srov.vale_y=__srov.vale_y/__srov.vale_x/__srov.vale_x
+ insp=__srov.trapeze(0.).vale_y[-1]
+ l_table.append(_F(LISTE_R=fini ,PARA='FREQ_INIT' ))
+ l_table.append(_F(LISTE_R=ffin ,PARA='FREQ_FIN' ))
+ l_table.append(_F(LISTE_R=amor ,PARA='AMOR_REDUIT'))
+ l_table.append(_F(LISTE_R=insp ,PARA='INTE_SPECT' ))
+ if option in('TOUT','DUREE_PHAS_FORT') :
+ __a2=__ac*__ac
+ __i2=__a2.trapeze(0.)
+ arias = __i2.vale_y[-1]*math.pi/NOCI_SEISME['PESANTEUR']/2.
+ valinf = arias * NOCI_SEISME['BORNE_INF']
+ valsup = arias * NOCI_SEISME['BORNE_SUP']
+ for i in range(len(__i2.vale_x)) :
+ ariask = __i2.vale_y[i]*math.pi/NOCI_SEISME['PESANTEUR']/2.
+ if ariask>=valinf : break
+ for j in range(len(__i2.vale_x)-1,-1,-1) :
+ ariask = __i2.vale_y[j]*math.pi/NOCI_SEISME['PESANTEUR']/2.
+ if ariask<=valsup : break
+ dphfor = __i2.vale_x[j] - __i2.vale_x[i]
+ l_table.append(_F(LISTE_R=dphfor,PARA='DUREE_PHAS_FORT'))
+ C_out=CREA_TABLE(LISTE=l_table)
+
+ return ier
-#@ MODIF macr_ascouf_calc_ops Macro DATE 22/11/2004 AUTEUR LEBOUVIE F.LEBOUVIER
+#@ MODIF macr_ascouf_calc_ops Macro DATE 08/02/2005 AUTEUR CIBHHLV L.VIVAN
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
# l epaisseur
#
l_grno=MAILLAGE.LIST_GROUP_NO()
+ tabprl=[None]*4
tablig=[None]*4
#
# prelevements des ligaments circonferentiels et longitudinaux
elif (tgrno[0][:4] in LIG) and (tgrno[0][4:6] not in ('GV','TU','MI')): lgrno.append(tgrno[0])
#
motscles={}
- motscles['SEGMENT']=[]
- for grno in lgrno : motscles['SEGMENT'].append(_F(INTITULE=grno,GROUP_NO=grno))
+ motscles['ACTION']=[]
+ for grno in lgrno :
+ motscles['ACTION'].append(_F(RESULTAT=nomres,
+ NOM_CHAM='SIEF_ELNO_ELGA',
+ TOUT_CMP='OUI',
+ INTITULE=grno,
+ GROUP_NO=grno,
+ OPERATION='EXTRACTION',))
motscles['TITRE']='TABLE DE POST-TRAITEMENT SECTION SOUS-EPAISSEUR'
+ tabprl[1]=POST_RELEVE_T(**motscles)
tablig[1]=POST_RCCM(MATER = rccmat,
- MAILLAGE = MAILLAGE,
TYPE_RESU_MECA = 'EVOLUTION',
OPTION = 'PM_PB',
- TRANSITOIRE=_F(RESULTAT=nomres,
- NOM_CHAM='SIEF_ELNO_ELGA',),**motscles)
+ TRANSITOIRE=_F(TABL_RESU_MECA = tabprl[1],),)
#
motscles={}
motscles['ACTION']=[]
# les 8 ligaments sont tous les 45 degres
#
ACOUR = mc_IMPR_TABLE['ANGLE']*pi/180.0
+ secprl=[None]*3
secrcm=[None]*3
secinv=[None]*3
secmoy=[None]*3
#
# moyenne RCCM sur les sections MI,TU et GV
#
- motscles['SEGMENT']=[]
- for j in range(8) : motscles['SEGMENT'].append(_F(INTITULE=LIG[j]+SECT[i],
- GROUP_NO=LIG[j]+SECT[i]))
- secrcm[i] = POST_RCCM( MAILLAGE = MAILLAGE ,
- MATER = rccmat ,
- TYPE_RESU_MECA = 'EVOLUTION' ,
- OPTION = 'PM_PB' ,
- TRANSITOIRE = _F(RESULTAT = nomres,NOM_CHAM='SIEF_ELNO_ELGA'),
- **motscles)
+ motscles={}
+ motscles['ACTION']=[]
+ for j in range(8) :
+ motscles['ACTION'].append(_F(RESULTAT=nomres,
+ NOM_CHAM='SIEF_ELNO_ELGA',
+ TOUT_CMP='OUI',
+ INTITULE=LIG[j]+SECT[i],
+ GROUP_NO=LIG[j]+SECT[i],
+ OPERATION='EXTRACTION',))
+ motscles['TITRE']='TABLE DE POST-TRAITEMENT MOYENNE RCCM SECTION '+SECT[i]
+ secprl[i]=POST_RELEVE_T(**motscles)
+ secrcm[i]=POST_RCCM(MATER = rccmat,
+ TYPE_RESU_MECA = 'EVOLUTION',
+ OPTION = 'PM_PB',
+ TRANSITOIRE=_F(TABL_RESU_MECA = secprl[i],),)
#
# invariants sur les sections MI,TU et GV
#
-#@ MODIF macr_ascouf_mail_ops Macro DATE 30/11/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF macr_ascouf_mail_ops Macro DATE 09/05/2005 AUTEUR LEBOUVIE F.LEBOUVIER
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
# coude fissure
#
if FISS_COUDE!=None:
- if (RM/EP1)<5. or (RM/EP1)>12.:
- print ' <MACR_ASCOUF_MAIL> valeur hors domaine de validite (5,12)'
+ if (RM/EP1)<5. or (RM/EP1)>50.:
+ print ' <MACR_ASCOUF_MAIL> valeur hors domaine de validite (5,50)'
print ' <MACR_ASCOUF_MAIL> rapport RM/EP1 :',(RM/EP1)
self.cr.fatal("<F> <MACR_ASCOUF_MAIL> erreur donnees ")
ier = ier+1
-#@ MODIF macr_aspic_calc_ops Macro DATE 22/11/2004 AUTEUR LEBOUVIE F.LEBOUVIER
+#@ MODIF macr_aspic_calc_ops Macro DATE 08/02/2005 AUTEUR CIBHHLV L.VIVAN
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
if i<10 : NUME = '0'+str(i)
else : NUME = str(i)
mcsimp={}
- mcsimp['PRECISION']=55.E-1
- mcsimp['GROUP_NO' ]='LD'+str(i)
+ mcsimp['INTITULE' ]='LD'+str(i)
+ mcsimp['GROUP_NO' ]='LD'+str(i)
+ mcsimp['RESULTAT' ]=nomres
+ mcsimp['TOUT_ORDRE' ]='OUI'
+ mcsimp['NOM_CHAM' ]='SIEF_ELNO_ELGA'
+ mcsimp['PRECISION' ]=55.E-1
+ mcsimp['TOUT_CMP' ]='OUI'
+ mcsimp['OPERATION' ]='EXTRACTION'
mcfact.append( _F(**mcsimp) )
- __pmpbsd=POST_RCCM(MATER = MRCCM,
- MAILLAGE = MAILLAGE,
+ __prelsd=POST_RELEVE_T(ACTION=mcfact)
+ __pmpbsd=POST_RCCM(OPTION = 'PM_PB',
TYPE_RESU_MECA = 'EVOLUTION',
- TYPE_RESU = 'VALE_MAX',
- OPTION = 'PM_PB',
- SEGMENT = mcfact,
- TRANSITOIRE = _F(RESULTAT =nomres,
- NOM_CHAM ='SIEF_ELNO_ELGA',
- TOUT_ORDRE='OUI',),
+ TYPE_RESU = 'VALE_MAX',
+ MATER = MRCCM,
+ TRANSITOIRE = _F(TABL_RESU_MECA = __prelsd,),
TITRE = '-- TRAITEMENT DES AZIMUTS DROITS --',)
IMPR_TABLE(TABLE = __pmpbsd, )
#
if i<10 : NUME = '0'+str(i)
else : NUME = str(i)
mcsimp={}
- mcsimp['PRECISION']=55.E-1
- mcsimp['GROUP_NO' ]='LI'+str(i)
+ mcsimp['INTITULE' ]='LI'+str(i)
+ mcsimp['GROUP_NO' ]='LI'+str(i)
+ mcsimp['RESULTAT' ]=nomres
+ mcsimp['TOUT_ORDRE' ]='OUI'
+ mcsimp['NOM_CHAM' ]='SIEF_ELNO_ELGA'
+ mcsimp['PRECISION' ]=55.E-1
+ mcsimp['TOUT_CMP' ]='OUI'
+ mcsimp['OPERATION' ]='EXTRACTION'
mcfact.append( _F(**mcsimp) )
- __pmpbsi=POST_RCCM(MATER = MRCCM,
- MAILLAGE = MAILLAGE,
+ __prelsi=POST_RELEVE_T(ACTION=mcfact)
+ __pmpbsi=POST_RCCM(OPTION = 'PM_PB',
TYPE_RESU_MECA = 'EVOLUTION',
- TYPE_RESU = 'VALE_MAX',
- OPTION = 'PM_PB',
- SEGMENT = mcfact,
- TRANSITOIRE = _F(RESULTAT =nomres,
- NOM_CHAM ='SIEF_ELNO_ELGA',
- TOUT_ORDRE='OUI',),
+ TYPE_RESU = 'VALE_MAX',
+ MATER = MRCCM,
+ TRANSITOIRE = _F(TABL_RESU_MECA = __prelsi,),
TITRE = '-- TRAITEMENT DES AZIMUTS INCLINES --',)
IMPR_TABLE(TABLE = __pmpbsi, )
#
-#@ MODIF macr_cabri_mail_ops Macro DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF macr_cabri_mail_ops Macro DATE 07/02/2005 AUTEUR MABBAS M.ABBAS
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
imp_formF = 1
else:
imp_formF = 0
- if IMPRESSION['FICHIER']!=None:
- imp_fich = IMPRESSION['FICHIER']
- imp_fichF = 1
- else:
- imp_fichF = 0
+# if IMPRESSION['FICHIER']!=None:
+# imp_fich = IMPRESSION['FICHIER']
+# imp_fichF = 1
+# else:
+# imp_fichF = 0
# Maillage
nrad = RAFF_MAILLAGE['NB_RADIAL']
nomres = LIRE_MAILLAGE(VERI_MAIL=_F(APLAT = ver_apla,
VERIF = ver_veri ),)
- if (imp_fichF == 1):
- print imp_fich
+# if (imp_fichF == 1):
+# print imp_fich
if (imp_formF == 1):
print imp_form
if (imp_unitF == 1):
-#@ MODIF macr_fiab_impr_ops Macro DATE 07/10/2004 AUTEUR GNICOLAS G.NICOLAS
+#@ MODIF macr_fiab_impr_ops Macro DATE 24/01/2005 AUTEUR DURAND C.DURAND
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
# ======================================================================
-
-
# RESPONSABLE GNICOLAS G.NICOLAS
#
def macr_fiab_impr_ops(self, INFO,
# 5. Ecritures des gradients
#____________________________________________________________________
#
- for val in GRADIENTS :
+ if GRADIENTS is not None :
#
- IMPR_TABLE ( TABLE = val["TABLE"],
- SENSIBILITE = val["PARA_SENSI"],
- NOM_PARA = (val["NOM_PARA"]),
- UNITE = Unite_Fichier_ASTER_vers_FIABILITE,
- FORMAT_R = FORMAT_R,
- INFO = INFO )
+ for val in GRADIENTS :
+#
+ IMPR_TABLE ( TABLE = val["TABLE"],
+ SENSIBILITE = val["PARA_SENSI"],
+ NOM_PARA = (val["NOM_PARA"]),
+ UNITE = Unite_Fichier_ASTER_vers_FIABILITE,
+ FORMAT_R = FORMAT_R,
+ INFO = INFO )
#____________________________________________________________________
#
# 6. Libération du fichier d'échange
-#@ MODIF macr_lign_coupe_ops Macro DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF macr_lign_coupe_ops Macro DATE 14/02/2005 AUTEUR DURAND C.DURAND
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
# COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
-# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
-# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
-# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
-# (AT YOUR OPTION) ANY LATER VERSION.
-#
-# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
-# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
-# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
-# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
-#
-# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
-# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
-# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
+# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
+# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
+# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
+# (AT YOUR OPTION) ANY LATER VERSION.
+#
+# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
+# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
+# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
+#
+# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
+# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
+# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
# ======================================================================
########################################################################
# script PYTHON de creation d un maillage de ligne de coupe
-def crea_mail_lig_coup(lignes):
+def crea_mail_lig_coup(dimension,lignes,groups):
import os,sys,copy
- try:
# construction du maillage au format Aster des segments de lignes de coupe
- nblig=len(lignes)
- dimension=len(lignes[0][0])
+ nblig=len(lignes)
+ nbngr=len(groups)
- resu='TITRE\n'
- titre='Maillage ligne de coupe'+'\n'
- resu=resu+'FINSF\n'
- resu=resu+'COOR_'+str(dimension)+'D\n'
+ resu='TITRE\n'
+ titre='Maillage ligne de coupe'+'\n'
+ resu=resu+'FINSF\n'
+ resu=resu+'COOR_'+str(dimension)+'D\n'
# creation des noeuds
- nbno=0
- for i in range(nblig):
- pt1 = lignes[i][0]
- pt2 = lignes[i][1]
- nbp_lig_coupe = lignes[i][2]
- for j in range(nbp_lig_coupe):
- if dimension==2:
- x=pt1[0]+j*(pt2[0]-pt1[0])/(nbp_lig_coupe-1)
- y=pt1[1]+j*(pt2[1]-pt1[1])/(nbp_lig_coupe-1)
- nbno=nbno+1
- noeud=' N'+str(nbno)+' '+str(x)+' '+str(y)+'\n'
- resu=resu+noeud
- elif dimension==3:
- x=pt1[0]+j*(pt2[0]-pt1[0])/(nbp_lig_coupe-1)
- y=pt1[1]+j*(pt2[1]-pt1[1])/(nbp_lig_coupe-1)
- z=pt1[2]+j*(pt2[2]-pt1[2])/(nbp_lig_coupe-1)
- nbno=nbno+1
- noeud=' N'+str(nbno)+' '+str(x)+' '+str(y)+' '+str(z)+'\n'
- resu=resu+noeud
- resu=resu+'FINSF\n'
+ nbno=0
+ for i in range(nblig):
+ pt1 = lignes[i][0]
+ pt2 = lignes[i][1]
+ nbp_lig_coupe = lignes[i][2]
+ for j in range(nbp_lig_coupe):
+ if dimension==2:
+ x=pt1[0]+j*(pt2[0]-pt1[0])/(nbp_lig_coupe-1)
+ y=pt1[1]+j*(pt2[1]-pt1[1])/(nbp_lig_coupe-1)
+ nbno=nbno+1
+ noeud=' N'+str(nbno)+' '+str(x)+' '+str(y)+'\n'
+ resu=resu+noeud
+ elif dimension==3:
+ x=pt1[0]+j*(pt2[0]-pt1[0])/(nbp_lig_coupe-1)
+ y=pt1[1]+j*(pt2[1]-pt1[1])/(nbp_lig_coupe-1)
+ z=pt1[2]+j*(pt2[2]-pt1[2])/(nbp_lig_coupe-1)
+ nbno=nbno+1
+ noeud=' N'+str(nbno)+' '+str(x)+' '+str(y)+' '+str(z)+'\n'
+ resu=resu+noeud
+ for i in range(nbngr):
+ for pt in groups[i][1:]:
+ if dimension==2:
+ nbno=nbno+1
+ noeud=' N'+str(nbno)+' '+str(pt[0])+' '+str(pt[1])+'\n'
+ resu=resu+noeud
+ elif dimension==3:
+ nbno=nbno+1
+ noeud=' N'+str(nbno)+' '+str(pt[0])+' '+str(pt[1])+' '+str(pt[2])+'\n'
+ resu=resu+noeud
+ resu=resu+'FINSF\n'
# creation des mailles
- nbma=0
- for i in range(nblig):
- nbp_lig_coupe = lignes[i][2]
- resu=resu+'SEG2\n'
- for j in range(nbp_lig_coupe-1):
- nbma=nbma+1
- maille=' M'+str(nbma)+' N'+str(nbma+i)+' N'+str(nbma+1+i)+'\n'
- resu=resu+maille
- resu=resu+'FINSF\n'
+ nbma=0
+ for i in range(nblig):
+ nbp_lig_coupe = lignes[i][2]
+ resu=resu+'SEG2\n'
+ for j in range(nbp_lig_coupe-1):
+ nbma=nbma+1
+ maille=' M'+str(nbma)+' N'+str(nbma+i)+' N'+str(nbma+1+i)+'\n'
+ resu=resu+maille
+ resu=resu+'FINSF\n'
+ for i in range(nbngr):
+ resu=resu+'SEG2\n'
+ for pt in groups[i][1:-1]:
+ nbma=nbma+1
+ maille=' M'+str(nbma)+' N'+str(nbma+nblig+i)+' N'+str(nbma+nblig+1+i)+'\n'
+ resu=resu+maille
+ resu=resu+'FINSF\n'
# creation des groupes de mailles (1 par ligne de coupe)
- nbma=0
- for i in range(nblig):
- resu=resu+'GROUP_MA\n'
- resu=resu+' LICOU'+str(i+1)
- nbp_lig_coupe = lignes[i][2]
- for j in range(nbp_lig_coupe-1):
- nbma=nbma+1
- resu=resu+' M'+str(nbma)+'\n'
- resu=resu+'\n'
- resu=resu+'FINSF\n'
- resu=resu+'FIN\n'
-
- return resu
-
- except :
- return 0
+ nbma=0
+ for i in range(nblig):
+ resu=resu+'GROUP_MA\n'
+ resu=resu+' LICOU'+str(i+1)
+ nbp_lig_coupe = lignes[i][2]
+ for j in range(nbp_lig_coupe-1):
+ nbma=nbma+1
+ resu=resu+' M'+str(nbma)+'\n'
+ resu=resu+'\n'
+ resu=resu+'FINSF\n'
+ for i in range(nbngr):
+ resu=resu+'GROUP_MA\n'
+ resu=resu+groups[i][0]
+ nbp_lig_coupe = len(groups[i])-1
+ for j in range(nbp_lig_coupe-1):
+ nbma=nbma+1
+ resu=resu+' M'+str(nbma)+'\n'
+ resu=resu+'\n'
+ resu=resu+'FINSF\n'
+ resu=resu+'FIN\n'
+
+ return resu
+
########################################################################
-def macr_lign_coupe_ops(self,RESULTAT,UNITE_MAILLAGE,LIGN_COUPE,MODELE,
- NOM_CHAM,**args):
+def macr_lign_coupe_ops(self,RESULTAT,UNITE_MAILLAGE,LIGN_COUPE,NOM_CHAM,MODELE,**args):
"""
Ecriture de la macro MACR_LIGN_COUPE
"""
- import os
+ import os,string,types
from Accas import _F
from Noyau.N_utils import AsType
+ import aster
ier=0
# On importe les definitions des commandes a utiliser dans la macro
AFFE_MODELE =self.get_cmd('AFFE_MODELE')
PROJ_CHAMP =self.get_cmd('PROJ_CHAMP')
POST_RELEVE_T =self.get_cmd('POST_RELEVE_T')
+ CREA_TABLE =self.get_cmd('CREA_TABLE')
# La macro compte pour 1 dans la numerotation des commandes
#self.icmd=1
self.set_icmd(1)
+
+ nomresu=RESULTAT.nom
+ l_modele=aster.getvectjev(nomresu.ljust(19)+'.MODL')
+ n_modele=string.strip(l_modele[0])
+ if n_modele=='' :
+ if MODELE==None:
+ ier=ier+1
+ self.cr.fatal("<F> <MACR_LIGN_COUPE> nom du modele absent dans le concept resultat "+nomresu)
+ return ier
+ else : n_modele=MODELE.nom
+ l_mailla=aster.getvectjev(n_modele.ljust(8)+'.MODELE .NOMA')
+ n_mailla=string.strip(l_mailla[0])
+ dime=aster.getvectjev(n_mailla.ljust(8)+'.DIME')[5]
+ collgrno=aster.getcolljev(n_mailla.ljust(8)+'.GROUPENO')
lignes=[]
+ groups=[]
+ minidim=dime
for m in LIGN_COUPE :
- lignes.append((m['COOR_ORIG'],m['COOR_EXTR'],m['NB_POINTS']))
+ if m['NB_POINTS'] !=None :
+ lignes.append((m['COOR_ORIG'],m['COOR_EXTR'],m['NB_POINTS']))
+ minidim=min(minidim,len(m['COOR_ORIG']),len(m['COOR_EXTR']))
+ elif m['GROUP_NO']!=None :
+ ngrno=m['GROUP_NO'].ljust(8).upper()
+ if ngrno not in collgrno.keys() :
+ ier=ier+1
+ self.cr.fatal("<F> <MACR_LIGN_COUPE> le group_no "+ngrno+" n est pas dans le maillage "+n_mailla)
+ return ier
+ grpn=collgrno[ngrno]
+ l_coor_group=[ngrno,]
+ for node in grpn:
+ l_coor_group.append(aster.getvectjev(n_mailla.ljust(8)+'.COORDO .VALE',3*(node-1),3))
+ groups.append(l_coor_group)
+
+ if minidim!=dime:
+ ier=ier+1
+ self.cr.fatal("<F> <MACR_LIGN_COUPE> dimensions de maillage et de coordonnees incoherentes")
+ return ier
+
# Création du maillage des NB_POINTS segments entre COOR_ORIG et COOR_EXTR
+ # ainsi que des segments reliant les noeuds issus des group_no demandés
# par appel au script python crea_mail_lig_coup
# le maillage est ensuite recopié dans l unité logique UNITE_MAILLAGE
- resu_mail=crea_mail_lig_coup(lignes)
+ resu_mail=crea_mail_lig_coup(dime,lignes,groups)
cur_dir=os.getcwd()
nomFichierSortie =cur_dir+'/fort.'+str(UNITE_MAILLAGE)
fproc=open(nomFichierSortie,'w')
iocc=1
motscles['CREA_GROUP_NO']=[]
for m in LIGN_COUPE :
- motscles['CREA_GROUP_NO'].append(_F(GROUP_MA='LICOU'+str(iocc),) )
- iocc=iocc+1
+ if m['NB_POINTS'] !=None :
+ motscles['CREA_GROUP_NO'].append(_F(GROUP_MA='LICOU'+str(iocc),) )
+ iocc=iocc+1
+ elif m['GROUP_NO']!=None :
+ motscles['CREA_GROUP_NO'].append(_F(GROUP_MA=m['GROUP_NO'].ljust(8).upper(),) )
__macou=DEFI_GROUP( reuse =__macou , MAILLAGE=__macou , **motscles );
if AsType(RESULTAT).__name__ in ('evol_elas','evol_noli') :
__recou=PROJ_CHAMP(METHODE='ELEM',
RESULTAT=RESULTAT,
- MODELE_1=MODELE,
+ MODELE_1=self.jdc.current_context[n_modele],
MODELE_2=__mocou,
+ TYPE_CHAM='NOEU',
NOM_CHAM=NOM_CHAM,);
- # Production d'une table par ligne de coupe
- # Toutes les tables sont des concepts sortant de la macro définies
- # dans chaque occurence du mcfact lign_coupe
+ # Production d'une table pour toutes les lignes de coupe
- iocc=1
+ ioc2=0
+ mcACTION=[]
for m in LIGN_COUPE :
- self.DeclareOut('tt',m['TABLE'])
- tt=POST_RELEVE_T(ACTION=_F(INTITULE = 'lig.coupe'+str(iocc),
- RESULTAT = __recou,
- GROUP_NO = 'LICOU'+str(iocc),
- NOM_CHAM = NOM_CHAM,
- TOUT_CMP = 'OUI',
- OPERATION = 'EXTRACTION', ),);
- iocc=iocc+1
+ if m['NB_POINTS'] !=None :
+ ioc2=ioc2+1
+ groupe='LICOU'+str(ioc2)
+ if m['INTITULE'] !=None : intitl=m['INTITULE']
+ else : intitl='l.coupe'+str(ioc2)
+ elif m['GROUP_NO']!=None :
+ groupe=m['GROUP_NO'].ljust(8).upper()
+ if m['INTITULE'] !=None : intitl=m['INTITULE']
+ else : intitl=groupe
+ mcACTION.append( _F(INTITULE = intitl,
+ RESULTAT = __recou,
+ GROUP_NO = groupe,
+ NOM_CHAM = NOM_CHAM,
+ TOUT_CMP = 'OUI',
+ OPERATION = 'EXTRACTION', ) )
+
+ __tabitm=POST_RELEVE_T(ACTION=mcACTION,);
+
+ # on repasse par les tables python pour supprimer les paramètres inutiles
+ # NOEUD (car il est propre au maillage de la ligne) et RESU
+
+ self.DeclareOut('nomres',self.sd)
+ dictab=__tabitm.EXTR_TABLE()
+ listpara=dictab.para
+ listpara.remove('NOEUD')
+ listpara.remove('RESU')
+
+ coltab=[]
+ for key in listpara :
+ val=dictab[key].values()[key]
+ if type(val[0])==types.IntType :
+ coltab.append(_F(PARA=key,LISTE_I=val))
+ elif type(val[0])==types.FloatType :
+ coltab.append(_F(PARA=key,LISTE_R=val))
+ elif type(val[0])==types.StringType :
+ coltab.append(_F(PARA=key,LISTE_K=val,TYPE_K='K16'))
+ nomres=CREA_TABLE(LISTE=coltab)
return ier
-#@ MODIF macr_recal_ops Macro DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF macr_recal_ops Macro DATE 14/03/2005 AUTEUR DURAND C.DURAND
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
import Macro
from Cata import cata
from Cata.cata import DEFI_LIST_REEL
- from Macro.recal import gestion,transforme_list_Num,EXTRACT,calcul_F,graphique
+ from Macro.recal import gestion,transforme_list_Num,calcul_F,graphique
from Macro import reca_message
from Macro import reca_algo
from Macro import reca_interp
if v.__class__.__name__ in ('OPER','MACRO'):
self.current_context[k]= v
self.current_context['_F']=cata.__dict__['_F']
- self.g_context['EXTRACT']=EXTRACT
#_____________________________________________
#
+++ /dev/null
-# -*- coding: utf-8 -*-
-#@ MODIF macro_cara_poutre_ops Macro DATE 25/06/2002 AUTEUR JMBHH01 J.M.PROIX
-# CONFIGURATION MANAGEMENT OF EDF VERSION
-# ======================================================================
-# COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
-# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
-# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
-# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
-# (AT YOUR OPTION) ANY LATER VERSION.
-#
-# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
-# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
-# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
-# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
-#
-# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
-# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
-# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
-# ======================================================================
-# RESPONSABLE JMBHH01 J.M.PROIX
-def macro_cara_poutre_ops(self,UNITE_MAILLAGE,SYME_X,SYME_Y,GROUP_MA_BORD,
- GROUP_MA,ORIG_INER,NOEUD,GROUP_MA_INTE,
- LONGUEUR,MATERIAU,LIAISON,
- **args):
- """
- Ecriture de la macro MACRO_CARA_POUTRE
- """
- import types
- from Accas import _F
- ier=0
- # On importe les definitions des commandes a utiliser dans la macro
- # Le nom de la variable doit etre obligatoirement le nom de la commande
- LIRE_MAILLAGE =self.get_cmd('LIRE_MAILLAGE')
- DEFI_GROUP =self.get_cmd('DEFI_GROUP')
- CREA_MAILLAGE =self.get_cmd('CREA_MAILLAGE')
- AFFE_MODELE =self.get_cmd('AFFE_MODELE')
- DEFI_MATERIAU =self.get_cmd('DEFI_MATERIAU')
- AFFE_MATERIAU =self.get_cmd('AFFE_MATERIAU')
- DEFI_FONCTION =self.get_cmd('DEFI_FONCTION')
- DEFI_CONSTANTE =self.get_cmd('DEFI_CONSTANTE')
- AFFE_CHAR_THER =self.get_cmd('AFFE_CHAR_THER')
- AFFE_CHAR_THER_F=self.get_cmd('AFFE_CHAR_THER_F')
- THER_LINEAIRE =self.get_cmd('THER_LINEAIRE')
- CALC_VECT_ELEM =self.get_cmd('CALC_VECT_ELEM')
- CALC_MATR_ELEM =self.get_cmd('CALC_MATR_ELEM')
- NUME_DDL =self.get_cmd('NUME_DDL')
- ASSE_VECTEUR =self.get_cmd('ASSE_VECTEUR')
- POST_ELEM =self.get_cmd('POST_ELEM')
- # La macro compte pour 1 dans la numerotation des commandes
- self.icmd=1
-
- # Le concept sortant (de type tabl_cara_geom) est nommé 'nomres' dans
- # le contexte de la macro
-
- self.DeclareOut('nomres',self.sd)
-
- if GROUP_MA_BORD and GROUP_MA:
- if not LIAISON:
- ier=ier+1
- self.cr.fatal("Avec GROUP_MA, il faut obligatoirement preciser LIAISON, LONGUEUR ET MATERIAU")
- return ier
-
- __nomlma=LIRE_MAILLAGE(UNITE=UNITE_MAILLAGE,)
-
- __nomamo=AFFE_MODELE(MAILLAGE=__nomlma,
- AFFE=_F(TOUT='OUI',
- PHENOMENE='MECANIQUE',
- MODELISATION='D_PLAN',), )
-
- __nomdma=DEFI_MATERIAU(ELAS=_F(E=1.0,NU=0.,RHO=1.0),)
-
-
- __nomama=AFFE_MATERIAU(MAILLAGE=__nomlma,
- AFFE=_F(TOUT='OUI',
- MATER=__nomdma,), )
-
-# --- CALCUL DES CARACTERISTIQUES GEOMETRIQUES DE LA SECTION :
-# ------------------------------------------------------
-
- motsimps={}
- if GROUP_MA : motsimps['GROUP_MA'] = GROUP_MA
- if SYME_X : motsimps['SYME_X'] = SYME_X
- if SYME_Y : motsimps['SYME_Y'] = SYME_Y
- motsimps['ORIG_INER'] = ORIG_INER
- mfact=_F(TOUT='OUI',**motsimps)
- nomres=POST_ELEM(MODELE=__nomamo,
- CHAM_MATER=__nomama,
- CARA_GEOM=mfact )
-
-# nb : si GROUP_MA n existe pas : le mot clé est ignoré
-
-#
-# ==================================================================
-# --- = CALCUL DE LA CONSTANTE DE TORSION SUR TOUT LE MAILLAGE =
-# --- = OU DU CENTRE DE TORSION/CISAILLEMENT =
-# --- = DES COEFFICIENTS DE CISAILLEMENT =
-# --- = ET DE L INERTIE DE GAUCHISSEMENT =
-# --- = ON CREE UN MODELE PLAN 2D THERMIQUE REPRESENTANT LA SECTION =
-# --- = DE LA POUTRE CAR ON A A RESOUDRE DES E.D.P. AVEC DES LAPLACIENS=
-# ==================================================================
-
- if GROUP_MA_BORD and not GROUP_MA:
-
-# --- TRANSFORMATION DES GROUP_MA EN GROUP_NO SUR-LESQUELS
-# --- ON POURRA APPLIQUER DES CONDITIONS DE TEMPERATURE IMPOSEE :
-# ---------------------------------------------------------
- motscles={}
- if type(GROUP_MA_BORD)==types.StringType:
- motscles['CREA_GROUP_NO']=_F(GROUP_MA=GROUP_MA_BORD,)
- else:
- motscles['CREA_GROUP_NO']=[]
- for grma in GROUP_MA_BORD:
- motscles['CREA_GROUP_NO'].append(_F(GROUP_MA=grma,))
- __nomlma=DEFI_GROUP(reuse=__nomlma,
- MAILLAGE=__nomlma,
- **motscles)
-
-
-# --- CREATION D UN MAILLAGE IDENTIQUE AU PREMIER A CECI PRES
-# --- QUE LES COORDONNEES SONT EXPRIMEES DANS LE REPERE PRINCIPAL
-# --- D INERTIE DONT L ORIGINE EST LE CENTRE DE GRAVITE DE LA SECTION :
-# ---------------------------------------------------------------
-
- __nomapi=CREA_MAILLAGE(MAILLAGE=__nomlma,
- REPERE=_F(TABLE=nomres,
- NOM_ORIG='CDG', ), )
-
-# --- AFFECTATION DU PHENOMENE 'THERMIQUE' AU MODELE EN VUE DE
-# --- LA CONSTRUCTION D UN OPERATEUR LAPLACIEN SUR CE MODELE :
-# ------------------------------------------------------
-
- __nomoth=AFFE_MODELE(MAILLAGE=__nomapi,
- AFFE=_F(TOUT='OUI',
- PHENOMENE='THERMIQUE',
- MODELISATION='PLAN',), )
-
-# --- POUR LA CONSTRUCTION DU LAPLACIEN, ON DEFINIT UN
-# --- PSEUDO-MATERIAU DONT LES CARACTERISTIQUES THERMIQUES SONT :
-# --- LAMBDA = 1, RHO*CP = 0 :
-# ----------------------
-
- __nomath=DEFI_MATERIAU(THER=_F(LAMBDA=1.0,RHO_CP=0.,),)
-
-# --- DEFINITION D UN CHAM_MATER A PARTIR DU MATERIAU PRECEDENT :
-# ---------------------------------------------------------
-
- __chmath=AFFE_MATERIAU(MAILLAGE=__nomapi,
- AFFE=_F(TOUT='OUI',
- MATER=__nomath,), )
-
-#
-# ------------------------------------------------------------
-# --- - CALCUL DE LA CONSTANTE DE TORSION PAR RESOLUTION -
-# --- - D UN LAPLACIEN AVEC UN TERME SOURCE EGAL A -2 -
-# --- - L INCONNUE ETANT NULLE SUR LE CONTOUR DE LA SECTION : -
-# --- - LAPLACIEN(PHI) = -2 DANS LA SECTION -
-# --- - PHI = 0 SUR LE CONTOUR : -
-# ------------------------------------------------------------
-#
-# --- ON IMPOSE LA VALEUR 0 A L INCONNUE SCALAIRE SUR LE CONTOUR
-# --- DE LA SECTION
-# --- ET ON A UN TERME SOURCE EGAL A -2 DANS TOUTE LA SECTION :
-# -------------------------------------------------------
-
- motscles={}
- if GROUP_MA_INTE:
- motscles['LIAISON_UNIF']=_F(GROUP_MA=GROUP_MA_INTE,DDL='TEMP'),
- __chart1=AFFE_CHAR_THER(MODELE=__nomoth,
- TEMP_IMPO =_F(GROUP_NO=GROUP_MA_BORD,
- TEMP=0. ),
- SOURCE =_F(TOUT='OUI',
- SOUR=2.0),
- **motscles )
-
-# --- POUR CHAQUE TROU DE LA SECTION :
-# --- .ON A IMPOSE QUE PHI EST CONSTANT SUR LE CONTOUR INTERIEUR
-# --- EN FAISANT LE LIAISON_UNIF DANS LE AFFE_CHAR_THER PRECEDENT
-# --- .ON IMPOSE EN PLUS D(PHI)/DN = 2*AIRE(TROU)/L(TROU)
-# --- OU D/DN DESIGNE LA DERIVEE PAR RAPPORT A LA
-# --- NORMALE ET L DESIGNE LA LONGUEUR DU BORD DU TROU :
-# -------------------------------------------------------
-
- if GROUP_MA_INTE:
- __tbaire=POST_ELEM(MODELE=__nomoth,
- AIRE_INTERNE=_F(GROUP_MA_BORD=GROUP_MA_INTE,), )
-
- motscles={}
- motscles['FLUX_REP']=[]
- if type(GROUP_MA_INTE)==types.StringType:
- motscles['FLUX_REP']=_F(GROUP_MA=GROUP_MA_INTE,CARA_TORSION=__tbaire)
- else:
- motscles['FLUX_REP']=[]
- for grma in GROUP_MA_INTE:
- motscles['FLUX_REP'].append(_F(GROUP_MA=grma,CARA_TORSION=__tbaire),)
- __chart2=AFFE_CHAR_THER(MODELE=__nomoth,**motscles)
-
-# --- RESOLUTION DE LAPLACIEN(PHI) = -2
-# --- AVEC PHI = 0 SUR LE CONTOUR :
-# ----------------------------------------
-
- motscles={}
- motscles['EXCIT']=[_F(CHARGE=__chart1,),]
- if GROUP_MA_INTE:
- motscles['EXCIT'].append(_F(CHARGE=__chart2,))
- __tempe1=THER_LINEAIRE(MODELE=__nomoth,
- CHAM_MATER=__chmath,
- SOLVEUR=_F(STOP_SINGULIER='NON',),
- **motscles )
-
-#
-# ----------------------------------------------
-# --- - CALCUL DU CENTRE DE TORSION/CISAILLEMENT -
-# --- - ET DES COEFFICIENTS DE CISAILLEMENT : -
-# ----------------------------------------------
-#
-# --- POUR LE CALCUL DES CONSTANTES DE CISAILLEMENT, ON VA DEFINIR
-# --- UN PREMIER TERME SOURCE, SECOND MEMBRE DE L EQUATION DE LAPLACE
-# --- PAR UNE FONCTION EGALE A Y :
-# --------------------------
-
- __fnsec1=DEFI_FONCTION(NOM_PARA='X',
- VALE=(0.,0.,10.,10.),
- PROL_DROITE='LINEAIRE',
- PROL_GAUCHE='LINEAIRE',
- )
-
- __fnsec0=DEFI_CONSTANTE(VALE=0.,)
-
-# --- LE TERME SOURCE CONSTITUANT LE SECOND MEMBRE DE L EQUATION
-# --- DE LAPLACE EST PRIS EGAL A Y DANS TOUTE LA SECTION :
-# --------------------------------------------------
-
-
- motscles={}
- if NOEUD:
- motscles['TEMP_IMPO']=(_F(NOEUD=NOEUD,TEMP=__fnsec0))
- __chart2=AFFE_CHAR_THER_F(MODELE=__nomoth,
- SOURCE=_F(TOUT='OUI',
- SOUR=__fnsec1,),
- **motscles )
-
-# --- RESOLUTION DE LAPLACIEN(PHI) = -Y
-# --- AVEC D(PHI)/D(N) = 0 SUR LE CONTOUR :
-# ------------------------------------------------
-
- __tempe2=THER_LINEAIRE(MODELE=__nomoth,
- CHAM_MATER=__chmath,
- EXCIT=_F(CHARGE=__chart2,),
- SOLVEUR=_F(STOP_SINGULIER='NON',),
- )
-
-# --- POUR LE CALCUL DES CONSTANTES DE CISAILLEMENT, ON VA DEFINIR
-# --- UN PREMIER TERME SOURCE, SECOND MEMBRE DE L EQUATION DE LAPLACE
-# --- PAR UNE FONCTION EGALE A Z :
-# --------------------------
-
- __fnsec2=DEFI_FONCTION(NOM_PARA='Y',
- VALE=(0.,0.,10.,10.),
- PROL_DROITE='LINEAIRE',
- PROL_GAUCHE='LINEAIRE',
- )
-
-# --- LE TERME SOURCE CONSTITUANT LE SECOND MEMBRE DE L EQUATION
-# --- DE LAPLACE EST PRIS EGAL A Z DANS TOUTE LA SECTION :
-# --------------------------------------------------
-
- motscles={}
- if NOEUD:
- motscles['TEMP_IMPO']=_F(NOEUD=NOEUD,TEMP=__fnsec0)
- __chart3=AFFE_CHAR_THER_F(MODELE=__nomoth,
- SOURCE=_F(TOUT='OUI',
- SOUR=__fnsec2,),
- **motscles)
-
-# --- RESOLUTION DE LAPLACIEN(PHI) = -Z
-# --- AVEC D(PHI)/D(N) = 0 SUR LE CONTOUR :
-# ------------------------------------------------
-
- __tempe3=THER_LINEAIRE(MODELE=__nomoth,
- CHAM_MATER=__chmath,
- EXCIT=_F(CHARGE=__chart3,),
- SOLVEUR=_F(STOP_SINGULIER='NON',),
- )
-
-# --- CALCUL DE LA CONSTANTE DE TORSION :
-# ---------------------------------
-
- motscles={}
- if GROUP_MA_INTE:
- motscles['CARA_POUTRE']=_F(CARA_GEOM=nomres,
- LAPL_PHI=__tempe1,
- TOUT='OUI',
- OPTION='CARA_TORSION',
- GROUP_MA_INTE=GROUP_MA_INTE,)
- else:
- motscles['CARA_POUTRE']=_F(CARA_GEOM=nomres,
- LAPL_PHI=__tempe1,
- TOUT='OUI',
- OPTION='CARA_TORSION', )
- nomres=POST_ELEM(reuse=nomres,
- MODELE=__nomoth,
- CHAM_MATER=__chmath,
- **motscles )
-
-# --- CALCUL DES COEFFICIENTS DE CISAILLEMENT ET DES COORDONNEES DU
-# --- CENTRE DE CISAILLEMENT/TORSION :
-# ------------------------------
-
- nomres=POST_ELEM(reuse=nomres,
- MODELE=__nomoth,
- CHAM_MATER=__chmath,
- CARA_POUTRE=_F(CARA_GEOM=nomres,
- LAPL_PHI_Y=__tempe2,
- LAPL_PHI_Z=__tempe3,
- TOUT='OUI',
- OPTION='CARA_CISAILLEMENT',), )
-
-#
-# ------------------------------------------------------------
-# --- - CALCUL DE L INERTIE DE GAUCHISSEMENT PAR RESOLUTION DE -
-# --- - LAPLACIEN(OMEGA) = 0 DANS LA SECTION -
-# --- - AVEC D(OMEGA)/D(N) = Z*NY-Y*NZ SUR LE -
-# --- - CONTOUR DE LA SECTION -
-# --- - NY ET NZ SONT LES COMPOSANTES DU VECTEUR N NORMAL -
-# --- - A CE CONTOUR -
-# --- - ET SOMME_S(OMEGA.DS) = 0 -
-# --- - OMEGA EST LA FONCTION DE GAUCHISSEMENT -
-# --- - L INERTIE DE GAUCHISSEMENT EST SOMME_S(OMEGA**2.DS) -
-# ------------------------------------------------------------
-#
-# --- CREATION D UN MAILLAGE DONT LES COORDONNEES SONT EXPRIMEES
-# --- DANS LE REPERE PRINCIPAL D INERTIE MAIS AVEC COMME ORIGINE
-# --- LE CENTRE DE TORSION DE LA SECTION, ON VA DONC UTILISER
-# --- LE MAILLAGE DE NOM NOMAPI DONT LES COORDONNEES SONT
-# --- EXPRIMEES DANS LE REPERE PRINCIPAL D'INERTIE, L'ORIGINE
-# --- ETANT LE CENTRE DE GRAVITE DE LA SECTION (QUI EST DONC
-# --- A CHANGER) :
-# ----------
-
- __nomapt=CREA_MAILLAGE(MAILLAGE=__nomapi,
- REPERE=_F(TABLE=nomres,
- NOM_ORIG='TORSION',) )
-
-# --- AFFECTATION DU PHENOMENE 'THERMIQUE' AU MODELE EN VUE DE
-# --- LA CONSTRUCTION D UN OPERATEUR LAPLACIEN SUR CE MODELE :
-# ------------------------------------------------------
-
- __nomot2=AFFE_MODELE(MAILLAGE=__nomapt,
- AFFE=_F(TOUT='OUI',
- PHENOMENE='THERMIQUE',
- MODELISATION='PLAN', ) )
-
-# --- DEFINITION D UN CHAM_MATER A PARTIR DU MATERIAU PRECEDENT :
-# ---------------------------------------------------------
-
- __chmat2=AFFE_MATERIAU(MAILLAGE=__nomapt,
- AFFE=_F(TOUT='OUI',
- MATER=__nomath, ), )
-
-# --- POUR LE CALCUL DE L INERTIE DE GAUCHISSEMENT, ON VA DEFINIR
-# --- LA COMPOSANTE SELON Y DU FLUX A IMPOSER SUR LE CONTOUR
-# --- PAR UNE FONCTION EGALE A -X :
-# ---------------------------
-
- __fnsec3=DEFI_FONCTION(NOM_PARA='X',
- VALE=(0.,0.,10.,-10.),
- PROL_DROITE='LINEAIRE',
- PROL_GAUCHE='LINEAIRE',
- )
-
-# --- POUR LE CALCUL DE L INERTIE DE GAUCHISSEMENT, ON VA DEFINIR
-# --- LA COMPOSANTE SELON X DU FLUX A IMPOSER SUR LE CONTOUR
-# --- PAR UNE FONCTION EGALE A Y :
-# --------------------------
-
- __fnsec4=DEFI_FONCTION(NOM_PARA='Y',
- VALE=(0.,0.,10.,10.),
- PROL_DROITE='LINEAIRE',
- PROL_GAUCHE='LINEAIRE',
- )
-
-# --- DANS LE BUT D IMPOSER LA RELATION LINEAIRE ENTRE DDLS
-# --- SOMME_SECTION(OMEGA.DS) = 0 ( CETTE CONDITION
-# --- VENANT DE L EQUATION D EQUILIBRE SELON L AXE DE LA POUTRE
-# --- N = 0, N ETANT L EFFORT NORMAL)
-# --- ON CALCULE LE VECTEUR DE CHARGEMENT DU A UN TERME SOURCE EGAL
-# --- A 1., LES TERMES DE CE VECTEUR SONT EGAUX A
-# --- SOMME_SECTION(NI.DS) ET SONT DONC LES COEFFICIENTS DE
-# --- LA RELATION LINEAIRE A IMPOSER.
-# --- ON DEFINIT DONC UN CHARGEMENT DU A UN TERME SOURCE EGAL A 1 :
-# -----------------------------------------------------------
-
- __chart4=AFFE_CHAR_THER(MODELE=__nomot2,
- SOURCE=_F(TOUT='OUI',
- SOUR=1.0), )
-
-# --- ON CALCULE LE VECT_ELEM DU AU CHARGEMENT PRECEDENT
-# --- IL S AGIT DES VECTEURS ELEMENTAIRES DONT LE TERME
-# --- AU NOEUD COURANT I EST EGAL A SOMME_SECTION(NI.DS) :
-# --------------------------------------------------
-
- __vecel=CALC_VECT_ELEM(CHARGE=__chart4,
- OPTION='CHAR_THER'
- )
-
-# --- ON CALCULE LE MATR_ELEM DES MATRICES ELEMENTAIRES
-# --- DE CONDUCTIVITE UNIQUEMENT POUR GENERER LE NUME_DDL
-# --- SUR-LEQUEL S APPUIERA LE CHAMNO UTILISE POUR ECRIRE LA
-# --- RELATION LINEAIRE ENTRE DDLS :
-# ----------------------------
-
- __matel=CALC_MATR_ELEM(MODELE=__nomot2,
- CHAM_MATER=__chmat2,
- CHARGE=__chart4,
- OPTION='RIGI_THER',)
-
-# --- ON DEFINIT LE NUME_DDL ASSOCIE AU MATR_ELEM DEFINI
-# --- PRECEDEMMENT POUR CONSTRUIRE LE CHAMNO UTILISE POUR ECRIRE LA
-# --- RELATION LINEAIRE ENTRE DDLS :
-# ----------------------------
-
- __numddl=NUME_DDL(MATR_RIGI=__matel,
- METHODE='LDLT', )
-
-# --- ON CONSTRUIT LE CHAMNO QUI VA ETRE UTILISE POUR ECRIRE LA
-# --- RELATION LINEAIRE ENTRE DDLS :
-# ----------------------------
-
- __chamno=ASSE_VECTEUR(VECT_ELEM=__vecel,
- NUME_DDL=__numddl, )
-
-# --- ON IMPOSE LA RELATION LINEAIRE ENTRE DDLS
-# --- SOMME_SECTION(OMEGA.DS) = 0 ( CETTE CONDITION
-# --- VENANT DE L EQUATION D EQUILIBRE SELON L AXE DE LA POUTRE
-# --- N = 0, N ETANT L EFFORT NORMAL)
-# --- POUR IMPOSER CETTE RELATION ON PASSE PAR LIAISON_CHAMNO,
-# --- LES TERMES DU CHAMNO (I.E. SOMME_SECTION(NI.DS))
-# --- SONT LES COEFFICIENTS DE LA RELATION LINEAIRE :
-# ---------------------------------------------
-
- __chart5=AFFE_CHAR_THER(MODELE=__nomot2,
- LIAISON_CHAMNO=_F(CHAM_NO=__chamno,
- COEF_IMPO=0.), )
-
-# --- LE CHARGEMENT EST UN FLUX REPARTI NORMAL AU CONTOUR
-# --- DONT LES COMPOSANTES SONT +Z (I.E. +Y) ET -Y (I.E. -X)
-# --- SELON LA DIRECTION NORMALE AU CONTOUR :
-# -------------------------------------
-
- __chart6=AFFE_CHAR_THER_F(MODELE=__nomot2,
- FLUX_REP=_F(GROUP_MA=GROUP_MA_BORD,
- FLUX_X =__fnsec4,
- FLUX_Y =__fnsec3,), )
-
-# --- RESOLUTION DE LAPLACIEN(OMEGA) = 0
-# --- AVEC D(OMEGA)/D(N) = Z*NY-Y*NZ SUR LE CONTOUR DE LA SECTION
-# --- ET SOMME_SECTION(OMEGA.DS) = 0 ( CETTE CONDITION
-# --- VENANT DE L EQUATION D EQUILIBRE SELON L AXE DE LA POUTRE
-# --- N = 0, N ETANT L EFFORT NORMAL) :
-# -------------------------------
-
- __tempe4=THER_LINEAIRE(MODELE=__nomot2,
- CHAM_MATER=__chmat2,
- EXCIT=(_F(CHARGE=__chart5,),
- _F(CHARGE=__chart6,),),
- SOLVEUR=_F(METHODE='LDLT',
- RENUM='SANS',
- STOP_SINGULIER='NON',), )
-
-# --- CALCUL DE L INERTIE DE GAUCHISSEMENT :
-# -------------------------------------
-
- nomres=POST_ELEM(reuse=nomres,
- MODELE=__nomot2,
- CHAM_MATER=__chmat2,
- CARA_POUTRE=_F(CARA_GEOM=nomres,
- LAPL_PHI=__tempe4,
- TOUT='OUI',
- OPTION='CARA_GAUCHI'), )
-
-#
-# ==================================================================
-# --- = CALCUL DE LA CONSTANTE DE TORSION SUR CHAQUE GROUPE =
-# --- = ET DU CENTRE DE TORSION/CISAILLEMENT =
-# --- = DES COEFFICIENTS DE CISAILLEMENT =
-# ==================================================================
-#
-
-
- if GROUP_MA_BORD and GROUP_MA:
-
- if type(GROUP_MA_BORD)==types.StringType :
- l_group_ma_bord=[GROUP_MA_BORD,]
- else:
- l_group_ma_bord= GROUP_MA_BORD
- if type(GROUP_MA)==types.StringType :
- l_group_ma=[GROUP_MA,]
- else:
- l_group_ma= GROUP_MA
-
- if NOEUD:
- if type(NOEUD)==types.StringType :
- l_noeud=[NOEUD,]
- else:
- l_noeud= NOEUD
-
- if len(l_group_ma)!=len(l_group_ma_bord):
- ier=ier+1
- self.cr.fatal("GROUP_MA et GROUP_MA_BORD incoherents")
- return ier
- if NOEUD and (len(l_group_ma)!=len(l_noeud)):
- ier=ier+1
- self.cr.fatal("GROUP_MA et NOEUD incoherents")
- return ier
-
- for i in range(0,len(l_group_ma_bord)):
-
-# --- TRANSFORMATION DES GROUP_MA EN GROUP_NO SUR-LESQUELS
-# --- ON POURRA APPLIQUER DES CONDITIONS DE TEMPERATURE IMPOSEE :
-# ---------------------------------------------------------
-
- __nomlma=DEFI_GROUP(reuse=__nomlma,
- MAILLAGE=__nomlma,
- CREA_GROUP_NO=_F(GROUP_MA=l_group_ma_bord[i],) )
-
-
-# --- CREATION D UN MAILLAGE IDENTIQUE AU PREMIER A CECI PRES
-# --- QUE LES COORDONNEES SONT EXPRIMEES DANS LE REPERE PRINCIPAL
-# --- D INERTIE DONT L ORIGINE EST LE CENTRE DE GRAVITE DE LA SECTION :
-# ---------------------------------------------------------------
-
- __nomapi=CREA_MAILLAGE(MAILLAGE=__nomlma,
- REPERE=_F(TABLE=nomres,
- NOM_ORIG='CDG',
- GROUP_MA=l_group_ma[i], ), )
-
-# --- AFFECTATION DU PHENOMENE 'THERMIQUE' AU MODELE EN VUE DE
-# --- LA CONSTRUCTION D UN OPERATEUR LAPLACIEN SUR CE MODELE :
-# ------------------------------------------------------
-
- __nomoth=AFFE_MODELE(MAILLAGE=__nomapi,
- AFFE=_F(GROUP_MA=l_group_ma[i],
- PHENOMENE='THERMIQUE',
- MODELISATION='PLAN', ) )
-
-# --- POUR LA CONSTRUCTION DU LAPLACIEN, ON DEFINIT UN
-# --- PSEUDO-MATERIAU DONT LES CARACTERISTIQUES THERMIQUES SONT :
-# --- LAMBDA = 1, RHO*CP = 0 :
-# ----------------------
-
- __nomath=DEFI_MATERIAU(THER=_F(LAMBDA=1.0,
- RHO_CP=0.0, ), )
-
-# --- DEFINITION D UN CHAM_MATER A PARTIR DU MATERIAU PRECEDENT :
-# ---------------------------------------------------------
-
- __chmath=AFFE_MATERIAU(MAILLAGE=__nomapi,
- AFFE=_F(TOUT='OUI',
- MATER=__nomath ), )
-
-#
-# ------------------------------------------------------------
-# --- - CALCUL DE LA CONSTANTE DE TORSION PAR RESOLUTION -
-# --- - D UN LAPLACIEN AVEC UN TERME SOURCE EGAL A -2 -
-# --- - L INCONNUE ETANT NULLE SUR LE CONTOUR DE LA SECTION : -
-# --- - LAPLACIEN(PHI) = -2 DANS LA SECTION -
-# --- - PHI = 0 SUR LE CONTOUR : -
-# ------------------------------------------------------------
-#
-# --- ON IMPOSE LA VALEUR 0 A L INCONNUE SCALAIRE SUR LE CONTOUR
-# --- DE LA SECTION
-# --- ET ON A UN TERME SOURCE EGAL A -2 DANS TOUTE LA SECTION :
-# -------------------------------------------------------
-
- __chart1=AFFE_CHAR_THER(MODELE=__nomoth,
- TEMP_IMPO=_F(GROUP_NO=l_group_ma_bord[i],
- TEMP=0.0 ),
- SOURCE=_F(TOUT='OUI',
- SOUR=2.0 ) )
-
-# --- RESOLUTION DE LAPLACIEN(PHI) = -2
-# --- AVEC PHI = 0 SUR LE CONTOUR :
-# ----------------------------------------
-
- __tempe1=THER_LINEAIRE(MODELE=__nomoth,
- CHAM_MATER=__chmath,
- EXCIT=_F(CHARGE=__chart1, ),
- SOLVEUR=_F(STOP_SINGULIER='NON',) )
-
-#
-# ----------------------------------------------
-# --- - CALCUL DU CENTRE DE TORSION/CISAILLEMENT -
-# --- - ET DES COEFFICIENTS DE CISAILLEMENT : -
-# ----------------------------------------------
-#
-# --- POUR LE CALCUL DES CONSTANTES DE CISAILLEMENT, ON VA DEFINIR
-# --- UN PREMIER TERME SOURCE, SECOND MEMBRE DE L EQUATION DE LAPLACE
-# --- PAR UNE FONCTION EGALE A Y :
-# --------------------------
-
- __fnsec1=DEFI_FONCTION(NOM_PARA='X',
- VALE=(0.,0.,10.,10.),
- PROL_DROITE='LINEAIRE',
- PROL_GAUCHE='LINEAIRE', )
-
- __fnsec0=DEFI_CONSTANTE(VALE=0.,)
-
-# --- LE TERME SOURCE CONSTITUANT LE SECOND MEMBRE DE L EQUATION
-# --- DE LAPLACE EST PRIS EGAL A Y DANS TOUTE LA SECTION :
-# --------------------------------------------------
-
- __chart2=AFFE_CHAR_THER_F(MODELE=__nomoth,
- TEMP_IMPO=_F(NOEUD=l_noeud[i],
- TEMP=__fnsec0),
- SOURCE=_F(TOUT='OUI',
- SOUR=__fnsec1) )
-
-# --- RESOLUTION DE LAPLACIEN(PHI) = -Y
-# --- AVEC D(PHI)/D(N) = 0 SUR LE CONTOUR :
-# ------------------------------------------------
-
- __tempe2=THER_LINEAIRE(MODELE=__nomoth,
- CHAM_MATER=__chmath,
- EXCIT=_F(CHARGE=__chart2, ),
- SOLVEUR=_F(STOP_SINGULIER='NON',) )
-
-# --- POUR LE CALCUL DES CONSTANTES DE CISAILLEMENT, ON VA DEFINIR
-# --- UN PREMIER TERME SOURCE, SECOND MEMBRE DE L EQUATION DE LAPLACE
-# --- PAR UNE FONCTION EGALE A Z :
-# --------------------------
-
- __fnsec2=DEFI_FONCTION(NOM_PARA='Y',
- VALE=(0.,0.,10.,10.),
- PROL_DROITE='LINEAIRE',
- PROL_GAUCHE='LINEAIRE', )
-
-# --- LE TERME SOURCE CONSTITUANT LE SECOND MEMBRE DE L EQUATION
-# --- DE LAPLACE EST PRIS EGAL A Z DANS TOUTE LA SECTION :
-# --------------------------------------------------
-
- __chart3=AFFE_CHAR_THER_F(MODELE=__nomoth,
- TEMP_IMPO=_F(NOEUD=l_noeud[i],
- TEMP=__fnsec0),
- SOURCE=_F(TOUT='OUI',
- SOUR=__fnsec2) )
-
-# --- RESOLUTION DE LAPLACIEN(PHI) = -Z
-# --- AVEC D(PHI)/D(N) = 0 SUR LE CONTOUR :
-# ------------------------------------------------
-
- __tempe3=THER_LINEAIRE(MODELE=__nomoth,
- CHAM_MATER=__chmath,
- EXCIT=_F(CHARGE=__chart3, ),
- SOLVEUR=_F(STOP_SINGULIER='NON',) )
-
-# --- CALCUL DE LA CONSTANTE DE TORSION :
-# ---------------------------------
-
- nomres=POST_ELEM(reuse=nomres,
- MODELE=__nomoth,
- CHAM_MATER=__chmath,
- CARA_POUTRE=_F(CARA_GEOM=nomres,
- LAPL_PHI=__tempe1,
- GROUP_MA=l_group_ma[i],
- OPTION='CARA_TORSION' ), )
-
-# --- CALCUL DES COEFFICIENTS DE CISAILLEMENT ET DES COORDONNEES DU
-# --- CENTRE DE CISAILLEMENT/TORSION :
-# ------------------------------
-
- nomres=POST_ELEM(reuse=nomres,
- MODELE=__nomoth,
- CHAM_MATER=__chmath,
- CARA_POUTRE=_F(CARA_GEOM=nomres,
- LAPL_PHI_Y=__tempe2,
- LAPL_PHI_Z=__tempe3,
- GROUP_MA=l_group_ma[i],
- LONGUEUR=LONGUEUR,
- MATERIAU=MATERIAU,
- LIAISON =LIAISON,
- OPTION='CARA_CISAILLEMENT' ), )
-
- return ier
-
-#@ MODIF macro_matr_asse_ops Macro DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF macro_matr_asse_ops Macro DATE 01/04/2005 AUTEUR VABHHTS J.PELLET
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
# COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
-# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
-# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
-# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
-# (AT YOUR OPTION) ANY LATER VERSION.
-#
-# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
-# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
-# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
-# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
-#
-# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
-# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
-# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
+# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
+# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
+# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
+# (AT YOUR OPTION) ANY LATER VERSION.
+#
+# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
+# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
+# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
+#
+# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
+# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
+# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
# ======================================================================
ier=ier+1
self.cr.fatal("<F> <MACRO_MATR_ASSE> Avec methode MULT_FRONT, RENUM doit etre MDA, MD ou RCMK.")
return ier
+ elif methode=='MUMPS':
+ if SOLVEUR['RENUM']:
+ renum=SOLVEUR['RENUM']
+ else:
+ renum='SANS'
+ if renum not in ('SANS',):
+ ier=ier+1
+ self.cr.fatal("<F> <MACRO_MATR_ASSE> Avec methode MUMPS, RENUM doit etre SANS.")
+ return ier
elif methode=='GCPC':
if SOLVEUR['RENUM']:
renum=SOLVEUR['RENUM']
lrigel = 0
lmasel = 0
-# decalage eventuel en premiere position dans la liste de l occurence de MATR_ASSE contenant
+# decalage eventuel en premiere position dans la liste de l occurence de MATR_ASSE contenant
# l option de rigidite
try :
for m in MATR_ASSE:
self.cr.fatal("<F> <MACRO_MATR_ASSE> UNE DES OPTIONS DOIT ETRE RIGI_MECA OU RIGI_THER OU RIGI_ACOU OU RIGI_MECA_LAGR")
return ier
- if m['SIEF_ELGA']!=None and option!='RIGI_GEOM':
- ier=ier+1
- self.cr.fatal("<F> <MACRO_MATR_ASSE> SIEF_ELGA N EST ADMIS QU AVEC L OPTION RIGI_GEOM")
- return ier
-
- if m['MODE_FOURIER']!=None and option not in ('RIGI_MECA','RIGI_FLUI_STRU','RIGI_THER'):
- ier=ier+1
- self.cr.fatal("<F> <MACRO_MATR_ASSE> MODE_FOURIER N EST ADMIS QU AVEC UNE DES OPTIONS RIGI_MECA RIGI_FLUI_STRU RIGI_THER")
- return ier
-
- if (m['THETA']!=None or m['PROPAGATION']!=None) and option!='RIGI_MECA_LAGR':
- ier=ier+1
- self.cr.fatal("<F> <MACRO_MATR_ASSE> PROPAGATION ET,OU THETA NE SONT ADMIS QU AVEC L OPTION RIGI_MECA_LAGR")
- return ier
motscles={'OPTION':option}
if option == 'AMOR_MECA':
if CHAM_MATER != None: motscles['CHAM_MATER'] =CHAM_MATER
if CARA_ELEM != None: motscles['CARA_ELEM'] =CARA_ELEM
if INST != None: motscles['INST'] =INST
- if m['SIEF_ELGA'] : motscles['SIEF_ELGA'] =m['SIEF_ELGA']
- if m['MODE_FOURIER']: motscles['MODE_FOURIER']=m['MODE_FOURIER']
- if m['THETA'] : motscles['THETA'] =m['THETA']
- if m['PROPAGATION'] : motscles['PROPAGATION'] =m['PROPAGATION']
+ try : motscles['SIEF_ELGA'] =m['SIEF_ELGA']
+ except IndexError : pass
+
+ try : motscles['MODE_FOURIER'] =m['MODE_FOURIER']
+ except IndexError : pass
+
+ try : motscles['THETA'] =m['THETA']
+ except IndexError : pass
+
+ try : motscles['PROPAGATION'] =m['PROPAGATION']
+ except IndexError : pass
+ print motscles
__a=CALC_MATR_ELEM(MODELE=MODELE,**motscles)
if option == 'RIGI_MECA':
+++ /dev/null
-# -*- coding: utf-8 -*-
-#@ MODIF pre_gmsh_ops Macro DATE 11/06/2002 AUTEUR DURAND C.DURAND
-# CONFIGURATION MANAGEMENT OF EDF VERSION
-# ======================================================================
-# COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
-# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
-# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
-# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
-# (AT YOUR OPTION) ANY LATER VERSION.
-#
-# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
-# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
-# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
-# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
-#
-# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
-# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
-# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
-# ======================================================================
-
-def pre_gmsh_ops(self,UNITE_MAILLAGE,UNITE_GMSH,MODI_QUAD,**args):
- """
- Ecriture de la macro PRE_GMSH
- """
- import os
- from Macro.ajout_quad_gmsh import ajout_quad_gmsh
- ier=0
-
- PRE_GMSH_LECT =self.get_cmd('PRE_GMSH_LECT')
-
- # La macro compte pour 1 dans la numerotation des commandes
- self.icmd=1
-
- if MODI_QUAD=='OUI':
- cur_dir=os.getcwd()
- unit = str(UNITE_GMSH)
- nomFichierGmsh = cur_dir+'/fort.'+unit
- nomFichierMail = cur_dir+'/sortie'
-
-# récupération du fichier .msh complet mis dans la string 'texte'
-
- fproc=open(nomFichierGmsh,'r')
- texte=fproc.read()
- fproc.close()
-
- resu=ajout_quad_gmsh(texte)
- if not resu:
- ier=ier+1
- self.cr.fatal("Erreur dans la methode python de transformation mailles lineaires-quadratiques")
- return ier
-
- fsort=open(nomFichierMail,'w')
- fsort.write(resu)
- fsort.close()
- os.system('cp '+nomFichierMail+' '+nomFichierGmsh)
-
- PRE_GMSH_LECT(UNITE_MAILLAGE = UNITE_MAILLAGE,
- UNITE_GMSH = UNITE_GMSH )
-
- return ier
-
-#@ MODIF reca_algo Macro DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF reca_algo Macro DATE 14/03/2005 AUTEUR DURAND C.DURAND
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
import LinearAlgebra
from Cata.cata import INFO_EXEC_ASTER
from Cata.cata import DETRUIRE
-from Macro.recal import EXTRACT
from Accas import _F
-#@ MODIF recal Macro DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS
+#@ MODIF recal Macro DATE 14/03/2005 AUTEUR DURAND C.DURAND
# -*- coding: iso-8859-1 -*-
# CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
import Cata
from Cata.cata import INCLUDE,DETRUIRE
from Accas import _F
-from Utilitai.extract import EXTRACT
import os
Fichier_Resu.append(post_bloc)
#--------------------------------------------------------------------------------
- #on va ajouter la fonction EXTRACT
+ #on va ajouter la fonction d'extraction du numarray de la table par la méthode Array
#et on stocke les réponses calculées dans la liste Lrep
#qui va etre retournée par la fonction calcul_F
self.g_context['Lrep'] = []
Fichier_Resu.append('Lrep=[]'+'\n')
for i in range(len(reponses)):
- Fichier_Resu.append('F = EXTRACT('+str(reponses[i][0])+','+"'"+str(reponses[i][1])+"'"+','+"'"+str(reponses[i][2])+"'"+')'+'\n')
+ Fichier_Resu.append('t'+str(reponses[i][0])+'='+str(reponses[i][0])+'.EXTR_TABLE()'+'\n')
+ Fichier_Resu.append('F = '+'t'+str(reponses[i][0])+'.Array('+"'"+str(reponses[i][1])+"'"+','+"'"+str(reponses[i][2])+"'"+')'+'\n')
Fichier_Resu.append('Lrep.append(F)'+'\n')
#ouverture du fichier fort.3 et mise a jour de celui ci
--- /dev/null
+# File to allow this directory to be treated as a python package.
--- /dev/null
+# Widgets whose name is the same as its module.
+_widgets = ('AlphaExample',)
+
+# Widgets whose name is not the same as its module.
+_extraWidgets = {}
+
+_functions = {}
+
+_modules = ()
--- /dev/null
+import string
+import Pmw
+
+_default_text = "AlphaExample example alpha Pmw megawidget.\nPmw version: " + \
+ Pmw.version() + '\nPmw Alpha versions: ' + \
+ string.join(Pmw.version(alpha = 1), ' ')
+
+class AlphaExample(Pmw.MessageDialog):
+ # Dummy widget for illustrating use of Pmw alpha version directory
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('message_text', _default_text, None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MessageDialog.__init__(self, parent)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions(AlphaExample)
--- /dev/null
+# File to allow this directory to be treated as a python package.
--- /dev/null
+# File to allow this directory to be treated as a python package.
--- /dev/null
+#!/usr/bin/env python
+
+# Helper script when freezing Pmw applications. It concatenates all
+# Pmw megawidget files into a single file, 'Pmw.py', in the current
+# directory. The script must be called with one argument, being the
+# path to the 'lib' directory of the required version of Pmw.
+# To freeze a Pmw application, you will also need to copy the
+# following files to the application directory before freezing:
+#
+# PmwBlt.py PmwColor.py
+
+import os
+import regsub
+import string
+import sys
+
+# The order of these files is significant. Files which reference
+# other files must appear later. Files may be deleted if they are not
+# used.
+files = [
+ 'Dialog', 'TimeFuncs', 'Balloon', 'ButtonBox', 'EntryField',
+ 'Group', 'LabeledWidget', 'MainMenuBar', 'MenuBar', 'MessageBar',
+ 'MessageDialog', 'NoteBook', 'OptionMenu', 'PanedWidget', 'PromptDialog',
+ 'RadioSelect', 'ScrolledCanvas', 'ScrolledField', 'ScrolledFrame',
+ 'ScrolledListBox', 'ScrolledText', 'HistoryText', 'SelectionDialog',
+ 'TextDialog', 'TimeCounter', 'AboutDialog', 'ComboBox', 'ComboBoxDialog',
+ 'Counter', 'CounterDialog',
+]
+
+# Set this to 0 if you do not use any of the Pmw.Color functions:
+needColor = 1
+
+# Set this to 0 if you do not use any of the Pmw.Blt functions:
+needBlt = 1
+
+def expandLinks(path):
+ if not os.path.isabs(path):
+ path = os.path.join(os.getcwd(), path)
+ while 1:
+ if not os.path.islink(path):
+ break
+ dir = os.path.dirname(path)
+ path = os.path.join(dir, os.readlink(path))
+
+ return path
+
+def mungeFile(file):
+ # Read the file and modify it so that it can be bundled with the
+ # other Pmw files.
+ file = 'Pmw' + file + '.py'
+ text = open(os.path.join(srcdir, file)).read()
+ text = regsub.gsub('import Pmw\>', '', text)
+ text = regsub.gsub('INITOPT = Pmw.INITOPT', '', text)
+ text = regsub.gsub('\<Pmw\.', '', text)
+ text = '\n' + ('#' * 70) + '\n' + '### File: ' + file + '\n' + text
+ return text
+
+# Work out which version is being bundled.
+file = sys.argv[0]
+file = os.path.normpath(file)
+file = expandLinks(file)
+
+dir = os.path.dirname(file)
+dir = expandLinks(dir)
+dir = os.path.dirname(dir)
+dir = expandLinks(dir)
+dir = os.path.basename(dir)
+
+version = string.replace(dir[4:], '_', '.')
+
+# Code to import the Color module.
+colorCode = """
+import PmwColor
+Color = PmwColor
+del PmwColor
+"""
+
+# Code to import the Blt module.
+bltCode = """
+import PmwBlt
+Blt = PmwBlt
+del PmwBlt
+"""
+
+# Code used when not linking with PmwBlt.py.
+ignoreBltCode = """
+_bltImported = 1
+_bltbusyOK = 0
+"""
+
+# Code to define the functions normally supplied by the dynamic loader.
+extraCode = """
+
+### Loader functions:
+
+_VERSION = '%s'
+
+def setversion(version):
+ if version != _VERSION:
+ raise ValueError, 'Dynamic versioning not available'
+
+def setalphaversions(*alpha_versions):
+ if alpha_versions != ():
+ raise ValueError, 'Dynamic versioning not available'
+
+def version(alpha = 0):
+ if alpha:
+ return ()
+ else:
+ return _VERSION
+
+def installedversions(alpha = 0):
+ if alpha:
+ return ()
+ else:
+ return (_VERSION,)
+
+"""
+
+if '-noblt' in sys.argv:
+ sys.argv.remove('-noblt')
+ needBlt = 0
+
+if '-nocolor' in sys.argv:
+ sys.argv.remove('-nocolor')
+ needColor = 0
+
+if len(sys.argv) != 2:
+ print 'usage: bundlepmw.py [-noblt] [-nocolor] /path/to/Pmw/Pmw_X_X_X/lib'
+ sys.exit()
+
+srcdir = sys.argv[1]
+
+if os.path.exists('Pmw.py'):
+ print 'Pmw.py already exists. Remove it and try again.'
+ sys.exit()
+
+outfile = open('Pmw.py', 'w')
+
+if needColor:
+ outfile.write(colorCode)
+
+if needBlt:
+ outfile.write(bltCode)
+
+outfile.write(extraCode % version)
+
+# Specially handle PmwBase.py file:
+text = mungeFile('Base')
+text = regsub.gsub('import PmwLogicalFont', '', text)
+text = regsub.gsub('PmwLogicalFont._font_initialise', '_font_initialise', text)
+outfile.write(text)
+if not needBlt:
+ outfile.write(ignoreBltCode)
+
+files.append('LogicalFont')
+for file in files:
+ text = mungeFile(file)
+ outfile.write(text)
+
+print ''
+print ' Pmw.py has been created.'
+
+if needColor or needBlt:
+ print ' Before running freeze, also copy the following file(s):'
+ if needBlt:
+ print ' ' + os.path.join(srcdir, 'PmwBlt.py')
+ if needColor:
+ print ' ' + os.path.join(srcdir, 'PmwColor.py')
--- /dev/null
+#
+# FILE: DirBrowser.py
+#
+# DESCRIPTION:
+# This file provides a generic Directory browser selection widget.
+#
+# AUTHOR: MontaVista Software, Inc. <source@mvista.com>
+#
+# Copyright 2001 MontaVista Software Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+# NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+
+import os
+import Tkinter
+import Pmw
+
+
+class DirBrowserDialog(Pmw.MegaToplevel):
+ def __init__(self, parent = None, **kw):
+ cwd = os.getcwd()
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('path', cwd, None),
+ ('hidedotfiles', 1, INITOPT),
+ ('label', None, INITOPT),
+ #('labelmargin', 0, INITOPT),
+ #('labelpos', None, INITOPT),
+ ('borderx', 20, INITOPT),
+ ('bordery', 20, INITOPT),
+ )
+
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaToplevel.__init__(self, parent)
+
+ interior = self.interior()
+
+ self.childframe = self.createcomponent('childframe', (), None,
+ Tkinter.Frame,
+ (interior,),
+ borderwidth = 1,
+ relief = 'raised',
+ )
+ self.childframe.pack(expand = 1,
+ fill = 'both',
+ )
+
+ self.labelframe = self.createcomponent('labelframe', (), None,
+ Tkinter.Frame,
+ (self.childframe,),
+ borderwidth = 2,
+ relief = 'groove',
+ )
+ self.labelframe.pack(padx = 10, pady = 10, expand = 1, fill = 'both')
+
+ if self['label']:
+ self.label = self.createcomponent('label', (), None,
+ Tkinter.Label,
+ (self.childframe,),
+ text = self['label'],
+ )
+ self.label.place(x = (10 + self['borderx']), y = 10, anchor = 'w')
+
+
+ self.workframe = self.createcomponent('workframe', (), None,
+ Tkinter.Frame,
+ (self.labelframe,),
+ #borderwidth = 2,
+ #relief = 'groove',
+ )
+ self.workframe.pack(padx = self['borderx'],
+ pady = self['bordery'],
+ expand = 1,
+ fill = 'both',
+ )
+
+ self.buttonframe = self.createcomponent('buttonframe', (), None,
+ Tkinter.Frame,
+ (interior,),
+ borderwidth = 1,
+ relief = 'raised',
+ )
+ self.buttonframe.pack(expand = 0,
+ fill = 'x',
+ )
+
+ self.optbox = self.createcomponent('optbox', (), None,
+ Pmw.OptionMenu,
+ (self.workframe,),
+ command = self.setpath,
+ )
+ self.optbox.bind('<Configure>', self._setMinimumSize)
+
+ self.listbox = self.createcomponent('listbox', (), None,
+ Pmw.ScrolledListBox,
+ (self.workframe,),
+ dblclickcommand = self._select,
+ )
+
+ path = self['path']
+ self.entry = self.createcomponent('entryfield', (), None,
+ Pmw.EntryField,
+ (self.workframe,),
+ value = path,
+ command = self.enteredpath,
+ labelpos = 'nw',
+ label_text = 'Current Path:',
+ )
+
+ #self.createlabel(self.workframe, childCols = 1, childRows = 3)
+
+ self.buttonbox = self.createcomponent('buttonbox', (), None,
+ Pmw.ButtonBox,
+ (self.buttonframe,),
+ )
+ self.buttonbox.add('OK', text = 'OK',
+ command = self.okbutton)
+ self.buttonbox.add('Cancel', text = 'Cancel',
+ command = self.cancelbutton)
+ self.buttonbox.add('New Directory', text = 'New Directory',
+ command = self.newdirbutton)
+
+ self.buttonbox.alignbuttons()
+ self.buttonbox.pack(expand = 1, fill = 'x')
+
+ self.optbox.grid(row = 2, column = 2, sticky = 'ew')
+ self.listbox.grid(row = 3, column = 2, sticky = 'news')
+ self.entry.grid(row = 5, column = 2, sticky = 'ew')
+ self.workframe.grid_rowconfigure(3, weight = 1)
+ self.workframe.grid_rowconfigure(4, minsize = 20)
+ self.workframe.grid_columnconfigure(2, weight = 1)
+
+
+ self.setpath(self['path'])
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def setpath(self, path):
+ path = os.path.abspath(os.path.expanduser(path))
+
+ if os.path.isfile(path):
+ path = os.path.dirname(path)
+
+ dirlist = []
+ hidedotfiles = self['hidedotfiles']
+ try:
+ posix = (os.name == 'posix')
+ for entry in os.listdir(path):
+ entryPath = path + '/' + entry
+ if hidedotfiles and entry[0] == '.':
+ # skip dot files if desired
+ continue
+ if not os.path.isdir(entryPath):
+ # skip files
+ continue
+ if not os.access(entryPath, os.R_OK | os.X_OK):
+ # skip directories we can't enter any way
+ continue
+ dirlist.append(entry)
+
+ except:
+ self.entry.setentry(self['path'])
+ return
+
+ self.entry.setentry(path)
+
+ self['path'] = path
+
+ dirlist.sort()
+ if path != '/':
+ dirlist.insert(0, '..')
+
+ self.listbox.setlist(dirlist)
+ pathlist = []
+ while path != '/':
+ pathlist.append(path)
+ path = os.path.dirname(path)
+ pathlist.append('/')
+ self.optbox.setitems(pathlist, 0)
+
+ def _setMinimumSize(self, event):
+ # If the optionmenu changes width, make sure it does not
+ # shrink later.
+ owidth = self.optbox.winfo_width()
+ self.workframe.grid_columnconfigure(2, minsize = owidth)
+
+ def _select(self):
+ sel = self.listbox.getcurselection()
+ if self['path'] == '/':
+ self['path'] = ''
+ if len(sel) > 0:
+ if sel[0] == '..':
+ self.setpath(os.path.dirname(self['path']))
+ else:
+ self.setpath(self['path'] + '/' + sel[0])
+
+
+ def getcurpath(self):
+ return self['path']
+
+ def enteredpath(self):
+ self.setpath(self.entry.get())
+
+ def okbutton(self):
+ self.deactivate(self['path'])
+
+ def cancelbutton(self):
+ self.deactivate(None)
+
+ def newdirbutton(self):
+ CreateDirectoryPopup(self.interior(), self['path'])
+ self.setpath(self['path'])
+
+
+
+class CreateDirectoryPopup:
+ def __init__(self, parent, path):
+ self.path = path
+ self.parent = parent
+ self.newdirpopup = Pmw.PromptDialog(parent,
+ buttons = ('OK', 'Cancel'),
+ defaultbutton = 'OK',
+ title = 'New Directory',
+ entryfield_labelpos = 'nw',
+ label_text = 'Enter new directory name for:\n%s'%self.path,
+ command = self._buttonpress
+ )
+
+ self.newdirpopup.activate()
+
+ def _buttonpress(self, button):
+ if button == 'OK':
+ newdirname = self.newdirpopup.get()
+ dirlist = os.listdir(self.path)
+ if newdirname in dirlist:
+ ErrorPopup(self.parent,
+ 'Error: "%s", already exists as a file or directory.'%newdirname)
+ else:
+ try:
+ os.mkdir(self.path + '/' + newdirname)
+ except:
+ ErrorPopup(self.parent,
+ 'Error: Could not create directory: "%s"'%newdirname)
+ else:
+ self.newdirpopup.deactivate()
+ else:
+ self.newdirpopup.deactivate()
+
+
+def ErrorPopup(parent, message):
+ error = Pmw.MessageDialog(parent, title = 'Error',
+ message_text = message,
+ defaultbutton = 0,
+ )
+ error.activate()
+
+if __name__ == '__main__':
+
+ rootWin = Tkinter.Tk()
+
+ Pmw.initialise()
+
+ rootWin.title('Directory Browser Dialog Demo')
+
+ def buildBrowser():
+ # Create the hierarchical directory browser widget
+ dirBrowserDialog = DirBrowserDialog(rootWin,
+ #labelpos = 'nw',
+ label = 'Select a directory',
+ title = 'Directory Selector',
+ #path = '~',
+ #hidedotfiles = 0,
+ )
+ dir = dirBrowserDialog.activate()
+ print 'Selected Directory:', dir
+
+ dirButton = Tkinter.Button(rootWin, text="Browser", command=buildBrowser)
+ dirButton.pack(side = 'left', padx = 10, pady = 10)
+
+ exitButton = Tkinter.Button(rootWin, text="Quit", command=rootWin.quit)
+ exitButton.pack(side = 'left', padx = 10, pady = 10)
+
+ rootWin.mainloop()
--- /dev/null
+#
+# FILE: MCListbox.py
+#
+# DESCRIPTION:
+# This file provides a generic Multi-Column Listbox widget. It is derived
+# from a heavily hacked version of Pmw.ScrolledFrame
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+# NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+import string
+import Tkinter
+import Pmw
+
+class MultiColumnListbox(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+ colors = Pmw.Color.getdefaultpalette(parent)
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ #('borderframe', 1, INITOPT),
+ ('horizflex', 'fixed', self._horizflex),
+ ('horizfraction', 0.05, INITOPT),
+ ('hscrollmode', 'dynamic', self._hscrollMode),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('scrollmargin', 2, INITOPT),
+ ('usehullsize', 0, INITOPT),
+ ('vertflex', 'fixed', self._vertflex),
+ ('vertfraction', 0.05, INITOPT),
+ ('vscrollmode', 'dynamic', self._vscrollMode),
+ ('labellist', None, INITOPT),
+ ('selectbackground', colors['selectBackground'], INITOPT),
+ ('selectforeground', colors['selectForeground'], INITOPT),
+ ('background', colors['background'], INITOPT),
+ ('foreground', colors['foreground'], INITOPT),
+ ('command', None, None),
+ ('dblclickcommand', None, None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ self._numcolumns = len(self['labellist'])
+ self._columnlabels = self['labellist']
+ self._lineid = 0
+ self._numrows = 0
+ self._lineitemframes = []
+ self._lineitems = []
+ self._lineitemdata = {}
+ self._labelframe = {}
+ self._cursel = []
+
+ # Create the components.
+ self.origInterior = Pmw.MegaWidget.interior(self)
+
+ if self['usehullsize']:
+ self.origInterior.grid_propagate(0)
+
+ # Create a frame widget to act as the border of the clipper.
+ self._borderframe = self.createcomponent('borderframe',
+ (), None,
+ Tkinter.Frame,
+ (self.origInterior,),
+ relief = 'sunken',
+ borderwidth = 2,
+ )
+ self._borderframe.grid(row = 2, column = 2,
+ rowspan = 2, sticky = 'news')
+
+ # Create the clipping windows.
+ self._hclipper = self.createcomponent('hclipper',
+ (), None,
+ Tkinter.Frame,
+ (self._borderframe,),
+ width = 400,
+ height = 300,
+ )
+ self._hclipper.pack(fill = 'both', expand = 1)
+
+ self._hsframe = self.createcomponent('hsframe', (), None,
+ Tkinter.Frame,
+ (self._hclipper,),
+ )
+
+
+ self._vclipper = self.createcomponent('vclipper',
+ (), None,
+ Tkinter.Frame,
+ (self._hsframe,),
+ #width = 400,
+ #height = 300,
+ highlightthickness = 0,
+ borderwidth = 0,
+ )
+
+ self._vclipper.grid(row = 1, column = 0,
+ columnspan = self._numcolumns,
+ sticky = 'news')#, expand = 1)
+ self._hsframe.grid_rowconfigure(1, weight = 1)#, minsize = 300)
+
+
+ gridcolumn = 0
+ for labeltext in self._columnlabels:
+ lframe = self.createcomponent(labeltext+'frame', (), None,
+ Tkinter.Frame,
+ (self._hsframe,),
+ borderwidth = 1,
+ relief = 'raised',
+ )
+ label = self.createcomponent(labeltext, (), None,
+ Tkinter.Label,
+ (lframe,),
+ text = labeltext,
+ )
+ label.pack(expand = 0, fill = 'y', side = 'left')
+ lframe.grid(row = 0, column = gridcolumn, sticky = 'ews')
+ self._labelframe[labeltext] = lframe
+ #lframe.update()
+ #print lframe.winfo_reqwidth()
+ self._hsframe.grid_columnconfigure(gridcolumn, weight = 1)
+ gridcolumn = gridcolumn + 1
+
+ lframe.update()
+ self._labelheight = lframe.winfo_reqheight()
+ self.origInterior.grid_rowconfigure(2, minsize = self._labelheight + 2)
+
+ self.origInterior.grid_rowconfigure(3, weight = 1, minsize = 0)
+ self.origInterior.grid_columnconfigure(2, weight = 1, minsize = 0)
+
+ # Create the horizontal scrollbar
+ self._horizScrollbar = self.createcomponent('horizscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar,
+ (self.origInterior,),
+ orient='horizontal',
+ command=self._xview
+ )
+
+ # Create the vertical scrollbar
+ self._vertScrollbar = self.createcomponent('vertscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar,
+ (self.origInterior,),
+ #(self._hclipper,),
+ orient='vertical',
+ command=self._yview
+ )
+
+ self.createlabel(self.origInterior, childCols = 3, childRows = 4)
+
+ # Initialise instance variables.
+ self._horizScrollbarOn = 0
+ self._vertScrollbarOn = 0
+ self.scrollTimer = None
+ self._scrollRecurse = 0
+ self._horizScrollbarNeeded = 0
+ self._vertScrollbarNeeded = 0
+ self.startX = 0
+ self.startY = 0
+ self._flexoptions = ('fixed', 'expand', 'shrink', 'elastic')
+
+ # Create a frame in the clipper to contain the widgets to be
+ # scrolled.
+ self._vsframe = self.createcomponent('vsframe',
+ (), None,
+ Tkinter.Frame,
+ (self._vclipper,),
+ #height = 300,
+ #borderwidth = 4,
+ #relief = 'groove',
+ )
+
+ # Whenever the clipping window or scrolled frame change size,
+ # update the scrollbars.
+ self._hsframe.bind('<Configure>', self._reposition)
+ self._vsframe.bind('<Configure>', self._reposition)
+ self._hclipper.bind('<Configure>', self._reposition)
+ self._vclipper.bind('<Configure>', self._reposition)
+
+ #elf._vsframe.bind('<Button-1>', self._vsframeselect)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self.scrollTimer is not None:
+ self.after_cancel(self.scrollTimer)
+ self.scrollTimer = None
+ Pmw.MegaWidget.destroy(self)
+
+ # ======================================================================
+
+ # Public methods.
+
+ def interior(self):
+ return self._vsframe
+
+ # Set timer to call real reposition method, so that it is not
+ # called multiple times when many things are reconfigured at the
+ # same time.
+ def reposition(self):
+ if self.scrollTimer is None:
+ self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+
+
+ def insertrow(self, index, rowdata):
+ #if len(rowdata) != self._numcolumns:
+ # raise ValueError, 'Number of items in rowdata does not match number of columns.'
+ if index > self._numrows:
+ index = self._numrows
+
+ rowframes = {}
+ for columnlabel in self._columnlabels:
+ celldata = rowdata.get(columnlabel)
+ cellframe = self.createcomponent(('cellframeid.%d.%s'%(self._lineid,
+ columnlabel)),
+ (), ('Cellframerowid.%d'%self._lineid),
+ Tkinter.Frame,
+ (self._vsframe,),
+ background = self['background'],
+ #borderwidth = 1,
+ #relief = 'flat'
+ )
+
+ cellframe.bind('<Double-Button-1>', self._cellframedblclick)
+ cellframe.bind('<Button-1>', self._cellframeselect)
+
+ if celldata:
+ cell = self.createcomponent(('cellid.%d.%s'%(self._lineid,
+ columnlabel)),
+ (), ('Cellrowid.%d'%self._lineid),
+ Tkinter.Label,
+ (cellframe,),
+ background = self['background'],
+ foreground = self['foreground'],
+ text = celldata,
+ )
+
+ cell.bind('<Double-Button-1>', self._celldblclick)
+ cell.bind('<Button-1>', self._cellselect)
+
+ cell.pack(expand = 0, fill = 'y', side = 'left', padx = 1, pady = 1)
+ rowframes[columnlabel] = cellframe
+
+ self._lineitemdata[self._lineid] = rowdata
+ self._lineitems.insert(index, self._lineid)
+ self._lineitemframes.insert(index, rowframes)
+ self._numrows = self._numrows + 1
+ self._lineid = self._lineid + 1
+
+ self._placedata(index)
+
+ def _placedata(self, index = 0):
+ gridy = index
+ for rowframes in self._lineitemframes[index:]:
+ gridx = 0
+ for columnlabel in self._columnlabels:
+ rowframes[columnlabel].grid(row = gridy,
+ column = gridx,
+ sticky = 'news')
+ gridx = gridx + 1
+ gridy = gridy + 1
+
+
+
+ def addrow(self, rowdata):
+ self.insertrow(self._numrows, rowdata)
+
+ def delrow(self, index):
+ rowframes = self._lineitemframes.pop(index)
+ for columnlabel in self._columnlabels:
+ rowframes[columnlabel].destroy()
+ self._placedata(index)
+ self._numrows = self._numrows - 1
+ del self._lineitems[index]
+ if index in self._cursel:
+ self._cursel.remove(index)
+
+
+ def curselection(self):
+ # Return a tuple of just one element as this will probably be the
+ # interface used in a future implementation when multiple rows can
+ # be selected at once.
+ return tuple(self._cursel)
+
+ def getcurselection(self):
+ # Return a tuple of just one row as this will probably be the
+ # interface used in a future implementation when multiple rows can
+ # be selected at once.
+ sellist = []
+ for sel in self._cursel:
+ sellist.append(self._lineitemdata[self._lineitems[sel]])
+ return tuple(sellist)
+
+ # ======================================================================
+
+ # Configuration methods.
+
+ def _hscrollMode(self):
+ # The horizontal scroll mode has been configured.
+
+ mode = self['hscrollmode']
+
+
+ if mode == 'static':
+ if not self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'none':
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ def _vscrollMode(self):
+ # The vertical scroll mode has been configured.
+
+ mode = self['vscrollmode']
+
+ if mode == 'static':
+ if not self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'none':
+ if self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ else:
+ message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ def _horizflex(self):
+ # The horizontal flex mode has been configured.
+
+ flex = self['horizflex']
+
+ if flex not in self._flexoptions:
+ message = 'bad horizflex option "%s": should be one of %s' % \
+ mode, str(self._flexoptions)
+ raise ValueError, message
+
+ self.reposition()
+
+ def _vertflex(self):
+ # The vertical flex mode has been configured.
+
+ flex = self['vertflex']
+
+ if flex not in self._flexoptions:
+ message = 'bad vertflex option "%s": should be one of %s' % \
+ mode, str(self._flexoptions)
+ raise ValueError, message
+
+ self.reposition()
+
+
+
+ # ======================================================================
+
+ # Private methods.
+
+ def _reposition(self, event):
+ gridx = 0
+ for col in self._columnlabels:
+ maxwidth = self._labelframe[col].winfo_reqwidth()
+ for row in self._lineitemframes:
+ cellwidth = row[col].winfo_reqwidth()
+ if cellwidth > maxwidth:
+ maxwidth = cellwidth
+ self._hsframe.grid_columnconfigure(gridx, minsize = maxwidth)
+ gridwidth = self._hsframe.grid_bbox(column = gridx, row = 0)[2]
+ if self['horizflex'] in ('expand', 'elastic') and gridwidth > maxwidth:
+ maxwidth = gridwidth
+ self._vsframe.grid_columnconfigure(gridx, minsize = maxwidth)
+ gridx = gridx + 1
+
+
+
+ self._vclipper.configure(height = self._hclipper.winfo_height() - self._labelheight)
+
+ self.reposition()
+
+ # Called when the user clicks in the horizontal scrollbar.
+ # Calculates new position of frame then calls reposition() to
+ # update the frame and the scrollbar.
+ def _xview(self, mode, value, units = None):
+
+ if mode == 'moveto':
+ frameWidth = self._hsframe.winfo_reqwidth()
+ self.startX = string.atof(value) * float(frameWidth)
+ else:
+ clipperWidth = self._hclipper.winfo_width()
+ if units == 'units':
+ jump = int(clipperWidth * self['horizfraction'])
+ else:
+ jump = clipperWidth
+
+ if value == '1':
+ self.startX = self.startX + jump
+ else:
+ self.startX = self.startX - jump
+
+ self.reposition()
+
+ # Called when the user clicks in the vertical scrollbar.
+ # Calculates new position of frame then calls reposition() to
+ # update the frame and the scrollbar.
+ def _yview(self, mode, value, units = None):
+
+ if mode == 'moveto':
+ frameHeight = self._vsframe.winfo_reqheight()
+ self.startY = string.atof(value) * float(frameHeight)
+ else:
+ clipperHeight = self._vclipper.winfo_height()
+ if units == 'units':
+ jump = int(clipperHeight * self['vertfraction'])
+ else:
+ jump = clipperHeight
+
+ if value == '1':
+ self.startY = self.startY + jump
+ else:
+ self.startY = self.startY - jump
+
+ self.reposition()
+
+ def _getxview(self):
+
+ # Horizontal dimension.
+ clipperWidth = self._hclipper.winfo_width()
+ frameWidth = self._hsframe.winfo_reqwidth()
+ if frameWidth <= clipperWidth:
+ # The scrolled frame is smaller than the clipping window.
+
+ self.startX = 0
+ endScrollX = 1.0
+
+ if self['horizflex'] in ('expand', 'elastic'):
+ relwidth = 1
+ else:
+ relwidth = ''
+ else:
+ # The scrolled frame is larger than the clipping window.
+
+ if self['horizflex'] in ('shrink', 'elastic'):
+ self.startX = 0
+ endScrollX = 1.0
+ relwidth = 1
+ else:
+ if self.startX + clipperWidth > frameWidth:
+ self.startX = frameWidth - clipperWidth
+ endScrollX = 1.0
+ else:
+ if self.startX < 0:
+ self.startX = 0
+ endScrollX = (self.startX + clipperWidth) / float(frameWidth)
+ relwidth = ''
+
+ # Position frame relative to clipper.
+ self._hsframe.place(x = -self.startX, relwidth = relwidth)
+ return (self.startX / float(frameWidth), endScrollX)
+
+ def _getyview(self):
+
+ # Vertical dimension.
+ clipperHeight = self._vclipper.winfo_height()
+ frameHeight = self._vsframe.winfo_reqheight()
+ if frameHeight <= clipperHeight:
+ # The scrolled frame is smaller than the clipping window.
+
+ self.startY = 0
+ endScrollY = 1.0
+
+ if self['vertflex'] in ('expand', 'elastic'):
+ relheight = 1
+ else:
+ relheight = ''
+ else:
+ # The scrolled frame is larger than the clipping window.
+
+ if self['vertflex'] in ('shrink', 'elastic'):
+ self.startY = 0
+ endScrollY = 1.0
+ relheight = 1
+ else:
+ if self.startY + clipperHeight > frameHeight:
+ self.startY = frameHeight - clipperHeight
+ endScrollY = 1.0
+ else:
+ if self.startY < 0:
+ self.startY = 0
+ endScrollY = (self.startY + clipperHeight) / float(frameHeight)
+ relheight = ''
+
+ # Position frame relative to clipper.
+ self._vsframe.place(y = -self.startY, relheight = relheight)
+ return (self.startY / float(frameHeight), endScrollY)
+
+ # According to the relative geometries of the frame and the
+ # clipper, reposition the frame within the clipper and reset the
+ # scrollbars.
+ def _scrollBothNow(self):
+ self.scrollTimer = None
+
+ # Call update_idletasks to make sure that the containing frame
+ # has been resized before we attempt to set the scrollbars.
+ # Otherwise the scrollbars may be mapped/unmapped continuously.
+ self._scrollRecurse = self._scrollRecurse + 1
+ self.update_idletasks()
+ self._scrollRecurse = self._scrollRecurse - 1
+ if self._scrollRecurse != 0:
+ return
+
+ xview = self._getxview()
+ yview = self._getyview()
+ self._horizScrollbar.set(xview[0], xview[1])
+ self._vertScrollbar.set(yview[0], yview[1])
+
+ self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+ self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+ # If both horizontal and vertical scrollmodes are dynamic and
+ # currently only one scrollbar is mapped and both should be
+ # toggled, then unmap the mapped scrollbar. This prevents a
+ # continuous mapping and unmapping of the scrollbars.
+ if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+ self._horizScrollbarNeeded != self._horizScrollbarOn and
+ self._vertScrollbarNeeded != self._vertScrollbarOn and
+ self._vertScrollbarOn != self._horizScrollbarOn):
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ self._toggleVertScrollbar()
+ return
+
+ if self['hscrollmode'] == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ def _toggleHorizScrollbar(self):
+
+ self._horizScrollbarOn = not self._horizScrollbarOn
+
+ interior = self.origInterior
+ if self._horizScrollbarOn:
+ self._horizScrollbar.grid(row = 5, column = 2, sticky = 'news')
+ interior.grid_rowconfigure(4, minsize = self['scrollmargin'])
+ else:
+ self._horizScrollbar.grid_forget()
+ interior.grid_rowconfigure(4, minsize = 0)
+
+ def _toggleVertScrollbar(self):
+
+ self._vertScrollbarOn = not self._vertScrollbarOn
+
+ interior = self.origInterior
+ if self._vertScrollbarOn:
+ self._vertScrollbar.grid(row = 3, column = 4, sticky = 'news')
+ interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
+ else:
+ self._vertScrollbar.grid_forget()
+ interior.grid_columnconfigure(3, minsize = 0)
+
+ # ======================================================================
+
+ # Selection methods.
+
+ #def _vsframeselect(self, event):
+ # print 'vsframe event x: %d y: %d'%(event.x, event.y)
+ # col, row = self._vsframe.grid_location(event.x, event.y)
+ # self._select(col, row)
+
+ def _cellframeselect(self, event):
+ #print 'cellframe event x: %d y: %d'%(event.x, event.y)
+ x = event.widget.winfo_x()
+ y = event.widget.winfo_y()
+ #col, row = self._vsframe.grid_location(x + event.x, y + event.y)
+ self._select(x + event.x, y + event.y)#(col, row)
+
+ def _cellselect(self, event):
+ #print 'cell event x: %d y: %d'%(event.x, event.y)
+ lx = event.widget.winfo_x()
+ ly = event.widget.winfo_y()
+ parent = event.widget.pack_info()['in']
+ fx = parent.winfo_x()
+ fy = parent.winfo_y()
+ #col, row = self._vsframe.grid_location(fx + lx + event.x, fy + ly + event.y)
+ self._select(fx + lx + event.x, fy + ly + event.y)#(col, row)
+
+ def _select(self, x, y):
+ col, row = self._vsframe.grid_location(x, y)
+ #print 'Clicked on col: %d row: %d'%(col,row)
+ cfg = {}
+ lineid = self._lineitems[row]
+ cfg['Cellrowid.%d_foreground'%lineid] = self['selectforeground']
+ cfg['Cellrowid.%d_background'%lineid] = self['selectbackground']
+ cfg['Cellframerowid.%d_background'%lineid] = self['selectbackground']
+ #cfg['Cellframerowid%d_relief'%row] = 'raised'
+
+ if self._cursel != []:
+ cursel = self._cursel[0]
+ lineid = self._lineitems[cursel]
+ if cursel != None and cursel != row:
+ cfg['Cellrowid.%d_foreground'%lineid] = self['foreground']
+ cfg['Cellrowid.%d_background'%lineid] = self['background']
+ cfg['Cellframerowid.%d_background'%lineid] = self['background']
+ #cfg['Cellframerowid%d_relief'%cursel] = 'flat'
+
+ apply(self.configure, (), cfg)
+ self._cursel = [row]
+
+ cmd = self['command']
+ if callable(cmd):
+ cmd()
+
+
+
+ def _cellframedblclick(self, event):
+ #print 'double click cell frame'
+ cmd = self['dblclickcommand']
+ if callable(cmd):
+ cmd()
+
+ def _celldblclick(self, event):
+ #print 'double click cell'
+ cmd = self['dblclickcommand']
+ if callable(cmd):
+ cmd()
+
+if __name__ == '__main__':
+
+ rootWin = Tkinter.Tk()
+
+ Pmw.initialise()
+
+ rootWin.title('MultiColumnListbox Demo')
+ rootWin.configure(width = 500, height = 300)
+ rootWin.update()
+
+ def dbl():
+ print listbox.getcurselection()
+
+ listbox = MultiColumnListbox(rootWin,
+ #usehullsize = 1,
+ labellist = ('Column 0',
+ 'Column 1',
+ 'Column 2',
+ 'Column 3',
+ 'Column 4',
+ #'Column 5',
+ #'Column 6',
+ #'Column 7',
+ #'Column 8',
+ #'Column 9',
+ ),
+ horizflex = 'expand',
+ #vertflex = 'elastic',
+ dblclickcommand = dbl,
+ )
+
+
+ #print 'start adding item'
+ for i in range(20):
+ r = {}
+ for j in range(5):
+ r[('Column %d'%j)] = 'Really long item name %d'%i
+ listbox.addrow(r)
+ #print 'items added'
+
+ listbox.pack(expand = 1, fill = 'both', padx = 10, pady = 10)
+
+
+ exitButton = Tkinter.Button(rootWin, text="Quit", command=rootWin.quit)
+ exitButton.pack(side = 'left', padx = 10, pady = 10)
+
+ rootWin.mainloop()
--- /dev/null
+#
+__version__ = '$Id: PmwFileDialog.py,v 1.2 2002/08/23 15:03:35 gregm Exp $'
+#
+# Filename dialogs using Pmw
+#
+# (C) Rob W.W. Hooft, Nonius BV, 1998
+#
+# Modifications:
+#
+# J. Willem M. Nissink, Cambridge Crystallographic Data Centre, 8/2002
+# Added optional information pane at top of dialog; if option
+# 'info' is specified, the text given will be shown (in blue).
+# Modified example to show both file and directory-type dialog
+#
+# No Guarantees. Distribute Freely.
+# Please send bug-fixes/patches/features to <r.hooft@euromail.com>
+#
+################################################################################
+import os,fnmatch,time
+import Tkinter,Pmw
+#Pmw.setversion("0.8.5")
+
+def _errorpop(master,text):
+ d=Pmw.MessageDialog(master,
+ title="Error",
+ message_text=text,
+ buttons=("OK",))
+ d.component('message').pack(ipadx=15,ipady=15)
+ d.activate()
+ d.destroy()
+
+class PmwFileDialog(Pmw.Dialog):
+ """File Dialog using Pmw"""
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ optiondefs = (
+ ('filter', '*', self.newfilter),
+ ('directory', os.getcwd(), self.newdir),
+ ('filename', '', self.newfilename),
+ ('historylen',10, None),
+ ('command', None, None),
+ ('info', None, None),
+ )
+ self.defineoptions(kw, optiondefs)
+ # Initialise base class (after defining options).
+ Pmw.Dialog.__init__(self, parent)
+
+ self.withdraw()
+
+ # Create the components.
+ interior = self.interior()
+
+ if self['info'] is not None:
+ rowoffset=1
+ dn = self.infotxt()
+ dn.grid(row=0,column=0,columnspan=2,padx=3,pady=3)
+ else:
+ rowoffset=0
+
+ dn = self.mkdn()
+ dn.grid(row=0+rowoffset,column=0,columnspan=2,padx=3,pady=3)
+ del dn
+
+ # Create the directory list component.
+ dnb = self.mkdnb()
+ dnb.grid(row=1+rowoffset,column=0,sticky='news',padx=3,pady=3)
+ del dnb
+
+ # Create the filename list component.
+ fnb = self.mkfnb()
+ fnb.grid(row=1+rowoffset,column=1,sticky='news',padx=3,pady=3)
+ del fnb
+
+ # Create the filter entry
+ ft = self.mkft()
+ ft.grid(row=2+rowoffset,column=0,columnspan=2,padx=3,pady=3)
+ del ft
+
+ # Create the filename entry
+ fn = self.mkfn()
+ fn.grid(row=3+rowoffset,column=0,columnspan=2,padx=3,pady=3)
+ fn.bind('<Return>',self.okbutton)
+ del fn
+
+ # Buttonbox already exists
+ bb=self.component('buttonbox')
+ bb.add('OK',command=self.okbutton)
+ bb.add('Cancel',command=self.cancelbutton)
+ del bb
+
+ Pmw.alignlabels([self.component('filename'),
+ self.component('filter'),
+ self.component('dirname')])
+
+ def infotxt(self):
+ """ Make information block component at the top """
+ return self.createcomponent(
+ 'infobox',
+ (), None,
+ Tkinter.Label, (self.interior(),),
+ width=51,
+ relief='groove',
+ foreground='darkblue',
+ justify='left',
+ text=self['info']
+ )
+
+ def mkdn(self):
+ """Make directory name component"""
+ return self.createcomponent(
+ 'dirname',
+ (), None,
+ Pmw.ComboBox, (self.interior(),),
+ entryfield_value=self['directory'],
+ entryfield_entry_width=40,
+ entryfield_validate=self.dirvalidate,
+ selectioncommand=self.setdir,
+ labelpos='w',
+ label_text='Directory:')
+
+ def mkdnb(self):
+ """Make directory name box"""
+ return self.createcomponent(
+ 'dirnamebox',
+ (), None,
+ Pmw.ScrolledListBox, (self.interior(),),
+ label_text='directories',
+ labelpos='n',
+ hscrollmode='none',
+ dblclickcommand=self.selectdir)
+
+ def mkft(self):
+ """Make filter"""
+ return self.createcomponent(
+ 'filter',
+ (), None,
+ Pmw.ComboBox, (self.interior(),),
+ entryfield_value=self['filter'],
+ entryfield_entry_width=40,
+ selectioncommand=self.setfilter,
+ labelpos='w',
+ label_text='Filter:')
+
+ def mkfnb(self):
+ """Make filename list box"""
+ return self.createcomponent(
+ 'filenamebox',
+ (), None,
+ Pmw.ScrolledListBox, (self.interior(),),
+ label_text='files',
+ labelpos='n',
+ hscrollmode='none',
+ selectioncommand=self.singleselectfile,
+ dblclickcommand=self.selectfile)
+
+ def mkfn(self):
+ """Make file name entry"""
+ return self.createcomponent(
+ 'filename',
+ (), None,
+ Pmw.ComboBox, (self.interior(),),
+ entryfield_value=self['filename'],
+ entryfield_entry_width=40,
+ entryfield_validate=self.filevalidate,
+ selectioncommand=self.setfilename,
+ labelpos='w',
+ label_text='Filename:')
+
+ def dirvalidate(self,string):
+ if os.path.isdir(string):
+ return Pmw.OK
+ else:
+ return Pmw.PARTIAL
+
+ def filevalidate(self,string):
+ if string=='':
+ return Pmw.PARTIAL
+ elif os.path.isfile(string):
+ return Pmw.OK
+ elif os.path.exists(string):
+ return Pmw.PARTIAL
+ else:
+ return Pmw.OK
+
+ def okbutton(self):
+ """OK action: user thinks he has input valid data and wants to
+ proceed. This is also called by <Return> in the filename entry"""
+ fn=self.component('filename').get()
+ self.setfilename(fn)
+ if self.validate(fn):
+ self.canceled=0
+ self.deactivate()
+
+ def cancelbutton(self):
+ """Cancel the operation"""
+ self.canceled=1
+ self.deactivate()
+
+ def tidy(self,w,v):
+ """Insert text v into the entry and at the top of the list of
+ the combobox w, remove duplicates"""
+ if not v:
+ return
+ entry=w.component('entry')
+ entry.delete(0,'end')
+ entry.insert(0,v)
+ list=w.component('scrolledlist')
+ list.insert(0,v)
+ index=1
+ while index<list.index('end'):
+ k=list.get(index)
+ if k==v or index>self['historylen']:
+ list.delete(index)
+ else:
+ index=index+1
+ w.checkentry()
+
+ def setfilename(self,value):
+ if not value:
+ return
+ value=os.path.join(self['directory'],value)
+ dir,fil=os.path.split(value)
+ self.configure(directory=dir,filename=value)
+
+ c=self['command']
+ if callable(c):
+ c()
+
+ def newfilename(self):
+ """Make sure a newly set filename makes it into the combobox list"""
+ self.tidy(self.component('filename'),self['filename'])
+
+ def setfilter(self,value):
+ self.configure(filter=value)
+
+ def newfilter(self):
+ """Make sure a newly set filter makes it into the combobox list"""
+ self.tidy(self.component('filter'),self['filter'])
+ self.fillit()
+
+ def setdir(self,value):
+ self.configure(directory=value)
+
+ def newdir(self):
+ """Make sure a newly set dirname makes it into the combobox list"""
+ self.tidy(self.component('dirname'),self['directory'])
+ self.fillit()
+
+ def singleselectfile(self):
+ """Single click in file listbox. Move file to "filename" combobox"""
+ cs=self.component('filenamebox').curselection()
+ if cs!=():
+ value=self.component('filenamebox').get(cs)
+ self.setfilename(value)
+
+ def selectfile(self):
+ """Take the selected file from the filename, normalize it, and OK"""
+ self.singleselectfile()
+ value=self.component('filename').get()
+ self.setfilename(value)
+ if value:
+ self.okbutton()
+
+ def selectdir(self):
+ """Take selected directory from the dirnamebox into the dirname"""
+ cs=self.component('dirnamebox').curselection()
+ if cs!=():
+ value=self.component('dirnamebox').get(cs)
+ dir=self['directory']
+ if not dir:
+ dir=os.getcwd()
+ if value:
+ if value=='..':
+ dir=os.path.split(dir)[0]
+ else:
+ dir=os.path.join(dir,value)
+ self.configure(directory=dir)
+ self.fillit()
+
+ def askfilename(self,directory=None,filter=None):
+ """The actual client function. Activates the dialog, and
+ returns only after a valid filename has been entered
+ (return value is that filename) or when canceled (return
+ value is None)"""
+ if directory!=None:
+ self.configure(directory=directory)
+ if filter!=None:
+ self.configure(filter=filter)
+ self.fillit()
+ self.canceled=1 # Needed for when user kills dialog window
+ self.activate()
+ if self.canceled:
+ return None
+ else:
+ return self.component('filename').get()
+
+ lastdir=""
+ lastfilter=None
+ lasttime=0
+ def fillit(self):
+ """Get the directory list and show it in the two listboxes"""
+ # Do not run unnecesarily
+ if self.lastdir==self['directory'] and self.lastfilter==self['filter'] and self.lasttime>os.stat(self.lastdir)[8]:
+ return
+ self.lastdir=self['directory']
+ self.lastfilter=self['filter']
+ self.lasttime=time.time()
+ dir=self['directory']
+ if not dir:
+ dir=os.getcwd()
+ dirs=['..']
+ files=[]
+ try:
+ fl=os.listdir(dir)
+ fl.sort()
+ except os.error,arg:
+ if arg[0] in (2,20):
+ return
+ raise
+ for f in fl:
+ if os.path.isdir(os.path.join(dir,f)):
+ dirs.append(f)
+ else:
+ filter=self['filter']
+ if not filter:
+ filter='*'
+ if fnmatch.fnmatch(f,filter):
+ files.append(f)
+ self.component('filenamebox').setlist(files)
+ self.component('dirnamebox').setlist(dirs)
+
+ def validate(self,filename):
+ """Validation function. Should return 1 if the filename is valid,
+ 0 if invalid. May pop up dialogs to tell user why. Especially
+ suited to subclasses: i.e. only return 1 if the file does/doesn't
+ exist"""
+ return 1
+
+class PmwDirDialog(PmwFileDialog):
+ """Directory Dialog using Pmw"""
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ optiondefs = (
+ ('directory', os.getcwd(), self.newdir),
+ ('historylen',10, None),
+ ('command', None, None),
+ ('info', None, None),
+ )
+ self.defineoptions(kw, optiondefs)
+ # Initialise base class (after defining options).
+ Pmw.Dialog.__init__(self, parent)
+
+ self.withdraw()
+
+ # Create the components.
+ interior = self.interior()
+
+ if self['info'] is not None:
+ rowoffset=1
+ dn = self.infotxt()
+ dn.grid(row=0,column=0,columnspan=2,padx=3,pady=3)
+ else:
+ rowoffset=0
+
+ dn = self.mkdn()
+ dn.grid(row=1+rowoffset,column=0,columnspan=2,padx=3,pady=3)
+ dn.bind('<Return>',self.okbutton)
+ del dn
+
+ # Create the directory list component.
+ dnb = self.mkdnb()
+ dnb.grid(row=0+rowoffset,column=0,columnspan=2,sticky='news',padx=3,pady=3)
+ del dnb
+
+ # Buttonbox already exists
+ bb=self.component('buttonbox')
+ bb.add('OK',command=self.okbutton)
+ bb.add('Cancel',command=self.cancelbutton)
+ del bb
+
+ lastdir=""
+ def fillit(self):
+ """Get the directory list and show it in the two listboxes"""
+ # Do not run unnecesarily
+ if self.lastdir==self['directory']:
+ return
+ self.lastdir=self['directory']
+ dir=self['directory']
+ if not dir:
+ dir=os.getcwd()
+ dirs=['..']
+ try:
+ fl=os.listdir(dir)
+ fl.sort()
+ except os.error,arg:
+ if arg[0] in (2,20):
+ return
+ raise
+ for f in fl:
+ if os.path.isdir(os.path.join(dir,f)):
+ dirs.append(f)
+ self.component('dirnamebox').setlist(dirs)
+
+ def okbutton(self):
+ """OK action: user thinks he has input valid data and wants to
+ proceed. This is also called by <Return> in the dirname entry"""
+ fn=self.component('dirname').get()
+ self.configure(directory=fn)
+ if self.validate(fn):
+ self.canceled=0
+ self.deactivate()
+
+ def askfilename(self,directory=None):
+ """The actual client function. Activates the dialog, and
+ returns only after a valid filename has been entered
+ (return value is that filename) or when canceled (return
+ value is None)"""
+ if directory!=None:
+ self.configure(directory=directory)
+ self.fillit()
+ self.activate()
+ if self.canceled:
+ return None
+ else:
+ return self.component('dirname').get()
+
+ def dirvalidate(self,string):
+ if os.path.isdir(string):
+ return Pmw.OK
+ elif os.path.exists(string):
+ return Pmw.PARTIAL
+ else:
+ return Pmw.OK
+
+ def validate(self,filename):
+ """Validation function. Should return 1 if the filename is valid,
+ 0 if invalid. May pop up dialogs to tell user why. Especially
+ suited to subclasses: i.e. only return 1 if the file does/doesn't
+ exist"""
+ if filename=='':
+ _errorpop(self.interior(),"Empty filename")
+ return 0
+ if os.path.isdir(filename) or not os.path.exists(filename):
+ return 1
+ else:
+ _errorpop(self.interior(),"This is not a directory")
+ return 0
+
+class PmwExistingFileDialog(PmwFileDialog):
+ def filevalidate(self,string):
+ if os.path.isfile(string):
+ return Pmw.OK
+ else:
+ return Pmw.PARTIAL
+
+ def validate(self,filename):
+ if os.path.isfile(filename):
+ return 1
+ elif os.path.exists(filename):
+ _errorpop(self.interior(),"This is not a plain file")
+ return 0
+ else:
+ _errorpop(self.interior(),"Please select an existing file")
+ return 0
+
+class PmwExistingDirDialog(PmwDirDialog):
+ def dirvalidate(self,string):
+ if os.path.isdir(string):
+ return Pmw.OK
+ else:
+ return Pmw.PARTIAL
+
+ def validate(self,filename):
+ if os.path.isdir(filename):
+ return 1
+ elif os.path.exists(filename):
+ _errorpop(self.interior(),"This is not a directory")
+ return 0
+ else:
+ _errorpop(self.interior(),"Please select an existing directory")
+
+if __name__=="__main__":
+ root=Tkinter.Tk()
+ root.withdraw()
+ Pmw.initialise()
+
+ f0=PmwFileDialog(root)
+ f0.title('File name dialog')
+ n=f0.askfilename()
+ print '\nFilename : ',repr(n),'\n'
+
+ f1=PmwDirDialog(root,info='This is a directory dialog')
+ f1.title('Directory name dialog')
+ while 1:
+ n=f1.askfilename()
+ if n is None:
+ break
+ print "Dirname : ",repr(n)
--- /dev/null
+# Authors: Joe VanAndel, Greg McFarlane and Daniel Michelson
+
+import string
+import sys
+import time
+import Tkinter
+import Pmw
+
+class FullTimeCounter(Pmw.MegaWidget):
+ """Up-down counter
+
+ A TimeCounter is a single-line entry widget with Up and Down arrows
+ which increment and decrement the Time value in the entry.
+ """
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('autorepeat', 1, INITOPT),
+ ('buttonaspect', 1.0, INITOPT),
+ ('initwait', 300, INITOPT),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('max', '', self._max),
+ ('min', '', self._min),
+ ('padx', 0, INITOPT),
+ ('pady', 0, INITOPT),
+ ('repeatrate', 50, INITOPT),
+ ('value', '', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ self.arrowDirection = {}
+ self._flag = 'stopped'
+ self._timerId = None
+
+ self._createComponents()
+
+ value = self['value']
+ if value is None or value == '':
+ now = time.time()
+ value = time.strftime('%Y:%m:%d:%H:%M',time.gmtime(now))
+ self._setTimeFromStr(value)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _createComponents(self):
+
+ # Create the components.
+ interior = self.interior()
+
+ # If there is no label, put the arrows and the entry directly
+ # into the interior, otherwise create a frame for them. In
+ # either case the border around the arrows and the entry will
+ # be raised (but not around the label).
+ if self['labelpos'] is None:
+ frame = interior
+ else:
+ frame = self.createcomponent('frame',
+ (), None,
+ Tkinter.Frame, (interior,))
+ frame.grid(column=2, row=2, sticky='nsew')
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ frame.configure(relief = 'raised', borderwidth = 1)
+
+ # Create the down arrow buttons.
+
+ # Create the year down arrow.
+ self._downYearArrowBtn = self.createcomponent('downyeararrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downYearArrowBtn] = 0
+ self._downYearArrowBtn.grid(column = 0, row = 2)
+
+ # Create the month down arrow.
+ self._downMonthArrowBtn = self.createcomponent('downmontharrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downMonthArrowBtn] = 0
+ self._downMonthArrowBtn.grid(column = 1, row = 2)
+
+ # Create the day down arrow.
+ self._downDayArrowBtn = self.createcomponent('downdayarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downDayArrowBtn] = 0
+ self._downDayArrowBtn.grid(column = 2, row = 2)
+
+ # Create the hour down arrow.
+ self._downHourArrowBtn = self.createcomponent('downhourarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downHourArrowBtn] = 0
+ self._downHourArrowBtn.grid(column = 3, row = 2)
+
+ # Create the minute down arrow.
+ self._downMinuteArrowBtn = self.createcomponent('downminutearrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downMinuteArrowBtn] = 0
+ self._downMinuteArrowBtn.grid(column = 4, row = 2)
+
+ # Create the entry fields.
+
+ # Create the year entry field.
+ self._yearCounterEntry = self.createcomponent('yearentryfield',
+ (('yearentry', 'yearentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 4)
+ self._yearCounterEntry.grid(column = 0, row = 1, sticky = 'news')
+
+ # Create the month entry field.
+ self._monthCounterEntry = self.createcomponent('monthentryfield',
+ (('monthentry', 'monthentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 2)
+ self._monthCounterEntry.grid(column = 1, row = 1, sticky = 'news')
+
+ # Create the day entry field.
+ self._dayCounterEntry = self.createcomponent('dayentryfield',
+ (('dayentry', 'dayentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 2)
+ self._dayCounterEntry.grid(column = 2, row = 1, sticky = 'news')
+
+ # Create the hour entry field.
+ self._hourCounterEntry = self.createcomponent('hourentryfield',
+ (('hourentry', 'hourentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 2)
+ self._hourCounterEntry.grid(column = 3, row = 1, sticky = 'news')
+
+ # Create the minute entry field.
+ self._minuteCounterEntry = self.createcomponent('minuteentryfield',
+ (('minuteentry', 'minuteentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 2)
+ self._minuteCounterEntry.grid(column = 4, row = 1, sticky = 'news')
+
+ # Create the up arrow buttons.
+
+ # Create the year up arrow.
+ self._upYearArrowBtn = self.createcomponent('upyeararrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upYearArrowBtn] = 1
+ self._upYearArrowBtn.grid(column = 0, row = 0)
+
+ # Create the month up arrow.
+ self._upMonthArrowBtn = self.createcomponent('upmontharrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upMonthArrowBtn] = 1
+ self._upMonthArrowBtn.grid(column = 1, row = 0)
+
+ # Create the day up arrow.
+ self._upDayArrowBtn = self.createcomponent('updayarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upDayArrowBtn] = 1
+ self._upDayArrowBtn.grid(column = 2, row = 0)
+
+ # Create the hour up arrow.
+ self._upHourArrowBtn = self.createcomponent('uphourarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upHourArrowBtn] = 1
+ self._upHourArrowBtn.grid(column = 3, row = 0)
+
+ # Create the minute up arrow.
+ self._upMinuteArrowBtn = self.createcomponent('upminutearrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upMinuteArrowBtn] = 1
+ self._upMinuteArrowBtn.grid(column = 4, row = 0)
+
+ # Make it resize nicely.
+ padx = self['padx']
+ pady = self['pady']
+ for col in range(5): # YY, MM, DD, HH, mm
+ frame.grid_columnconfigure(col, weight = 1, pad = padx)
+ frame.grid_rowconfigure(0, pad = pady)
+ frame.grid_rowconfigure(2, pad = pady)
+
+ frame.grid_rowconfigure(1, weight = 1)
+
+ # Create the label.
+ self.createlabel(interior)
+
+ # Set bindings.
+
+ # Up year
+ self._upYearArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upYearArrowBtn:
+ s._drawArrow(button, 1))
+ self._upYearArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upYearArrowBtn:
+ s._countUp(button))
+ self._upYearArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upYearArrowBtn:
+ s._stopUpDown(button))
+
+ # Up month
+ self._upMonthArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upMonthArrowBtn:
+ s._drawArrow(button, 1))
+ self._upMonthArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upMonthArrowBtn:
+ s._countUp(button))
+ self._upMonthArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upMonthArrowBtn:
+ s._stopUpDown(button))
+
+ # Up day
+ self._upDayArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upDayArrowBtn:
+ s._drawArrow(button, 1))
+ self._upDayArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upDayArrowBtn:
+ s._countUp(button))
+ self._upDayArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upDayArrowBtn:
+ s._stopUpDown(button))
+
+ # Up hour
+ self._upHourArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upHourArrowBtn:
+ s._drawArrow(button, 1))
+ self._upHourArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upHourArrowBtn:
+ s._countUp(button))
+ self._upHourArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upHourArrowBtn:
+ s._stopUpDown(button))
+
+ # Up minute
+ self._upMinuteArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upMinuteArrowBtn:
+ s._drawArrow(button, 1))
+ self._upMinuteArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upMinuteArrowBtn:
+ s._countUp(button))
+ self._upMinuteArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upMinuteArrowBtn:
+ s._stopUpDown(button))
+
+
+ # Down year
+ self._downYearArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downYearArrowBtn:
+ s._drawArrow(button, 0))
+ self._downYearArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._downYearArrowBtn:
+ s._countDown(button))
+ self._downYearArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downYearArrowBtn:
+ s._stopUpDown(button))
+
+ # Down month
+ self._downMonthArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downMonthArrowBtn:
+ s._drawArrow(button, 0))
+ self._downMonthArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._downMonthArrowBtn:
+ s._countDown(button))
+ self._downMonthArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downMonthArrowBtn:
+ s._stopUpDown(button))
+
+ # Down day
+ self._downDayArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downDayArrowBtn:
+ s._drawArrow(button, 0))
+ self._downDayArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._downDayArrowBtn:
+ s._countDown(button))
+ self._downDayArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downDayArrowBtn:
+ s._stopUpDown(button))
+
+ # Down hour
+ self._downHourArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downHourArrowBtn:
+ s._drawArrow(button, 0))
+ self._downHourArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._downHourArrowBtn:
+ s._countDown(button))
+ self._downHourArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downHourArrowBtn:
+ s._stopUpDown(button))
+
+ # Down minute
+ self._downMinuteArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downMinuteArrowBtn:
+ s._drawArrow(button, 0))
+ self._downMinuteArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._downMinuteArrowBtn: s._countDown(button))
+ self._downMinuteArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downMinuteArrowBtn:
+ s._stopUpDown(button))
+
+
+ self._yearCounterEntry.bind('<Return>', self.invoke)
+ self._monthCounterEntry.bind('<Return>', self.invoke)
+ self._dayCounterEntry.bind('<Return>', self.invoke)
+ self._hourCounterEntry.bind('<Return>', self.invoke)
+ self._minuteCounterEntry.bind('<Return>', self.invoke)
+
+ self._yearCounterEntry.bind('<Configure>', self._resizeArrow)
+ self._monthCounterEntry.bind('<Configure>', self._resizeArrow)
+ self._dayCounterEntry.bind('<Configure>', self._resizeArrow)
+ self._hourCounterEntry.bind('<Configure>', self._resizeArrow)
+ self._minuteCounterEntry.bind('<Configure>', self._resizeArrow)
+
+ def _drawArrow(self, arrow, direction):
+ arrow.delete('arrow')
+
+ fg = self._yearCounterEntry.cget('entry_foreground')
+
+ bw = (string.atoi(arrow['borderwidth']) +
+ string.atoi(arrow['highlightthickness'])) / 2
+ h = string.atoi(arrow['height']) + 2 * bw
+ w = string.atoi(arrow['width']) + 2 * bw
+
+ if direction == 0:
+ # down arrow
+ arrow.create_polygon(
+ 0.25 * w + bw, 0.25 * h + bw,
+ 0.50 * w + bw, 0.75 * h + bw,
+ 0.75 * w + bw, 0.25 * h + bw,
+ fill=fg, tag='arrow')
+ else:
+ arrow.create_polygon(
+ 0.25 * w + bw, 0.75 * h + bw,
+ 0.50 * w + bw, 0.25 * h + bw,
+ 0.75 * w + bw, 0.75 * h + bw,
+ fill=fg, tag='arrow')
+
+ def _resizeArrow(self, event = None):
+ for btn in (self._upYearArrowBtn, self._upMonthArrowBtn,
+ self._upDayArrowBtn, self._upHourArrowBtn,
+ self._upMinuteArrowBtn, self._downYearArrowBtn,
+ self._downMonthArrowBtn, self._downDayArrowBtn,
+ self._downHourArrowBtn, self._downMinuteArrowBtn):
+ bw = (string.atoi(btn['borderwidth']) + \
+ string.atoi(btn['highlightthickness']))
+ newHeight = self._yearCounterEntry.winfo_reqheight() - 2 * bw
+ newWidth = newHeight * self['buttonaspect']
+ btn.configure(width=newWidth, height=newHeight)
+ self._drawArrow(btn, self.arrowDirection[btn])
+
+ def _min(self):
+ self._minVal = None
+
+ def _max(self):
+ self._maxVal = None
+
+ def _setTimeFromStr(self, str):
+ list = string.split(str, ':')
+ if len(list) != 5:
+ raise ValueError, 'invalid value: ' + str
+
+ self._year = string.atoi(list[0])
+ self._month = string.atoi(list[1])
+ self._day = string.atoi(list[2])
+ self._hour = string.atoi(list[3])
+ self._minute = string.atoi(list[4])
+
+ self._setHMS()
+
+ def getstring(self):
+ return '%04d:%02d:%02d:%02d:%02d' % (self._year, self._month,
+ self._day, self._hour,
+ self._minute)
+
+ def getint(self):
+ pass
+
+ def _countUp(self, button):
+ self._relief = self._upYearArrowBtn.cget('relief')
+ button.configure(relief='sunken')
+ if button == self._upYearArrowBtn: datetype = "year"
+ elif button == self._upMonthArrowBtn: datetype = "month"
+ elif button == self._upDayArrowBtn: datetype = "day"
+ elif button == self._upHourArrowBtn: datetype = "hour"
+ elif button == self._upMinuteArrowBtn: datetype = "minute"
+ self._count(1, datetype, 'start')
+
+ def _countDown(self, button):
+ self._relief = self._downYearArrowBtn.cget('relief')
+ button.configure(relief='sunken')
+ if button == self._downYearArrowBtn: datetype = "year"
+ elif button == self._downMonthArrowBtn: datetype = "month"
+ elif button == self._downDayArrowBtn: datetype = "day"
+ elif button == self._downHourArrowBtn: datetype = "hour"
+ elif button == self._downMinuteArrowBtn: datetype = "minute"
+ self._count(-1, datetype, 'start')
+
+ def _count(self, factor, datetype, newFlag=None):
+ if newFlag != 'force':
+ if newFlag is not None:
+ self._flag = newFlag
+
+ if self._flag == 'stopped':
+ return
+
+ if datetype == "year": self._year = self._year + factor
+ elif datetype == "month": self._month = self._month + factor
+ elif datetype == "day": self._day = self._day + factor
+ elif datetype == "hour": self._hour = self._hour + factor
+ elif datetype == "minute": self._minute = self._minute + factor
+ secs = time.mktime((self._year, self._month, self._day, self._hour,
+ self._minute, 0, 0, 0, -1))
+ tt = time.localtime(secs) # NOT gmtime!
+
+ self._year = tt[0]
+ self._month = tt[1]
+ self._day = tt[2]
+ self._hour = tt[3]
+ self._minute = tt[4]
+ self._setHMS()
+
+ if newFlag != 'force':
+ if self['autorepeat']:
+ if self._flag == 'start':
+ delay = self['initwait']
+ self._flag = 'running'
+ else:
+ delay = self['repeatrate']
+ self._timerId = self.after(
+ delay, lambda self=self, factor=factor, datetype=datetype:
+ self._count(factor, datetype, 'running'))
+
+ def _setHMS(self):
+ self._yearCounterEntry.setentry('%04d' % self._year)
+ self._monthCounterEntry.setentry('%02d' % self._month)
+ self._dayCounterEntry.setentry('%02d' % self._day)
+ self._hourCounterEntry.setentry('%02d' % self._hour)
+ self._minuteCounterEntry.setentry('%02d' % self._minute)
+
+ def _stopUpDown(self, button):
+ if self._timerId is not None:
+ self.after_cancel(self._timerId)
+ self._timerId = None
+ button.configure(relief=self._relief)
+ self._flag = 'stopped'
+
+ def invoke(self, event = None):
+ cmd = self['command']
+ if callable(cmd):
+ cmd()
+
+ def destroy(self):
+ if self._timerId is not None:
+ self.after_cancel(self._timerId)
+ self._timerId = None
+ Pmw.MegaWidget.destroy(self)
+
+if __name__=="__main__":
+
+ def showString():
+ stringVal = _time.getstring()
+ print stringVal
+
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title('FullTimeCounter')
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+
+ _time = FullTimeCounter(root,
+ labelpos = 'n',
+ label_text = 'YYYY:MM:DD:HH:mm')
+ _time.pack(fill = 'both', expand = 1, padx=10, pady=5)
+
+ button = Tkinter.Button(root, text = 'Show', command = showString)
+ button.pack()
+ root.mainloop()
--- /dev/null
+"""
+I needed a simple gauge, so I've made on with Pmw.
+It might be useful for others to use as a base to develop more comples
+gauges with.
+
+Is it worth cleaning up and submitting?
+
+cheers and thanks
+
+chris
+
+Dr. Chris Wright
+Intensive Care Unit
+Monash Medical Centre
+Clayton. VIC Australia
+"""
+
+import sys
+import Tkinter
+import Pmw
+import time
+
+
+if sys.platform == 'win32':
+ # MS-Windows specific fonts
+ label_font = "-family Ariel -size 12"
+ value_font = "-family Ariel -size 12"
+ small_font = "-family {MS Sans Serif} -size 9 -weight bold"
+ header_font = "-family {MS Sans Serif} -weight bold"
+else:
+ # X-Windows specific fonts
+ label_font = "-*-helvetica-*-r-*-*-*-160-*-*-*-*-*-*"
+ value_font = "-*-courier-*-r-*-*-*-160-*-*-*-*-*-*"
+ small_font = "-*-helvetica-*-r-*-*-*-130-*-*-*-*-*-*"
+ header_font = "-*-helvetica-bold-r-*-*-*-150-*-*-*-*-*-*"
+
+class VerticalGauge(Pmw.MegaWidget):
+ """Vertical gauge with actual and desired settings"""
+
+ def __init__(self, parent = None, **kw):
+ optiondefs = (
+ ('min', 0, None),
+ ('max', 100, None),
+ ('majortickinterval', 10, None),
+ ('minortickinterval', 5, None),
+ ('units', '', None),
+ ('bg', 'grey', self._backgroundSet),
+ ('actualvalue', 50, self._actualSet),
+ ('desiredvalue', 50, self._desiredSet),
+ ('actualcolour', 'yellow1', None),
+ ('desiredcolour', 'turquoise1', None),
+ ('label', 'Label', None),
+ )
+ self.defineoptions(kw, optiondefs)
+ Pmw.MegaWidget.__init__(self, parent)
+
+ interior = self.interior()
+ interior.grid_rowconfigure(1, weight = 1)
+ for r in range(3):
+ interior.grid_columnconfigure(r, weight = 1)
+
+ self.actuallabel = self.createcomponent('actualLabel',
+ (), None,
+ Tkinter.Label, (interior,),
+ text = '',
+ width = 3,
+ relief = 'sunken',
+ bd = 1,
+ fg = self['actualcolour'],
+ font = value_font)
+ self.actuallabel.grid(sticky = "nswe", row = 0, column = 0)
+
+ self.label = self.createcomponent('label',
+ (), None,
+ Tkinter.Label, (interior,),
+ text = self['label'],
+ relief = 'raised',
+ font = label_font,
+ fg = 'navy',
+ bd = 2)
+ self.label.grid(sticky = "nsew", row = 0, column = 1)
+
+ self.desiredlabel = self.createcomponent('desiredLabel',
+ (), None,
+ Tkinter.Label, (interior,),
+ text = '',
+ width = 3,
+ relief = 'sunken',
+ bd = 1,
+ fg = self['desiredcolour'],
+ font = value_font)
+ self.desiredlabel.grid(sticky = "nswe", row = 0, column = 2)
+
+ self.canvas = self.createcomponent('canvas',
+ (), None,
+ Tkinter.Canvas, (interior,),
+ width = 100,
+ height = 300,
+ bg = 'grey')
+
+ self.canvas.grid(sticky = "nsew", columnspan = 3, pady = 1)
+ self.canvas.bind("<Configure>", self._createGaugeAxes)
+
+ self._createGaugeAxes()
+
+ self.initialiseoptions()
+
+ def _createGaugeAxes(self, event = None):
+ min = self['min']
+ max = self['max']
+ units = self['units']
+ majortickinterval = self['majortickinterval']
+
+ gauge_range = max - min
+
+ c = self.canvas
+ c.delete("all")
+ if event:
+ h, w = event.height, event.width
+ else:
+ h = int(c.configure("height")[4])
+ w = int(c.configure("width")[4])
+
+ self.lower = h - 15
+ self.upper = 15
+ self.middle = w / 2
+ c.create_line(self.middle, self.lower, self.middle, self.upper)
+
+ majortickcount = int((max - min) / majortickinterval)
+ self.axislength = self.lower - self.upper
+ self.majortickdistance = float(self.axislength) / majortickcount
+ self.majortickwidth = w / 5
+ labeloffset = (w / 4) + 10
+
+ for i in range(majortickcount + 1):
+ v = min + i * majortickinterval
+ d = self.lower - i * self.majortickdistance
+ c.create_line(self.middle, d, self.middle + self.majortickwidth, d)
+ c.create_text(self.middle + labeloffset, d, font = small_font, text = str(v))
+
+ self._desiredSet(event)
+ self._actualSet(event)
+
+ def _backgroundSet(self):
+ self.canvas.configure(bg = self['bg'])
+
+ def _desiredSet(self, event = None):
+ c = self.canvas
+ desired = self['desiredvalue']
+ desiredcolour = self['desiredcolour']
+
+ min = self['min']
+ max = self['max']
+
+ if desired > max: desired = max
+ if desired < min: desired = min
+ gauge_range = max - min
+
+ c = self.canvas
+ if event:
+ h, w = event.height, event.width
+ else:
+ h = int(c.configure("height")[4])
+ w = int(c.configure("width")[4])
+
+
+ desired_y = self.lower - (float(desired - min) / gauge_range) * self.axislength
+
+ try:
+ c.delete('desiredBar')
+ except:
+ pass
+
+ c.create_line(self.middle - self.majortickwidth, desired_y,
+ self.middle + self.majortickwidth, desired_y,
+ fill = desiredcolour, stipple = 'gray50',
+ width = 10, tag = 'desiredBar')
+ self.desiredlabel.configure(text = desired)
+
+ def setActual(self, value):
+ self.configure(actualvalue = value)
+
+ def getActual(self):
+ return self.cget('actualvalue')
+
+ def _actualSet(self, event = None):
+ c = self.canvas
+ actual = self['actualvalue']
+ actualcolour = self['actualcolour']
+
+ min = self['min']
+ max = self['max']
+
+ if actual > max: actual = max
+ if actual < min: actual = min
+ gauge_range = max - min
+
+ c = self.canvas
+ if event:
+ h, w = event.height, event.width
+ else:
+ h = int(c.configure("height")[4])
+ w = int(c.configure("width")[4])
+
+ actual_y = self.lower - (float(actual - min) / gauge_range) * self.axislength
+
+ try:
+ c.delete('actualPointer')
+ except:
+ pass
+
+ triangle = ((self.middle, actual_y),
+ (self.middle - 1.4 * self.majortickwidth, actual_y - self.majortickwidth / 2),
+ (self.middle - 1.4 * self.majortickwidth, actual_y + self.majortickwidth / 2))
+
+ c.create_polygon(triangle, fill = actualcolour, tag = 'actualPointer')
+ self.actuallabel.configure(text = actual)
+
+
+Pmw.forwardmethods(VerticalGauge, Tkinter.Canvas, 'canvas')
+
+if __name__ == '__main__':
+
+
+ # Initialise Tkinter and Pmw.
+ root = Pmw.initialise()
+ root.title('Pmw VerticalGauge demonstration')
+
+
+ def increase():
+ av = g1.getActual()
+ g1.setActual(av + 1)
+
+ def decrease():
+ av = g1.getActual()
+ g1.setActual(av - 1)
+
+ g1 = VerticalGauge(min = 0,
+ max = 30,
+ actualvalue = 15,
+ desiredvalue = 22,
+ majortickinterval = 2,
+ label = "Pms")
+ g1.grid(sticky = "nsew")
+ root.grid_rowconfigure(0, weight = 1)
+ root.grid_columnconfigure(0, weight = 1)
+ b1 = Tkinter.Button(text = "Increase", command = increase)
+ b1.grid()
+ b2 = Tkinter.Button(text = "Decrease", command = decrease)
+ b2.grid()
+
+ # Let's go.
+ root.mainloop()
--- /dev/null
+This directory contains contributed Pmw megawidgets.
+
+DirBrowser.py directory selection dialog
+MCListbox.py multi-column selectable listbox
+PmwFileDialog.py file selection dialog
+PmwFullTimeCounter.py time counter which includes year, month and day
+PmwVerticalGauge.py a simple gauge indicating a value and a threshold
+TreeBrowser.py generic hierarchical tree browser
+
+Each file can be executed and will display a demo of its megawidget.
--- /dev/null
+#
+# FILE: TreeBrowser.py
+#
+# DESCRIPTION:
+# This file provides a generic hierarchical tree browser widget.
+#
+# AUTHOR: Steve Kinneberg <skinneberg@mvista.com>,
+# MontaVista Software, Inc. <source@mvista.com>
+#
+# Copyright 2001 MontaVista Software Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+# NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+
+import types
+import Tkinter
+import Pmw
+
+
+class _Branching:
+ def __init__(self):
+ # List of branch names
+ self._nodeNames = []
+
+ # Map from branch name to branch info
+ # branch Either _LeafNode or _BranchNode widget of the branch
+ # nodetype Either 'TreeNode' or 'LeafNode'
+ self._nodeAttrs = {}
+
+ def addbranch(self, branchName = None, **kw):
+ kw['indent'] = self['indent']
+ return apply(self._insertnode,
+ ('tree', branchName, len(self._nodeNames),
+ self._treeRoot),
+ kw)
+
+ def addleaf(self, leafName = None, **kw):
+ return apply(self._insertnode,
+ ('leaf', leafName, len(self._nodeNames),
+ self._treeRoot),
+ kw)
+
+ def insertbranch(self, branchName = None, before = 0, **kw):
+ kw['indent'] = self['indent']
+ return apply(self._insertnode,
+ ('tree', branchName, before, self._treeRoot),
+ kw)
+
+ def insertleaf(self, leafName = None, before = 0, **kw):
+ return apply(self._insertnode,
+ ('leaf', leafName, before, self._treeRoot),
+ kw)
+
+ def _insertnode(self, type, nodeName, before, treeRoot, **kw):
+ if 'selectbackground' not in kw.keys():
+ kw['selectbackground'] = self['selectbackground']
+
+ if 'selectforeground' not in kw.keys():
+ kw['selectforeground'] = self['selectforeground']
+
+ if 'background' not in kw.keys():
+ kw['background'] = self['background']
+
+ if 'foreground' not in kw.keys():
+ kw['foreground'] = self['foreground']
+
+ if nodeName == None:
+ nodeName = self._nodeName + ".%d" % (len(self._nodeNames) + 1)
+
+ if self._nodeAttrs.has_key(nodeName):
+ msg = 'Node "%s" already exists.' % nodeName
+ raise ValueError, msg
+
+ # Do this early to catch bad <before> spec before creating any items.
+ beforeIndex = self.index(before, 1)
+ attributes = {}
+
+ last = (beforeIndex == len(self._nodeNames))
+ if last and len(self._nodeNames) > 0:
+ # set the previous node to not last
+ self._nodeAttrs[self._nodeNames[-1]]['branch']._setlast(0)
+
+ if(type == 'tree'):
+ node = apply(self.createcomponent, ('branch%d'%len(self._nodeNames),
+ (), None,
+ _BranchNode,
+ self._branchFrame,
+ nodeName,
+ treeRoot,
+ self,
+ last,
+ ), kw)
+ attributes['nodetype'] = 'TreeNode'
+ else:
+ node = apply(self.createcomponent, ('leaf%d'%len(self._nodeNames),
+ (), None,
+ _LeafNode,
+ self._branchFrame,
+ nodeName,
+ treeRoot,
+ self,
+ last,
+ ), kw)
+ attributes['nodetype'] = 'LeafNode'
+
+ if len(self._nodeNames) == beforeIndex:
+ node.pack(anchor='w')
+ else:
+ bname = self._nodeNames[beforeIndex]
+ battrs = self._nodeAttrs[bname]
+ node.pack(anchor='w', before=battrs['branch'])
+
+ attributes['branch'] = node
+
+ self._nodeAttrs[nodeName] = attributes
+ self._nodeNames.insert(beforeIndex, nodeName)
+ self._sizechange()
+ return node
+
+ def delete(self, *nodes):
+ curSel = self._treeRoot.curselection()[0]
+ for node in nodes:
+ index = self.index(node)
+ name = self._nodeNames.pop(index)
+ dnode = self._nodeAttrs[name]['branch']
+ del self._nodeAttrs[name]
+ if dnode == curSel:
+ self._treeRoot._unhightlightnode(dnode)
+ dnode.destroy()
+ self._sizechange()
+
+ def destroy(self):
+ for node in len(self._nodeNames):
+ self.delete(node)
+ Pmw.MegaWidget.destroy(self)
+
+ def index(self, index, forInsert = 0):
+ if isinstance(index, _LeafNode):
+ index = index._nodeName
+ listLength = len(self._nodeNames)
+ if type(index) == types.IntType:
+ if forInsert and index <= listLength:
+ return index
+ elif not forInsert and index < listLength:
+ return index
+ else:
+ raise ValueError, 'index "%s" is out of range' % index
+ elif type(index) == types.StringType:
+ if index in self._nodeNames:
+ return self._nodeNames.index(index)
+ raise ValueError, 'bad branch or leaf name: %s' % index
+ elif index is Pmw.END:
+ if forInsert:
+ return listLength
+ elif listLength > 0:
+ return listLength - 1
+ else:
+ raise ValueError, 'TreeNode has no branches'
+ #elif index is Pmw.SELECT:
+ # if listLength == 0:
+ # raise ValueError, 'TreeNode has no branches'
+ # return self._pageNames.index(self.getcurselection())
+ else:
+ validValues = 'a name, a number, Pmw.END, Pmw.SELECT, or a reference to a TreeBrowser Leaf or Branch'
+ raise ValueError, \
+ 'bad index "%s": must be %s' % (index, validValues)
+
+ def getnodenames(self):
+ return self._nodeNames
+
+ def getnode(self, node):
+ nodeName = self._nodeNames[self.index(node)]
+ return self._nodeAttrs[nodeName]['branch']
+
+
+class _LeafNode(Pmw.MegaWidget):
+
+ def __init__(self, parent, nodeName, treeRoot, parentnode, last = 1, **kw):
+ colors = Pmw.Color.getdefaultpalette(parent)
+
+ self._nodeName = nodeName
+ self._treeRoot = treeRoot
+ self._parentNode = parentnode
+
+ self._last = last
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('selectbackground', colors['selectBackground'], INITOPT),
+ ('selectforeground', colors['selectForeground'], INITOPT),
+ ('background', colors['background'], INITOPT),
+ ('foreground', colors['foreground'], INITOPT),
+ ('selectcommand', None, None),
+ ('deselectcommand', None, None),
+ ('labelpos', 'e', INITOPT),
+ ('labelmargin', 0, INITOPT),
+ ('label', None, None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components
+ interior = self._hull
+
+
+ labelpos = self['labelpos']
+
+ if self['label'] == None:
+ self._labelWidget = self.createcomponent('labelwidget',
+ (), None,
+ Pmw.LabeledWidget,
+ (interior,),
+ #background = self['background'],
+ #foreground = self['foreground'],
+ )
+ else:
+ self._labelWidget = self.createcomponent('labelwidget',
+ (), None,
+ Pmw.LabeledWidget,
+ (interior,),
+ label_background = self['background'],
+ label_foreground = self['foreground'],
+ labelpos = labelpos,
+ labelmargin = self['labelmargin'],
+ label_text = self['label'],
+ )
+ self._labelWidget.component('label').bind('<ButtonRelease-1>',
+ self._selectevent)
+
+ self._labelWidget.grid(column = 1, row = 0, sticky = 'w')
+
+ self._labelWidget.update()
+
+ self._labelheight = self._labelWidget.winfo_height()
+
+ self._lineCanvas = self.createcomponent('linecanvas',
+ (), None,
+ Tkinter.Canvas,
+ (interior,),
+ width = self._labelheight,
+ height = self._labelheight,
+ )
+ self._lineCanvas.grid( column = 0, row = 0, sticky = 'news')
+ self._lineCanvas.update()
+
+ cw = int(self._lineCanvas['width'])
+ ch = int(self._lineCanvas['height'])
+
+ self._lineCanvas.create_line(cw/2, ch/2, cw, ch/2, tag='hline')
+ if last:
+ self._lineCanvas.create_line(cw/2, 0, cw/2, ch/2, tag='vline')
+ else:
+ self._lineCanvas.create_line(cw/2, 0, cw/2, ch, tag='vline')
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+
+ def interior(self):
+ return self._labelWidget.interior()
+
+ def select(self):
+ self._highlight()
+
+ def getname(self):
+ return self._nodeName
+
+ def getlabel(self):
+ return self['label']
+
+ def _selectevent(self, event):
+ self._highlight()
+
+ def _highlight(self):
+ self._treeRoot._highlightnode(self)
+ #self._subHull.configure(background = self._selectbg, relief = 'raised')
+ if self['label'] != None:
+ self._labelWidget.configure(label_background = self['selectbackground'])
+ self._labelWidget.configure(label_foreground = self['selectforeground'])
+ #self._viewButton.configure(background = self._selectbg)
+ cmd = self['selectcommand']
+ if callable(cmd):
+ cmd(self)
+
+ def _unhighlight(self):
+ #self._subHull.configure(background = self._bg, relief = 'flat')
+ if self['label'] != None:
+ self._labelWidget.configure(label_background = self['background'])
+ self._labelWidget.configure(label_foreground = self['foreground'])
+ #self._viewButton.configure(background = self._bg)
+ cmd = self['deselectcommand']
+ if callable(cmd):
+ cmd(self)
+
+ def _setlast(self, last):
+ self._last = last
+
+ cw = int(self._lineCanvas['width'])
+ ch = int(self._lineCanvas['height'])
+
+ if last:
+ self._lineCanvas.create_line(cw/2, 0, cw/2, ch/2, tag='vline')
+ else:
+ self._lineCanvas.create_line(cw/2, 0, cw/2, ch, tag='vline')
+
+
+class _BranchNode(_LeafNode, _Branching): #Pmw.MegaWidget):
+
+ def __init__(self, parent, nodeName, treeRoot, parentnode, last = 1, **kw):
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('view', 'collapsed', None),
+ ('expandcommand', None, None),
+ ('collapsecommand', None, None),
+ ('indent', 0, INITOPT)
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ apply(_LeafNode.__init__,
+ (self, parent, nodeName, treeRoot, parentnode, last),
+ kw)
+ _Branching.__init__(self)
+
+ # Create the components
+ interior = self._hull
+
+ # Create the expand/collapse button
+ self._viewButton = self.createcomponent('viewbutton', (), None,
+ Tkinter.Canvas,
+ (interior,),
+ background = self['background'],
+ width = self._labelheight - 4,
+ height = self._labelheight - 4,
+ borderwidth = 2,
+ relief = 'raised')
+
+ self._viewButton.grid(column = 0, row = 0, sticky='se')
+ self._viewButton.bind('<ButtonPress-1>', self._showbuttonpress)
+ self._viewButton.bind('<ButtonRelease-1>', self._toggleview)
+
+ # The label widget is already created by the base class, however
+ # we do need to make some slight modifications.
+ if self['label'] != None:
+ self._labelWidget.component('label').bind('<Double-1>',
+ self._toggleview)
+ self._labelWidget.grid(column=1, row=0, columnspan = 3, sticky='sw')
+
+ # A line canvas is already created for us, we just need to make
+ # some slight modifications
+ self._lineCanvas.delete('hline')
+ self._lineCanvas.grid_forget()
+
+
+ # Set the minsize of column 1 to control additional branch frame indentation
+ self.grid_columnconfigure(1, minsize = self['indent'])
+
+ # Create the branch frame that will contain all the branch/leaf nodes
+ self._branchFrame = self.createcomponent('frame', (), None,
+ Tkinter.Frame, (interior,),
+ #borderwidth=2,
+ #relief='ridge',
+ )
+ self.grid_columnconfigure(2,minsize=0, weight=1)
+ #self.grid_rowconfigure(0,minsize=0)
+
+ if(self['view'] == 'expanded'):
+ Pmw.drawarrow(self._viewButton,
+ self['foreground'],
+ 'down', 'arrow')
+ self._branchFrame.grid(column = 2, row = 1, sticky='nw')
+ if not self._last:
+ self._branchFrame.update()
+ bh = self._branchFrame.winfo_height()
+ self._lineCanvas.configure(height = bh)
+ self._lineCanvas.grid(column = 0, row = 1, sticky='news')
+ cw = int(self._lineCanvas['width'])
+ ch = int(self._lineCanvas['height'])
+ #self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag = 'vline')
+ self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch)
+ else:
+ Pmw.drawarrow(self._viewButton,
+ self['foreground'],
+ 'right', 'arrow')
+ self._viewButton.configure(relief = 'raised')
+
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+
+
+ def _showbuttonpress(self, event):
+ self._viewButton.configure(relief = 'sunken')
+
+ def _toggleview(self, event):
+ self._viewButton.configure(relief = 'sunken')
+ self.select()
+ if(self['view'] == 'expanded'):
+ self.collapsetree()
+ else:
+ self.expandtree()
+ self._viewButton.configure(relief = 'raised')
+
+ def expandtree(self):
+ if(self['view'] == 'collapsed'):
+ cmd = self['expandcommand']
+ if cmd is not None:
+ cmd(self)
+ self['view'] = 'expanded'
+ Pmw.drawarrow(self._viewButton,
+ self['foreground'],
+ 'down', 'arrow')
+ self._branchFrame.grid(column = 2, row = 1, sticky='nw')
+
+ if not self._last:
+ self._branchFrame.update()
+ bh = self._branchFrame.winfo_height()
+ self._lineCanvas.configure(height = bh)
+ self._lineCanvas.grid(column = 0, row = 1, sticky='news')
+ cw = int(self._lineCanvas['width'])
+ ch = int(self._lineCanvas['height'])
+ #self._lineCanvas.create_line( cw/2, 1, cw/2, ch, tag = 'vline')
+ self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch)
+ self._parentNode._sizechange()
+
+ def collapsetree(self):
+ if(self['view'] == 'expanded'):
+ cmd = self['collapsecommand']
+ if cmd is not None:
+ cmd(self)
+ self['view'] = 'collapsed'
+ Pmw.drawarrow(self._viewButton,
+ self['foreground'],
+ 'right', 'arrow')
+ self._branchFrame.grid_forget()
+ if not self._last:
+ #self._lineCanvas.delete('vline')
+ self._lineCanvas.grid_forget()
+ self._parentNode._sizechange()
+
+ def _setlast(self, last):
+ self._last = last
+ if self['view'] == 'expanded':
+ self._branchFrame.update()
+ bh = self._branchFrame.winfo_height()
+ self._lineCanvas.configure(height = bh)
+ cw = int(self._lineCanvas['width'])
+ ch = int(self._lineCanvas['height'])
+ self._lineCanvas.delete('vline')
+ if not last:
+ self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag='vline')
+
+
+ def _sizechange(self):
+ if not self._last and self['view'] == 'expanded':
+ self._branchFrame.update()
+ bh = self._branchFrame.winfo_height()
+ self._lineCanvas.configure(height = bh)
+ if self._lineCanvas.coords('vline')[3] < bh:
+ cw = int(self._lineCanvas['width'])
+ ch = int(self._lineCanvas['height'])
+ #self._lineCanvas.delete('vline')
+ #self._lineCanvas.create_line(cw/2, 1, cw/2, ch, tag='vline')
+ self._lineCanvas.coords('vline', cw/2, 1, cw/2, ch)
+ self._parentNode._sizechange()
+
+class TreeBrowser(Pmw.MegaWidget, _Branching):
+
+ def __init__(self, parent = None, nodeName = '0', **kw):
+ colors = Pmw.Color.getdefaultpalette(parent)
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('indent', 0, INITOPT),
+ ('selectbackground', colors['selectBackground'], INITOPT),
+ ('selectforeground', colors['selectForeground'], INITOPT),
+ ('background', colors['background'], INITOPT),
+ ('foreground', colors['foreground'], INITOPT),
+ #('selectrelief', 'raised', INITOPT),
+ )
+
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+ _Branching.__init__(self)
+
+
+ # Create the components
+ interior = self._hull
+
+ browserFrame = self.createcomponent('frame', (), None,
+ Pmw.ScrolledFrame,
+ (interior,),
+ )
+
+ browserFrame.pack(expand = 1, fill='both')
+
+ self._branchFrame = browserFrame.interior()
+
+ self._highlightedNode = None
+ self._treeRoot = self
+ self._nodeName = nodeName
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _highlightnode(self, newNode):
+ if self._highlightedNode != newNode:
+ if self._highlightedNode != None:
+ self._highlightedNode._unhighlight()
+ self._highlightedNode = newNode
+
+ def _unhighlightnode(self):
+ if self._highlightedNode != None:
+ self._highlightedNode._unhighlight()
+ self._highlightedNode = None
+
+ def curselection(self):
+ retVal = None
+ if self._highlightedNode != None:
+ retVal = (self._highlightedNode,
+ self._highlightedNode._nodeName,
+ self._highlightedNode['label'])
+ return retVal
+
+ def getname(self):
+ return self._nodeName
+
+ # The top-level TreeBrowser widget only shows nodes in an expanded view
+ # but still provides collapsetree() and expandtree() methods so that users
+ # don't have to special case the top-level node
+
+ def collapsetree(self):
+ return
+
+ def expandtree(self):
+ return
+
+ def _sizechange(self):
+ return
+
+if __name__ == '__main__':
+
+ rootWin = Tkinter.Tk()
+
+ Pmw.initialise()
+
+ rootWin.title('TreeBrowser Demo')
+
+ # Create the hierarchical tree browser widget
+ treeBrowser = TreeBrowser(rootWin,
+ #selectbackground = "darkgreen",
+ #selectforeground = 'lightgreen',
+ #background = 'green',
+ #indent = 10,
+ )
+
+
+ def printselected(node):
+ selection = treeBrowser.curselection()
+ if selection != None:
+ print "Selected node name:", selection[1], " label:", selection[2]
+
+
+ def printdeselected(node):
+ selection = treeBrowser.curselection()
+ if selection != None:
+ print "Deselected node name:", selection[1], " label:", selection[2]
+
+ def printexpanded(node):
+ print "Expanded node name:", node.getname(), " label:", node.getlabel()
+
+ def printcollapsed(node):
+ print "Collapsed node name:", node.getname(), " label:", node.getlabel()
+
+
+
+ for i in range(3):
+ # Add a tree node to the top level
+ treeNodeLevel1 = treeBrowser.addbranch(label = 'TreeNode %d'%i,
+ selectcommand = printselected,
+ deselectcommand = printdeselected,
+ expandcommand = printexpanded,
+ collapsecommand = printcollapsed,
+ )
+ for j in range(3):
+ # Add a tree node to the second level
+ treeNodeLevel2 = treeNodeLevel1.addbranch(label = 'TreeNode %d.%d'%(i,j),
+ #selectforeground = 'yellow',
+ selectcommand = printselected,
+ deselectcommand = printdeselected,
+ expandcommand = printexpanded,
+ collapsecommand = printcollapsed,
+ )
+ if i == 0 and j == 1:
+ dynamicTreeRootNode = treeNodeLevel1
+ dynamicTreePosNode = treeNodeLevel2
+
+ for item in range((i+1)*(j+1)):
+ # Add a leaf node to the third level
+ leaf = treeNodeLevel2.addleaf(label = "Item %c"%(item+65),
+ #selectbackground = 'blue',
+ selectcommand = printselected,
+ deselectcommand = printdeselected)
+ for item in range(i+1):
+ # Add a leaf node to the top level
+ leaf = treeNodeLevel1.addleaf(label = "Item %c"%(item+65),
+ selectcommand = printselected,
+ deselectcommand = printdeselected)
+
+
+ treeNodeLevel1 = treeBrowser.addbranch(label = 'Check Button Label',
+ selectcommand = printselected,
+ deselectcommand = printdeselected,
+ expandcommand = printexpanded,
+ collapsecommand = printcollapsed,
+ )
+ checkButton = Tkinter.Checkbutton(treeNodeLevel1.interior(),
+ text = 'Da Check Button',
+ relief = 'ridge',
+ command = treeNodeLevel1.select)
+ checkButton.pack()
+
+ treeNodeLevel1.addleaf(label = 'Labeled Leaf',
+ selectcommand = printselected,
+ deselectcommand = printdeselected)
+ leaf = treeNodeLevel1.addleaf(label = 'Labeled Leaf w/ Checkbutton',
+ selectcommand = printselected,
+ deselectcommand = printdeselected)
+ checkButton = Tkinter.Checkbutton(leaf.interior(),
+ text = 'Da Check Button',
+ relief = 'ridge',
+ command = leaf.select)
+ checkButton.pack()
+
+
+ treeNodeLevel1 = treeBrowser.addbranch(selectcommand = printselected,
+ deselectcommand = printdeselected,
+ expandcommand = printexpanded,
+ collapsecommand = printcollapsed,
+ )
+ checkButton = Tkinter.Checkbutton(treeNodeLevel1.interior(),
+ text = 'Check Button with no label',
+ relief = 'ridge',
+ command = treeNodeLevel1.select)
+ checkButton.pack()
+
+ treeNodeLevel1 = treeBrowser.addbranch(label = 'Label',
+ selectcommand = printselected,
+ deselectcommand = printdeselected,
+ expandcommand = printexpanded,
+ collapsecommand = printcollapsed,
+ )
+
+ # setup dynamic tree node insertion and removal
+ class dynTree:
+ def __init__(self):
+ self.dyn = Tkinter.IntVar()
+ self.dtree = None
+
+ self.dLeaf = treeBrowser.addleaf(selectcommand = self.dynSelected,
+ deselectcommand = self.dynDeselected)
+
+ self.dCheckButton = Tkinter.Checkbutton(self.dLeaf.interior(),
+ text = 'Enable Dynamic Tree',
+ variable = self.dyn,
+ command = self.ChkBtnHandler)
+ self.dCheckButton.pack()
+
+
+ def dynSelected(self, node):
+ self.dCheckButton.configure(background = self.dLeaf.configure('selectbackground')[4])
+ printselected(node)
+
+ def dynDeselected(self, node):
+ self.dCheckButton.configure(background = self.dLeaf.configure('background')[4])
+ printdeselected(node)
+
+ def ChkBtnHandler(self):
+ self.dLeaf.select()
+ if self.dyn.get() == 1:
+ self.dtree = dynamicTreeRootNode.insertbranch(label = 'Dynamic Tree Node',
+ selectcommand = printselected,
+ deselectcommand = printdeselected,
+ expandcommand = printexpanded,
+ collapsecommand = printcollapsed,
+ before = dynamicTreePosNode)
+ self.dtree.addleaf(label = 'Dynamic Leaf 1',
+ selectcommand = printselected,
+ deselectcommand = printdeselected)
+ self.dtree.addleaf(label = 'Dynamic Leaf 2',
+ selectcommand = printselected,
+ deselectcommand = printdeselected)
+ else:
+ if self.dtree != None:
+ dynamicTreeRootNode.delete(self.dtree)
+ self.dtree = None
+
+
+ foo = dynTree()
+
+
+ treeBrowser.pack(expand = 1, fill='both')
+
+ exitButton = Tkinter.Button(rootWin, text="Quit", command=rootWin.quit)
+ exitButton.pack()
+
+ rootWin.mainloop()
--- /dev/null
+title = 'Pmw.AboutDialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create dialog.
+ Pmw.aboutversion('9.9')
+ Pmw.aboutcopyright('Copyright My Company 1999\nAll rights reserved')
+ Pmw.aboutcontact(
+ 'For information about this application contact:\n' +
+ ' My Help Desk\n' +
+ ' Phone: +61 2 9876 5432\n' +
+ ' email: help@my.company.com.au'
+ )
+ self.about = Pmw.AboutDialog(parent, applicationname = 'My Application')
+ self.about.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show about dialog',
+ command = self.execute)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self):
+ self.about.show()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+#!/usr/bin/env python
+
+# ------------------------------------------------------------------
+# Display a splash screen as quickly as possible (before importing
+# modules and initialising Pmw).
+
+import Tkinter
+root = Tkinter.Tk(className = 'Demo')
+root.withdraw()
+
+splash = Tkinter.Toplevel()
+splash.withdraw()
+splash.title('Welcome to the Pmw demos')
+text = Tkinter.Label(splash,
+ font=('Helvetica', 16, 'bold'),
+ relief = 'raised',
+ borderwidth = 2,
+ padx=50, pady=50,
+ text =
+ 'Welcome to the Pmw megawidgets demo.\n'
+ '\n'
+ 'In a moment the main window will appear.\n'
+ 'Please enjoy yourself while you wait.\n'
+ 'You may be interested to know that splash screens\n'
+ '(as this window is called) were first devised to draw\n'
+ 'attention away from the fact the certain applications\n'
+ 'are slow to start. They are normally flashier and more\n'
+ 'entertaining than this one. This is a budget model.'
+)
+text.pack(fill = 'both', expand = 1)
+splash.update_idletasks()
+
+width = splash.winfo_reqwidth()
+height = splash.winfo_reqheight()
+x = (root.winfo_screenwidth() - width) / 2 - root.winfo_vrootx()
+y = (root.winfo_screenheight() - height) / 3 - root.winfo_vrooty()
+if x < 0:
+ x = 0
+if y < 0:
+ y = 0
+geometry = '%dx%d+%d+%d' % (width, height, x, y)
+
+splash.geometry(geometry)
+splash.update_idletasks()
+splash.deiconify()
+root.update()
+
+# ------------------------------------------------------------------
+
+# Now crank up the application windows.
+
+import imp
+import os
+import re
+import string
+import sys
+import types
+import Tkinter
+import DemoVersion
+import Args
+
+# Find where the other scripts are, so they can be listed.
+if __name__ == '__main__':
+ script_name = sys.argv[0]
+else:
+ script_name = imp.find_module('DemoVersion')[1]
+
+script_name = os.path.normpath(script_name)
+script_name = DemoVersion.expandLinks(script_name)
+script_dir = os.path.dirname(script_name)
+script_dir = DemoVersion.expandLinks(script_dir)
+
+# Add the '../../..' directory to the path.
+package_dir = os.path.dirname(script_dir)
+package_dir = DemoVersion.expandLinks(package_dir)
+package_dir = os.path.dirname(package_dir)
+package_dir = DemoVersion.expandLinks(package_dir)
+package_dir = os.path.dirname(package_dir)
+package_dir = DemoVersion.expandLinks(package_dir)
+sys.path[:0] = [package_dir]
+
+# Import Pmw after modifying sys.path (it may not be in the default path).
+import Pmw
+DemoVersion.setPmwVersion()
+
+class Demo(Pmw.MegaWidget):
+
+ def __init__(self, parent=None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = ()
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the contents.
+ top = self.interior()
+
+ panes = Pmw.PanedWidget(top, orient = 'horizontal')
+ panes.pack(fill = 'both', expand = 1)
+
+ panes.add('widgetlist')
+ self._widgetlist = Pmw.ScrolledListBox(panes.pane('widgetlist'),
+ selectioncommand = Pmw.busycallback(self.startDemo),
+ label_text = 'Select a widget:',
+ labelpos = 'nw',
+ vscrollmode = 'dynamic',
+ hscrollmode = 'none',
+ listbox_exportselection = 0)
+ self._widgetlist.pack(fill = 'both', expand = 1, padx = 8)
+
+ panes.add('info')
+ self._status = Tkinter.Label(panes.pane('info'))
+ self._status.pack(padx = 8, anchor = 'w')
+
+ self._example = Tkinter.Frame(panes.pane('info'),
+ borderwidth = 2,
+ relief = 'sunken',
+ background = 'white')
+ self._example.pack(fill = 'both', expand = 1, padx = 8)
+
+ self.buttonBox = Pmw.ButtonBox(top)
+ self.buttonBox.pack(fill = 'x')
+
+ # Add the buttons and make them all the same width.
+ self._traceText = 'Trace tk calls'
+ self._stopTraceText = 'Stop trace'
+ self.buttonBox.add('Trace', text = self._traceText,
+ command = self.trace)
+ self.buttonBox.add('Code', text = 'Show code', command = self.showCode)
+ self.buttonBox.add('Exit', text = 'Exit', command = sys.exit)
+ self.buttonBox.alignbuttons()
+
+ # Create the window to display the python code.
+ self.codeWindow = Pmw.TextDialog(parent,
+ title = 'Python source',
+ buttons = ('Dismiss',),
+ scrolledtext_labelpos = 'n',
+ label_text = 'Source')
+ self.codeWindow.withdraw()
+ self.codeWindow.insert('end', '')
+
+ self.demoName = None
+ self._loadDemos()
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def startDemo(self):
+ # Import the selected module and create and instance of the module's
+ # Demo class.
+
+ sels = self._widgetlist.getcurselection()
+ if len(sels) == 0:
+ print 'No demonstrations to display'
+ return
+ demoName = sels[0]
+
+ # Ignore if this if it is a sub title.
+ if demoName[0] != ' ':
+ self._widgetlist.bell()
+ return
+
+ # Strip the leading two spaces.
+ demoName = demoName[2:]
+
+ # Ignore if this demo is already being shown.
+ if self.demoName == demoName:
+ return
+
+ self.demoName = demoName
+
+ self.showStatus('Loading ' + demoName)
+ # Busy cursor
+ self.update_idletasks()
+
+ for window in self._example.winfo_children():
+ window.destroy()
+
+ frame = Tkinter.Frame(self._example)
+ frame.pack(expand = 1)
+ exec 'import ' + demoName
+ # Need to keep a reference to the widget, so that variables, etc
+ # are not deleted.
+ self.widget = eval(demoName + '.Demo(frame)')
+ title = eval(demoName + '.title')
+ self.showStatus(title)
+
+ if self.codeWindow.state() == 'normal':
+ self.insertCode()
+
+ def showStatus(self, text):
+ self._status.configure(text = text)
+
+ def showCode(self):
+ if self.codeWindow.state() != 'normal':
+ if self.demoName is None:
+ print 'No demonstration selected'
+ return
+ self.insertCode()
+
+ self.codeWindow.show()
+
+ def insertCode(self):
+ self.codeWindow.clear()
+ fileName = os.path.join(script_dir, self.demoName + '.py')
+ self.codeWindow.importfile(fileName)
+ self.codeWindow.configure(label_text = self.demoName + ' source')
+
+ def trace(self):
+ text = self.buttonBox.component('Trace').cget('text')
+ if text == self._traceText:
+ self.buttonBox.configure(Trace_text = self._stopTraceText)
+ Pmw.tracetk(root, 1)
+ self.showStatus('Trace will appear on standard output')
+ else:
+ self.buttonBox.configure(Trace_text = self._traceText)
+ Pmw.tracetk(root, 0)
+ self.showStatus('Tk call tracing stopped')
+
+ def _loadDemos(self):
+ files = os.listdir(script_dir)
+ files.sort()
+ megawidgets = []
+ others = []
+ for file in files:
+ if re.search('.py$', file) is not None and \
+ file not in ['All.py', 'DemoVersion.py', 'Args.py']:
+ demoName = file[:-3]
+ index = string.find(demoName, '_')
+ if index < 0:
+ testattr = demoName
+ else:
+ testattr = demoName[:index]
+ if hasattr(Pmw, testattr):
+ megawidgets.append(demoName)
+ else:
+ others.append(demoName)
+
+ self._widgetlist.insert('end', 'Megawidget demos:')
+ for name in megawidgets:
+ self._widgetlist.insert('end', ' ' + name)
+ self._widgetlist.insert('end', 'Other demos:')
+ for name in others:
+ self._widgetlist.insert('end', ' ' + name)
+ self._widgetlist.select_set(1)
+
+class StdOut:
+ def __init__(self, displayCommand):
+ self.displayCommand = displayCommand
+ self.text = '\n'
+
+ def write(self, text):
+ if self.text[-1] == '\n':
+ self.text = text
+ else:
+ self.text = self.text + text
+ if self.text[-1] == '\n':
+ text = self.text[:-1]
+ else:
+ text = self.text
+ self.displayCommand(text)
+
+if os.name == 'nt':
+ defaultFontSize = 16
+else:
+ defaultFontSize = 12
+
+commandLineArgSpecs = (
+ ('fontscheme', 0, 'scheme', 'fonts to use [eg pmw2] (Tk defaults)'),
+ ('fontsize', 0, 'num', 'size of fonts to use with fontscheme', defaultFontSize),
+ ('stdout', 0, Args.Bool, 'print messages rather than display in label'),
+)
+
+program = 'All.py'
+msg = Args.parseArgs(program, sys.argv, commandLineArgSpecs, 0)
+if msg is not None:
+ print msg
+ sys.exit()
+
+size = Args.get('fontsize')
+fontScheme = Args.get('fontscheme')
+Pmw.initialise(root, size = size, fontScheme = fontScheme, useTkOptionDb = 1)
+
+root.title('Pmw ' + Pmw.version() + ' megawidget demonstration')
+if size < 18:
+ geometry = '800x550'
+else:
+ geometry = '1000x700'
+root.geometry(geometry)
+
+demo = Demo(root)
+demo.pack(fill = 'both', expand = 1)
+demo.focus()
+
+# Redirect standard output from demos to status line (unless -stdout
+# option given on command line).
+if not Args.get('stdout'):
+ sys.stdout = StdOut(demo.showStatus)
+
+# Start the first demo.
+demo.startDemo()
+
+# Get rid of the splash screen
+root.deiconify()
+root.update()
+splash.destroy()
+
+root.mainloop()
--- /dev/null
+"""Handle command line arguments.
+
+This module contains functions to parse and access the arguments given
+to the program on the command line.
+"""
+
+import types
+import string
+import sys
+
+# Symbolic constants for the indexes into an argument specifier tuple.
+NAME = 0
+MANDATORY = 1
+TYPE = 2
+HELP = 3
+DEFAULT = 4
+SPEC_LENGTH = 5
+
+Bool = []
+
+helpSpec = (
+ ('help', 0, Bool, 'print help and exit'),
+)
+
+def parseArgs(title, argv, argSpecs, filesOK):
+ """Parse and check command line arguments.
+
+ Scan the command line arguments in *argv* according to the argument
+ specifier *argSpecs*. Return **None** if there are no errors in
+ the arguments, otherwise return an error string describing the error.
+
+ This function must be called to initialise this module.
+
+ title -- The name of the program. This is used when returning
+ error messages or help text.
+
+ argv -- A sequence containing the arguments given to the program.
+ Normally **sys.argv**.
+
+ argSpecs -- A sequence of argument specifiers. Each specifier describes
+ a valid command line argument and consists of 4 or 5 items:
+
+ - The argument name (without a leading minus sign **-**).
+
+ - A boolean value, true if the argument is mandatory.
+
+ - This should be **Args.Bool** if the argument has no option.
+ Otherwise it should be a string describing the option
+ required for this argument. This is used when printing help.
+
+ - A short string describing the argument.
+
+ - The default value of the argument. This should only be used
+ for non-mandatory arguments expecting an option.
+
+ For example:
+ (
+ ('foreground', 0, 'colour', 'colour of text', 'black'),
+ ('geometry', 0, 'spec', 'geometry of initial window'),
+ ('server', 1, 'ompserver', 'ompserver to connect to'),
+ ('silent', 0, Args.Bool, 'do not sound bell'),
+ )
+ """
+
+ global programName
+ global _fileList
+
+ errMsg = title + ' command line error: '
+ programName = argv[0];
+
+ argSpecs = helpSpec + argSpecs
+ argSpecDic = {}
+ for spec in argSpecs:
+ arg = spec[NAME]
+ argSpecDic[arg] = spec
+ if len(spec) >= SPEC_LENGTH:
+ set(arg, spec[DEFAULT])
+ elif spec[TYPE] is Bool:
+ set(arg, 0)
+ else:
+ set(arg, None)
+
+ knownKeys = argSpecDic.keys()
+
+ i = 1
+ _fileList = []
+ argc = len(argv)
+ while i < argc:
+ arg = argv[i]
+ key = arg[1:]
+ if key in knownKeys:
+ spec = argSpecDic[key]
+ if spec[TYPE] is Bool:
+ set(key, 1)
+ else:
+ i = i + 1
+ if i >= argc:
+ return errMsg + 'missing argument to \'' + arg + '\' option.'
+ value = argv[i]
+ if len(spec) >= SPEC_LENGTH:
+ try:
+ if type(spec[DEFAULT]) == types.IntType:
+ typeStr = 'integer'
+ value = string.atoi(value)
+ elif type(spec[DEFAULT]) == types.FloatType:
+ typeStr = 'float'
+ value = string.atof(value)
+ except:
+ sys.exc_traceback = None # Clean up object references
+ return errMsg + 'cannot convert string \'' + value + \
+ '\' to ' + typeStr + ' for option \'-' + key + '\'.'
+ set(key, value)
+ else:
+ _fileList.append(arg)
+ i = i + 1
+
+ if get('help'):
+ return _helpString(title, argSpecs)
+
+ if not filesOK and len(_fileList) > 0:
+ if len(_fileList) == 1:
+ return errMsg + 'unknown option \'' + str(_fileList[0]) + '\'.'
+ else:
+ return errMsg + 'unknown options ' + str(_fileList) + '.'
+
+
+ _missing = []
+ for spec in argSpecs:
+ if spec[MANDATORY] and get(spec[NAME]) is None:
+ _missing.append(spec[NAME])
+ if len(_missing) == 1:
+ return errMsg + 'required argument \'-' + \
+ str(_missing[0]) + '\' is missing.'
+ elif len(_missing) > 1:
+ return errMsg + 'required arguments ' + \
+ str(map(lambda s: '-' + s, _missing)) + ' are missing.'
+
+ return None
+
+def fileList():
+ return _fileList
+
+def _helpString(title, argSpecs):
+ max = 0
+ for spec in argSpecs:
+ if spec[TYPE] is Bool:
+ width = len(spec[NAME]) + 1
+ else:
+ width = len(spec[NAME]) + 4 + len(spec[TYPE])
+ if width > max:
+ max = width
+
+ rtn = title + ' command line arguments:'
+ format = '\n %-' + str(max) + 's %s'
+ for mandatory in (1, 0):
+ needHeader = 1
+ for spec in argSpecs:
+ if mandatory and spec[MANDATORY] or not mandatory and not spec[MANDATORY]:
+ if needHeader:
+ if mandatory:
+ rtn = rtn + '\n Mandatory arguments:'
+ else:
+ rtn = rtn + '\n Optional arguments (defaults in parentheses):'
+ needHeader = 0
+ if spec[TYPE] is Bool:
+ arg = '-%s' % spec[NAME]
+ else:
+ arg = '-%s <%s>' % (spec[NAME], spec[TYPE])
+ if len(spec) >= SPEC_LENGTH:
+ if type(spec[DEFAULT]) == types.StringType:
+ definition = spec[HELP] + ' (' + spec[DEFAULT] + ')'
+ else:
+ definition = spec[HELP] + ' (' + str(spec[DEFAULT]) + ')'
+ else:
+ definition = spec[HELP]
+ rtn = rtn + format % (arg, definition)
+
+ return rtn
+
+def exists(key):
+ return configDict.has_key(key)
+
+def get(key):
+ return configDict[key]
+
+def set(key, value):
+ global configDict
+
+ configDict[key] = value
+
+configDict = {}
--- /dev/null
+title = 'Pmw.Balloon demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the Balloon.
+ self.balloon = Pmw.Balloon(parent)
+
+ # Create some widgets and megawidgets with balloon help.
+ frame = Tkinter.Frame(parent)
+ frame.pack(padx = 10, pady = 5)
+ field = Pmw.EntryField(frame,
+ labelpos = 'nw',
+ label_text = 'Command:')
+ field.setentry('mycommand -name foo')
+ field.pack(side = 'left', padx = 10)
+ self.balloon.bind(field, 'Command to\nstart/stop',
+ 'Enter the shell command to control')
+
+ start = Tkinter.Button(frame, text='Start')
+ start.pack(side='left', padx = 10)
+ self.balloon.bind(start, 'Start the command')
+
+ stop = Tkinter.Button(frame, text='Stop')
+ stop.pack(side='left', padx = 10)
+ self.balloon.bind(stop, 'Stop the command')
+
+ self.suicide = Tkinter.Button(frame, text='Kill me soon!',
+ command = self.killButton)
+ self.suicide.pack(side='left', padx = 10)
+ self.balloon.bind(self.suicide, 'Watch this button disappear!')
+
+ scrolledCanvas = Pmw.ScrolledCanvas(parent,
+ canvas_width = 300,
+ canvas_height = 115,
+ )
+ scrolledCanvas.pack()
+ canvas = scrolledCanvas.component('canvas')
+ self.canvas = canvas
+
+ # Create some canvas items and individual help.
+ item = canvas.create_arc(5, 5, 35, 35, fill = 'red', extent = 315)
+ self.balloon.tagbind(canvas, item, 'This is help for\nan arc item')
+ item = canvas.create_bitmap(20, 150, bitmap = 'question')
+ self.balloon.tagbind(canvas, item, 'This is help for\na bitmap')
+ item = canvas.create_line(50, 60, 70, 80, 85, 20, width = 5)
+ self.balloon.tagbind(canvas, item, 'This is help for\na line item')
+ item = canvas.create_text(10, 90, text = 'Canvas items with balloons',
+ anchor = 'nw', font = field.cget('entry_font'))
+ self.balloon.tagbind(canvas, item, 'This is help for\na text item')
+
+ # Create two canvas items which have the same tag and which use
+ # the same help.
+ canvas.create_rectangle(100, 10, 170, 50, fill = 'aliceblue',
+ tags = 'TAG1')
+ self.bluecircle = canvas.create_oval(110, 30, 160, 80, fill = 'blue',
+ tags = 'TAG1')
+ self.balloon.tagbind(canvas, 'TAG1',
+ 'This is help for the two blue items' + '\n' * 10 +
+ 'It is very, very big.',
+ 'This is help for the two blue items')
+ item = canvas.create_text(180, 10, text = 'Delete',
+ anchor = 'nw', font = field.cget('entry_font'))
+ self.balloon.tagbind(canvas, item,
+ 'After 2 seconds,\ndelete the blue circle')
+ canvas.tag_bind(item, '<ButtonPress>', self._canvasButtonpress)
+ scrolledCanvas.resizescrollregion()
+
+ scrolledText = Pmw.ScrolledText(parent,
+ text_width = 32,
+ text_height = 4,
+ text_wrap = 'none',
+ )
+ scrolledText.pack(pady = 5)
+ text = scrolledText.component('text')
+ self.text = text
+
+ text.insert('end',
+ 'This is a text widget with ', '',
+ ' balloon', 'TAG1',
+ '\nhelp. Find the ', '',
+ ' text ', 'TAG1',
+ ' tagged with', '',
+ ' help.', 'TAG2',
+ '\n', '',
+ 'Remove tag 1.', 'TAG3',
+ '\nAnother line.\nAnd another', '',
+ )
+ text.tag_configure('TAG1', borderwidth = 2, relief = 'sunken')
+ text.tag_configure('TAG3', borderwidth = 2, relief = 'raised')
+
+ self.balloon.tagbind(text, 'TAG1',
+ 'There is one secret\nballoon help.\nCan you find it?')
+ self.balloon.tagbind(text, 'TAG2',
+ 'Well done!\nYou found it!')
+ self.balloon.tagbind(text, 'TAG3',
+ 'After 2 seconds\ndelete the tag')
+ text.tag_bind('TAG3', '<ButtonPress>', self._textButtonpress)
+
+ frame = Tkinter.Frame(parent)
+ frame.pack(padx = 10)
+ self.toggleBalloonVar = Tkinter.IntVar()
+ self.toggleBalloonVar.set(1)
+ toggle = Tkinter.Checkbutton(frame,
+ variable = self.toggleBalloonVar,
+ text = 'Balloon help', command = self.toggle)
+ toggle.pack(side = 'left', padx = 10)
+ self.balloon.bind(toggle, 'Toggle balloon help\non and off')
+
+ self.toggleStatusVar = Tkinter.IntVar()
+ self.toggleStatusVar.set(1)
+ toggle = Tkinter.Checkbutton(frame,
+ variable = self.toggleStatusVar,
+ text = 'Status help', command = self.toggle)
+ toggle.pack(side = 'left', padx = 10)
+ self.balloon.bind(toggle,
+ 'Toggle status help on and off, on and off' + '\n' * 10 +
+ 'It is very, very big, too.',
+ 'Toggle status help on and off')
+
+ # Create and pack the MessageBar.
+ messageBar = Pmw.MessageBar(parent,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ messageBar.pack(fill = 'x', expand = 1, padx = 10, pady = 5)
+
+ # Configure the balloon to display its status messages in the
+ # message bar.
+ self.balloon.configure(statuscommand = messageBar.helpmessage)
+
+ def toggle(self):
+ if self.toggleBalloonVar.get():
+ if self.toggleStatusVar.get():
+ self.balloon.configure(state = 'both')
+ else:
+ self.balloon.configure(state = 'balloon')
+ else:
+ if self.toggleStatusVar.get():
+ self.balloon.configure(state = 'status')
+ else:
+ self.balloon.configure(state = 'none')
+
+ def killButton(self):
+ # Test for old bug when destroying widgets 1) while the
+ # balloon was up and 2) during the initwait period.
+ print 'Destroying button in 2 seconds'
+ self.suicide.after(2000, self.suicide.destroy)
+
+ def _canvasButtonpress(self, event):
+ print 'Destroying blue circle in 2 seconds'
+ self.canvas.after(2000, self.deleteBlueCircle)
+
+ def deleteBlueCircle(self):
+ self.balloon.tagunbind(self.canvas, self.bluecircle)
+ self.canvas.delete(self.bluecircle)
+
+ def _textButtonpress(self, event):
+ print 'Deleting the text tag in 2 seconds'
+ self.text.after(2000, self.deleteTextTag)
+
+ def deleteTextTag(self):
+ self.balloon.tagunbind(self.text, 'TAG1')
+ self.text.tag_delete('TAG1')
+
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root, 12, fontScheme = 'default')
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Blt Graph demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+# Simple random number generator.
+rand = 12345
+def random():
+ global rand
+ rand = (rand * 125) % 2796203
+ return rand
+
+class GraphDemo(Pmw.MegaToplevel):
+
+ def __init__(self, parent=None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = (
+ ('size', 10, Pmw.INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaToplevel.__init__(self, parent)
+
+ # Create the graph.
+ self.createWidgets()
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def createWidgets(self):
+ # Create vectors for use as x and y data points.
+ self._numElements = 7
+ self._vectorSize = self['size']
+ self._vector_x = Pmw.Blt.Vector()
+ self._vector_y = []
+ for y in range(self._numElements):
+ self._vector_y.append(Pmw.Blt.Vector())
+ for index in range(self._vectorSize):
+ self._vector_x.append(index)
+ for y in range(self._numElements):
+ self._vector_y[y].append(random() % 100)
+
+ interior = self.interior()
+
+ controlFrame = Tkinter.Frame(interior)
+ controlFrame.pack(side = 'bottom', fill = 'x', expand = 0)
+
+ # Create an option menu for the kind of elements to create.
+ elementtype = Pmw.OptionMenu(controlFrame,
+ labelpos = 'nw',
+ label_text = 'Element type',
+ items = ['bars', 'lines', 'mixed', 'none'],
+ command = self._setelementtype,
+ menubutton_width = 8,
+ )
+ elementtype.pack(side = 'left')
+
+ # Create an option menu for the barmode option.
+ barmode = Pmw.OptionMenu(controlFrame,
+ labelpos = 'nw',
+ label_text = 'Bar mode',
+ items = ['normal', 'stacked', 'aligned', 'overlap'],
+ command = self._setbarmode,
+ menubutton_width = 8,
+ )
+ barmode.pack(side = 'left')
+
+ # Create an option menu for the smooth option.
+ self.smooth = Pmw.OptionMenu(controlFrame,
+ labelpos = 'nw',
+ label_text = 'Smooth',
+ items = ['linear', 'step', 'natural', 'quadratic'],
+ command = self._setsmooth,
+ menubutton_width = 9,
+ )
+ self.smooth.pack(side = 'left')
+
+ # Create an option menu to reverse sort the elements.
+ sortelements = Pmw.OptionMenu(controlFrame,
+ labelpos = 'nw',
+ label_text = 'Order',
+ items = ['normal', 'reverse'],
+ command = self._setsortelements,
+ menubutton_width = 8,
+ )
+ sortelements.pack(side = 'left')
+
+ # Create an option menu for the bufferelements option.
+ bufferelements = Pmw.OptionMenu(controlFrame,
+ labelpos = 'nw',
+ label_text = 'Buffering',
+ items = ['buffered', 'unbuffered'],
+ command = self._setbufferelements,
+ menubutton_width = 10,
+ )
+ bufferelements.pack(side = 'left')
+
+ # Create a button to add a point to the vector.
+ addpoint = Tkinter.Button(controlFrame, text = 'Add point',
+ command = Pmw.busycallback(self._addpoint))
+ addpoint.pack(side = 'left', fill = 'x', expand = 0)
+
+ # Create a button to close the window
+ close = Tkinter.Button(controlFrame, text = 'Close',
+ command = Pmw.busycallback(self.destroy))
+ close.pack(side = 'left', fill = 'x', expand = 0)
+
+ # Create the graph and its elements.
+ self._graph = Pmw.Blt.Graph(interior)
+ self._graph.pack(expand = 1, fill = 'both')
+ self._graph.yaxis_configure(command=self.yaxisCommand)
+ elementtype.invoke('mixed')
+ bufferelements.invoke('buffered')
+
+ def yaxisCommand(self, graph, value):
+ try:
+ num = string.atoi(value)
+ return '%d %3d' % (num * 3, num)
+ except ValueError:
+ num = string.atof(value)
+ return '%g %3g' % (num * 3, num)
+
+ def _setelementtype(self, type):
+ elements = self._graph.element_names()
+ apply(self._graph.element_delete, elements)
+
+ if type == 'none':
+ return
+
+ colorList = Pmw.Color.spectrum(self._numElements)
+ for elem in range(self._numElements):
+ if elem == 0:
+ hue = None
+ else:
+ hue = (elem + 1.0) / self._numElements * 6.28318
+ foreground = colorList[elem]
+ background = Pmw.Color.changebrightness(self, foreground, 0.8)
+ if type == 'mixed':
+ if elem < self._numElements / 2:
+ bar = 0
+ else:
+ bar = 1
+ elif type == 'bars':
+ bar = 1
+ else:
+ bar = 0
+ if bar:
+ self._graph.bar_create(
+ 'var' + str(elem),
+ xdata=self._vector_x,
+ ydata=self._vector_y[elem],
+ foreground = foreground,
+ background = background)
+ else:
+ self._graph.line_create(
+ 'var' + str(elem),
+ linewidth = 4,
+ xdata=self._vector_x,
+ ydata=self._vector_y[elem],
+ smooth = self.smooth.getcurselection(),
+ color = foreground)
+
+ def _setbarmode(self, tag):
+ self._graph.configure(barmode = tag)
+
+ def _setsmooth(self, tag):
+ for element in self._graph.element_show():
+ if self._graph.element_type(element) == 'line':
+ self._graph.element_configure(element, smooth = tag)
+
+ def _setbufferelements(self, tag):
+ self._graph.configure(bufferelements = (tag == 'buffered'))
+
+ def _setsortelements(self, tag):
+ element_list = list(self._graph.element_show())
+ if len(element_list) > 1:
+ if (tag == 'normal') == (element_list[-1] != 'var0'):
+ element_list.reverse()
+ self._graph.element_show(element_list)
+
+ def _addpoint(self):
+ self._vector_x.append(self._vectorSize)
+ for y in range(self._numElements):
+ self._vector_y[y].append(random() % 100)
+ self._vectorSize = self._vectorSize + 1
+
+class Demo:
+ def __init__(self, parent):
+ if not Pmw.Blt.haveblt(parent):
+ message = 'Sorry\nThe BLT package has not been\n' + \
+ 'installed on this system.\n' + \
+ 'Please install it and try again.'
+ w = Tkinter.Label(parent, text = message)
+ w.pack(padx = 8, pady = 8)
+ return
+
+ message = 'This is a simple demonstration of the\n' + \
+ 'BLT graph widget.\n' + \
+ 'Select the number of points to display and\n' + \
+ 'click on the button to display the graph.'
+ w = Tkinter.Label(parent, text = message)
+ w.pack(padx = 8, pady = 8)
+
+ # Create combobox to select number of points to display.
+ self.combo = Pmw.ComboBox(parent,
+ scrolledlist_items = ('10', '25', '50', '100', '300'),
+ entryfield_value = '10')
+ self.combo.pack(padx = 8, pady = 8)
+
+ # Create button to start blt graph.
+ start = Tkinter.Button(parent,
+ text = 'Show BLT graph',
+ command = Pmw.busycallback(self.showGraphDemo))
+ start.pack(padx = 8, pady = 8)
+
+ self.parent = parent
+
+ def showGraphDemo(self):
+ size = string.atoi(self.combo.get())
+ demo = GraphDemo(self.parent, size = size)
+ demo.focus()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Blt Tabset demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ if not Pmw.Blt.haveblt(parent):
+ message = 'Sorry\nThe BLT package has not been\n' + \
+ 'installed on this system.\n' + \
+ 'Please install it and try again.'
+ w = Tkinter.Label(parent, text = message)
+ w.pack(padx = 8, pady = 8)
+ return
+
+ self.tabset = Pmw.Blt.Tabset(parent,
+ borderwidth = 0,
+ highlightthickness = 0,
+ selectpad = 0,
+ tiers = 2,
+ )
+ background = self.tabset.cget('background')
+ self.tabset.configure(selectbackground = background,
+ tabbackground = background, activebackground = background)
+
+ configurePanel = Tkinter.Frame(self.tabset)
+ sideMenu = Pmw.OptionMenu (configurePanel,
+ labelpos = 'w',
+ label_text = 'Side:',
+ items = ('top', 'bottom', 'left', 'right'),
+ menubutton_width = 10,
+ command = self.changeSide,
+ )
+ sideMenu.pack(anchor = 'w', padx = 10, pady = 10)
+
+ rotateMenu = Pmw.ComboBox(configurePanel,
+ labelpos = 'w',
+ label_text = 'Text rotation:',
+ entryfield_validate = 'integer',
+ entry_width = 8,
+ selectioncommand = self.rotateText,
+ scrolledlist_items = (0, 45, 90, 135, 180, 225, 270, 315),
+ )
+ rotateMenu.pack(side = 'left', padx = 10, pady = 10)
+
+ rotateMenu.selectitem(0)
+ self.rotateText('0')
+
+ self.appearancePanel = Tkinter.Label(self.tabset)
+ helpersPanel = Tkinter.Button(self.tabset,
+ text = 'This is a lot\nof help!')
+
+ self.tabset.insert('end',
+ 'Appearance', 'Configure', 'Helpers', 'Images')
+
+ self.tabset.tab_configure('Appearance',
+ command = self.appearance_cb, fill = 'both')
+ self.tabset.tab_configure('Configure', window = configurePanel)
+ self.tabset.tab_configure('Images',
+ command = self.images_cb, fill = 'both')
+ self.tabset.tab_configure('Helpers',
+ window = helpersPanel, padx = 100, pady = 150)
+
+ self.tabset.invoke(1)
+ self.tabset.pack(fill = 'both', expand = 1, padx = 5, pady = 5)
+ self.tabset.focus()
+
+ def appearance_cb(self):
+ self.appearancePanel.configure(
+ text = 'Don\'t judge a book\nby it\'s cover.')
+ self.tabset.tab_configure('Appearance', window = self.appearancePanel)
+
+ def images_cb(self):
+ self.appearancePanel.configure(text = 'Beauty is only\nskin deep.')
+ self.tabset.tab_configure('Images', window = self.appearancePanel)
+
+ def changeSide(self, side):
+ self.tabset.configure(side = side)
+
+ def rotateText(self, angle):
+ if Pmw.integervalidator(angle) == Pmw.OK:
+ self.tabset.configure(rotate = angle)
+ else:
+ self.tabset.bell()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ButtonBox demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the ButtonBox.
+ self.buttonBox = Pmw.ButtonBox(parent,
+ labelpos = 'nw',
+ label_text = 'ButtonBox:',
+ frame_borderwidth = 2,
+ frame_relief = 'groove')
+ self.buttonBox.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+
+ # Add some buttons to the ButtonBox.
+ self.buttonBox.add('OK', command = self.ok)
+ self.buttonBox.add('Apply', command = self.apply)
+ self.buttonBox.add('Cancel', command = self.cancel)
+
+ # Set the default button (the one executed when <Return> is hit).
+ self.buttonBox.setdefault('OK')
+ parent.bind('<Return>', self._processReturnKey)
+ parent.focus_set()
+
+ # Make all the buttons the same width.
+ self.buttonBox.alignbuttons()
+
+ def _processReturnKey(self, event):
+ self.buttonBox.invoke()
+
+ def ok(self):
+ print 'You clicked on OK'
+
+ def apply(self):
+ print 'You clicked on Apply'
+
+ def cancel(self):
+ print 'You clicked on Cancel'
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Colorscheme demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ frame = Tkinter.Frame(parent)
+ frame.pack(fill = 'both', expand = 1)
+
+ defaultPalette = Pmw.Color.getdefaultpalette(parent)
+
+ colors = ('red', 'green', 'blue')
+ items = ('Testing', 'More testing', 'a test', 'foo', 'blah')
+ for count in range(len(colors)):
+ color = colors[count]
+ normalcolor = Pmw.Color.changebrightness(parent, color, 0.85)
+ Pmw.Color.setscheme(parent, normalcolor)
+ combo = Pmw.ComboBox(frame,
+ scrolledlist_items = items,
+ entryfield_value = items[0])
+ combo.grid(sticky='nsew', row = count, column = 0)
+
+ normalcolor = Pmw.Color.changebrightness(parent, color, 0.35)
+ Pmw.Color.setscheme(parent, normalcolor, foreground = 'white')
+ combo = Pmw.ComboBox(frame,
+ scrolledlist_items = items,
+ entryfield_value = items[0])
+ combo.grid(sticky='nsew', row = count, column = 1)
+
+ apply(Pmw.Color.setscheme, (parent,), defaultPalette)
+ #normalcolor = Pmw.Color.changebrightness(parent, 'red', 0.85)
+ #Pmw.Color.setscheme(parent, normalcolor)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ComboBox demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ parent.configure(background = 'white')
+
+ # Create and pack the widget to be configured.
+ self.target = Tkinter.Label(parent,
+ relief = 'sunken',
+ padx = 20,
+ pady = 20,
+ )
+ self.target.pack(fill = 'x', padx = 8, pady = 8)
+
+ # Create and pack the simple ComboBox.
+ words = ('Monti', 'Python', 'ik', 'den', 'Holie', 'Grailen', '(Bok)')
+ simple = Pmw.ComboBox(parent,
+ label_text = 'Simple ComboBox:',
+ labelpos = 'nw',
+ selectioncommand = self.changeText,
+ scrolledlist_items = words,
+ dropdown = 0,
+ )
+ simple.pack(side = 'left', fill = 'both',
+ expand = 1, padx = 8, pady = 8)
+
+ # Display the first text.
+ first = words[0]
+ simple.selectitem(first)
+ self.changeText(first)
+
+ # Create and pack the dropdown ComboBox.
+ colours = ('cornsilk1', 'snow1', 'seashell1', 'antiquewhite1',
+ 'bisque1', 'peachpuff1', 'navajowhite1', 'lemonchiffon1',
+ 'ivory1', 'honeydew1', 'lavenderblush1', 'mistyrose1')
+ dropdown = Pmw.ComboBox(parent,
+ label_text = 'Dropdown ComboBox:',
+ labelpos = 'nw',
+ selectioncommand = self.changeColour,
+ scrolledlist_items = colours,
+ )
+ dropdown.pack(side = 'left', anchor = 'n',
+ fill = 'x', expand = 1, padx = 8, pady = 8)
+
+ # Display the first colour.
+ first = colours[0]
+ dropdown.selectitem(first)
+ self.changeColour(first)
+
+ def changeColour(self, colour):
+ print 'Colour: ' + colour
+ self.target.configure(background = colour)
+
+ def changeText(self, text):
+ print 'Text: ' + text
+ self.target.configure(text = text)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ComboBoxDialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog.
+ self.dialog = Pmw.ComboBoxDialog(parent,
+ title = 'My ComboBoxDialog',
+ buttons = ('OK', 'Cancel'),
+ defaultbutton = 'OK',
+ combobox_labelpos = 'n',
+ label_text = 'What do you think of Pmw?',
+ scrolledlist_items = ('Cool man', 'Cool', 'Good', 'Bad', 'Gross'))
+ self.dialog.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent,
+ text = 'Show combo box dialog',
+ command = self.doit)
+ w.pack(padx = 8, pady = 8)
+
+ def doit(self):
+ result = self.dialog.activate()
+ print 'You clicked on', result, self.dialog.get()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
+
--- /dev/null
+title = 'Component python class configuration demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class MyButton(Tkinter.Button):
+ # This is just an ordinary button with special colors.
+
+ def __init__(self, master=None, cnf={}, **kw):
+ self.__toggle = 0
+ kw['background'] = 'green'
+ kw['activebackground'] = 'red'
+ apply(Tkinter.Button.__init__, (self, master, cnf), kw)
+
+class Demo:
+ def __init__(self, parent):
+
+ # Create a title label:
+ label = Tkinter.Label(parent,
+ text = 'EntryFields with label components of specified type:')
+ label.pack(fill='x', expand=1, padx=10, pady=5)
+
+ # Create and pack some EntryFields.
+ entries = []
+ entry = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Label'
+ )
+ entry.pack(fill='x', expand=1, padx=10, pady=5)
+ entries.append(entry)
+
+ entry = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_pyclass = Tkinter.Button,
+ label_text = 'Button'
+ )
+ entry.pack(fill='x', expand=1, padx=10, pady=5)
+ entries.append(entry)
+
+ entry = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_pyclass = MyButton,
+ label_text = 'Special button'
+ )
+ entry.pack(fill='x', expand=1, padx=10, pady=5)
+ entries.append(entry)
+
+ Pmw.alignlabels(entries)
+
+ # Create and pack a ButtonBox.
+ buttonBox = Pmw.ButtonBox(parent,
+ labelpos = 'nw',
+ label_text = 'ButtonBox:')
+ buttonBox.pack(fill = 'both', expand = 1, padx=10, pady=5)
+
+ # Add some buttons to the ButtonBox.
+ buttonBox.add('with a')
+ buttonBox.add('special', pyclass = MyButton)
+ buttonBox.add('button')
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.Counter demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import time
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Need to use long ints here because on the Macintosh the maximum size
+ # of an integer is smaller than the value returned by time.time().
+ now = (long(time.time()) / 300) * 300
+
+ # Create the Counters.
+ self._date = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Date (4-digit year):',
+ entryfield_value =
+ time.strftime('%d/%m/%Y', time.localtime(now)),
+ entryfield_command = self.execute,
+ entryfield_validate = {'validator' : 'date', 'format' : 'dmy'},
+ datatype = {'counter' : 'date', 'format' : 'dmy', 'yyyy' : 1})
+
+ self._isodate = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'ISO-Date (4-digit year):',
+ entryfield_value =
+ time.strftime('%Y-%m-%d', time.localtime(now)),
+ entryfield_command = self.execute,
+ entryfield_validate = {'validator' : 'date', 'format' : 'ymd',
+ 'separator' : '-' },
+ datatype = {'counter' : 'date', 'format' : 'ymd', 'yyyy' : 1,
+ 'separator' : '-' })
+
+ self._time = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Time:',
+ entryfield_value =
+ time.strftime('%H:%M:%S', time.localtime(now)),
+ entryfield_validate = {'validator' : 'time',
+ 'min' : '00:00:00', 'max' : '23:59:59',
+ 'minstrict' : 0, 'maxstrict' : 0},
+ datatype = {'counter' : 'time', 'time24' : 1},
+ increment=5*60)
+ self._real = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Real (with comma)\nand extra\nlabel lines:',
+ label_justify = 'left',
+ entryfield_value = '1,5',
+ datatype = {'counter' : 'real', 'separator' : ','},
+ entryfield_validate = {'validator' : 'real',
+ 'min' : '-2,0', 'max' : '5,0',
+ 'separator' : ','},
+ increment = 0.1)
+ self._custom = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Custom:',
+ entryfield_value = specialword[:4],
+ datatype = _custom_counter,
+ entryfield_validate = _custom_validate)
+ self._int = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Vertical integer:',
+ orient = 'vertical',
+ entry_width = 2,
+ entryfield_value = 50,
+ entryfield_validate = {'validator' : 'integer',
+ 'min' : 0, 'max' : 99}
+ )
+
+ counters = (self._date, self._isodate, self._time, self._real,
+ self._custom)
+ Pmw.alignlabels(counters)
+
+ # Pack them all.
+ for counter in counters:
+ counter.pack(fill='both', expand=1, padx=10, pady=5)
+ self._int.pack(padx=10, pady=5)
+
+ def execute(self):
+ print 'Return pressed, value is', self._date.get()
+
+specialword = 'Monti Python ik den Holie Grailen (Bok)'
+
+def _custom_validate(text):
+ if string.find(specialword, text) == 0:
+ return 1
+ else:
+ return -1
+
+def _custom_counter(text, factor, increment):
+ # increment is ignored here.
+ if string.find(specialword, text) == 0:
+ length = len(text)
+ if factor == 1:
+ if length >= len(specialword):
+ raise ValueError, 'maximum length reached'
+ return specialword[:length + 1]
+ else:
+ if length == 0:
+ raise ValueError, 'empty string'
+ return specialword[:length - 1]
+ else:
+ raise ValueError, 'bad string ' + text
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.CounterDialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog to prompt for the number of times to ring the bell.
+ self.dialog = Pmw.CounterDialog(parent,
+ label_text = 'Enter the number of times to\n' + \
+ 'sound the bell (1 to 5)\n',
+ counter_labelpos = 'n',
+ entryfield_value = 2,
+ counter_datatype = 'numeric',
+ entryfield_validate =
+ {'validator' : 'numeric', 'min' : 1, 'max' : 5},
+ buttons = ('OK', 'Cancel'),
+ defaultbutton = 'OK',
+ title = 'Bell ringing',
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show counter dialog',
+ command = self.dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self, result):
+ if result is None or result == 'Cancel':
+ print 'Bell ringing cancelled'
+ self.dialog.deactivate()
+ else:
+ count = self.dialog.get()
+ if not self.dialog.valid():
+ print 'Invalid entry: "' + count + '"'
+ else:
+ print 'Ringing the bell ' + count + ' times'
+ for num in range(string.atoi(count)):
+ if num != 0:
+ self.dialog.after(200)
+ self.dialog.bell()
+ self.dialog.deactivate()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+# Set the version of Pmw to use for the demonstrations based on the
+# directory name.
+
+import imp
+import os
+import string
+
+def expandLinks(path):
+ if not os.path.isabs(path):
+ path = os.path.join(os.getcwd(), path)
+ while 1:
+ if not os.path.islink(path):
+ break
+ dir = os.path.dirname(path)
+ path = os.path.join(dir, os.readlink(path))
+
+ return path
+
+def setPmwVersion():
+ file = imp.find_module(__name__)[1]
+ file = os.path.normpath(file)
+ file = expandLinks(file)
+
+ dir = os.path.dirname(file)
+ dir = expandLinks(dir)
+ dir = os.path.dirname(dir)
+ dir = expandLinks(dir)
+ dir = os.path.basename(dir)
+
+ version = string.replace(dir[4:], '_', '.')
+ import Pmw
+ if version in Pmw.installedversions():
+ Pmw.setversion(version)
+ else:
+ print 'No such Pmw version', `version` + '.',
+ print 'Using default version', `Pmw.version()`
--- /dev/null
+title = 'Pmw.Dialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create two buttons to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show application modal dialog',
+ command = self.showAppModal)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Show global modal dialog',
+ command = self.showGlobalModal)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Show dialog with "no grab"',
+ command = self.showDialogNoGrab)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text =
+ 'Show toplevel window which\n' +
+ 'will not get a busy cursor',
+ command = self.showExcludedWindow)
+ w.pack(padx = 8, pady = 8)
+
+ # Create the dialog.
+ self.dialog = Pmw.Dialog(parent,
+ buttons = ('OK', 'Apply', 'Cancel', 'Help'),
+ defaultbutton = 'OK',
+ title = 'My dialog',
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Add some contents to the dialog.
+ w = Tkinter.Label(self.dialog.interior(),
+ text = 'Pmw Dialog\n(put your widgets here)',
+ background = 'black',
+ foreground = 'white',
+ pady = 20)
+ w.pack(expand = 1, fill = 'both', padx = 4, pady = 4)
+
+ # Create the window excluded from showbusycursor.
+ self.excluded = Pmw.MessageDialog(parent,
+ title = 'I still work',
+ message_text =
+ 'This window will not get\n' +
+ 'a busy cursor when modal dialogs\n' +
+ 'are activated. In addition,\n' +
+ 'you can still interact with\n' +
+ 'this window when a "no grab"\n' +
+ 'modal dialog is displayed.')
+ self.excluded.withdraw()
+ Pmw.setbusycursorattributes(self.excluded.component('hull'),
+ exclude = 1)
+
+ def showAppModal(self):
+ self.dialog.activate(geometry = 'centerscreenalways')
+
+ def showGlobalModal(self):
+ self.dialog.activate(globalMode = 1)
+
+ def showDialogNoGrab(self):
+ self.dialog.activate(globalMode = 'nograb')
+
+ def showExcludedWindow(self):
+ self.excluded.show()
+
+ def execute(self, result):
+ print 'You clicked on', result
+ if result not in ('Apply', 'Help'):
+ self.dialog.deactivate(result)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.EntryField demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import time
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the EntryFields.
+ self._any = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Any:',
+ validate = None,
+ command = self.execute)
+ self._real = Pmw.EntryField(parent,
+ labelpos = 'w',
+ value = '55.5',
+ label_text = 'Real (10.0 to 99.0):',
+ validate = {'validator' : 'real',
+ 'min' : 10, 'max' : 99, 'minstrict' : 0},
+ modifiedcommand = self.changed)
+ self._odd = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Odd length:',
+ validate = self.custom_validate,
+ value = 'ABC')
+ self._date = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Date (in 2000):',
+ value = '2000/2/29',
+ validate = {'validator' : 'date',
+ 'min' : '2000/1/1', 'max' : '2000/12/31',
+ 'minstrict' : 0, 'maxstrict' : 0,
+ 'format' : 'ymd'},
+ )
+ now = time.localtime(time.time())
+ self._date2 = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Date (d.m.y):',
+ value = '%d.%d.%d' % (now[2], now[1], now[0]),
+ validate = {'validator' : 'date',
+ 'format' : 'dmy', 'separator' : '.'},
+ )
+ self._time = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Time (24hr clock):',
+ value = '8:00:00',
+ validate = {'validator' : 'time',
+ 'min' : '00:00:00', 'max' : '23:59:59',
+ 'minstrict' : 0, 'maxstrict' : 0},
+ )
+ self._comma = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Real (with comma):',
+ value = '123,456',
+ validate = {'validator' : 'real', 'separator' : ','},
+ )
+
+ entries = (self._any, self._real, self._odd, self._date, self._date2,
+ self._time, self._comma)
+
+ for entry in entries:
+ entry.pack(fill='x', expand=1, padx=10, pady=5)
+ Pmw.alignlabels(entries)
+
+ self._any.component('entry').focus_set()
+
+ def changed(self):
+ print 'Text changed, value is', self._real.getvalue()
+
+ def execute(self):
+ print 'Return pressed, value is', self._any.getvalue()
+
+ # This implements a custom validation routine. It simply checks
+ # if the string is of odd length.
+ def custom_validate(self, text):
+ print 'text:', text
+ if len(text) % 2 == 0:
+ return -1
+ else:
+ return 1
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw error handling demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create two buttons to generate errors.
+ w = Tkinter.Button(parent, text = 'Click here to generate\n' +
+ 'an error in a command callback.', command = self.execute)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Click here to generate\n' +
+ 'an error in a callback called\nfrom an event binding.')
+ w.pack(padx = 8, pady = 8)
+ w.bind('<ButtonRelease-1>', self.execute)
+ w.bind('<Key-space>', self.execute)
+
+ def execute(self, event = None):
+ self._error()
+
+ def _error(self):
+ # Divide by zero
+ 1/0
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.EXAMPLE demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+
+ # Create and pack the EXAMPLEs.
+ self.widget1 = Pmw.Counter(parent)
+ self.widget1.setentry('1')
+ self.widget1.pack()
+
+ self.widget2 = Pmw.Counter(parent, increment = 10)
+ self.widget2.setentry('100')
+ self.widget2.pack()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Grid geometry manager demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ frame = Tkinter.Frame(parent)
+ frame.pack(fill = 'both', expand = 1)
+
+ button = {}
+ for num in range(0, 10):
+ button[num] = Tkinter.Button(frame, text = 'Button ' + str(num))
+
+ button[0].grid(column=0, row=0, rowspan=2, sticky='nsew')
+ button[1].grid(column=1, row=0, columnspan=3, sticky='nsew')
+ button[2].grid(column=1, row=1, rowspan=2, sticky='nsew')
+ button[3].grid(column=2, row=1)
+ button[4].grid(column=3, row=1)
+ button[5].grid(column=0, row=2)
+ button[6].grid(column=0, row=3, columnspan=2, sticky='nsew')
+ button[7].grid(column=2, row=2, columnspan=2, rowspan=2, sticky='nsew')
+ button[8].grid(column=0, row=4)
+ button[9].grid(column=3, row=4, sticky='e')
+
+ frame.grid_rowconfigure(3, weight=1)
+ frame.grid_columnconfigure(3, weight=1)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.Group demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+
+ # Create and pack the Groups.
+ w = Pmw.Group(parent, tag_text='label')
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(),
+ text = 'A group with the\ndefault Label tag')
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ w = Pmw.Group(parent, tag_pyclass = None)
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(), text = 'A group\nwithout a tag')
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ radiogroups = []
+ self.var = Tkinter.IntVar()
+ self.var.set(0)
+ radioframe = Tkinter.Frame(parent)
+ w = Pmw.Group(radioframe,
+ tag_pyclass = Tkinter.Radiobutton,
+ tag_text='radiobutton 1',
+ tag_value = 0,
+ tag_variable = self.var)
+ w.pack(fill = 'both', expand = 1, side='left')
+ cw = Tkinter.Frame(w.interior(),width=200,height=20)
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+ radiogroups.append(w)
+
+ w = Pmw.Group(radioframe,
+ tag_pyclass = Tkinter.Radiobutton,
+ tag_text='radiobutton 2',
+ tag_font = Pmw.logicalfont('Helvetica', 4),
+ tag_value = 1,
+ tag_variable = self.var)
+ w.pack(fill = 'both', expand = 1, side='left')
+ cw = Tkinter.Frame(w.interior(),width=200,height=20)
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+ radiogroups.append(w)
+ radioframe.pack(padx = 6, pady = 6, expand='yes', fill='both')
+ Pmw.aligngrouptags(radiogroups)
+
+ w = Pmw.Group(parent,
+ tag_pyclass = Tkinter.Checkbutton,
+ tag_text='checkbutton',
+ tag_foreground='blue')
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Frame(w.interior(),width=150,height=20)
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ w = Pmw.Group(parent,
+ tag_pyclass = Tkinter.Button,
+ tag_text='Tkinter.Button')
+ w.configure(tag_command = w.toggle)
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(),
+ background = 'aliceblue',
+ text = 'A group with\na Button tag!?'
+ )
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ w = Pmw.Group(parent,
+ tag_pyclass = Tkinter.Button,
+ tag_text='Show/Hide')
+ w.configure(tag_command = w.toggle)
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(),
+ background = 'aliceblue',
+ text = 'Now you see me.\nNow you don\'t.'
+ )
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.HistoryText demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the PanedWidget to hold the query and result
+ # windows.
+ # !! panedwidget should automatically size to requested size
+ panedWidget = Pmw.PanedWidget(parent,
+ orient = 'vertical',
+ hull_height = 400,
+ hull_width = 550)
+ panedWidget.add('query', min = 0.05, size = 0.2)
+ panedWidget.add('buttons', min = 0.1, max = 0.1)
+ panedWidget.add('results', min = 0.05)
+ panedWidget.pack(fill = 'both', expand = 1)
+
+ # Create and pack the HistoryText.
+ self.historyText = Pmw.HistoryText(panedWidget.pane('query'),
+ text_wrap = 'none',
+ text_width = 60,
+ text_height = 10,
+ historycommand = self.statechange,
+ )
+ self.historyText.pack(fill = 'both', expand = 1)
+ self.historyText.component('text').focus()
+
+ buttonList = (
+ [20, None],
+ ['Clear', self.clear],
+ ['Undo', self.historyText.undo],
+ ['Redo', self.historyText.redo],
+ [20, None],
+ ['Prev', self.historyText.prev],
+ ['Next', self.historyText.next],
+ [30, None],
+ ['Execute', Pmw.busycallback(self.executeQuery)],
+ )
+ self.buttonDict = {}
+
+ buttonFrame = panedWidget.pane('buttons')
+ for text, cmd in buttonList:
+ if type(text) == type(69):
+ frame = Tkinter.Frame(buttonFrame, width = text)
+ frame.pack(side = 'left')
+ else:
+ button = Tkinter.Button(buttonFrame, text = text, command = cmd)
+ button.pack(side = 'left')
+ self.buttonDict[text] = button
+
+ for text in ('Prev', 'Next'):
+ self.buttonDict[text].configure(state = 'disabled')
+
+ self.results = Pmw.ScrolledText(panedWidget.pane('results'), text_wrap = 'none')
+ self.results.pack(fill = 'both', expand = 1)
+
+ def statechange(self, prevstate, nextstate):
+ self.buttonDict['Prev'].configure(state = prevstate)
+ self.buttonDict['Next'].configure(state = nextstate)
+
+ def clear(self):
+ self.historyText.delete('1.0', 'end')
+
+ def addnewlines(self, text):
+ if len(text) == 1:
+ text = text + '\n'
+ if text[-1] != '\n':
+ text = text + '\n'
+ if text[-2] != '\n':
+ text = text + '\n'
+ return text
+
+ def executeQuery(self):
+ sql = self.historyText.get()
+ self.results.insert('end', 'Query:\n' + self.addnewlines(sql))
+ self.results.see('end')
+ self.results.update_idletasks()
+ self.historyText.addhistory()
+ results = 'Results:\nfoo'
+ if len(results) > 0:
+ self.results.insert('end', self.addnewlines(results))
+ self.results.see('end')
+
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.LabeledWidget demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+
+ # Create a frame to put the LabeledWidgets into
+ frame = Tkinter.Frame(parent, background = 'grey90')
+ frame.pack(fill = 'both', expand = 1)
+
+ # Create and pack the LabeledWidgets.
+ column = 0
+ row = 0
+ for pos in ('n', 'nw', 'wn', 'w'):
+ lw = Pmw.LabeledWidget(frame,
+ labelpos = pos,
+ label_text = pos + ' label')
+ lw.component('hull').configure(relief='sunken', borderwidth=2)
+ lw.grid(column=column, row=row, padx=10, pady=10)
+ cw = Tkinter.Button(lw.interior(), text='child\nsite')
+ cw.pack(padx=10, pady=10, expand='yes', fill='both')
+
+ # Get ready for next grid position.
+ column = column + 1
+ if column == 2:
+ column = 0
+ row = row + 1
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ widget = Demo(root)
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack()
+ root.mainloop()
--- /dev/null
+title = 'Pmw LogicalFont demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+class Demo:
+
+ # The fonts to demonstrate.
+ fontList = (
+ (('Times', 0), {}),
+ (('Helvetica', 0), {}),
+ (('Typewriter', 0), {}),
+ (('Fixed', 0), {}),
+ (('Courier', 0), {}),
+ (('Helvetica', 2), {'slant' : 'italic'}),
+ (('Helvetica', 0), {'size' : 18}),
+ (('Helvetica', 0), {'weight' : 'bold'}),
+ (('Helvetica', 12), {'weight' : 'bold', 'slant' : 'italic'}),
+ (('Typewriter', 0), {'size' : 8, 'weight' : 'bold'}),
+ (('Fixed', 0), {'size' : 8, 'weight' : 'bold'}),
+ (('Times', 0), {'size' : 24, 'weight' : 'bold', 'slant' : 'italic'}),
+ (('Typewriter', 0), {'width' : 'condensed'}),
+ (('Typewriter', -1), {'width' : 'condensed'}),
+ (('Fixed', 0), {'width' : 'condensed'}),
+ (('Fixed', -1), {'width' : 'condensed'}),
+ (('Helvetica', 0), {'weight' : 'bogus'}),
+ )
+
+ fontText = []
+
+ def __init__(self, parent):
+
+ self.parent = parent
+
+ # Create the text to display to the user to represent each font.
+ if Demo.fontText == []:
+ for args, dict in Demo.fontList:
+ text = args[0]
+ if args[1] != 0:
+ text = text + ' ' + str(args[1])
+ for name, value in dict.items():
+ text = text + ' ' + name + ': ' + str(value)
+ Demo.fontText.append(text)
+
+ # Create a listbox to contain the font selections.
+ self.box = Pmw.ScrolledListBox(parent, listbox_selectmode='single',
+ listbox_width = 35,
+ listbox_height = 10,
+ items=Demo.fontText,
+ label_text='Font', labelpos='nw',
+ selectioncommand=self.selectionCommand)
+ self.box.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+
+ # Create a label to display the selected font.
+ self.target = Tkinter.Label(parent,
+ text = 'The quick brown fox jumps\nover the lazy dog',
+ relief = 'sunken', padx = 10, pady = 10)
+ self.target.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+
+ def selectionCommand(self):
+ sel = self.box.curselection()
+ if len(sel) > 0:
+ args, dict = Demo.fontList[string.atoi(sel[0])]
+ font = apply(Pmw.logicalfont, args, dict)
+ self.target.configure(font = font)
+ print font
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.MainMenuBar demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create button to launch the toplevel with main menubar.
+ w = Tkinter.Button(parent, text = 'Show Pmw.MainMenuBar demo',
+ command = lambda parent=parent: MainMenuBarToplevel(parent))
+ w.pack(padx = 8, pady = 8)
+
+class MainMenuBarToplevel:
+ def __init__(self, parent):
+ # Create the toplevel to contain the main menubar.
+ megaToplevel = Pmw.MegaToplevel(parent, title = title)
+ toplevel = megaToplevel.interior()
+
+ # Create the Balloon for this toplevel.
+ self.balloon = Pmw.Balloon(toplevel)
+
+ # Create and install the MenuBar.
+ menuBar = Pmw.MainMenuBar(toplevel,
+ balloon = self.balloon)
+ toplevel.configure(menu = menuBar)
+ self.menuBar = menuBar
+
+ # Add some buttons to the MainMenuBar.
+ menuBar.addmenu('File', 'Close this window or exit')
+ menuBar.addmenuitem('File', 'command', 'Close this window',
+ command = PrintOne('Action: close'),
+ label = 'Close')
+ menuBar.addmenuitem('File', 'separator')
+ menuBar.addmenuitem('File', 'command', 'Exit the application',
+ command = PrintOne('Action: exit'),
+ label = 'Exit')
+
+ menuBar.addmenu('Edit', 'Cut, copy or paste')
+ menuBar.addmenuitem('Edit', 'command', 'Delete the current selection',
+ command = PrintOne('Action: delete'),
+ label = 'Delete')
+
+ menuBar.addmenu('Options', 'Set user preferences')
+ menuBar.addmenuitem('Options', 'command', 'Set general preferences',
+ command = PrintOne('Action: general options'),
+ label = 'General...')
+
+ # Create a checkbutton menu item.
+ self.toggleVar = Tkinter.IntVar()
+ # Initialise the checkbutton to 1:
+ self.toggleVar.set(1)
+ menuBar.addmenuitem('Options', 'checkbutton', 'Toggle me on/off',
+ label = 'Toggle',
+ command = self._toggleMe,
+ variable = self.toggleVar)
+ self._toggleMe()
+
+ menuBar.addcascademenu('Options', 'Size',
+ 'Set some other preferences', traverseSpec = 'z', tearoff = 1)
+ for size in ('tiny', 'small', 'average', 'big', 'huge'):
+ menuBar.addmenuitem('Size', 'command', 'Set size to ' + size,
+ command = PrintOne('Action: size ' + size),
+ label = size)
+
+ menuBar.addmenu('Help', 'User manuals', name = 'help')
+ menuBar.addmenuitem('Help', 'command', 'About this application',
+ command = PrintOne('Action: about'),
+ label = 'About...')
+
+ # Create and pack the main part of the window.
+ self.mainPart = Tkinter.Label(toplevel,
+ text = 'This is the\nmain part of\nthe window',
+ background = 'black',
+ foreground = 'white',
+ padx = 30,
+ pady = 30)
+ self.mainPart.pack(fill = 'both', expand = 1)
+
+ # Create and pack the MessageBar.
+ self.messageBar = Pmw.MessageBar(toplevel,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ self.messageBar.pack(fill = 'x', padx = 10, pady = 10)
+ self.messageBar.message('state',
+ 'Balloon/status help not working properly - Tk menubar bug')
+
+ buttonBox = Pmw.ButtonBox(toplevel)
+ buttonBox.pack(fill = 'x')
+ buttonBox.add('Disable\nall', command = menuBar.disableall)
+ buttonBox.add('Enable\nall', command = menuBar.enableall)
+ buttonBox.add('Create\nmenu', command = self.add)
+ buttonBox.add('Delete\nmenu', command = self.delete)
+ buttonBox.add('Create\nitem', command = self.additem)
+ buttonBox.add('Delete\nitem', command = self.deleteitem)
+
+ # Configure the balloon to displays its status messages in the
+ # message bar.
+ self.balloon.configure(statuscommand = self.messageBar.helpmessage)
+
+ self.testMenuList = []
+
+ def _toggleMe(self):
+ print 'Toggle value:', self.toggleVar.get()
+
+ def add(self):
+ if len(self.testMenuList) == 0:
+ num = 0
+ else:
+ num = self.testMenuList[-1]
+ num = num + 1
+ name = 'Menu%d' % num
+ self.testMenuList.append(num)
+
+ self.menuBar.addmenu(name, 'This is ' + name)
+
+ def delete(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[0]
+ name = 'Menu%d' % num
+ del self.testMenuList[0]
+ self.menuBar.deletemenu(name)
+
+ def additem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName)
+ if menu.index('end') is None:
+ label = 'item X'
+ else:
+ label = menu.entrycget('end', 'label') + 'X'
+ self.menuBar.addmenuitem(menuName, 'command', 'Help for ' + label,
+ command = PrintOne('Action: ' + menuName + ': ' + label),
+ label = label)
+
+ def deleteitem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName)
+ if menu.index('end') is None:
+ self.menuBar.bell()
+ else:
+ self.menuBar.deletemenuitems(menuName, 0)
+
+class PrintOne:
+ def __init__(self, text):
+ self.text = text
+
+ def __call__(self):
+ print self.text
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.MenuBar demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the Balloon.
+ self.balloon = Pmw.Balloon(parent)
+
+ # Create and pack the MenuBar.
+ menuBar = Pmw.MenuBar(parent,
+ hull_relief = 'raised',
+ hull_borderwidth = 1,
+ balloon = self.balloon)
+ menuBar.pack(fill = 'x')
+ self.menuBar = menuBar
+
+ # Add some buttons to the MenuBar.
+ menuBar.addmenu('File', 'Close this window or exit')
+ menuBar.addmenuitem('File', 'command', 'Close this window',
+ command = PrintOne('Action: close'),
+ label = 'Close')
+ menuBar.addmenuitem('File', 'separator')
+ menuBar.addmenuitem('File', 'command', 'Exit the application',
+ command = PrintOne('Action: exit'),
+ label = 'Exit')
+
+ menuBar.addmenu('Edit', 'Cut, copy or paste')
+ menuBar.addmenuitem('Edit', 'command', 'Delete the current selection',
+ command = PrintOne('Action: delete'),
+ label = 'Delete')
+
+ menuBar.addmenu('Options', 'Set user preferences')
+ menuBar.addmenuitem('Options', 'command', 'Set general preferences',
+ command = PrintOne('Action: general options'),
+ label = 'General...')
+
+ # Create a checkbutton menu item.
+ self.toggleVar = Tkinter.IntVar()
+ # Initialise the checkbutton to 1:
+ self.toggleVar.set(1)
+ menuBar.addmenuitem('Options', 'checkbutton', 'Toggle me on/off',
+ label = 'Toggle',
+ command = self._toggleMe,
+ variable = self.toggleVar)
+ self._toggleMe()
+
+ menuBar.addcascademenu('Options', 'Size',
+ 'Set some other preferences', traverseSpec = 'z', tearoff = 1)
+ for size in ('tiny', 'small', 'average', 'big', 'huge'):
+ menuBar.addmenuitem('Size', 'command', 'Set size to ' + size,
+ command = PrintOne('Action: size ' + size),
+ label = size)
+
+ menuBar.addmenu('Help', 'User manuals', side = 'right')
+ menuBar.addmenuitem('Help', 'command', 'About this application',
+ command = PrintOne('Action: about'),
+ label = 'About...')
+
+ # Create and pack the main part of the window.
+ self.mainPart = Tkinter.Label(parent,
+ text = 'This is the\nmain part of\nthe window',
+ background = 'black',
+ foreground = 'white',
+ padx = 30,
+ pady = 30)
+ self.mainPart.pack(fill = 'both', expand = 1)
+
+ # Create and pack the MessageBar.
+ self.messageBar = Pmw.MessageBar(parent,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ self.messageBar.pack(fill = 'x', padx = 10, pady = 10)
+ self.messageBar.message('state', 'OK')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(fill = 'x')
+ buttonBox.add('Disable\nall', command = menuBar.disableall)
+ buttonBox.add('Enable\nall', command = menuBar.enableall)
+ buttonBox.add('Create\nmenu', command = self.add)
+ buttonBox.add('Delete\nmenu', command = self.delete)
+ buttonBox.add('Create\nitem', command = self.additem)
+ buttonBox.add('Delete\nitem', command = self.deleteitem)
+
+ # Configure the balloon to displays its status messages in the
+ # message bar.
+ self.balloon.configure(statuscommand = self.messageBar.helpmessage)
+
+ self.testMenuList = []
+
+ def _toggleMe(self):
+ print 'Toggle value:', self.toggleVar.get()
+
+ def add(self):
+ if len(self.testMenuList) == 0:
+ num = 0
+ else:
+ num = self.testMenuList[-1]
+ num = num + 1
+ name = 'Menu%d' % num
+ self.testMenuList.append(num)
+
+ self.menuBar.addmenu(name, 'This is ' + name)
+
+ def delete(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[0]
+ name = 'Menu%d' % num
+ del self.testMenuList[0]
+ self.menuBar.deletemenu(name)
+
+ def additem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName + '-menu')
+ if menu.index('end') is None:
+ label = 'item X'
+ else:
+ label = menu.entrycget('end', 'label') + 'X'
+ self.menuBar.addmenuitem(menuName, 'command', 'Help for ' + label,
+ command = PrintOne('Action: ' + menuName + ': ' + label),
+ label = label)
+
+ def deleteitem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName + '-menu')
+ if menu.index('end') is None:
+ self.menuBar.bell()
+ else:
+ self.menuBar.deletemenuitems(menuName, 0)
+
+class PrintOne:
+ def __init__(self, text):
+ self.text = text
+
+ def __call__(self):
+ print self.text
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.MessageBar demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the MessageBar.
+ self._messagebar = Pmw.MessageBar(parent,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ self._messagebar.pack(side = 'bottom', fill = 'x',
+ expand = 1, padx = 10, pady = 10)
+
+ # Create and pack the ScrolledListBox to change the MessageBar.
+ self.box = Pmw.ScrolledListBox(parent,
+ listbox_selectmode='single',
+ items=('state', 'help', 'userevent', 'systemevent',
+ 'usererror', 'systemerror', 'busy',),
+ label_text='Message type',
+ labelpos='n',
+ selectioncommand=self.selectionCommand)
+ self.box.pack(fill = 'both', expand = 'yes', padx = 10, pady = 10)
+
+ self._index = 0
+ self._stateCounter = 0
+
+ def selectionCommand(self):
+ sels = self.box.getcurselection()
+ if len(sels) > 0:
+ self._index = self._index + 1
+ messagetype = sels[0]
+ if messagetype == 'state':
+ self._stateCounter = (self._stateCounter + 1) % 3
+ text = stateMessages[self._stateCounter]
+ if text != '':
+ text = text + ' (' + messagetype + ')'
+ self._messagebar.message('state', text)
+ else:
+ text = messages[messagetype]
+ text = text + ' (' + messagetype + ')'
+ self._messagebar.message(messagetype, text)
+ if messagetype == 'busy':
+ Pmw.showbusycursor()
+ self.box.after(2000)
+ Pmw.hidebusycursor()
+ self._messagebar.resetmessages('busy')
+ text = 'All files successfully removed'
+ text = text + ' (userevent)'
+ self._messagebar.message('userevent', text)
+
+
+messages = {
+ 'help': 'Save current file',
+ 'userevent': 'Saving file "foo"',
+ 'busy': 'Busy deleting all files from file system ...',
+ 'systemevent': 'File "foo" saved',
+ 'usererror': 'Invalid file name "foo/bar"',
+ 'systemerror': 'Failed to save file: file system full',
+}
+
+stateMessages = {
+ 0: '',
+ 1: 'Database is down',
+ 2: 'Waiting for reply from database',
+}
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.MessageDialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ self.parent = parent
+
+ # Create dialog 1.
+ self.dialog1 = Pmw.MessageDialog(parent,
+ title = 'Simple message dialog',
+ defaultbutton = 0,
+ message_text = 'A simple message dialog\nwith no callback.')
+ self.dialog1.iconname('Simple message dialog')
+ self.dialog1.withdraw()
+
+ # Create dialog 2.
+ self.dialog2 = Pmw.MessageDialog(parent,
+ title = 'Bell ringing dialog',
+ message_text = 'This message dialog\nwill ring the bell ' +
+ 'when\nyou click on the buttons.',
+ iconpos = 'w',
+ icon_bitmap = 'error',
+ command = self.execute2,
+ buttons = ('One', 'Two', 'Three', 'Close'))
+ self.dialog2.iconname('Bell ringing dialog')
+ self.dialog2.withdraw()
+
+ # Create dialog 3.
+ self.dialog3 = Pmw.MessageDialog(parent,
+ title = 'Vertical button dialog',
+ message_text = 'This message dialog\nhas the buttons on the\n' +
+ 'right hand side.',
+ buttonboxpos = 'e',
+ iconpos = 'n',
+ icon_bitmap = 'warning',
+ buttons = ('Goodbye', 'Au revoir', 'Sayonara', 'Close'),
+ defaultbutton = 'Close')
+ self.dialog3.iconname('Vertical button dialog')
+ self.dialog3.withdraw()
+
+ # Create some buttons to launch the dialogs.
+ w = Tkinter.Button(parent, text = 'Simple dialog',
+ command = lambda self = self:
+ self.dialog1.activate(geometry = 'first+100+100'))
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Bell ringing dialog',
+ command = self.dialog2.activate)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Vertical buttons',
+ command = self.dialog3.activate)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'On the fly dialog',
+ command = self._createOnTheFly)
+ w.pack(padx = 8, pady = 8)
+
+ def execute2(self, result):
+ print 'You clicked on', result
+ if result is None:
+ self.dialog2.deactivate(result)
+ elif result == 'Close':
+ self.dialog2.deactivate(result)
+ else:
+ for count in range({'One': 1, 'Two': 2, 'Three': 3}[result]):
+ if count != 0:
+ self.dialog2.after(200)
+ self.dialog2.bell()
+
+ def _createOnTheFly(self):
+ dialog = Pmw.MessageDialog(self.parent,
+ title = 'On the fly dialog',
+ defaultbutton = 0,
+ buttons = ('OK', 'Apply', 'Cancel', 'Help'),
+ message_text = 'This dialog was created when you clicked ' +
+ 'on the button.')
+ dialog.iconname('Simple message dialog')
+ result = dialog.activate()
+
+ print 'You selected', result
+
+
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw toplevel megawidget demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class MessageInfo(Pmw.MegaToplevel):
+
+ # Demo Pmw toplevel megawidget.
+
+ def __init__(self, parent=None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = ()
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaToplevel.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ self._dismiss = self.createcomponent('dismiss',
+ (), None,
+ Tkinter.Button, (interior,),
+ text = 'Dismiss',
+ command = self.goodbye)
+ self._dismiss.pack(side = 'bottom', pady = 4)
+
+ self._separator = self.createcomponent('separator',
+ (), None,
+ Tkinter.Frame, (interior,),
+ height = 2,
+ borderwidth = 1,
+ relief = 'sunken')
+ self._separator.pack(side = 'bottom', fill = 'x', pady = 4)
+
+ self._icon = self.createcomponent('icon',
+ (), None,
+ Tkinter.Label, (interior,))
+ self._icon.pack(side = 'left', padx = 8, pady = 8)
+
+ self._infoFrame = self.createcomponent('infoframe',
+ (), None,
+ Tkinter.Frame, (interior,))
+ self._infoFrame.pack(
+ side = 'left',
+ fill = 'both',
+ expand = 1,
+ padx = 4,
+ pady = 4)
+
+ self._message = self.createcomponent('message',
+ (), None,
+ Tkinter.Label, (interior,))
+ self._message.pack(expand = 1, fill = 'both', padx = 10, pady = 10)
+
+ self.bind('<Return>', self.goodbye)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def goodbye(self, event = None):
+ self.destroy()
+
+class Demo:
+ def __init__(self, parent):
+ # Create button to launch the megawidget.
+ self.button = Tkinter.Button(parent,
+ command = self.showMessageInfo,
+ text = 'Show toplevel megawidget')
+ self.button.pack(padx = 8, pady = 8)
+
+ self.count = 0
+ self.parent = parent
+
+ def showMessageInfo(self):
+ bitmaps = ('warning', 'hourglass', 'error', 'info',
+ 'gray25', 'gray50', 'question', 'questhead')
+ bitmap = bitmaps[self.count % len(bitmaps)]
+
+ message = 'This is a demonstration of\na megawidget.\n' + \
+ 'It contains a configurable\nmessage area and bitmap.\n' + \
+ 'This instance is displaying\nthe "' + bitmap + '" bitmap.'
+
+ # Make the toplevel window a child of this window, so that it
+ # is destroyed when the demo is destroyed.
+ MessageInfo(self.parent, message_text = message, icon_bitmap = bitmap)
+
+ self.count = self.count + 1
+ if self.count == 1:
+ self.button.configure(text = 'Show another\ntoplevel megawidget')
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Multi-line label demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+
+ frame = Tkinter.Frame(parent, background = '#eeeeee')
+ frame.pack(fill = 'both', expand = 1, padx = 5, pady = 5)
+
+ stickys = ('n', 's', 'e', 'w', 'ns', 'ew', 'ne', 'nw', 'se', 'sw',
+ 'nsw', 'nse', 'new', 'sew', 'nsew',)
+
+ widgets = []
+ row = 0
+ column = 0
+
+ # Choose one megawidget class to demonstrate:
+ cls = Pmw.EntryField
+ # cls = Pmw.Counter
+ # cls = Pmw.ComboBox
+ # cls = Pmw.LabeledWidget
+ # cls = Pmw.MessageBar
+
+ for sticky in stickys:
+ dict = {}
+ dict['sticky'] = sticky
+ dict['labelpos'] = 'w'
+ dict['label_text'] = '1\n' + sticky + ':\n3'
+ if cls == Pmw.EntryField:
+ dict['value'] = sticky
+ dict['entry_width'] = 6
+ if cls == Pmw.Counter or cls == Pmw.ComboBox:
+ dict['entryfield_value'] = sticky
+ dict['entry_width'] = 6
+ widget = apply(cls, (frame,), dict)
+ if cls == Pmw.LabeledWidget:
+ f = Tkinter.Button(widget.interior(), text = sticky)
+ f.pack(fill = 'both', expand = 1)
+ if cls == Pmw.MessageBar:
+ widget.message('state', sticky)
+ widget.grid(column=column, row=row, sticky='ew', padx = 10, pady = 5)
+ frame.grid_columnconfigure(column, weight=1)
+ frame.grid_rowconfigure(row, weight=1)
+
+ widgets.append(widget)
+
+ if row < 4:
+ row = row + 1
+ else:
+ row = 0
+ column = column + 1
+
+ Pmw.alignlabels(widgets, sticky = 'e')
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Modal dialog nesting demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show first dialog',
+ command = self.showFirstDialog)
+ w.pack(padx = 8, pady = 8)
+
+ self.timerId = None
+
+ self.dialog1 = Pmw.MessageDialog(parent,
+ message_text = 'This is the first modal dialog.\n' +
+ 'You can see how dialogs nest by\n' +
+ 'clicking on the "Next" button.',
+ title = 'Dialog 1',
+ buttons = ('Next', 'Cancel'),
+ defaultbutton = 'Next',
+ command = self.next_dialog)
+ self.dialog1.withdraw()
+
+ self.dialog2 = Pmw.Dialog(self.dialog1.interior(),
+ title = 'Dialog 2',
+ buttons = ('Cancel',),
+ deactivatecommand = self.cancelTimer,
+ defaultbutton = 'Cancel')
+ self.dialog2.withdraw()
+ w = Tkinter.Label(self.dialog2.interior(),
+ text = 'This is the second modal dialog.\n' +
+ 'It will automatically disappear shortly')
+ w.pack(padx = 10, pady = 10)
+
+ def showFirstDialog(self):
+ self.dialog1.activate()
+
+ def cancelTimer(self):
+ if self.timerId is not None:
+ self.dialog2.after_cancel(self.timerId)
+ self.timerId = None
+
+ def deactivateSecond(self):
+ self.timerId = None
+ self.dialog2.deactivate()
+
+ def next_dialog(self, result):
+ if result != 'Next':
+ self.dialog1.deactivate()
+ return
+
+ self.timerId = self.dialog2.after(3000, self.deactivateSecond)
+ self.dialog2.activate()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.NoteBook demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the NoteBook.
+ notebook = Pmw.NoteBook(parent)
+ notebook.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+
+ # Add the "Appearance" page to the notebook.
+ page = notebook.add('Appearance')
+ notebook.tab('Appearance').focus_set()
+
+ # Create the "Toolbar" contents of the page.
+ group = Pmw.Group(page, tag_text = 'Toolbar')
+ group.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+ b1 = Tkinter.Checkbutton(group.interior(), text = 'Show toolbar')
+ b1.grid(row = 0, column = 0)
+ b2 = Tkinter.Checkbutton(group.interior(), text = 'Toolbar tips')
+ b2.grid(row = 0, column = 1)
+
+ # Create the "Startup" contents of the page.
+ group = Pmw.Group(page, tag_text = 'Startup')
+ group.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+ home = Pmw.EntryField(group.interior(), labelpos = 'w',
+ label_text = 'Home page location:')
+ home.pack(fill = 'x', padx = 20, pady = 10)
+
+ # Add two more empty pages.
+ page = notebook.add('Helpers')
+ page = notebook.add('Images')
+
+ notebook.setnaturalsize()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ widget = Demo(root)
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack()
+ root.mainloop()
--- /dev/null
+title = 'Pmw.NoteBook demonstration (more complex)'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent, withTabs = 1):
+
+ # Repeat random number sequence for each run.
+ self.rand = 12345
+
+ # Default demo is to display a tabbed notebook.
+ self.withTabs = withTabs
+
+ # Create a frame to put everything in
+ self.mainframe = Tkinter.Frame(parent)
+ self.mainframe.pack(fill = 'both', expand = 1)
+
+ # Find current default colors
+ button = Tkinter.Button()
+ defaultbg = button.cget('background')
+ defaultfg = button.cget('foreground')
+ button.destroy()
+
+ # Create the list of colors to cycle through
+ self.colorList = []
+ self.colorList.append((defaultbg, defaultfg))
+ self.colorIndex = 0
+ for color in Pmw.Color.spectrum(6, 1.5, 1.0, 1.0, 1):
+ bg = Pmw.Color.changebrightness(self.mainframe, color, 0.85)
+ self.colorList.append((bg, 'black'))
+ bg = Pmw.Color.changebrightness(self.mainframe, color, 0.55)
+ self.colorList.append((bg, 'white'))
+
+ # Set the color to the current default
+ Pmw.Color.changecolor(self.mainframe, defaultbg, foreground = defaultfg)
+ defaultPalette = Pmw.Color.getdefaultpalette(self.mainframe)
+ Pmw.Color.setscheme(self.mainframe, defaultbg, foreground = defaultfg)
+
+ # Create the notebook, but don't pack it yet.
+ if self.withTabs:
+ tabpos = 'n'
+ else:
+ tabpos = None
+ self.notebook = Pmw.NoteBook(self.mainframe,
+ tabpos = tabpos,
+ createcommand = PrintOne('Create'),
+ lowercommand = PrintOne('Lower'),
+ raisecommand = PrintOne('Raise'),
+ hull_width = 300,
+ hull_height = 200,
+ )
+
+ # Create a buttonbox to configure the notebook and pack it first.
+ buttonbox = Pmw.ButtonBox(self.mainframe)
+ buttonbox.pack(side = 'bottom', fill = 'x')
+
+ # Add some buttons to the buttonbox to configure the notebook.
+ buttonbox.add('Insert\npage', command = self.insertpage)
+ buttonbox.add('Delete\npage', command = self.deletepage)
+ buttonbox.add('Add\nbutton', command = self.addbutton)
+ buttonbox.add('Change\ncolor', command = self.changecolor)
+ buttonbox.add('Natural\nsize', command =
+ self.notebook.setnaturalsize)
+
+ if not self.withTabs:
+ # Create the selection widget to select the page in the notebook.
+ self.optionmenu = Pmw.OptionMenu(self.mainframe,
+ menubutton_width = 10,
+ command = self.notebook.selectpage
+ )
+ self.optionmenu.pack(side = 'left', padx = 10)
+
+ # Pack the notebook last so that the buttonbox does not disappear
+ # when the window is made smaller.
+ self.notebook.pack(fill = 'both', expand = 1, padx = 5, pady = 5)
+
+ # Populate some pages of the notebook.
+ page = self.notebook.add('tmp')
+ self.notebook.delete('tmp')
+ page = self.notebook.add('Appearance')
+ if self.withTabs:
+ self.notebook.tab('Appearance').focus_set()
+ button = Tkinter.Button(page,
+ text = 'Welcome\nto\nthe\nAppearance\npage')
+ button.pack(expand = 1)
+ page = self.notebook.add('Fonts')
+ button = Tkinter.Button(page,
+ text = 'This is a very very very very wide Fonts page')
+ button.pack(expand = 1)
+ page = self.notebook.insert('Applications', before = 'Fonts')
+ button = Tkinter.Button(page, text = 'This is the Applications page')
+ button.pack(expand = 1)
+
+ # Initialise the first page and the initial colour.
+ if not self.withTabs:
+ self.optionmenu.setitems(self.notebook.pagenames())
+ apply(Pmw.Color.setscheme, (self.mainframe,), defaultPalette)
+ self.pageCounter = 0
+
+ def insertpage(self):
+ # Create a page at a random position
+
+ defaultPalette = Pmw.Color.getdefaultpalette(self.mainframe)
+ bg, fg = self.colorList[self.colorIndex]
+ Pmw.Color.setscheme(self.mainframe, bg, foreground = fg)
+
+ self.pageCounter = self.pageCounter + 1
+ before = self.randomchoice(self.notebook.pagenames() + [Pmw.END])
+ pageName = 'page%d' % self.pageCounter
+ if self.pageCounter % 5 == 0:
+ tab_text = pageName + '\nline two'
+ else:
+ tab_text = pageName
+ classes = (None, Tkinter.Button, Tkinter.Label, Tkinter.Checkbutton)
+ cls = self.randomchoice((None,) + classes)
+ if cls is None:
+ print 'Adding', pageName, 'as a frame with a button'
+ if self.withTabs:
+ page = self.notebook.insert(pageName, before, tab_text = tab_text)
+ else:
+ page = self.notebook.insert(pageName, before)
+ button = Tkinter.Button(page,
+ text = 'This is button %d' % self.pageCounter)
+ button.pack(expand = 1)
+ else:
+ print 'Adding', pageName, 'using', cls
+ if self.withTabs:
+ page = self.notebook.insert(pageName, before,
+ tab_text = tab_text,
+ page_pyclass = cls,
+ page_text = 'This is a page using\na %s' % str(cls)
+ )
+ else:
+ page = self.notebook.insert(pageName, before,
+ page_pyclass = cls,
+ page_text = 'This is a page using\na %s' % str(cls)
+ )
+ if not self.withTabs:
+ self.optionmenu.setitems(
+ self.notebook.pagenames(), self.notebook.getcurselection())
+
+ apply(Pmw.Color.setscheme, (self.mainframe,), defaultPalette)
+
+ def addbutton(self):
+ # Add a button to a random page.
+
+ defaultPalette = Pmw.Color.getdefaultpalette(self.mainframe)
+ bg, fg = self.colorList[self.colorIndex]
+ Pmw.Color.setscheme(self.mainframe, bg, foreground = fg)
+
+ framePages = []
+ for pageName in self.notebook.pagenames():
+ page = self.notebook.page(pageName)
+ if page.__class__ == Tkinter.Frame:
+ framePages.append(pageName)
+
+ if len(framePages) == 0:
+ self.notebook.bell()
+ return
+
+ pageName = self.randomchoice(framePages)
+ print 'Adding extra button to', pageName
+ page = self.notebook.page(pageName)
+ button = Tkinter.Button(page, text = 'This is an extra button')
+ button.pack(expand = 1)
+
+ apply(Pmw.Color.setscheme, (self.mainframe,), defaultPalette)
+
+ def deletepage(self):
+ # Delete a random page
+
+ pageNames = self.notebook.pagenames()
+ if len(pageNames) == 0:
+ self.notebook.bell()
+ return
+
+ pageName = self.randomchoice(pageNames)
+ print 'Deleting', pageName
+ self.notebook.delete(pageName)
+ if not self.withTabs:
+ self.optionmenu.setitems(
+ self.notebook.pagenames(), self.notebook.getcurselection())
+
+ def changecolor(self):
+ self.colorIndex = self.colorIndex + 1
+ if self.colorIndex == len(self.colorList):
+ self.colorIndex = 0
+
+ bg, fg = self.colorList[self.colorIndex]
+ print 'Changing color to', bg
+ Pmw.Color.changecolor(self.mainframe, bg, foreground = fg)
+ self.notebook.recolorborders()
+
+ # Simple random number generator.
+ def randomchoice(self, selection):
+ num = len(selection)
+ self.rand = (self.rand * 125) % 2796203
+ index = self.rand % num
+ return selection[index]
+
+class PrintOne:
+ def __init__(self, text):
+ self.text = text
+
+ def __call__(self, text):
+ print self.text, text
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ widget = Demo(root)
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack()
+ root.mainloop()
--- /dev/null
+title = 'Pmw.NoteBook demonstration (with no tabs)'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+# Reuse the NoteBook with tabs demo.
+import NoteBook_2
+
+class Demo(NoteBook_2.Demo):
+ def __init__(self, parent):
+ NoteBook_2.Demo.__init__(self, parent, withTabs = 0)
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ widget = Demo(root)
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack()
+ root.mainloop()
--- /dev/null
+title = 'Pmw.OptionMenu demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the OptionMenu megawidgets.
+ # The first one has a textvariable.
+ self.var = Tkinter.StringVar()
+ self.var.set('steamed')
+ self.method_menu = Pmw.OptionMenu(parent,
+ labelpos = 'w',
+ label_text = 'Choose method:',
+ menubutton_textvariable = self.var,
+ items = ['baked', 'steamed', 'stir fried', 'boiled', 'raw'],
+ menubutton_width = 10,
+ )
+ self.method_menu.pack(anchor = 'w', padx = 10, pady = 10)
+
+ self.vege_menu = Pmw.OptionMenu (parent,
+ labelpos = 'w',
+ label_text = 'Choose vegetable:',
+ items = ('broccoli', 'peas', 'carrots', 'pumpkin'),
+ menubutton_width = 10,
+ command = self._printOrder,
+ )
+ self.vege_menu.pack(anchor = 'w', padx = 10, pady = 10)
+
+ self.direction_menu = Pmw.OptionMenu (parent,
+ labelpos = 'w',
+ label_text = 'Menu direction:',
+ items = ('flush', 'above', 'below', 'left', 'right'),
+ menubutton_width = 10,
+ command = self._changeDirection,
+ )
+ self.direction_menu.pack(anchor = 'w', padx = 10, pady = 10)
+
+ menus = (self.method_menu, self.vege_menu, self.direction_menu)
+ Pmw.alignlabels(menus)
+
+ def _printOrder(self, vege):
+ # Can use 'self.var.get()' instead of 'getcurselection()'.
+ print 'You have chosen %s %s.' % \
+ (self.method_menu.getcurselection(), vege)
+
+ def _changeDirection(self, direction):
+ for menu in (self.method_menu, self.vege_menu, self.direction_menu):
+ menu.configure(menubutton_direction = direction)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.PanedWidget demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+
+ # Create a main PanedWidget with a few panes.
+ self.pw = Pmw.PanedWidget(parent,
+ orient='vertical',
+ hull_borderwidth = 1,
+ hull_relief = 'sunken',
+ hull_width=300,
+ hull_height=400)
+ for self.numPanes in range(4):
+ if self.numPanes == 1:
+ name = 'Fixed size'
+ pane = self.pw.add(name, min = .1, max = .1)
+ else:
+ name = 'Pane ' + str(self.numPanes)
+ pane = self.pw.add(name, min = .1, size = .25)
+ label = Tkinter.Label(pane, text = name)
+ label.pack(side = 'left', expand = 1)
+ button = Tkinter.Button(pane, text = 'Delete',
+ command = lambda s=self, n=name: s.deletePane(n))
+ button.pack(side = 'left', expand = 1)
+ # TODO: add buttons to invoke self.moveOneUp and self.moveOneUp.
+
+ self.pw.pack(expand = 1, fill='both')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(fill = 'x')
+ buttonBox.add('Add pane', command = self.addPane)
+ buttonBox.add('Move pane', command = self.move)
+ self.moveSrc = 0
+ self.moveNewPos = 1
+ self.moveBack = 0
+
+ def move(self):
+ numPanes = len(self.pw.panes())
+ if numPanes == 0:
+ print 'No panes to move!'
+ return
+
+ if self.moveSrc >= numPanes:
+ self.moveSrc = numPanes - 1
+ if self.moveNewPos >= numPanes:
+ self.moveNewPos = numPanes - 1
+ print 'Moving pane', self.moveSrc, 'to new position', self.moveNewPos
+ self.pw.move(self.moveSrc, self.moveNewPos)
+
+ self.moveSrc, self.moveNewPos = self.moveNewPos, self.moveSrc
+ if self.moveBack:
+ if self.moveNewPos == numPanes - 1:
+ self.moveNewPos = 0
+ if self.moveSrc == numPanes - 1:
+ self.moveSrc = 0
+ else:
+ self.moveSrc = self.moveSrc + 1
+ else:
+ self.moveNewPos = self.moveNewPos + 1
+ self.moveBack = not self.moveBack
+
+ def addPane(self):
+ self.numPanes = self.numPanes + 1
+ name = 'Pane ' + str(self.numPanes)
+ print 'Adding', name
+ pane = self.pw.add(name, min = .1, size = .25)
+ label = Tkinter.Label(pane, text = name)
+ label.pack(side = 'left', expand = 1)
+ button = Tkinter.Button(pane, text = 'Delete',
+ command = lambda s=self, n=name: s.deletePane(n))
+ button.pack(side = 'left', expand = 1)
+ self.pw.updatelayout()
+
+ def deletePane(self, name):
+ print 'Deleting', name
+ self.pw.delete(name)
+ self.pw.updatelayout()
+
+ def moveOneUp(self, name):
+ self.pw.move(name, name, -1)
+
+ def moveOneDown(self, name):
+ self.pw.move(name, name, 1)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.PanedWidget demonstration (pane factory)'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ self.paneCount = 0
+
+ # Create a "pane factory".
+ label = Tkinter.Label(parent,
+ pady = 10,
+ text = 'Below is a simple "pane factory".\n' +
+ 'Drag the handle on the left\nto create new panes.')
+ label.pack()
+ self.factory = Pmw.PanedWidget(parent,
+ orient='horizontal',
+ command = self.resize,
+ hull_borderwidth = 1,
+ hull_relief = 'raised',
+ hull_width=300, hull_height=200
+ )
+ self.factory.add('starter', size = 0.0)
+ self.factory.add('main')
+ button = Tkinter.Button(self.factory.pane('main'),
+ text = 'Pane\n0')
+ button.pack(expand = 1)
+ self.factory.pack(expand = 1, fill = 'both')
+
+ def resize(self, list):
+ # Remove any panes less than 2 pixel wide.
+ for i in range(len(list) - 1, 0, -1):
+ if list[i] < 2:
+ self.factory.delete(i)
+
+ # If the user has dragged the left hand handle, create a new pane.
+ if list[0] > 1:
+ self.paneCount = self.paneCount + 1
+
+ # Add a button to the new pane.
+ name = self.factory.panes()[0]
+ text = 'Pane\n' + str(self.paneCount)
+ button = Tkinter.Button(self.factory.pane(name), text = text)
+ button.pack(expand = 1)
+
+ # Create a new starter pane.
+ name = 'Pane ' + str(self.paneCount)
+ self.factory.insert(name, size=0.0)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.PromptDialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+# This may demonstrate a bug in Tk. Click on Cancel in the confirm
+# dialog and then click on OK in the password dialog. Under Solaris
+# 2.5 and python 1.5, the Cancel button in the confirm dialog is still
+# displayed active, that is, it has a lighter background.
+
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog to prompt for the password.
+ self.dialog = Pmw.PromptDialog(parent,
+ title = 'Password',
+ label_text = 'Password:',
+ entryfield_labelpos = 'n',
+ entry_show = '*',
+ defaultbutton = 0,
+ buttons = ('OK', 'Cancel'),
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Create the confirmation dialog.
+ self.confirm = Pmw.MessageDialog(
+ title = 'Are you sure?',
+ message_text = 'Are you really sure?',
+ defaultbutton = 0,
+ buttons = ('OK', 'Cancel'))
+ self.confirm.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show prompt dialog',
+ command = self.dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self, result):
+ if result is None or result == 'Cancel':
+ print 'Password prompt cancelled'
+ self.dialog.deactivate(result)
+ else:
+ result = self.confirm.activate()
+ if result == 'OK':
+ print 'Password entered ' + self.dialog.get()
+ self.dialog.deactivate()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.RadioSelect demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack a horizontal RadioSelect widget.
+ horiz = Pmw.RadioSelect(parent,
+ labelpos = 'w',
+ command = self.callback,
+ label_text = 'Horizontal',
+ frame_borderwidth = 2,
+ frame_relief = 'ridge'
+ )
+ horiz.pack(fill = 'x', padx = 10, pady = 10)
+
+ # Add some buttons to the horizontal RadioSelect.
+ for text in ('Fruit', 'Vegetables', 'Cereals', 'Legumes'):
+ horiz.add(text)
+ horiz.invoke('Cereals')
+
+ # Create and pack a multiple selection RadioSelect widget.
+ self.multiple = Pmw.RadioSelect(parent,
+ labelpos = 'w',
+ command = self.multcallback,
+ label_text = 'Multiple\nselection',
+ frame_borderwidth = 2,
+ frame_relief = 'ridge',
+ selectmode = 'multiple',
+ )
+ self.multiple.pack(fill = 'x', padx = 10)
+
+ # Add some buttons to the multiple selection RadioSelect.
+ for text in ('Apricots', 'Eggplant', 'Rice', 'Lentils'):
+ self.multiple.add(text)
+ self.multiple.invoke('Rice')
+
+ # Create and pack a vertical RadioSelect widget, with checkbuttons.
+ self.checkbuttons = Pmw.RadioSelect(parent,
+ buttontype = 'checkbutton',
+ orient = 'vertical',
+ labelpos = 'w',
+ command = self.checkbuttoncallback,
+ label_text = 'Vertical,\nusing\ncheckbuttons',
+ hull_borderwidth = 2,
+ hull_relief = 'ridge',
+ )
+ self.checkbuttons.pack(side = 'left', expand = 1, padx = 10, pady = 10)
+
+ # Add some buttons to the checkbutton RadioSelect.
+ for text in ('Male', 'Female'):
+ self.checkbuttons.add(text)
+ self.checkbuttons.invoke('Male')
+ self.checkbuttons.invoke('Female')
+
+ # Create and pack a RadioSelect widget, with radiobuttons.
+ radiobuttons = Pmw.RadioSelect(parent,
+ buttontype = 'radiobutton',
+ orient = 'vertical',
+ labelpos = 'w',
+ command = self.callback,
+ label_text = 'Vertical,\nusing\nradiobuttons',
+ hull_borderwidth = 2,
+ hull_relief = 'ridge',
+ )
+ radiobuttons.pack(side = 'left', expand = 1, padx = 10, pady = 10)
+
+ # Add some buttons to the radiobutton RadioSelect.
+ for text in ('Male', 'Female', 'Both', 'Neither'):
+ radiobuttons.add(text)
+ radiobuttons.invoke('Both')
+
+ def callback(self, tag):
+ # This is called whenever the user clicks on a button
+ # in a single select RadioSelect widget.
+ print 'Button', tag, 'was pressed.'
+
+ def multcallback(self, tag, state):
+ # This is called whenever the user clicks on a button
+ # in the multiple select RadioSelect widget.
+ if state:
+ action = 'pressed.'
+ else:
+ action = 'released.'
+
+ print 'Button', tag, 'was', action, \
+ 'Selection:', self.multiple.getcurselection()
+
+ def checkbuttoncallback(self, tag, state):
+ # This is called whenever the user clicks on a button
+ # in the checkbutton RadioSelect widget.
+ if state:
+ action = 'pressed.'
+ else:
+ action = 'released.'
+
+ print 'Button', tag, 'was', action, \
+ 'Selection:', self.checkbuttons.getcurselection()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Using Tk option database to configure Tk widgets'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+info = """
+ The Tk widgets contained in this
+ simple megawidget have been
+ configured using the Tk option
+ database.
+ *DemoClass*Listbox.cursor is 'heart'
+ *DemoClass*Entry.cursor is 'hand1'
+ *DemoClass*background is 'pink'
+ *DemoClass*highlightBackground is 'green'
+ *DemoClass*foreground is 'blue'
+"""
+
+class DemoClass(Pmw.MegaWidget):
+
+ # Demo Pmw megawidget.
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = ()
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ interior = self.interior()
+ listbox = Tkinter.Listbox(interior, height = 12, width = 40)
+ listbox.pack(fill='both', expand='yes')
+ for line in string.split(info, '\n'):
+ listbox.insert('end', line)
+
+ entry = Tkinter.Entry(interior)
+ entry.pack(fill='y')
+ entry.insert(0, 'Hello, World!')
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+class Demo:
+ def __init__(self, parent):
+
+ # Test Tk option database settings.
+ parent.option_add('*DemoClass*Listbox.cursor', 'heart')
+ parent.option_add('*DemoClass*Entry.cursor', 'hand1')
+ parent.option_add('*DemoClass*background', 'pink')
+ parent.option_add('*DemoClass*highlightBackground', 'green')
+ parent.option_add('*DemoClass*foreground', 'blue')
+
+ # Create and pack the megawidget.
+ demo = DemoClass(parent)
+ demo.pack(fill = 'both', expand = 1)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Using Tk option database to configure Pmw megawidgets'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ self.parent = parent
+
+ header = Tkinter.Label(parent, text = 'Select some Tk option ' +
+ 'database values from\nthe lists, then click ' +
+ '\'Create dialog\' to create\na MessageDialog with ' +
+ 'these values as defaults.')
+ header.pack(padx = 10, pady = 10)
+
+ # Create and pack the ComboBoxes to select options.
+ buttons = (
+ "('OK',)",
+ "('Read', 'Write')",
+ "('OK', 'Cancel')",
+ "('OK', 'Apply', 'Cancel', 'Help')",
+ )
+
+ if Tkinter.TkVersion >= 8.4:
+ disabledState = 'readonly'
+ else:
+ disabledState = 'disabled'
+
+ self._buttons = Pmw.ComboBox(parent, label_text = 'buttons:',
+ labelpos = 'w',
+ entry_state = disabledState,
+ scrolledlist_items = buttons)
+ self._buttons.pack(fill = 'x', expand = 1, padx = 8, pady = 8)
+ self._buttons.selectitem(3)
+
+ buttonboxpos = ('n', 's', 'e', 'w',)
+ self._buttonboxpos = Pmw.ComboBox(parent, label_text = 'buttonboxpos:',
+ labelpos = 'w',
+ entry_state = disabledState,
+ scrolledlist_items = buttonboxpos)
+ self._buttonboxpos.pack(fill = 'x', expand = 1, padx = 8, pady = 8)
+ self._buttonboxpos.selectitem(2)
+
+ pad = ('0', '8', '20', '50',)
+ self._pad = Pmw.ComboBox(parent, label_text = 'padx, pady:',
+ labelpos = 'w',
+ entry_state = disabledState,
+ scrolledlist_items = pad)
+ self._pad.pack(fill = 'x', expand = 1, padx = 8, pady = 8)
+ self._pad.selectitem(1)
+
+ Pmw.alignlabels((self._buttons, self._buttonboxpos, self._pad))
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Create dialog',
+ command = self._createDialog)
+ w.pack(padx = 8, pady = 8)
+
+ self.dialog = None
+
+ def _createDialog(self):
+
+ # Set the option database.
+ buttons = self._buttons.get()
+ buttonboxpos = self._buttonboxpos.get()
+ pad = self._pad.get()
+ self.parent.option_add('*MessageDialog.buttons', buttons)
+ self.parent.option_add('*MessageDialog.buttonboxpos', buttonboxpos)
+ self.parent.option_add('*ButtonBox.padx', pad)
+ self.parent.option_add('*ButtonBox.pady', pad)
+
+ # Create the dialog.
+ if self.dialog is not None:
+ self.dialog.destroy()
+
+ text = ('This dialog was created by setting the Tk ' +
+ 'option database:\n\n *MessageDialog.buttons: ' + buttons +
+ '\n *MessageDialog.buttonboxpos: ' + buttonboxpos +
+ '\n *ButtonBox.padx: ' + pad +
+ '\n *ButtonBox.pady: ' + pad)
+ self.dialog = Pmw.MessageDialog(self.parent,
+ defaultbutton = 0,
+ title = 'Pmw option database demonstration',
+ message_justify = 'left',
+ message_text = text)
+ self.dialog.iconname('Test dialog')
+
+ # Return the defaults to normal, otherwise all other demos
+ # will be affected.
+ self.parent.option_add('*MessageDialog.buttons', "('OK',)")
+ self.parent.option_add('*MessageDialog.buttonboxpos', 's')
+ self.parent.option_add('*ButtonBox.padx', 8)
+ self.parent.option_add('*ButtonBox.pady', 8)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root, useTkOptionDb = 1)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ScrolledCanvas demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the ScrolledCanvas.
+ self.sc = Pmw.ScrolledCanvas(parent,
+ borderframe = 1,
+ labelpos = 'n',
+ label_text = 'ScrolledCanvas',
+ usehullsize = 1,
+ hull_width = 400,
+ hull_height = 300,
+ )
+
+ # Create a group widget to contain the scrollmode options.
+ w = Pmw.Group(parent, tag_text='Scroll mode')
+ w.pack(side = 'bottom', padx = 5, pady = 5)
+
+ hmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.sethscrollmode,
+ menubutton_width = 8,
+ )
+ hmode.pack(side = 'left', padx = 5, pady = 5)
+ hmode.invoke('dynamic')
+
+ vmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.setvscrollmode,
+ menubutton_width = 8,
+ )
+ vmode.pack(side = 'left', padx = 5, pady = 5)
+ vmode.invoke('dynamic')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(side = 'bottom')
+ buttonBox.add('yview', text = 'Show\nyview', command = self.showYView)
+ buttonBox.add('scroll', text = 'Page\ndown', command = self.pageDown)
+ buttonBox.add('center', text = 'Center', command = self.centerPage)
+
+ # Pack this last so that the buttons do not get shrunk when
+ # the window is resized.
+ self.sc.pack(padx = 5, pady = 5, fill = 'both', expand = 1)
+
+ self.sc.component('canvas').bind('<1>', self.addcircle)
+
+ testEntry = Tkinter.Entry(parent)
+ self.sc.create_line(20, 20, 100, 100)
+ self.sc.create_oval(100, 100, 200, 200, fill = 'green')
+ self.sc.create_text(100, 20, anchor = 'nw',
+ text = 'Click in the canvas\nto draw ovals',
+ font = testEntry.cget('font'))
+ button = Tkinter.Button(self.sc.interior(),
+ text = 'Hello,\nWorld!\nThis\nis\na\nbutton.')
+ self.sc.create_window(200, 200,
+ anchor='nw',
+ window = button)
+
+ # Set the scroll region of the canvas to include all the items
+ # just created.
+ self.sc.resizescrollregion()
+
+ self.colours = ('red', 'green', 'blue', 'yellow', 'cyan', 'magenta',
+ 'black', 'white')
+ self.oval_count = 0
+ self.rand = 12345
+
+ def sethscrollmode(self, tag):
+ self.sc.configure(hscrollmode = tag)
+
+ def setvscrollmode(self, tag):
+ self.sc.configure(vscrollmode = tag)
+
+ def addcircle(self, event):
+ x = self.sc.canvasx(event.x)
+ y = self.sc.canvasy(event.y)
+ width = 10 + self.random() % 100
+ height = 10 + self.random() % 100
+ self.sc.create_oval(
+ x - width, y - height, x + width, y + height,
+ fill = self.colours[self.oval_count])
+ self.oval_count = (self.oval_count + 1) % len(self.colours)
+ self.sc.resizescrollregion()
+
+ # Simple random number generator.
+ def random(self):
+ self.rand = (self.rand * 125) % 2796203
+ return self.rand
+
+ def showYView(self):
+ print self.sc.yview()
+
+ def pageDown(self):
+ self.sc.yview('scroll', 1, 'page')
+
+ def centerPage(self):
+ top, bottom = self.sc.yview()
+ size = bottom - top
+ middle = 0.5 - size / 2
+ self.sc.yview('moveto', middle)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ScrolledField demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the ScrolledField.
+ self._field = Pmw.ScrolledField(parent, entry_width = 30,
+ entry_relief='groove', labelpos = 'n',
+ label_text = 'Scroll the field using the\nmiddle mouse button')
+ self._field.pack(fill = 'x', expand = 1, padx = 10, pady = 10)
+
+ # Create and pack a button to change the ScrolledField.
+ self._button = Tkinter.Button(parent, text = 'Change field',
+ command = self.execute)
+ self._button.pack(padx = 10, pady = 10)
+
+ self._index = 0
+ self.execute()
+
+ def execute(self):
+ self._field.configure(text = lines[self._index % len(lines)])
+ self._index = self._index + 1
+
+lines = (
+ 'Alice was beginning to get very tired of sitting by her sister',
+ 'on the bank, and of having nothing to do: once or twice she had',
+ 'peeped into the book her sister was reading, but it had no',
+ 'pictures or conversations in it, "and what is the use of a book,"',
+ 'thought Alice "without pictures or conversation?"',
+ 'Alice\'s Adventures in Wonderland',
+ 'Lewis Carroll',
+)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ScrolledFrame demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the ScrolledFrame.
+ self.sf = Pmw.ScrolledFrame(parent,
+ labelpos = 'n', label_text = 'ScrolledFrame',
+ usehullsize = 1,
+ hull_width = 400,
+ hull_height = 220,
+ )
+
+ # Create a group widget to contain the flex options.
+ w = Pmw.Group(parent, tag_text='Flex')
+ w.pack(side = 'bottom', padx = 5, pady = 3)
+
+ hflex = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['fixed', 'expand', 'shrink', 'elastic'],
+ command = self.sethflex,
+ menubutton_width = 8,
+ )
+ hflex.pack(side = 'left', padx = 5, pady = 3)
+ hflex.invoke('fixed')
+
+ vflex = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['fixed', 'expand', 'shrink', 'elastic'],
+ command = self.setvflex,
+ menubutton_width = 8,
+ )
+ vflex.pack(side = 'left', padx = 5, pady = 3)
+ vflex.invoke('fixed')
+
+ # Create a group widget to contain the scrollmode options.
+ w = Pmw.Group(parent, tag_text='Scroll mode')
+ w.pack(side = 'bottom', padx = 5, pady = 0)
+
+ hmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.sethscrollmode,
+ menubutton_width = 8,
+ )
+ hmode.pack(side = 'left', padx = 5, pady = 3)
+ hmode.invoke('dynamic')
+
+ vmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.setvscrollmode,
+ menubutton_width = 8,
+ )
+ vmode.pack(side = 'left', padx = 5, pady = 3)
+ vmode.invoke('dynamic')
+
+ self.radio = Pmw.RadioSelect(parent, selectmode = 'multiple',
+ command = self.radioSelected)
+ self.radio.add('center', text = 'Keep centered vertically')
+ self.radio.pack(side = 'bottom')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(side = 'bottom')
+ buttonBox.add('add', text = 'Add a button', command = self.addButton)
+ buttonBox.add('yview', text = 'Show yview', command = self.showYView)
+ buttonBox.add('scroll', text = 'Page down', command = self.pageDown)
+
+ # Pack this last so that the buttons do not get shrunk when
+ # the window is resized.
+ self.sf.pack(padx = 5, pady = 3, fill = 'both', expand = 1)
+
+ self.frame = self.sf.interior()
+
+ self.row = 0
+ self.col = 0
+
+ for count in range(15):
+ self.addButton()
+
+ def sethscrollmode(self, tag):
+ self.sf.configure(hscrollmode = tag)
+
+ def setvscrollmode(self, tag):
+ self.sf.configure(vscrollmode = tag)
+
+ def sethflex(self, tag):
+ self.sf.configure(horizflex = tag)
+
+ def setvflex(self, tag):
+ self.sf.configure(vertflex = tag)
+
+ def addButton(self):
+ button = Tkinter.Button(self.frame,
+ text = '(%d,%d)' % (self.col, self.row))
+ button.grid(row = self.row, column = self.col, sticky = 'nsew')
+
+ self.frame.grid_rowconfigure(self.row, weight = 1)
+ self.frame.grid_columnconfigure(self.col, weight = 1)
+ if self.sf.cget('horizflex') == 'expand' or \
+ self.sf.cget('vertflex') == 'expand':
+ self.sf.reposition()
+
+ if 'center' in self.radio.getcurselection():
+ self.sf.update_idletasks()
+ self.centerPage()
+
+ if self.col == self.row:
+ self.col = 0
+ self.row = self.row + 1
+ else:
+ self.col = self.col + 1
+
+ def showYView(self):
+ print self.sf.yview()
+
+ def pageDown(self):
+ self.sf.yview('scroll', 1, 'page')
+
+ def radioSelected(self, name, state):
+ if state:
+ self.centerPage()
+
+ def centerPage(self):
+ # Example of how to use the yview() method of Pmw.ScrolledFrame.
+ top, bottom = self.sf.yview()
+ size = bottom - top
+ middle = 0.5 - size / 2
+ self.sf.yview('moveto', middle)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ import os
+ if os.name == 'nt':
+ size = 16
+ else:
+ size = 12
+ root = Tkinter.Tk()
+ Pmw.initialise(root, size = size, fontScheme = 'pmw2')
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ScrolledListBox demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the ScrolledListBox.
+ self.box = Pmw.ScrolledListBox(parent,
+ items=('Sydney', 'Melbourne', 'Brisbane'),
+ labelpos='nw',
+ label_text='Cities',
+ listbox_height = 6,
+ selectioncommand=self.selectionCommand,
+ dblclickcommand=self.defCmd,
+ usehullsize = 1,
+ hull_width = 200,
+ hull_height = 200,
+ )
+
+ # Create a group widget to contain the scrollmode options.
+ w = Pmw.Group(parent, tag_text='Scroll mode')
+ w.pack(side = 'bottom', padx = 5, pady = 5)
+
+ hmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.sethscrollmode,
+ menubutton_width = 8,
+ )
+ hmode.pack(side = 'top', padx = 5, pady = 5)
+ hmode.invoke('dynamic')
+
+ vmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.setvscrollmode,
+ menubutton_width = 8,
+ )
+ vmode.pack(side = 'top', padx = 5, pady = 5)
+ vmode.invoke('dynamic')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(side = 'bottom')
+ buttonBox.add('yview', text = 'Show\nyview', command = self.showYView)
+ buttonBox.add('scroll', text = 'Page\ndown', command = self.pageDown)
+ buttonBox.add('center', text = 'Center', command = self.centerPage)
+
+ # Pack this last so that the buttons do not get shrunk when
+ # the window is resized.
+ self.box.pack(fill = 'both', expand = 1, padx = 5, pady = 5)
+
+ # Do this after packing the scrolled list box, so that the
+ # window does not resize as soon as it appears (because
+ # alignlabels has to do an update_idletasks).
+ Pmw.alignlabels((hmode, vmode))
+
+ # Add some more entries to the listbox.
+ items = ('Andamooka', 'Coober Pedy', 'Innamincka', 'Oodnadatta')
+ self.box.setlist(items)
+ self.box.insert(2, 'Wagga Wagga', 'Perth', 'London')
+ self.box.insert('end', 'Darwin', 'Auckland', 'New York')
+ index = list(self.box.get(0, 'end')).index('London')
+ self.box.delete(index)
+ self.box.delete(7, 8)
+ self.box.insert('end', 'Bulli', 'Alice Springs', 'Woy Woy')
+ self.box.insert('end', 'Wallumburrawang', 'Willandra Billabong')
+
+ def sethscrollmode(self, tag):
+ self.box.configure(hscrollmode = tag)
+
+ def setvscrollmode(self, tag):
+ self.box.configure(vscrollmode = tag)
+
+ def selectionCommand(self):
+ sels = self.box.getcurselection()
+ if len(sels) == 0:
+ print 'No selection'
+ else:
+ print 'Selection:', sels[0]
+
+ def defCmd(self):
+ sels = self.box.getcurselection()
+ if len(sels) == 0:
+ print 'No selection for double click'
+ else:
+ print 'Double click:', sels[0]
+
+ def showYView(self):
+ print self.box.yview()
+
+ def pageDown(self):
+ self.box.yview('scroll', 1, 'page')
+
+ def centerPage(self):
+ top, bottom = self.box.yview()
+ size = bottom - top
+ middle = 0.5 - size / 2
+ self.box.yview('moveto', middle)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ScrolledText demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import os
+import math
+import string
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+
+ # Create the ScrolledText with headers.
+ fixedFont = Pmw.logicalfont('Fixed')
+ self.st = Pmw.ScrolledText(parent,
+ # borderframe = 1,
+ labelpos = 'n',
+ label_text='ScrolledText with headers',
+ columnheader = 1,
+ rowheader = 1,
+ rowcolumnheader = 1,
+ usehullsize = 1,
+ hull_width = 400,
+ hull_height = 300,
+ text_wrap='none',
+ text_font = fixedFont,
+ Header_font = fixedFont,
+ Header_foreground = 'blue',
+ rowheader_width = 3,
+ rowcolumnheader_width = 3,
+ text_padx = 4,
+ text_pady = 4,
+ Header_padx = 4,
+ rowheader_pady = 4,
+ )
+
+ self.st.pack(padx = 5, pady = 5, fill = 'both', expand = 1)
+
+ funcs = 'atan cos cosh exp log log10 sin sinh sqrt tan tanh'
+ funcs = string.split(funcs)
+
+ # Create the header for the row headers
+ self.st.component('rowcolumnheader').insert('end', 'x')
+
+ # Create the column headers
+ headerLine = ''
+ for column in range(len(funcs)):
+ headerLine = headerLine + ('%-7s ' % (funcs[column],))
+ headerLine = headerLine[:-3]
+ self.st.component('columnheader').insert('0.0', headerLine)
+
+ self.st.tag_configure('yellow', background = 'yellow')
+
+ # Create the data rows and the row headers
+ numRows = 50
+ tagList = []
+ for row in range(1, numRows):
+ dataLine = ''
+ x = row / 5.0
+ for column in range(len(funcs)):
+ value = eval('math.' + funcs[column] + '(' + str(x) + ')')
+ data = str(value)[:7]
+ if value < 0:
+ tag1 = '%d.%d' % (row, len(dataLine))
+ tag2 = '%d.%d' % (row, len(dataLine) + len(data))
+ tagList.append(tag1)
+ tagList.append(tag2)
+ data = '%-7s' % (data,)
+ dataLine = dataLine + data + ' '
+ dataLine = dataLine[:-3]
+ header = '%.1f' % (x,)
+ if row < numRows - 1:
+ dataLine = dataLine + '\n'
+ header = header + '\n'
+ self.st.insert('end', dataLine)
+ self.st.component('rowheader').insert('end', header)
+ apply(self.st.tag_add, ('yellow',) + tuple(tagList))
+
+ # Prevent users' modifying text and headers
+ self.st.configure(
+ text_state = 'disabled',
+ Header_state = 'disabled',
+ )
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.ScrolledText demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import os
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the ScrolledText.
+ self.st = Pmw.ScrolledText(parent,
+ borderframe = 1,
+ labelpos = 'n',
+ label_text='ScrolledText.py',
+ usehullsize = 1,
+ hull_width = 400,
+ hull_height = 300,
+ text_padx = 10,
+ text_pady = 10,
+ text_wrap='none'
+ )
+
+ # Create a group widget to contain the scrollmode options.
+ w = Pmw.Group(parent, tag_text='Scroll mode')
+ w.pack(side = 'bottom', padx = 5, pady = 5)
+
+ hmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.sethscrollmode,
+ menubutton_width = 8,
+ )
+ hmode.pack(side = 'left', padx = 5, pady = 5)
+ hmode.invoke('dynamic')
+
+ vmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.setvscrollmode,
+ menubutton_width = 8,
+ )
+ vmode.pack(side = 'left', padx = 5, pady = 5)
+ vmode.invoke('dynamic')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(side = 'bottom')
+ buttonBox.add('yview', text = 'Show\nyview', command = self.showYView)
+ buttonBox.add('scroll', text = 'Page\ndown', command = self.pageDown)
+ buttonBox.add('center', text = 'Center', command = self.centerPage)
+
+ # Pack this last so that the buttons do not get shrunk when
+ # the window is resized.
+ self.st.pack(padx = 5, pady = 5, fill = 'both', expand = 1)
+
+ # Read this file into the text widget.
+ head, tail = os.path.split(sys.argv[0])
+ self.st.importfile(os.path.join(head,'ScrolledText.py'))
+
+ self.st.insert('end', '\nThis demonstrates how to\n' +
+ 'add a window to a text widget: ')
+ counter = Pmw.Counter(self.st.component('text'),
+ entryfield_value = 9999)
+ self.st.window_create('end', window = counter)
+
+ def sethscrollmode(self, tag):
+ self.st.configure(hscrollmode = tag)
+
+ def setvscrollmode(self, tag):
+ self.st.configure(vscrollmode = tag)
+
+ def showYView(self):
+ print self.st.yview()
+
+ def pageDown(self):
+ self.st.yview('scroll', 1, 'page')
+
+ def centerPage(self):
+ top, bottom = self.st.yview()
+ size = bottom - top
+ middle = 0.5 - size / 2
+ self.st.yview('moveto', middle)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.SelectionDialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog.
+ self.dialog = Pmw.SelectionDialog(parent,
+ title = 'My SelectionDialog',
+ buttons = ('OK', 'Cancel'),
+ defaultbutton = 'OK',
+ scrolledlist_labelpos = 'n',
+ label_text = 'What do you think of Pmw?',
+ scrolledlist_items = ('Cool man', 'Cool', 'Good', 'Bad', 'Gross'),
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show selection dialog',
+ command = self.dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self, result):
+ sels = self.dialog.getcurselection()
+ if len(sels) == 0:
+ print 'You clicked on', result, '(no selection)'
+ else:
+ print 'You clicked on', result, sels[0]
+ self.dialog.deactivate(result)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Blt busy cursor demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ self.parent = parent
+
+ if Pmw.Blt.havebltbusy(parent):
+ text = 'Click here to show the\nbusy cursor for one second.'
+ else:
+ text = 'Sorry\n' \
+ 'Either the BLT package has not\n' \
+ 'been installed on this system or\n' \
+ 'it does not support the busy command.\n' \
+ 'Clicking on this button will pause\n' \
+ 'for one second but will not display\n' \
+ 'the busy cursor.'
+
+ button = Tkinter.Button(parent,
+ text = text,
+ command = Pmw.busycallback(self.sleep, parent.update))
+ button.pack(padx = 10, pady = 10)
+
+ entry = Tkinter.Entry(parent, width = 30)
+ entry.insert('end', 'Try to enter some text while busy.')
+ entry.pack(padx = 10, pady = 10)
+
+ def sleep(self):
+ self.parent.after(1000)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Subclassing Pmw.Counter'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import time
+import types
+import Tkinter
+import Pmw
+
+class LabeledDateCounter(Pmw.Counter):
+
+ def __init__(self, parent=None , **kw):
+
+ # Need to use long ints here because on the Macintosh the maximum size
+ # of an integer is smaller than the value returned by time.time().
+ now = (long(time.time()) / 300) * 300
+ text = time.strftime('%y/%m/%d', time.localtime(now))
+
+ kw['datatype'] = 'date'
+ kw['entryfield_validate'] = 'date'
+ kw['entryfield_value'] = text
+ kw['labelpos'] = 'w'
+
+ apply(Pmw.Counter.__init__, (self, parent), kw)
+
+class LabeledRealCounter(Pmw.Counter):
+
+ def __init__(self, parent=None , **kw):
+
+ # Define the validate option dictionary.
+ validate = {'validator' : 'real', 'min' : 0.0, 'max' : 100.0}
+
+ kw['datatype'] = 'real'
+ kw['entryfield_validate'] = validate
+ kw['entryfield_value'] = 50.0
+ kw['labelpos'] = 'w'
+
+ apply(Pmw.Counter.__init__, (self, parent), kw)
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack some LabeledDateCounters and LabeledRealCounter.
+ self._date1 = LabeledDateCounter(parent, label_text = 'Date:')
+ self._date2 = LabeledDateCounter(parent, label_text = 'Another Date:')
+ self._real1 = LabeledRealCounter(parent, label_text = 'Real:')
+ self._real2 = LabeledRealCounter(parent, label_text = 'Another Real:')
+
+ counters = (self._date1, self._date2, self._real1, self._real2)
+
+ for counter in counters:
+ counter.pack(fill='x', expand=1, padx=10, pady=5)
+ Pmw.alignlabels(counters)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Subclassing Pmw.EntryField'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import time
+import types
+import Tkinter
+import Pmw
+
+class SpecialEntry(Pmw.EntryField):
+
+ def __init__(self, parent=None , **kw):
+
+ kw['extravalidators'] = _myValidators
+ apply(Pmw.EntryField.__init__, (self, parent), kw)
+ self._converter = None
+
+ def setentry(self, text):
+ # Override Pmw.EntryField.setentry to pass string through
+ # the appropriate converter.
+
+ val = self['validate']
+ if type(val) == types.DictionaryType:
+ val = val['validator']
+ if _converters.has_key(val):
+ text = _converters[val](text, output = 0)
+ Pmw.EntryField.setentry(self, text)
+
+ def getentry(self):
+ text = self.get()
+ val = self['validate']
+ if type(val) == types.DictionaryType:
+ val = val['validator']
+ if _converters.has_key(val):
+ return _converters[val](text, output = 1)
+ else:
+ return text
+
+def _date(text):
+ return Pmw.datevalidator(text, 'dmy', '.')
+
+def _real(text):
+ return Pmw.realvalidator(text, ',')
+
+def _dateconv(text, output = 0):
+ # On output, convert from dd.mm.yy to mm-dd-yy. On input, convert
+ # mm-dd-yy to dd.mm.yy and also from +NN+ or -NN- to date NN days
+ # before or after today.
+
+ if len(text) == 0:
+ return ''
+ if output:
+ try:
+ d = string.split(text, '.')
+ return d[1] + '-' + d[0] + '-' + d[2]
+ except:
+ return text
+ else:
+ if text[-1] == '+' or text[-1] == '-':
+ text = text[:-1]
+ if text[0] == '+' or text[0] == '-':
+ secondsAhead = string.atoi(text) * 3600 * 24
+ return time.strftime('%d.%m.%Y',
+ time.localtime(time.time() + secondsAhead))
+ try:
+ d = string.split(text,'-')
+ return d[1] + '.' + d[0] + '.' + d[2]
+ except:
+ return text
+
+def _realconv(text, output = 0):
+ # Convert between DD.DD and DD,DD.
+
+ if output:
+ index = string.find(text, ',')
+ if index >= 0:
+ return text[:index] + '.' + text[index + 1:]
+ else:
+ return text
+ else:
+ index = string.find(text, '.')
+ if index >= 0:
+ return text[:index] + ',' + text[index + 1:]
+ else:
+ return text
+
+
+_converters = {
+ 'real' : _realconv,
+ 'float8' : _realconv,
+ 'date' : _dateconv
+}
+
+_myValidators = {
+ 'date' : (_date, lambda s: Pmw.datestringtojdn(s, 'dmy', '.')),
+ 'real' : (_real, lambda s: string.atof(_realconv(s, 1))),
+ 'int4' : ('numeric', 'numeric'),
+ 'oid' : ('int4', 'int4'),
+ 'float8' : ('real', 'real'),
+ 'varchar' : ('alphanumeric', 'alphanumeric'),
+ 'text' : ('alphanumeric', 'alphanumeric'),
+}
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the SpecialEntry megawidgets.
+ self._any = SpecialEntry(parent,
+ labelpos = 'w',
+ label_text = 'Text (max 10 chars):',
+ validate = {'validator' : 'text', 'max' : 10},
+ command = self.execute)
+ self._any.setentry('abc')
+ self._int = SpecialEntry(parent,
+ labelpos = 'w',
+ label_text = 'Int4:',
+ validate = 'int4')
+ self._int.setentry(1)
+ self._real = SpecialEntry(parent,
+ labelpos = 'w',
+ label_text = 'Real (max 2,5e+9):',
+ validate = {'validator' : 'real', 'max' : +2.5e+9},
+ )
+ self._real.setentry('+2.5e+6')
+ self._date = SpecialEntry(parent,
+ labelpos = 'w',
+ label_text = 'Date (dd.mm.yy):',
+ validate = 'date',
+ modifiedcommand = self.changed
+ )
+ # Set entry to one week from now, using special intput format.
+ self._date.setentry('+7+')
+
+ entries = (self._any, self._int, self._real, self._date)
+
+ for entry in entries:
+ entry.pack(fill='x', expand=1, padx=10, pady=5)
+ Pmw.alignlabels(entries)
+
+ self._any.component('entry').focus_set()
+
+ def changed(self):
+ print 'Text changed, converted value is', self._date.getentry()
+
+ def execute(self):
+ print 'Return pressed, value is', self._any.get()
+
+ # This implements a custom validation routine. It simply checks
+ # if the string is of odd length.
+ def custom_validate(self, text):
+ print 'text:', text
+ if len(text) % 2 == 0:
+ return -1
+ else:
+ return 1
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Color spectrum demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ parent = Tkinter.Frame(parent)
+ parent.pack(padx=10, pady=10, fill='both', expand=1)
+ self.width = 350
+ self.height = 250
+ self.canvas = Tkinter.Canvas(parent,
+ width = self.width, height = self.height)
+ self.canvas.grid(row = 0, column = 0, columnspan = 2, sticky = 'news')
+
+ self.numColors = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Number of colors:',
+ entry_width = 5,
+ validate = 'numeric',
+ command = Pmw.busycallback(self.execute))
+ self.numColors.grid(row = 1, column = 0, sticky = 'ew')
+
+ self.correction = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Correction:',
+ validate = 'real',
+ entry_width = 5,
+ command = Pmw.busycallback(self.execute))
+ self.correction.grid(row = 1, column = 1, sticky = 'ew')
+
+ self.saturation = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Saturation:',
+ validate = 'real',
+ entry_width = 5,
+ command = Pmw.busycallback(self.execute))
+ self.saturation.grid(row = 2, column = 0, sticky = 'ew')
+
+ self.intensity = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Intensity:',
+ validate = 'real',
+ entry_width = 5,
+ command = Pmw.busycallback(self.execute))
+ self.intensity.grid(row = 2, column = 1, sticky = 'ew')
+
+ self.extraOrange = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Emphasize orange (0 or 1):',
+ validate = {'validator' : 'numeric', 'min' : 0, 'max' : 1},
+ entry_width = 5,
+ command = Pmw.busycallback(self.execute))
+ self.extraOrange.grid(row = 3, column = 0, sticky = 'ew')
+
+ self.text = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Text:',
+ entry_width = 20,
+ command = Pmw.busycallback(self.execute))
+ self.text.grid(row = 4, column = 0, sticky = 'ew')
+
+ self.brightness = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Brightness:',
+ validate = 'real',
+ entry_width = 5,
+ command = Pmw.busycallback(self.execute))
+ self.brightness.grid(row = 3, column = 1, sticky = 'ew')
+
+ self.radiobuttons = Pmw.RadioSelect(parent,
+ command = Pmw.busycallback(self.radio_cb),
+ )
+ self.radiobuttons.grid(row = 4, column = 1)
+ self.radiobuttons.add('Use saturation\nand intensity')
+ self.radiobuttons.add('Use\nbrightness')
+
+ parent.grid_columnconfigure(0, weight = 1)
+ parent.grid_columnconfigure(1, weight = 1)
+ parent.grid_rowconfigure(0, weight = 1)
+
+ Pmw.alignlabels((self.numColors, self.saturation, self.extraOrange))
+ Pmw.alignlabels((self.correction, self.intensity, self.brightness))
+
+ # Set initial values for all entries.
+ self.numColors.setentry('64')
+ self.correction.setentry('1.0')
+ self.saturation.setentry('1.0')
+ self.intensity.setentry('1.0')
+ self.extraOrange.setentry('1')
+ self.brightness.setentry('0.7')
+ self.text.setentry('This is a test')
+ self.radiobuttons.invoke('Use saturation\nand intensity')
+
+ self.execute()
+
+ def radio_cb(self, value):
+ self.execute()
+
+ def execute(self):
+ try:
+ numColors = string.atoi(self.numColors.get())
+ correction = string.atof(self.correction.get())
+ saturation = string.atof(self.saturation.get())
+ intensity = string.atof(self.intensity.get())
+ extraOrange = string.atof(self.extraOrange.get())
+ brightness = string.atof(self.brightness.get())
+ except ValueError:
+ self.numColors.bell()
+ return
+
+ if numColors <= 0:
+ self.numColors.bell()
+ return
+
+ self.canvas.delete('all')
+
+ colorList = Pmw.Color.spectrum(
+ numColors, correction, saturation, intensity, extraOrange)
+ extent = 360.0 / numColors
+
+ useBrightness = \
+ (self.radiobuttons.getcurselection() == 'Use\nbrightness')
+
+ if numColors == 1:
+ # Special case circle, since create_arc does not work when
+ # extent is 360.
+ background = colorList[0]
+ if useBrightness:
+ background = Pmw.Color.changebrightness(
+ self.canvas, background, brightness)
+ self.canvas.create_oval(10, 10, self.width - 10, self.height - 10,
+ fill = background, outline = background)
+
+ for index in range(numColors):
+ start = index * extent - extent / 2
+ background = colorList[index]
+ if useBrightness:
+ background = Pmw.Color.changebrightness(
+ self.canvas, background, brightness)
+ self.canvas.create_arc(10, 10, self.width - 10, self.height - 10,
+ start = start, extent = extent,
+ fill = background, outline = background)
+
+ text = self.text.get()
+ self.canvas.create_text(self.width / 2, self.height / 3, text = text)
+ self.canvas.create_text(self.width / 2, self.height / 2, text = text)
+ self.canvas.create_text(self.width / 2, 2 * self.height / 3, text = text)
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Test of the speed of creating Pmw megawidgets'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import time
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ self.parent = parent
+
+ message = 'This is a test of the time\n' + \
+ 'it takes to create 20 Pmw\nEntryField megawidgets.\n' + \
+ 'Click on the button to create them.'
+ w = Tkinter.Label(parent, text = message)
+ w.pack(padx = 8, pady = 8)
+
+ # Create button to run speed test.
+ w = Tkinter.Button(parent,
+ text = 'Create 20 EntryFields',
+ command = self.createEntries)
+ w.pack(padx = 8, pady = 8)
+
+ def createEntries(self):
+ entryTop = Tkinter.Toplevel(self.parent)
+
+ startClock = time.clock()
+ fields = []
+ for num in range(20):
+ field = Pmw.EntryField(entryTop,
+ labelpos = 'w',
+ label_text='*' + ('*' * num),
+ hull_background = 'lightsteelblue',
+ label_background = 'lightsteelblue',
+ hull_highlightbackground = 'lightsteelblue',
+ label_highlightbackground = 'lightsteelblue',
+ entry_highlightbackground = 'lightsteelblue',
+ entry_background = 'aliceblue')
+ field.pack()
+ fields.append(field)
+
+ Pmw.alignlabels(fields)
+ print 'Time to create 20 EntryFields:', \
+ time.clock() - startClock, 'seconds'
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'More examples of subclassing'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class ExtraMethods(Pmw.EntryField):
+
+ # How to subclass a Pmw megawidget when you only want to add or
+ # override methods.
+
+ def doubletext(self):
+ self.setvalue(self.getvalue() + ' ' + self.getvalue())
+
+class OverrideInit(Pmw.EntryField):
+
+ # How to subclass a Pmw megawidget when you want to define
+ # a new __init__ method.
+
+ def __init__(self, textToAdd, parent = None, **kw):
+ self._textToAdd = textToAdd
+ apply(Pmw.EntryField.__init__, (self, parent), kw)
+
+ def addtext(self):
+ self.setvalue(self.getvalue() + ' ' + self._textToAdd)
+
+class DefaultOptions(Pmw.EntryField):
+
+ # How to subclass a Pmw megawidget when you only want to set
+ # existing options to new default values.
+
+ def __init__(self, parent = None, **kw):
+ kw['label_foreground'] = 'blue'
+ kw['entry_background'] = 'white'
+ apply(Pmw.EntryField.__init__, (self, parent), kw)
+
+class NewOptions(Pmw.EntryField):
+
+ # How to subclass a Pmw megawidget when you want to add new options.
+
+ def __init__(self, parent=None , **kw):
+
+ # Define the megawidget options.
+ optiondefs = (
+ ('backgrounds', None, self._backgrounds),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.EntryField.__init__(self, parent)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _backgrounds(self):
+ background = self['backgrounds']
+ Pmw.Color.changecolor(self.component('hull'), background)
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the megawidgets.
+ self._extraMethod = ExtraMethods(parent,
+ labelpos = 'w',
+ label_text = 'Sub class with extra method:',
+ value = 'Hello'
+ )
+ self._overrideInit = OverrideInit('Again', parent,
+ labelpos = 'w',
+ label_text = 'Sub class with new __init__ method:',
+ value = 'Hello'
+ )
+ self._defaultOptions = DefaultOptions(parent,
+ labelpos = 'w',
+ label_text = 'Sub class with new default options:',
+ value = 'Hello'
+ )
+
+ self._newOptions = NewOptions(parent,
+ labelpos = 'w',
+ label_text = 'Sub class with new option:',
+ value = 'Hello',
+ backgrounds = 'white',
+ )
+
+ entries = (self._extraMethod, self._overrideInit,
+ self._defaultOptions, self._newOptions)
+
+ for entry in entries:
+ entry.pack(fill='x', expand=1, padx=10, pady=5)
+ Pmw.alignlabels(entries)
+
+ bb = Pmw.ButtonBox(parent)
+ bb.add('Double text', command = self._doubleText)
+ bb.pack()
+ bb.add('Add text', command = self._addText)
+ bb.pack()
+ bb.add('White', command = self._changeColorWhite)
+ bb.pack()
+ bb.add('Green', command = self._changeColorGreen)
+ bb.pack()
+
+ def _doubleText(self):
+ self._extraMethod.doubletext()
+
+ def _addText(self):
+ self._overrideInit.addtext()
+
+ def _changeColorWhite(self):
+ self._newOptions.configure(backgrounds = 'white')
+
+ def _changeColorGreen(self):
+ self._newOptions.configure(backgrounds = 'green')
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.TextDialog demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog.
+ dialog = Pmw.TextDialog(parent, scrolledtext_labelpos = 'n',
+ title = 'My TextDialog',
+ defaultbutton = 0,
+ label_text = 'Lawyer jokes')
+ dialog.withdraw()
+ dialog.insert('end', jokes)
+ dialog.configure(text_state = 'disabled')
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show text dialog',
+ command = dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+jokes = """
+Q: What do you call 5000 dead lawyers at the bottom of the ocean?
+A: A good start!
+
+Q: How can you tell when a lawyer is lying?
+A: His lips are moving.
+
+Q: Why won't sharks attack lawyers?
+A: Professional courtesy.
+
+Q: What do have when a lawyer is buried up to his neck in sand?
+A: Not enough sand.
+
+Q: How do you get a lawyer out of a tree?
+A: Cut the rope.
+
+Q: What is the definition of a shame (as in "that's a shame")?
+A: When a bus load of lawyers goes off a cliff.
+
+Q: What is the definition of a "crying shame"?
+A: There was an empty seat.
+
+Q: What do you get when you cross the Godfather with a lawyer?
+A: An offer you can't understand.
+
+Q. What do lawyers use as contraceptives?
+A. Their personalities.
+
+Q. What's brown and black and looks good on a lawyer?
+A. A doberman.
+
+Q. Why are lawyers buried 12 feet underground?
+A. Deep down their good.
+
+Q. What's the difference between a catfish and a lawyer?
+A. One's a slimy scum-sucking scavenger, the other is just a fish.
+
+"""
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Demonstration of how to create a megawidget'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class TextDisplay(Pmw.MegaWidget):
+
+ # Demo Pmw megawidget.
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = ()
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ self._text = self.createcomponent('text',
+ (), None,
+ Tkinter.Text, (interior,), state = 'disabled')
+ self._text.pack(side='left', fill='both', expand='yes')
+
+ self._scrollbar = self.createcomponent('scrollbar',
+ (), None,
+ Tkinter.Scrollbar, (interior,), command = self._text.yview)
+ self._scrollbar.pack(side='right', fill='y')
+ self._text.configure(yscrollcommand = self._scrollbar.set)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def display(self, info):
+ self._text.configure(state = 'normal')
+ self._text.delete('1.0', 'end')
+ self._text.insert('1.0', info)
+ self._text.configure(state = 'disabled')
+
+ def append(self, info):
+ self._text.configure(state = 'normal')
+ self._text.insert('end', info)
+ self._text.configure(state = 'disabled')
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the megawidget.
+ text = TextDisplay(parent,
+ text_background = 'aliceblue',
+ text_width = 40,
+ text_height = 10,
+ text_wrap = 'none',
+ )
+ text.pack(fill = 'both', expand = 1)
+ text.display('This is an example of a simple Pmw megawidget.\n\n' +
+ 'Public attributes of the Tkinter module:\n\n')
+ for name in dir(Tkinter):
+ if name[0] != '_':
+ text.append(' ' + name + '\n')
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Pmw.TimeCounter demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import string
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ self._time = Pmw.TimeCounter(parent,
+ labelpos = 'w',
+ label_text = 'HH:MM:SS',
+ min = '00:00:00',
+ max = '23:59:59')
+ self._time.pack(padx=10, pady=5)
+
+ button = Tkinter.Button(parent, text = 'Show', command = self.show)
+ button.pack()
+
+ def show(self):
+ stringVal = self._time.getstring()
+ intVal = self._time.getint()
+ print stringVal + ' (' + str(intVal) + ')'
+
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+title = 'Demonstration of Pmw megawidget destruction'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+ # Create and pack an EntryField.
+ self.entryfield = Pmw.EntryField(parent,
+ command = self.execute,
+ value = 'Press <Return> to destroy me',
+ entry_width = 30)
+ self.entryfield.pack(fill='x', expand=1, padx=10, pady=5)
+
+ self.entryfield.component('entry').focus_set()
+
+ def execute(self):
+ print 'Return pressed, destroying EntryField.'
+ self.entryfield.destroy()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.AboutDialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.AboutDialog</h1>
+
+<center><IMG SRC=AboutDialog.gif ALT="" WIDTH=378 HEIGHT=284></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.AboutDialog() -
+ window to display version and contact information
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MessageDialog.html">Pmw.MessageDialog</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ An about dialog is a dialog window which displays information
+ about the application, such as name, version, copyright and
+ contact details.</p>
+
+<p> The text of the message is constructed from the application name
+ (given by the <strong>applicationname</strong> option) followed by the values
+ supplied in the most recent calls to <code>Pmw.aboutversion()</code>,
+ <code>Pmw.aboutcopyright()</code> and <code>Pmw.aboutcontact()</code> functions.</p>
+
+<p> The icon of the message defaults to <strong>'info'</strong>, but may be changed
+ using the <strong>icon_bitmap</strong> component option.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.applicationname></a>
+<dl><dt> <strong>applicationname
+</strong></dt><dd>
+Initialisation option. The name of application, to be dispayed in the dialog body and in
+ the window title if the <strong>title</strong> option is not given. The default is <strong>''</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderx></a>
+<dl><dt> <strong>borderx
+</strong></dt><dd>
+Initialisation option. The padding to the left and right of the text message and icon. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.bordery></a>
+<dl><dt> <strong>bordery
+</strong></dt><dd>
+Initialisation option. The padding above and below the text message and icon. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('Close',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.iconmargin></a>
+<dl><dt> <strong>iconmargin
+</strong></dt><dd>
+Initialisation option. The padding between the text message and icon. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.iconpos></a>
+<dl><dt> <strong>iconpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the text message to place the icon.
+ Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'w'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.icon></a>
+<dl><dt> <strong>icon
+</strong></dt><dd>
+If the <strong>iconpos</strong> option is not <strong>None</strong>, this component is created
+ to contain the icon label for the dialog. To display a bitmap as
+ an icon, set the <strong>icon_bitmap</strong> component option to any of the
+ forms acceptable to Tk, such as <strong>'warning'</strong> or <strong>'error'</strong>. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.message></a>
+<dl><dt> <strong>message
+</strong></dt><dd>
+The label to contain the text message for the dialog. To set
+ the text, use the <strong>message_text</strong> component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+This megawidget has no methods of its own.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MessageDialog.html#methods">Pmw.MessageDialog</a></strong>.
+<p></p>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create dialog.
+ Pmw.aboutversion('9.9')
+ Pmw.aboutcopyright('Copyright My Company 1999\nAll rights reserved')
+ Pmw.aboutcontact(
+ 'For information about this application contact:\n' +
+ ' My Help Desk\n' +
+ ' Phone: +61 2 9876 5432\n' +
+ ' email: help@my.company.com.au'
+ )
+ self.about = Pmw.AboutDialog(parent, applicationname = 'My Application')
+ self.about.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show about dialog',
+ command = self.execute)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self):
+ self.about.show()
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.Balloon reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.Balloon</h1>
+
+<center><IMG SRC=Balloon.gif ALT="" WIDTH=428 HEIGHT=189></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.Balloon() -
+ display "tool tips" for a number of widgets
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaToplevel.html">Pmw.MegaToplevel</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A balloon megawidget can be used to give short help messages to
+ the user when they place the mouse over a button or other widget
+ for a short time. It can also be used to display help messages
+ for canvas or text items.</p>
+
+<p> One balloon megawidget can be used to display help for many
+ widgets or items. For each widget or item that requires balloon
+ help, the <code>bind()</code> or <code>bindtag()</code> method is used to specify the
+ help text that should be displayed.</p>
+
+<p> The help message is displayed in a popup balloon window when the
+ mouse remains over the widget or item for a short time. The popup
+ balloon is withdrawn when the mouse leaves the widget or item, or
+ any mouse buttons are pressed.</p>
+
+<p> The position of the popup balloon is configurable and may appear
+ either relative to the widget or item or relative to the position
+ of the mouse.</p>
+
+<p> The popup balloon is displayed without any window manager
+ decorations.</p>
+
+<p> The megawidget can cooperate with a <a href="MessageBar.html">Pmw.MessageBar</a> to display a
+ single-line help message as well as the balloon help.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.initwait></a>
+<dl><dt> <strong>initwait
+</strong></dt><dd>
+The number of milliseconds delay between when the mouse enters a
+ widget or item and when the popup balloon window should be
+ displayed. The default is <strong>500</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.relmouse></a>
+<dl><dt> <strong>relmouse
+</strong></dt><dd>
+This may be one of <strong>'both'</strong>, <strong>'x'</strong>, <strong>'y'</strong> or <strong>'none'</strong> and
+ indicates that the top left corner of the popup balloon window
+ should be placed relative to the current position of the mouse
+ rather than relative to the bottom left corner of the widget or
+ item (the default). The positioning may be set for the horizontal
+ (x) and vertical (y) axes independently. The default is <strong>'none'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.state></a>
+<dl><dt> <strong>state
+</strong></dt><dd>
+This may be one of <strong>'both'</strong>, <strong>'balloon'</strong>, <strong>'status'</strong> or <strong>'none'</strong>
+ and indicates whether the help message should be displayed in the
+ popup balloon window, in an associated messagebar (via the
+ <strong>statuscommand</strong> option), or both. The default is <strong>'both'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.statuscommand></a>
+<dl><dt> <strong>statuscommand
+</strong></dt><dd>
+This specifies a function to call when the mouse enters a widget
+ or item bound to this balloon megawidget. To configure a
+ <a href="MessageBar.html">Pmw.MessageBar</a> to display help, set this option to the <code>helpmessage</code>
+ method of the messagebar. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.xoffset></a>
+<dl><dt> <strong>xoffset
+</strong></dt><dd>
+This specifies the horizontal offset of the position of the left
+ side of the popup balloon window relative the point determined by
+ the <strong>relmouse</strong> option. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.yoffset></a>
+<dl><dt> <strong>yoffset
+</strong></dt><dd>
+This specifies the vertical offset of the position of the top of
+ the popup balloon window relative the point determined by the
+ <strong>relmouse</strong> option. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+This component displays the text of the help message in the popup
+ balloon window. By default it is created with a <strong>'lightyellow'</strong>
+ background, a <strong>'black'</strong> foreground and is <strong>'left'</strong> justified. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaToplevel.html#methods">Pmw.MegaToplevel</a></strong>.
+<p></p>
+<a name=method.bind></a>
+<dl><dt> <strong>bind</strong>(<em>widget</em>, <em>balloonHelp</em>, <em>statusHelp</em> = <strong>None</strong>)</dt><dd>
+Create bindings for <em>widget</em> so that balloon help and/or status
+ help is displayed when the mouse enters the widget. The balloon
+ help message is given by <em>balloonHelp</em> and the status help message
+ is given by <em>statusHelp</em>. If <em>balloonHelp</em> is <strong>None</strong>, no balloon
+ is displayed. If <em>statusHelp</em> is not set, it defaults to
+ <em>balloonHelp</em>. Any previous bindings for this widget are removed.</p>
+
+
+</dd></dl>
+<a name=method.clearstatus></a>
+<dl><dt> <strong>clearstatus</strong>()</dt><dd>
+Clear the text in the associated messagebar by passing <strong>None</strong> to
+ the <strong>statuscommand</strong> function.</p>
+
+
+</dd></dl>
+<a name=method.showstatus></a>
+<dl><dt> <strong>showstatus</strong>(<em>statusHelp</em>)</dt><dd>
+Set the text in the associated messagebar by passing <em>statusHelp</em>
+ to the <strong>statuscommand</strong> function.</p>
+
+
+</dd></dl>
+<a name=method.tagbind></a>
+<dl><dt> <strong>tagbind</strong>(<em>widget</em>, <em>tagOrItem</em>, <em>balloonHelp</em>, <em>statusHelp</em> = <strong>None</strong>)</dt><dd>
+Create bindings for the tag or item specified by <em>tagOrItem</em> in
+ the text or canvas <em>widget</em> so that balloon help and/or status
+ help is displayed when the mouse enters the tag or item. The
+ balloon help message is given by <em>balloonHelp</em> and the status help
+ message is given by <em>statusHelp</em>. If <em>balloonHelp</em> is <strong>None</strong>, no
+ balloon is displayed. If <em>statusHelp</em> is not set, it defaults to
+ <em>balloonHelp</em>. Any previous bindings for this tag or item are
+ removed.</p>
+
+
+</dd></dl>
+<a name=method.tagunbind></a>
+<dl><dt> <strong>tagunbind</strong>(<em>widget</em>, <em>tagOrItem</em>)</dt><dd>
+Remove the balloon help bindings from the tag or item specified by
+ <em>tagOrItem</em> in the text or canvas <em>widget</em>.</p>
+<p> Note that <code>tagunbind()</code> must be called when deleting a canvas
+ item, so that the popup balloon window can be withdrawn if it was
+ triggered by the item. (Unfortunately this can not be automated
+ as is done for widgets since Tk does not support <code><Destroy></code>
+ bindings on canvas items, so there is no way that Pmw.Balloon can
+ be notified of the deletion of an item.)</p>
+
+
+
+</dd></dl>
+<a name=method.unbind></a>
+<dl><dt> <strong>unbind</strong>(<em>widget</em>)</dt><dd>
+Remove the balloon help bindings from <em>widget</em>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the Balloon.
+ self.balloon = Pmw.Balloon(parent)
+
+ # Create some widgets and megawidgets with balloon help.
+ frame = Tkinter.Frame(parent)
+ frame.pack(padx = 10, pady = 5)
+ field = Pmw.EntryField(frame,
+ labelpos = 'nw',
+ label_text = 'Command:')
+ field.setentry('mycommand -name foo')
+ field.pack(side = 'left', padx = 10)
+ self.balloon.bind(field, 'Command to\nstart/stop',
+ 'Enter the shell command to control')
+
+ start = Tkinter.Button(frame, text='Start')
+ start.pack(side='left', padx = 10)
+ self.balloon.bind(start, 'Start the command')
+
+ stop = Tkinter.Button(frame, text='Stop')
+ stop.pack(side='left', padx = 10)
+ self.balloon.bind(stop, 'Stop the command')
+
+ self.suicide = Tkinter.Button(frame, text='Kill me soon!',
+ command = self.killButton)
+ self.suicide.pack(side='left', padx = 10)
+ self.balloon.bind(self.suicide, 'Watch this button disappear!')
+
+ scrolledCanvas = Pmw.ScrolledCanvas(parent,
+ canvas_width = 300,
+ canvas_height = 115,
+ )
+ scrolledCanvas.pack()
+ canvas = scrolledCanvas.component('canvas')
+ self.canvas = canvas
+
+ # Create some canvas items and individual help.
+ item = canvas.create_arc(5, 5, 35, 35, fill = 'red', extent = 315)
+ self.balloon.tagbind(canvas, item, 'This is help for\nan arc item')
+ item = canvas.create_bitmap(20, 150, bitmap = 'question')
+ self.balloon.tagbind(canvas, item, 'This is help for\na bitmap')
+ item = canvas.create_line(50, 60, 70, 80, 85, 20, width = 5)
+ self.balloon.tagbind(canvas, item, 'This is help for\na line item')
+ item = canvas.create_text(10, 90, text = 'Canvas items with balloons',
+ anchor = 'nw', font = field.cget('entry_font'))
+ self.balloon.tagbind(canvas, item, 'This is help for\na text item')
+
+ # Create two canvas items which have the same tag and which use
+ # the same help.
+ canvas.create_rectangle(100, 10, 170, 50, fill = 'aliceblue',
+ tags = 'TAG1')
+ self.bluecircle = canvas.create_oval(110, 30, 160, 80, fill = 'blue',
+ tags = 'TAG1')
+ self.balloon.tagbind(canvas, 'TAG1',
+ 'This is help for the two blue items' + '\n' * 10 +
+ 'It is very, very big.',
+ 'This is help for the two blue items')
+ item = canvas.create_text(180, 10, text = 'Delete',
+ anchor = 'nw', font = field.cget('entry_font'))
+ self.balloon.tagbind(canvas, item,
+ 'After 2 seconds,\ndelete the blue circle')
+ canvas.tag_bind(item, '<ButtonPress>', self._canvasButtonpress)
+ scrolledCanvas.resizescrollregion()
+
+ scrolledText = Pmw.ScrolledText(parent,
+ text_width = 32,
+ text_height = 4,
+ text_wrap = 'none',
+ )
+ scrolledText.pack(pady = 5)
+ text = scrolledText.component('text')
+ self.text = text
+
+ text.insert('end',
+ 'This is a text widget with ', '',
+ ' balloon', 'TAG1',
+ '\nhelp. Find the ', '',
+ ' text ', 'TAG1',
+ ' tagged with', '',
+ ' help.', 'TAG2',
+ '\n', '',
+ 'Remove tag 1.', 'TAG3',
+ '\nAnother line.\nAnd another', '',
+ )
+ text.tag_configure('TAG1', borderwidth = 2, relief = 'sunken')
+ text.tag_configure('TAG3', borderwidth = 2, relief = 'raised')
+
+ self.balloon.tagbind(text, 'TAG1',
+ 'There is one secret\nballoon help.\nCan you find it?')
+ self.balloon.tagbind(text, 'TAG2',
+ 'Well done!\nYou found it!')
+ self.balloon.tagbind(text, 'TAG3',
+ 'After 2 seconds\ndelete the tag')
+ text.tag_bind('TAG3', '<ButtonPress>', self._textButtonpress)
+
+ frame = Tkinter.Frame(parent)
+ frame.pack(padx = 10)
+ self.toggleBalloonVar = Tkinter.IntVar()
+ self.toggleBalloonVar.set(1)
+ toggle = Tkinter.Checkbutton(frame,
+ variable = self.toggleBalloonVar,
+ text = 'Balloon help', command = self.toggle)
+ toggle.pack(side = 'left', padx = 10)
+ self.balloon.bind(toggle, 'Toggle balloon help\non and off')
+
+ self.toggleStatusVar = Tkinter.IntVar()
+ self.toggleStatusVar.set(1)
+ toggle = Tkinter.Checkbutton(frame,
+ variable = self.toggleStatusVar,
+ text = 'Status help', command = self.toggle)
+ toggle.pack(side = 'left', padx = 10)
+ self.balloon.bind(toggle,
+ 'Toggle status help on and off, on and off' + '\n' * 10 +
+ 'It is very, very big, too.',
+ 'Toggle status help on and off')
+
+ # Create and pack the MessageBar.
+ messageBar = Pmw.MessageBar(parent,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ messageBar.pack(fill = 'x', expand = 1, padx = 10, pady = 5)
+
+ # Configure the balloon to display its status messages in the
+ # message bar.
+ self.balloon.configure(statuscommand = messageBar.helpmessage)
+
+ def toggle(self):
+ if self.toggleBalloonVar.get():
+ if self.toggleStatusVar.get():
+ self.balloon.configure(state = 'both')
+ else:
+ self.balloon.configure(state = 'balloon')
+ else:
+ if self.toggleStatusVar.get():
+ self.balloon.configure(state = 'status')
+ else:
+ self.balloon.configure(state = 'none')
+
+ def killButton(self):
+ # Test for old bug when destroying widgets 1) while the
+ # balloon was up and 2) during the initwait period.
+ print 'Destroying button in 2 seconds'
+ self.suicide.after(2000, self.suicide.destroy)
+
+ def _canvasButtonpress(self, event):
+ print 'Destroying blue circle in 2 seconds'
+ self.canvas.after(2000, self.deleteBlueCircle)
+
+ def deleteBlueCircle(self):
+ self.balloon.tagunbind(self.canvas, self.bluecircle)
+ self.canvas.delete(self.bluecircle)
+
+ def _textButtonpress(self, event):
+ print 'Deleting the text tag in 2 seconds'
+ self.text.after(2000, self.deleteTextTag)
+
+ def deleteTextTag(self):
+ self.balloon.tagunbind(self.text, 'TAG1')
+ self.text.tag_delete('TAG1')
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 20 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.Blt reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.Blt</h1>
+
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.Blt -
+ interface to some BLT widgets and commands</p>
+<p></p>
+
+
+
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This module contains function interfaces to the BLT <code>busy</code> command
+ as well as the classes <strong>Pmw.Blt.Vector</strong>, <strong>Pmw.Blt.Graph</strong>,
+ <strong>Pmw.Blt.Stripchart</strong> and <strong>Pmw.Blt.Tabset</strong>, which are interfaces to
+ the vector, graph, stripchart and tabset commands of version 2.4
+ of the <strong>BLT</strong> extension to Tk. The interfaces are complete except
+ for <strong>Pmw.Blt.Vector</strong> where several creation options, methods and
+ operations have not been implemented.</p>
+
+<p> The blt graph and barchart widgets are essentially the same and so
+ only the graph widget has been ported. The <code>element_create()</code>
+ method is not implememted for <strong>Pmw.Blt.Graph</strong>, so instead:</p>
+<ul><li><p>to create a <em>line</em> element, use the <code>line_create()</code> method and</p>
+
+</li>
+<li><p>to create a <em>bar</em> element, use the <code>bar_create()</code> method.</p>
+
+</li></ul>
+
+<p> To operate on elements, use the <code>element_*()</code> methods, such as
+ <code>element_bind()</code>, <code>element_activate()</code>, etc.</p>
+
+<p> <strong>Note:</strong> Full documentation of Pmw.Blt.Graph is available in
+ <a href="http://www.ifi.uio.no/~hpl/Pmw.Blt/doc/">A User's Guide to Pmw.Blt</a>
+ written by Bjørn Ove Thue and Hans Petter Langtangen.
+ You can also download
+ <a href="http://www.ifi.uio.no/~hpl/Pmw.Blt/Pmw.Blt.doc.tar.gz">the full HTML document</a>
+ of the guide for local viewing.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Functions</h3></dt><dd>
+The following functions are available.<p></p>
+<dl>
+<dt> <strong>Pmw.Blt.busy_forget</strong>(<em>window</em>)</dt><dd>
+
+ Interface to the BLT <code>busy forget</code> command.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Blt.busy_hold</strong>(<em>window</em>, <em>cursor</em> = <strong>None</strong>)</dt><dd>
+
+ Interface to the BLT <code>busy hold</code> command.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Blt.busy_release</strong>(<em>window</em>)</dt><dd>
+
+ Interface to the BLT <code>busy release</code> command.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Blt.haveblt</strong>(<em>window</em>)</dt><dd>
+
+ Return true if any commands in the BLT extension are available.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Blt.havebltbusy</strong>(<em>window</em>)</dt><dd>
+
+ Return true if the BLT <strong>busy</strong> command is available.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Blt.vector_expr</strong>(<em>expression</em>)</dt><dd>
+
+ Interface to the BLT <code>vector expr</code> command.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Blt.vector_names</strong>(<em>pattern</em> = <strong>None</strong>)</dt><dd>
+
+ Interface to the BLT <code>vector names</code> command.</p>
+
+<p></p>
+
+
+</dd>
+</dl>
+</dd></dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 25 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ButtonBox reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ButtonBox</h1>
+
+<center><IMG SRC=ButtonBox.gif ALT="" WIDTH=297 HEIGHT=65></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ButtonBox() -
+ manager megawidget for buttons
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A button box is a container megawidget which manages a number of
+ buttons. One of these buttons may be specified as the default and
+ it will be displayed with the platform specific appearance for a
+ default button. The buttons may be laid out either horizontally
+ or vertically.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.orient></a>
+<dl><dt> <strong>orient
+</strong></dt><dd>
+Initialisation option. Specifies the orientation of the button box. This may be
+ <strong>'horizontal'</strong> or <strong>'vertical'</strong>. The default is <strong>'horizontal'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.padx></a>
+<dl><dt> <strong>padx
+</strong></dt><dd>
+Initialisation option. Specifies a padding distance to leave between each button in the x
+ direction and also between the buttons and the outer edge of the
+ button box. The default is <strong>3</strong>.</p>
+
+
+</dd></dl>
+<a name=option.pady></a>
+<dl><dt> <strong>pady
+</strong></dt><dd>
+Initialisation option. Specifies a padding distance to leave between each button in the y
+ direction and also between the buttons and the outer edge of the
+ button box. The default is <strong>3</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.frame></a>
+<dl><dt> <strong>frame
+</strong></dt><dd>
+If the <strong>label</strong> component has been created (that is, the <strong>labelpos</strong>
+ option is not <strong>None</strong>), the <strong>frame</strong> component is created to act as
+ the container of the buttons created by the <code>add()</code> and
+ <code>insert()</code> methods. If there is no <strong>label</strong> component, then no
+ <strong>frame</strong> component is created and the <strong>hull</strong> component acts as the
+ container. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Dynamic components</h3></dt><dd>
+<p>
+ Button components are created dynamically by the <code>add()</code> and
+ <code>insert()</code> methods. By default, the buttons are of type
+ Tkinter.Button and are created with a component group of
+ <strong>Button</strong>.</p>
+<p> </p>
+
+
+
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.add></a>
+<dl><dt> <strong>add</strong>(<em>componentName</em>, **<em>kw</em>)</dt><dd>
+Add a button to the end of the button box as a component named
+ <em>componentName</em>. Any keyword arguments present will be passed to the
+ constructor when creating the button. If the <strong>text</strong> keyword
+ argument is not given, the <strong>text</strong> option of the button defaults to
+ <em>componentName</em>. The method returns the component widget.</p>
+
+
+</dd></dl>
+<a name=method.alignbuttons></a>
+<dl><dt> <strong>alignbuttons</strong>(<em>when</em> = <strong>'later'</strong>)</dt><dd>
+Set the widths of all the buttons to be the same as the width of
+ the widest button. If <em>when</em> is <strong>'later'</strong>, this will occur when the
+ interpreter next becomes idle, otherwise the resizing will occur
+ immediately.</p>
+
+
+</dd></dl>
+<a name=method.button></a>
+<dl><dt> <strong>button</strong>(<em>buttonIndex</em>)</dt><dd>
+Return the button specified by <em>buttonIndex</em>, which may have any
+ of the forms accepted by the <code>index()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.delete></a>
+<dl><dt> <strong>delete</strong>(<em>index</em>)</dt><dd>
+Delete the button given by <em>index</em> from the button box. <em>index</em>
+ may have any of the forms accepted by the <code>index()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.index></a>
+<dl><dt> <strong>index</strong>(<em>index</em>, <em>forInsert</em> = <strong>0</strong>)</dt><dd>
+Return the numerical index of the button corresponding to <em>index</em>.
+ This may be specified in any of the following forms:</p>
+<dl><dt><em>name</em></dt><dd>Specifies the button named <em>name</em>.<p></p>
+
+</dd>
+<dt><em>number</em></dt><dd>Specifies the button numerically, where <strong>0</strong> corresponds to
+ the left (or top) button.<p></p>
+
+</dd>
+<dt><strong>Pmw.END</strong></dt><dd>Specifies the right (or bottom) button.<p></p>
+
+</dd>
+<dt><strong>Pmw.DEFAULT</strong></dt><dd>Specifies the current default button.<p></p>
+
+</dd></dl>
+<p> If <em>forInsert</em> is true, <strong>Pmw.END</strong> returns the number of buttons rather
+ than the index of the last button.</p>
+
+
+
+</dd></dl>
+<a name=method.insert></a>
+<dl><dt> <strong>insert</strong>(<em>componentName</em>, <em>beforeComponent</em> = <strong>0</strong>, **<em>kw</em>)</dt><dd>
+Add a button to the button box as a component named
+ <em>componentName</em>. The button is added just before the button
+ specified by <em>beforeComponent</em>, which may have any of the forms
+ accepted by the <code>index()</code> method. Any keyword arguments present
+ will be passed to the constructor when creating the button. If
+ the <strong>text</strong> keyword argument is not given, the <strong>text</strong> option of the
+ button defaults to <em>componentName</em>. To add a button to the end of
+ the button box, use <code>add()</code>. The method returns the component
+ widget.</p>
+
+
+</dd></dl>
+<a name=method.invoke></a>
+<dl><dt> <strong>invoke</strong>(<em>index</em> = <strong>Pmw.DEFAULT</strong>, <em>noFlash</em> = <strong>0</strong>)</dt><dd>
+Invoke the callback command associated with the button specified
+ by <em>index</em> and return the value returned by the callback.
+ Unless <em>noFlash</em> is true, flash the button to
+ indicate to the user that something happened.
+ <em>index</em> may have any of the forms accepted by the <code>index()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.numbuttons></a>
+<dl><dt> <strong>numbuttons</strong>()</dt><dd>
+Return the number of buttons in the button box.</p>
+
+
+</dd></dl>
+<a name=method.setdefault></a>
+<dl><dt> <strong>setdefault</strong>(<em>index</em>)</dt><dd>
+Set the default button to the button given by <em>index</em>. This
+ causes the specified button to be displayed with the platform
+ specific appearance for a default button. If <em>index</em> is <strong>None</strong>,
+ there will be no default button. <em>index</em> may have any of the
+ forms accepted by the <code>index()</code> method.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the ButtonBox.
+ self.buttonBox = Pmw.ButtonBox(parent,
+ labelpos = 'nw',
+ label_text = 'ButtonBox:',
+ frame_borderwidth = 2,
+ frame_relief = 'groove')
+ self.buttonBox.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+
+ # Add some buttons to the ButtonBox.
+ self.buttonBox.add('OK', command = self.ok)
+ self.buttonBox.add('Apply', command = self.apply)
+ self.buttonBox.add('Cancel', command = self.cancel)
+
+ # Set the default button (the one executed when <Return> is hit).
+ self.buttonBox.setdefault('OK')
+ parent.bind('<Return>', self._processReturnKey)
+ parent.focus_set()
+
+ # Make all the buttons the same width.
+ self.buttonBox.alignbuttons()
+
+ def _processReturnKey(self, event):
+ self.buttonBox.invoke()
+
+ def ok(self):
+ print 'You clicked on OK'
+
+ def apply(self):
+ print 'You clicked on Apply'
+
+ def cancel(self):
+ print 'You clicked on Cancel'
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 24 May 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.Color reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.Color</h1>
+
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.Color -
+ contains functions for handling colors and color schemes</p>
+<p></p>
+
+
+
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This module is a set of functions for manipulating colors and for
+ modifying the color scheme of an application or a widget. Many of
+ the functions in this module take or return colors. These values
+ may represent colors in the following ways:</p>
+<dl><dt><strong>name</strong></dt><dd>a standard color name, eg <code>'orange'</code> or <code>'#ffa500'</code><p></p>
+
+</dd>
+<dt><strong>rgb</strong></dt><dd>a 3-element sequence of red, green and blue intensities
+ each between 0.0 (dark) and 1.0 (light), eg <code>[1.0, 0.6, 0.0]</code>.<p></p>
+
+</dd>
+<dt><strong>hsi</strong></dt><dd>a 3-element sequence (<em>hue</em>, <em>saturation</em>,
+ <em>intensity</em>). The value of <em>hue</em> is between 0.0 and <strong>2pi</strong>
+ (6.28318) giving a range of colors covering, in order, red,
+ orange, yellow green, cyan, blue, magenta and back to red.
+ The value of <em>saturation</em> is between 0.0 (grey) and 1.0
+ (brilliant) and the value of <em>intensity</em> is between 0.0 (dark)
+ and 1.0 (bright).<p></p>
+
+</dd></dl>
+
+<p> As used in these functions, the <strong>brightness</strong> of a color is the
+ perceived grey level of the color as registered by the human eye.
+ For example, even though the colors red, blue and yellow have the
+ same intensity (1.0), they have different brightnesses, 0.299,
+ 0.114 and 0.886 respectively, reflecting the different way these
+ colors appear to the eye. The brightness of a color is a value
+ between 0.0 (dark) and 1.0 (bright).</p>
+
+<p> A <strong>color scheme</strong> is a set of colors defined for each of the
+ default color options in the Tk option database. Color schemes
+ can be used in two ways. Firstly, using <code>Pmw.Color.setscheme()</code>,
+ the Tk option database can be set to the values in the color
+ scheme. This will not have any effect on currently existing
+ widgets, but any new widgets created after setting the options
+ will have these colors as their defaults. Secondly, using
+ <code>Pmw.Color.changecolor()</code> the color scheme can be used to change
+ the colors of a widget and all its child widgets.</p>
+
+<p> A color scheme is specified by defining one or more color options
+ (one of the defined options must be <code>background</code>). Not all
+ options need be specified - if any options are not defined, they
+ are calculated from the other colors. These are the options used
+ by a color scheme, together with their values if not specified:</p>
+<dl><dd><pre> background: (must be specified)
+ foreground: black
+ activeForeground: same as foreground
+ insertBackground: same as foreground
+ selectForeground: same as foreground
+ highlightColor: same as foreground
+ disabledForeground: between fg and bg but closer to bg
+ highlightBackground: same as background
+ activeBackground: a little lighter that bg
+ selectBackground: a little darker that bg
+ troughColor: a little darker that bg
+ selectColor: yellow</pre></dd></dl>
+
+
+<p> There are many functions in this module. As well as
+ <code>Pmw.Color.setscheme()</code> and <code>Pmw.Color.changecolor()</code>, some of the
+ most useful are <code>Pmw.Color.spectrum()</code>,
+ <code>Pmw.Color.changebrightness()</code> and
+ <code>Pmw.Color.getdefaultpalette()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Functions</h3></dt><dd>
+The following functions are available.<p></p>
+<dl>
+<dt> <strong>Pmw.Color.average</strong>(<em>rgb1</em>, <em>rgb2</em>, <em>fraction</em>)</dt><dd>
+
+ Return an <strong>rgb</strong> color <em>fraction</em> of the way "between" the colors
+ <em>rgb1</em> and <em>rgb2</em>, where <em>fraction</em> must be between <strong>0.0</strong> and
+ <strong>1.0</strong>. If <em>fraction</em> is close to <strong>0.0</strong>, then the color returned
+ will be close to <em>rgb1</em>. If it is close to <strong>1.0</strong>, then the color
+ returned will be close to <em>rgb2</em>. If it is near <strong>0.5</strong>, then the
+ color returned will be half way between the two colors.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.bhi2saturation</strong>(<em>brightness</em>, <em>hue</em>, <em>intensity</em>)</dt><dd>
+
+ Return the saturation of the color represented by <em>brightness</em>,
+ <em>hue</em> and <em>intensity</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.bordercolors</strong>(<em>root</em>, <em>colorName</em>)</dt><dd>
+
+ Return a tuple <code>(light, dark)</code> of color names that can be used as
+ the light and dark border shadows on a widget where the background
+ is <em>colorName</em>. This is the same method that Tk uses for shadows
+ when drawing reliefs on widget borders. The <em>root</em> argument is
+ only used to query Tk for the <strong>rgb</strong> values of <em>colorName</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.changebrightness</strong>(<em>root</em>, <em>colorName</em>, <em>brightness</em>)</dt><dd>
+
+ Find the hue of the color <em>colorName</em> and return a color of this
+ hue with the required <em>brightness</em>. If <em>brightness</em> is <strong>None</strong>,
+ return the name of color with the given hue and with saturation
+ and intensity both <strong>1.0</strong>. The <em>root</em> argument is only used to
+ query Tk for the <strong>rgb</strong> values of <em>colorName</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.changecolor</strong>(<em>widget</em>, <em>background</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+
+ Change the color of <em>widget</em> and all its child widgets according
+ to the color scheme specified by the other arguments. This is done
+ by modifying all of the color options of existing widgets that
+ have the default value. The color options are the lower case
+ versions of those described in the <strong>color scheme</strong> section. Any
+ options which are different to the previous color scheme (or the
+ defaults, if this is the first call) are not changed.</p>
+
+<p> For example to change a widget to have a red color scheme with a
+ white foreground:</p>
+
+<dl><dd><pre> Pmw.Color.changecolor(widget,
+ background = 'red3', foreground = 'white')</pre></dd></dl>
+
+<p> The colors of widgets created after this call will not be
+ affected.</p>
+
+<p> Note that <em>widget</em> must be a Tk widget or toplevel. To change the
+ color of a Pmw megawidget, use it's <strong>hull</strong> component. For example:</p>
+
+<dl><dd><pre> widget = megawidget.component('hull')
+ Pmw.Color.changecolor(widget, background = 'red3')</pre></dd></dl>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.correct</strong>(<em>rgb</em>, <em>correction</em>)</dt><dd>
+
+ Return the "corrected" value of <em>rgb</em>. This can be used to
+ correct for dull monitors. If <em>correction</em> is less than <strong>1.0</strong>,
+ the color is dulled. If <em>correction</em> is greater than <strong>1.0</strong>, the
+ color is brightened.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.getdefaultpalette</strong>(<em>root</em>)</dt><dd>
+
+ Return a dictionary of the default values of the color options
+ described in the <strong>color scheme</strong> section.</p>
+
+<p> To do this, a few widgets are created as children of <em>root</em>, their
+ defaults are queried, and then the widgets are destroyed. (Tk
+ supplies no other way to get widget default values.)</p>
+
+<p> Note that <em>root</em> must be a Tk widget or toplevel. To use a Pmw
+ megawidget as the root, use it's <strong>hull</strong> component. For example:</p>
+
+<dl><dd><pre> root = megawidget.component('hull')
+ Pmw.Color.getdefaultpalette(root)</pre></dd></dl>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.hsi2rgb</strong>(<em>hue</em>, <em>saturation</em>, <em>intensity</em>)</dt><dd>
+
+ Return the <strong>rgb</strong> representation of the color represented by <em>hue</em>,
+ <em>saturation</em> and <em>intensity</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.hue2name</strong>(<em>hue</em>, <em>brightness</em> = <strong>None</strong>)</dt><dd>
+
+ Return the name of the color with the specified <em>hue</em> and
+ <em>brightness</em>. If <em>hue</em> is <strong>None</strong>, return a grey of the requested
+ brightness. Otherwise, the value of <em>hue</em> should be as described
+ above. If <em>brightness</em> is <strong>None</strong>, return the name of color with
+ the given hue and with saturation and intensity both <strong>1.0</strong>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.name2rgb</strong>(<em>root</em>, <em>colorName</em>, <em>asInt</em> = <strong>0</strong>)</dt><dd>
+
+ Return <em>colorName</em> as an <strong>rgb</strong> value. If <em>asInt</em> is true, then
+ the elements of the return sequence are in the range <strong>0</strong> to
+ <strong>65535</strong> rather than <strong>0.0</strong> to <strong>1.0</strong>. The <em>root</em> argument is only
+ used to query Tk for the <strong>rgb</strong> values of <em>colorName</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.rgb2brightness</strong>(<em>rgb</em>)</dt><dd>
+
+ Return the brightness of the color represented by <em>rgb</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.rgb2hsi</strong>(<em>rgb</em>)</dt><dd>
+
+ Return a tuple (<em>hue</em>, <em>saturation</em>, <em>intensity</em>) corresponding to
+ the color specified by the <em>rgb</em> sequence.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.rgb2name</strong>(<em>rgb</em>)</dt><dd>
+
+ Return the name of the color represented by <em>rgb</em> as a string of
+ the form <code>'#RRGGBB'</code> suitable for use with Tk color functions.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.setscheme</strong>(<em>root</em>, <em>background</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+
+ Set the color scheme for the application by setting default colors
+ (in the Tk option database of the root window of <em>root</em>) according
+ to the color scheme specified by the other arguments. This will
+ affect the initial colours of all widgets created after the call
+ to this function.</p>
+
+<p> For example to initialise an application to have a red color
+ scheme with a white foreground:</p>
+
+<dl><dd><pre> Pmw.Color.setscheme(root,
+ background = 'red3', foreground = 'white')</pre></dd></dl>
+
+<p> This function does not modify the colors of already existing
+ widgets. Use <strong>Pmw.Color.changecolor()</strong> to do this.</p>
+
+<p> Note that <em>root</em> must be a Tk widget or toplevel. To use the Tk
+ option database of the root window of a Pmw megawidget, use the
+ megawidget's <strong>hull</strong> component. For example:</p>
+
+<dl><dd><pre> root = megawidget.component('hull')
+ Pmw.Color.setscheme(root, background = 'red3')</pre></dd></dl>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.Color.spectrum</strong>(<em>numColors</em>, <em>correction</em> = <strong>1.0</strong>, <em>saturation</em> = <strong>1.0</strong>, <em>intensity</em> = <strong>1.0</strong>, <em>extraOrange</em> = <strong>1</strong>, <em>returnHues</em> = <strong>0</strong>)</dt><dd>
+
+ Return a list of <em>numColors</em> different colors making up a
+ <em>spectrum</em>. If <em>extraOrange</em> is false, the colors are evenly
+ spaced by hue from one end of the spectrum (red) to the other
+ (magenta). If <em>extraOrange</em> is true, the hues are not quite
+ evenly spaced - the hues around orange are emphasised, thus
+ preventing the spectrum from appearing to have to many <em>cool</em>
+ hues. </p>
+
+<p> If <em>returnHues</em> is false, the return values are the names of the
+ colors represented by the hues together with <em>saturation</em> and
+ <em>intensity</em> and corrected by <em>correction</em>.</p>
+
+<p> If <em>returnHues</em> is true, the return values are hues.</p>
+
+<p></p>
+
+
+</dd>
+</dl>
+</dd></dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 25 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ComboBox reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ComboBox</h1>
+
+<center><IMG SRC=ComboBox.gif ALT="" WIDTH=376 HEIGHT=246></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ComboBox() -
+ dropdown or simple combination box
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A combobox contains an entry field and an associated scrolled
+ listbox. When an item in the listbox is selected, it is displayed
+ in the entry field. Optionally, the user may also edit the entry
+ field directly.</p>
+
+<p> For a simple combobox, the scrolled listbox is displayed beneath
+ the entry field. For a dropdown combobox (the default), the
+ scrolled listbox is displayed in a window which pops up beneath
+ the entry field when the user clicks on an arrow button on the
+ right of the entry field. Either style allows an optional label.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.autoclear></a>
+<dl><dt> <strong>autoclear
+</strong></dt><dd>
+Initialisation option. If both <strong>autoclear</strong> and <strong>history</strong> are true, clear the entry field
+ whenever <strong><Return></strong> is pressed, after adding the value to the
+ history list. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonaspect></a>
+<dl><dt> <strong>buttonaspect
+</strong></dt><dd>
+Initialisation option. The width of the arrow button as a proportion of the height. The
+ height of the arrow button is set to the height of the entry
+ widget. The default is <strong>1.0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.dropdown></a>
+<dl><dt> <strong>dropdown
+</strong></dt><dd>
+Initialisation option. Specifies whether the combobox should be dropdown or simple. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.fliparrow></a>
+<dl><dt> <strong>fliparrow
+</strong></dt><dd>
+Initialisation option. If true, the arrow button is draw upside down when the listbox is
+ being displayed. Used only in dropdown megawidgets. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.history></a>
+<dl><dt> <strong>history
+</strong></dt><dd>
+Initialisation option. When <strong><Return></strong> is pressed in the entry field, the current value
+ of the entry field is appended to the listbox if <strong>history</strong> is
+ true. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.listheight></a>
+<dl><dt> <strong>listheight
+</strong></dt><dd>
+Initialisation option. The height, in pixels, of the dropdown listbox. The default is <strong>200</strong>.</p>
+
+
+</dd></dl>
+<a name=option.selectioncommand></a>
+<dl><dt> <strong>selectioncommand
+</strong></dt><dd>
+The function to call when an item is selected.
+ If this function takes a long time to run, and you want the entry
+ field to be updated quickly, call <code>update_idletasks()</code> at the
+ beginning of the function. Alternatively, wrap the function using
+ <code>Pmw.busycallback()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.sticky></a>
+<dl><dt> <strong>sticky
+</strong></dt><dd>
+Initialisation option. The default is <strong>'ew'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.unique></a>
+<dl><dt> <strong>unique
+</strong></dt><dd>
+Initialisation option. If both <strong>unique</strong> and <strong>history</strong> are true, the current value of the
+ entry field is not added to the listbox if it is already in the
+ list. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.arrowbutton></a>
+<dl><dt> <strong>arrowbutton
+</strong></dt><dd>
+In a dropdown combobox, the button to popup the listbox. By default, this component is a Tkinter.Canvas.</p>
+
+
+</dd></dl>
+<a name=component.entryfield></a>
+<dl><dt> <strong>entryfield
+</strong></dt><dd>
+The entry field where the current selection is displayed. By default, this component is a <a href="EntryField.html">Pmw.EntryField</a>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.popup></a>
+<dl><dt> <strong>popup
+</strong></dt><dd>
+In a dropdown combobox, the dropdown window. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.scrolledlist></a>
+<dl><dt> <strong>scrolledlist
+</strong></dt><dd>
+The scrolled listbox which displays the items to select. By default, this component is a <a href="ScrolledListBox.html">Pmw.ScrolledListBox</a>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+Alias for <strong>entryfield_entry</strong>.
+</dd></dl>
+<dl><dt> <strong>listbox
+</strong></dt><dd>
+Alias for <strong>scrolledlist_listbox</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the following classes
+are forwarded by this megawidget.
+Methods from <strong><a href="ScrolledListBox.html#methods">Pmw.ScrolledListBox</a></strong>
+are forwarded to the
+<strong>scrolledlist</strong> component.
+Methods from <strong><a href="EntryField.html#methods">Pmw.EntryField</a></strong>
+are forwarded to the
+<strong>entryfield</strong> component.
+Forwarded methods are searched in the order given.
+<p></p>
+<a name=method.bbox></a>
+<dl><dt> <strong>bbox</strong>(<em>index</em>)</dt><dd>
+This method is explicitly forwarded to the <strong>scrolledlist</strong>
+ component's <code>bbox()</code> method. Without this explicit forwarding,
+ the <code>bbox()</code> method (aliased to <code>grid_bbox()</code>) of the <strong>hull</strong> would
+ be invoked, which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+<a name=method.clear></a>
+<dl><dt> <strong>clear</strong>()</dt><dd>
+Delete all items from the scrolled listbox and delete all text
+ from the entry widget.</p>
+
+
+</dd></dl>
+<a name=method.get></a>
+<dl><dt> <strong>get</strong>(<em>first</em> = <strong>None</strong>, <em>last</em> = <strong>None</strong>)</dt><dd>
+This is the same as the <code>get()</code> method of the <strong>scrolledlist</strong>
+ component, except that if <em>first</em> is <strong>None</strong> then
+ the value of the entry field is returned.</p>
+
+
+</dd></dl>
+<a name=method.invoke></a>
+<dl><dt> <strong>invoke</strong>()</dt><dd>
+If a dropdown combobox, display the dropdown listbox. In a simple
+ combobox, select the currently selected item in the listbox,
+ call the <strong>selectioncommand</strong> and return the result.</p>
+
+
+</dd></dl>
+<a name=method.selectitem></a>
+<dl><dt> <strong>selectitem</strong>(<em>index</em>, <em>setentry</em> = <strong>1</strong>)</dt><dd>
+Select the item in the listbox specified by <em>index</em> which may be
+ either one of the items in the listbox or the integer index of one
+ of the items in the listbox.</p>
+<p> If <em>setentry</em> is true, also set the entry field to the selected
+ item.</p>
+
+
+
+</dd></dl>
+<a name=method.size></a>
+<dl><dt> <strong>size</strong>()</dt><dd>
+This method is explicitly forwarded to the <strong>scrolledlist</strong>
+ component's <code>size()</code> method. Without this explicit forwarding,
+ the <code>size()</code> method (aliased to <code>grid_size()</code>) of the <strong>hull</strong> would
+ be invoked, which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ parent.configure(background = 'white')
+
+ # Create and pack the widget to be configured.
+ self.target = Tkinter.Label(parent,
+ relief = 'sunken',
+ padx = 20,
+ pady = 20,
+ )
+ self.target.pack(fill = 'x', padx = 8, pady = 8)
+
+ # Create and pack the simple ComboBox.
+ words = ('Monti', 'Python', 'ik', 'den', 'Holie', 'Grailen', '(Bok)')
+ simple = Pmw.ComboBox(parent,
+ label_text = 'Simple ComboBox:',
+ labelpos = 'nw',
+ selectioncommand = self.changeText,
+ scrolledlist_items = words,
+ dropdown = 0,
+ )
+ simple.pack(side = 'left', fill = 'both',
+ expand = 1, padx = 8, pady = 8)
+
+ # Display the first text.
+ first = words[0]
+ simple.selectitem(first)
+ self.changeText(first)
+
+ # Create and pack the dropdown ComboBox.
+ colours = ('cornsilk1', 'snow1', 'seashell1', 'antiquewhite1',
+ 'bisque1', 'peachpuff1', 'navajowhite1', 'lemonchiffon1',
+ 'ivory1', 'honeydew1', 'lavenderblush1', 'mistyrose1')
+ dropdown = Pmw.ComboBox(parent,
+ label_text = 'Dropdown ComboBox:',
+ labelpos = 'nw',
+ selectioncommand = self.changeColour,
+ scrolledlist_items = colours,
+ )
+ dropdown.pack(side = 'left', anchor = 'n',
+ fill = 'x', expand = 1, padx = 8, pady = 8)
+
+ # Display the first colour.
+ first = colours[0]
+ dropdown.selectitem(first)
+ self.changeColour(first)
+
+ def changeColour(self, colour):
+ print 'Colour: ' + colour
+ self.target.configure(background = colour)
+
+ def changeText(self, text):
+ print 'Text: ' + text
+ self.target.configure(text = text)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 1 November 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ComboBoxDialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ComboBoxDialog</h1>
+
+<center><IMG SRC=ComboBoxDialog.gif ALT="" WIDTH=218 HEIGHT=236></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ComboBoxDialog() -
+ selection dialog displaying a list and an entry field
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="Dialog.html">Pmw.Dialog</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A combobox dialog is a dialog window which displays a list and
+ an entry field which can be used to prompt the user for a value.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderx></a>
+<dl><dt> <strong>borderx
+</strong></dt><dd>
+Initialisation option. The padding to the left and right of the combobox. The default is <strong>10</strong>.</p>
+
+
+</dd></dl>
+<a name=option.bordery></a>
+<dl><dt> <strong>bordery
+</strong></dt><dd>
+Initialisation option. The padding above and below the combobox. The default is <strong>10</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('OK',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.combobox></a>
+<dl><dt> <strong>combobox
+</strong></dt><dd>
+The combobox for the user to enter a value. By default it is
+ created using the option <code>dropdown = 0</code>. By default, this component is a <a href="ComboBox.html">Pmw.ComboBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+Alias for <strong>combobox_entry</strong>.
+</dd></dl>
+<dl><dt> <strong>label
+</strong></dt><dd>
+Alias for <strong>combobox_label</strong>.
+</dd></dl>
+<dl><dt> <strong>listbox
+</strong></dt><dd>
+Alias for <strong>combobox_listbox</strong>.
+</dd></dl>
+<dl><dt> <strong>scrolledlist
+</strong></dt><dd>
+Alias for <strong>combobox_scrolledlist</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="Dialog.html#methods">Pmw.Dialog</a></strong>.
+In addition, methods from the
+<strong><a href="ComboBox.html#methods">Pmw.ComboBox</a></strong> class
+are forwarded by this megawidget to the
+<strong>combobox</strong> component.
+<p></p>
+<a name=method.bbox></a>
+<dl><dt> <strong>bbox</strong>(<em>index</em>)</dt><dd>
+This method is explicitly forwarded to the <strong>combobox</strong> component's
+ <code>bbox()</code> method. Without this explicit forwarding, the <code>bbox()</code>
+ method (aliased to <code>grid_bbox()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+<a name=method.size></a>
+<dl><dt> <strong>size</strong>()</dt><dd>
+This method is explicitly forwarded to the <strong>combobox</strong> component's
+ <code>size()</code> method. Without this explicit forwarding, the <code>size()</code>
+ method (aliased to <code>grid_size()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog.
+ self.dialog = Pmw.ComboBoxDialog(parent,
+ title = 'My ComboBoxDialog',
+ buttons = ('OK', 'Cancel'),
+ defaultbutton = 'OK',
+ combobox_labelpos = 'n',
+ label_text = 'What do you think of Pmw?',
+ scrolledlist_items = ('Cool man', 'Cool', 'Good', 'Bad', 'Gross'))
+ self.dialog.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent,
+ text = 'Show combo box dialog',
+ command = self.doit)
+ w.pack(padx = 8, pady = 8)
+
+ def doit(self):
+ result = self.dialog.activate()
+ print 'You clicked on', result, self.dialog.get()
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.Counter reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.Counter</h1>
+
+<center><IMG SRC=Counter.gif ALT="" WIDTH=400 HEIGHT=110></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.Counter() -
+ entry field with up and down arrow buttons
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A counter contains an entry field and two arrow buttons to
+ increment and decrement the value in the entry field. Standard
+ counting types include numbers, times and dates. A user defined
+ counting function may also be supplied for specialised counting.
+ Counting can be used in combination with the entry field's
+ validation. The components may be laid out horizontally or
+ vertically.</p>
+
+<p> Each time an arrow button is pressed the value displayed in the
+ entry field is incremented or decremented by the value of the
+ <strong>increment</strong> option. If the new value is invalid (according to the
+ entry field's <strong>validate</strong> option, perhaps due to exceeding minimum
+ or maximum limits), the old value is restored.</p>
+
+<p> When an arrow button is pressed and the value displayed is not an
+ exact multiple of the <strong>increment</strong>, it is "truncated" up or down to
+ the nearest increment.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.autorepeat></a>
+<dl><dt> <strong>autorepeat
+</strong></dt><dd>
+If true, the counter will continue to count up or down while an
+ arrow button is held pressed down. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonaspect></a>
+<dl><dt> <strong>buttonaspect
+</strong></dt><dd>
+Initialisation option. Specifies the width of the arrow buttons as a proportion of their
+ height. Values less than <strong>1.0</strong> will produce thin arrow buttons.
+ Values greater than <strong>1.0</strong> will produce fat arrow buttons. The default is <strong>1.0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.datatype></a>
+<dl><dt> <strong>datatype
+</strong></dt><dd>
+Specifies how the counter should count up and down.</p>
+<p> The most general way to specify the <strong>datatype</strong> option is as a
+ dictionary. The kind of counting is specified by the <strong>'counter'</strong>
+ dictionary field, which may be either a function or the name of
+ one of the standard counters described below. If the dictionary
+ does not have a <strong>'counter'</strong> field, the field defaults to
+ <strong>'numeric'</strong>.</p>
+
+<p> Any other fields in the dictionary are passed on to the <em>counter</em>
+ function as keyword arguments.</p>
+
+<p> If <strong>datatype</strong> is not a dictionary, then it is equivalent to
+ specifying it as a dictionary with a single <strong>'counter'</strong> field.
+ For example, <code>datatype = 'real'</code> is equivalent to
+ <code>datatype = {'counter' : 'real'}</code>.</p>
+
+<p> The standard counters are:</p>
+
+<dl><dt><strong>'numeric'</strong></dt><dd>An integer number, as accepted by <code>string.atol()</code>.<p></p>
+
+</dd>
+<dt><strong>'integer'</strong></dt><dd>Same as <strong>'numeric'</strong>.<p></p>
+
+</dd>
+<dt><strong>'real'</strong></dt><dd>A real number, as accepted by <code>string.atof()</code>. This
+ counter accepts a <strong>'separator'</strong> argument, which specifies
+ the character used to represent the decimal point. The
+ default <strong>'separator'</strong> is <strong>'.'</strong>.<p></p>
+
+</dd>
+<dt><strong>'time'</strong></dt><dd>A time specification, as accepted by
+ <code>Pmw.timestringtoseconds()</code>. This counter accepts a
+ <strong>'separator'</strong> argument, which specifies the character used to
+ separate the time fields. The default separator is <strong>':'</strong>.
+ This counter also accepts a <strong>'time24'</strong> argument. If this is
+ true, the time value is converted to a value between
+ <strong>'00:00:00'</strong> and <strong>'23:59:59'</strong>. The default is false.<p></p>
+
+</dd>
+<dt><strong>'date'</strong></dt><dd>A date specification, as accepted by
+ <code>Pmw.datestringtojdn()</code>. This counter accepts a <strong>'separator'</strong>
+ argument, which specifies the character used to separate the
+ three date fields. The default is <strong>'/'</strong>. This counter also
+ accepts a <strong>'format'</strong> argument, which is passed to
+ <code>Pmw.datestringtojdn()</code> to specify the desired ordering of the
+ fields. The default is <strong>'ymd'</strong>.
+ This counter also accepts a <strong>'yyyy'</strong> argument. If this is
+ false, the year field will be displayed as the year within the
+ century, otherwise it will be fully displayed. In both cases
+ it will be displayed with at least 2 digits, using leading
+ zeroes. The default is false.<p></p>
+
+</dd></dl>
+<p> If the <strong>'counter'</strong> dictionary field is a function, then it will be
+ called whenever the counter is to be incremented or decremented.
+ The function is called with at least three arguments, the first
+ three being (<em>text</em>, <em>factor</em>, <em>increment</em>), where <em>text</em> is the
+ current contents of the entry field, <em>factor</em> is <strong>1</strong> when
+ incrementing or <strong>-1</strong> when decrementing, and <em>increment</em> is the
+ value of the <strong>increment</strong> megawidget option.</p>
+
+<p> The other arguments are keyword arguments made up of the fields of
+ the <strong>datatype</strong> dictionary (excluding the <strong>'counter'</strong> field).</p>
+
+<p> The <em>counter</em> function should return a string representing the
+ incremented or decremented value. It should raise a
+ <strong>ValueError</strong> exception if the <em>text</em> is invalid. In this case the
+ bell is rung and the entry text is not changed.</p>
+
+<p> The default for <strong>datatype</strong> is <strong>numeric</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.increment></a>
+<dl><dt> <strong>increment
+</strong></dt><dd>
+Specifies how many units should be added or subtracted when the
+ counter is incremented or decremented. If the currently displayed
+ value is not a multiple of <strong>increment</strong>, the value is changed to
+ the next multiple greater or less than the current value.</p>
+<p> For the number datatypes, the value of <strong>increment</strong> is a number.
+ For the <strong>'time'</strong> datatype, the value is in seconds. For the
+ <strong>'date'</strong> datatype, the value is in days. The default is <strong>1</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.initwait></a>
+<dl><dt> <strong>initwait
+</strong></dt><dd>
+Specifies the initial delay (in milliseconds) before a depressed
+ arrow button automatically starts to repeat counting. The default is <strong>300</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.orient></a>
+<dl><dt> <strong>orient
+</strong></dt><dd>
+Initialisation option. Specifies whether the arrow buttons should appear to the left and
+ right of the entry field (<strong>'horizontal'</strong>) or above and below
+ (<strong>'vertical'</strong>). The default is <strong>'horizontal'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.padx></a>
+<dl><dt> <strong>padx
+</strong></dt><dd>
+Initialisation option. Specifies a padding distance to leave around the arrow buttons in
+ the x direction. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.pady></a>
+<dl><dt> <strong>pady
+</strong></dt><dd>
+Initialisation option. Specifies a padding distance to leave around the arrow buttons in
+ the y direction. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.repeatrate></a>
+<dl><dt> <strong>repeatrate
+</strong></dt><dd>
+Specifies the delay (in milliseconds) between automatic counts
+ while an arrow button is held pressed down. The default is <strong>50</strong>.</p>
+
+
+</dd></dl>
+<a name=option.sticky></a>
+<dl><dt> <strong>sticky
+</strong></dt><dd>
+Initialisation option. The default is <strong>'ew'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.downarrow></a>
+<dl><dt> <strong>downarrow
+</strong></dt><dd>
+The arrow button used for decrementing the counter. Depending on
+ the value of <strong>orient</strong>, it will appear on the left or below the
+ entry field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+<a name=component.entryfield></a>
+<dl><dt> <strong>entryfield
+</strong></dt><dd>
+The entry field widget where the text is entered, displayed and
+ validated. By default, this component is a <a href="EntryField.html">Pmw.EntryField</a>.</p>
+
+
+</dd></dl>
+<a name=component.frame></a>
+<dl><dt> <strong>frame
+</strong></dt><dd>
+If the <strong>label</strong> component has been created (that is, the <strong>labelpos</strong>
+ option is not <strong>None</strong>), the <strong>frame</strong> component is created to act as
+ the container of the entry field and arrow buttons. If there is
+ no <strong>label</strong> component, then no <strong>frame</strong> component is created and the
+ <strong>hull</strong> component acts as the container. In either case the border
+ around the container of the entry field and arrow buttons will be
+ raised (but not around the label). By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.uparrow></a>
+<dl><dt> <strong>uparrow
+</strong></dt><dd>
+The arrow button used for incrementing the counter. Depending on
+ the value of <strong>orient</strong>, it will appear on the right or above the
+ entry field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+Alias for <strong>entryfield_entry</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the
+<strong><a href="EntryField.html#methods">Pmw.EntryField</a></strong> class
+are forwarded by this megawidget to the
+<strong>entryfield</strong> component.
+<p></p>
+<a name=method.decrement></a>
+<dl><dt> <strong>decrement</strong>()</dt><dd>
+Decrement the counter once, as if the down arrow had been pressed.</p>
+
+
+</dd></dl>
+<a name=method.increment></a>
+<dl><dt> <strong>increment</strong>()</dt><dd>
+Increment the counter once, as if the up arrow had been pressed.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Need to use long ints here because on the Macintosh the maximum size
+ # of an integer is smaller than the value returned by time.time().
+ now = (long(time.time()) / 300) * 300
+
+ # Create the Counters.
+ self._date = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Date (4-digit year):',
+ entryfield_value =
+ time.strftime('%d/%m/%Y', time.localtime(now)),
+ entryfield_command = self.execute,
+ entryfield_validate = {'validator' : 'date', 'format' : 'dmy'},
+ datatype = {'counter' : 'date', 'format' : 'dmy', 'yyyy' : 1})
+
+ self._isodate = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'ISO-Date (4-digit year):',
+ entryfield_value =
+ time.strftime('%Y-%m-%d', time.localtime(now)),
+ entryfield_command = self.execute,
+ entryfield_validate = {'validator' : 'date', 'format' : 'ymd',
+ 'separator' : '-' },
+ datatype = {'counter' : 'date', 'format' : 'ymd', 'yyyy' : 1,
+ 'separator' : '-' })
+
+ self._time = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Time:',
+ entryfield_value =
+ time.strftime('%H:%M:%S', time.localtime(now)),
+ entryfield_validate = {'validator' : 'time',
+ 'min' : '00:00:00', 'max' : '23:59:59',
+ 'minstrict' : 0, 'maxstrict' : 0},
+ datatype = {'counter' : 'time', 'time24' : 1},
+ increment=5*60)
+ self._real = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Real (with comma)\nand extra\nlabel lines:',
+ label_justify = 'left',
+ entryfield_value = '1,5',
+ datatype = {'counter' : 'real', 'separator' : ','},
+ entryfield_validate = {'validator' : 'real',
+ 'min' : '-2,0', 'max' : '5,0',
+ 'separator' : ','},
+ increment = 0.1)
+ self._custom = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Custom:',
+ entryfield_value = specialword[:4],
+ datatype = _custom_counter,
+ entryfield_validate = _custom_validate)
+ self._int = Pmw.Counter(parent,
+ labelpos = 'w',
+ label_text = 'Vertical integer:',
+ orient = 'vertical',
+ entry_width = 2,
+ entryfield_value = 50,
+ entryfield_validate = {'validator' : 'integer',
+ 'min' : 0, 'max' : 99}
+ )
+
+ counters = (self._date, self._isodate, self._time, self._real,
+ self._custom)
+ Pmw.alignlabels(counters)
+
+ # Pack them all.
+ for counter in counters:
+ counter.pack(fill='both', expand=1, padx=10, pady=5)
+ self._int.pack(padx=10, pady=5)
+
+ def execute(self):
+ print 'Return pressed, value is', self._date.get()
+
+specialword = 'Monti Python ik den Holie Grailen (Bok)'
+
+def _custom_validate(text):
+ if string.find(specialword, text) == 0:
+ return 1
+ else:
+ return -1
+
+def _custom_counter(text, factor, increment):
+ # increment is ignored here.
+ if string.find(specialword, text) == 0:
+ length = len(text)
+ if factor == 1:
+ if length >= len(specialword):
+ raise ValueError, 'maximum length reached'
+ return specialword[:length + 1]
+ else:
+ if length == 0:
+ raise ValueError, 'empty string'
+ return specialword[:length - 1]
+ else:
+ raise ValueError, 'bad string ' + text
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 24 May 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.CounterDialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.CounterDialog</h1>
+
+<center><IMG SRC=CounterDialog.gif ALT="" WIDTH=268 HEIGHT=198></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.CounterDialog() -
+ selection dialog displaying a counter
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="Dialog.html">Pmw.Dialog</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A counter dialog is a dialog window which displays a counter
+ which can be used to prompt the user for a value.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderx></a>
+<dl><dt> <strong>borderx
+</strong></dt><dd>
+Initialisation option. The padding to the left and right of the counter. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.bordery></a>
+<dl><dt> <strong>bordery
+</strong></dt><dd>
+Initialisation option. The padding above and below the counter. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('OK',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.counter></a>
+<dl><dt> <strong>counter
+</strong></dt><dd>
+The counter for the user to enter a value. By default, this component is a <a href="Counter.html">Pmw.Counter</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+Alias for <strong>counter_entryfield_entry</strong>.
+</dd></dl>
+<dl><dt> <strong>entryfield
+</strong></dt><dd>
+Alias for <strong>counter_entryfield</strong>.
+</dd></dl>
+<dl><dt> <strong>label
+</strong></dt><dd>
+Alias for <strong>counter_label</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="Dialog.html#methods">Pmw.Dialog</a></strong>.
+In addition, methods from the
+<strong><a href="Counter.html#methods">Pmw.Counter</a></strong> class
+are forwarded by this megawidget to the
+<strong>counter</strong> component.
+<p></p>
+<a name=method.deleteentry></a>
+<dl><dt> <strong>deleteentry</strong>(<em>first</em>, <em>last</em> = <strong>None</strong>)</dt><dd>
+Delete text from the counter's entry widget. An alias for
+ <code>component('entry').delete()</code>.</p>
+
+
+</dd></dl>
+<a name=method.indexentry></a>
+<dl><dt> <strong>indexentry</strong>(<em>index</em>)</dt><dd>
+An alias for <code>component('entry').index()</code>.</p>
+
+
+</dd></dl>
+<a name=method.insertentry></a>
+<dl><dt> <strong>insertentry</strong>(<em>index</em>, <em>text</em>)</dt><dd>
+Insert text into the counter's entry widget. An alias for
+ <code>component('entry').insert()</code>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog to prompt for the number of times to ring the bell.
+ self.dialog = Pmw.CounterDialog(parent,
+ label_text = 'Enter the number of times to\n' + \
+ 'sound the bell (1 to 5)\n',
+ counter_labelpos = 'n',
+ entryfield_value = 2,
+ counter_datatype = 'numeric',
+ entryfield_validate =
+ {'validator' : 'numeric', 'min' : 1, 'max' : 5},
+ buttons = ('OK', 'Cancel'),
+ defaultbutton = 'OK',
+ title = 'Bell ringing',
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show counter dialog',
+ command = self.dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self, result):
+ if result is None or result == 'Cancel':
+ print 'Bell ringing cancelled'
+ self.dialog.deactivate()
+ else:
+ count = self.dialog.get()
+ if not self.dialog.valid():
+ print 'Invalid entry: "' + count + '"'
+ else:
+ print 'Ringing the bell ' + count + ' times'
+ for num in range(string.atoi(count)):
+ if num != 0:
+ self.dialog.after(200)
+ self.dialog.bell()
+ self.dialog.deactivate()
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.Dialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.Dialog</h1>
+
+<center><IMG SRC=Dialog.gif ALT="" WIDTH=374 HEIGHT=162></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.Dialog() -
+ toplevel window with button box
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaToplevel.html">Pmw.MegaToplevel</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A dialog is a toplevel window composed of a button box and a child
+ site area. The child site area can be used to specialise the
+ megawidget by creating other widgets within it. This can be done
+ by using this class directly or by deriving from it.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('OK',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaToplevel.html#methods">Pmw.MegaToplevel</a></strong>.
+<p></p>
+<a name=method.interior></a>
+<dl><dt> <strong>interior</strong>()</dt><dd>
+Return the child site for the dialog. This is the same as
+ <code>component('dialogchildsite')</code>.</p>
+
+
+</dd></dl>
+<a name=method.invoke></a>
+<dl><dt> <strong>invoke</strong>(<em>index</em> = <strong>Pmw.DEFAULT</strong>)</dt><dd>
+Invoke the command specified by the <strong>command</strong> option as if the
+ button specified by <em>index</em> had been pressed and return the
+ result. <em>index</em> may have any of the forms accepted by the
+ <a href="ButtonBox.html">Pmw.ButtonBox</a> <code>index()</code> method.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create two buttons to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show application modal dialog',
+ command = self.showAppModal)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Show global modal dialog',
+ command = self.showGlobalModal)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Show dialog with "no grab"',
+ command = self.showDialogNoGrab)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text =
+ 'Show toplevel window which\n' +
+ 'will not get a busy cursor',
+ command = self.showExcludedWindow)
+ w.pack(padx = 8, pady = 8)
+
+ # Create the dialog.
+ self.dialog = Pmw.Dialog(parent,
+ buttons = ('OK', 'Apply', 'Cancel', 'Help'),
+ defaultbutton = 'OK',
+ title = 'My dialog',
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Add some contents to the dialog.
+ w = Tkinter.Label(self.dialog.interior(),
+ text = 'Pmw Dialog\n(put your widgets here)',
+ background = 'black',
+ foreground = 'white',
+ pady = 20)
+ w.pack(expand = 1, fill = 'both', padx = 4, pady = 4)
+
+ # Create the window excluded from showbusycursor.
+ self.excluded = Pmw.MessageDialog(parent,
+ title = 'I still work',
+ message_text =
+ 'This window will not get\n' +
+ 'a busy cursor when modal dialogs\n' +
+ 'are activated. In addition,\n' +
+ 'you can still interact with\n' +
+ 'this window when a "no grab"\n' +
+ 'modal dialog is displayed.')
+ self.excluded.withdraw()
+ Pmw.setbusycursorattributes(self.excluded.component('hull'),
+ exclude = 1)
+
+ def showAppModal(self):
+ self.dialog.activate(geometry = 'centerscreenalways')
+
+ def showGlobalModal(self):
+ self.dialog.activate(globalMode = 1)
+
+ def showDialogNoGrab(self):
+ self.dialog.activate(globalMode = 'nograb')
+
+ def showExcludedWindow(self):
+ self.excluded.show()
+
+ def execute(self, result):
+ print 'You clicked on', result
+ if result not in ('Apply', 'Help'):
+ self.dialog.deactivate(result)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.EntryField reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.EntryField</h1>
+
+<center><IMG SRC=EntryField.gif ALT="" WIDTH=313 HEIGHT=177></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.EntryField() -
+ entry widget with validation
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ An entry field contains an entry widget with optional validation of
+ various kinds. Built-in validation may be used, such as
+ <strong>integer</strong>, <strong>real</strong>, <strong>time</strong> or <strong>date</strong>, or an external validation
+ function may be supplied. If valid text is entered, it will be
+ displayed with the normal background. If invalid text is entered,
+ it is not displayed and the previously displayed text is restored.
+ If partially valid text is entered, it will be displayed with a
+ background color to indicate it is in error. An example of
+ partially valid <strong>real</strong> text is <strong>'-.'</strong>, which may be the first two
+ charactes of the valid string <strong>'-.5'</strong>. Some validators, such as
+ <strong>date</strong>, have a relaxed interpretation of partial validity, which
+ allows the user flexibility in how they enter the text.</p>
+
+<p> Validation is performed <em>early</em>, at each keystroke or other event
+ which modifies the text. However, if partially valid text is
+ permitted, the validity of the entered text can be checked just
+ before it is to be used, which is a form of <em>late</em> validation.</p>
+
+<p> Minimum and maximum values may be specified. Some validators also
+ accept other specifications, such as date and time formats and
+ separators.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Validation function return values</h3></dt><dd>
+<p>
+ Validation is performed by a function which takes as its first
+ argument the entered text and returns one of three standard
+ values, indicating whether the text is valid:</p>
+
+<dl><dt><strong>Pmw.OK</strong></dt><dd>The text is valid.<p></p>
+
+</dd>
+<dt><strong>Pmw.ERROR</strong></dt><dd>The text is invalid and is not acceptable for
+ display. In this case the entry will be restored to its
+ previous value.<p></p>
+
+</dd>
+<dt><strong>Pmw.PARTIAL</strong></dt><dd>The text is partially valid and is acceptable
+ for display. In this case the text will be displayed
+ using the <strong>errorbackground</strong> color.<p></p>
+<p> </p>
+
+
+</dd></dl>
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+This specifies a function to call whenever the <strong><Return></strong> key is
+ pressed or <code>invoke()</code> is called. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.errorbackground></a>
+<dl><dt> <strong>errorbackground
+</strong></dt><dd>
+Specifies the background color to use when displaying invalid or
+ partially valid text. The default is <strong>'pink'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.extravalidators></a>
+<dl><dt> <strong>extravalidators
+</strong></dt><dd>
+This is a dictionary of extra validators. The keys are the names
+ of validators which may be used in a future call to the
+ <strong>validate</strong> option. Each value in the dictionary is a tuple of
+ (<em>validate_function</em>, <em>stringtovalue_function</em>).</p>
+<p> The <em>validate_function</em> is used to implement the validation and
+ the <em>stringtovalue_function</em> is used to convert the entry input
+ into a value which can be compared with the minimum and maximum
+ limits. These functions are as described for the <strong>validate</strong>
+ option.</p>
+
+<p> If either of these is not given as a function, it is assumed to be
+ the name of one of the other extra validators or one of the
+ standard validators. The alias search is performed when the
+ <strong>validate</strong> option is configured, not when the <strong>extravalidators</strong>
+ option is configured or when the <strong>validate</strong> function is called.</p>
+
+<p> If the name of one of the extra validators is the same as one of
+ the standard validators, the extra validator takes precedence. The default is <strong>{}</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.invalidcommand></a>
+<dl><dt> <strong>invalidcommand
+</strong></dt><dd>
+This is executed when invalid text is entered and the text is
+ restored to its previous value (that is, when the <strong>validate</strong>
+ function returns <strong>Pmw.ERROR</strong>). It is also called if an attempt is
+ made to set invalid text in a call to <code>setentry()</code>. The default
+ is <strong>self.bell</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.modifiedcommand></a>
+<dl><dt> <strong>modifiedcommand
+</strong></dt><dd>
+This is called whenever the text of the entry has been changed
+ due to user action or by a call to <code>setentry()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.sticky></a>
+<dl><dt> <strong>sticky
+</strong></dt><dd>
+Initialisation option. The default is <strong>'ew'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.validate></a>
+<dl><dt> <strong>validate
+</strong></dt><dd>
+Specifies what kind of validation should be performed on the entry
+ input text.</p>
+<p> The most general way to specify the <strong>validate</strong> option is as a
+ dictionary. The kind of validation is specified by the
+ <strong>'validator'</strong> dictionary field, which may be the name of one of
+ the standard validators described below, the name of a validator
+ supplied by the <strong>extravalidators</strong> option, a function or <strong>None</strong>.
+ The default is <strong>None</strong>.</p>
+
+<p> Any other dictionary fields specify other restrictions on the
+ entered values. For all validators, the following fields may be
+ specified:</p>
+
+<dl><dt><strong>'min'</strong></dt><dd>Specifies the minimum acceptable value, or <strong>None</strong> if no
+ minimum checking should be performed. The default is <strong>None</strong>.<p></p>
+
+</dd>
+<dt><strong>'max'</strong></dt><dd>Specifies the maximum acceptable value, or <strong>None</strong> if no
+ maximum checking should be performed. The default is <strong>None</strong>.<p></p>
+
+</dd>
+<dt><strong>'minstrict'</strong></dt><dd>If true, then minimum checking is strictly enforced.
+ Otherwise, the entry input may be less than <strong>min</strong>, but will be
+ displayed using the <strong>errorbackground</strong> color. The default is true.<p></p>
+
+</dd>
+<dt><strong>'maxstrict'</strong></dt><dd>If true, then maximum checking is strictly enforced.
+ Otherwise, the entry input may be more than <strong>max</strong>, but will be
+ displayed using the <strong>errorbackground</strong> color. The default is true.<p></p>
+
+</dd></dl>
+<p> If the dictionary contains a <strong>'stringtovalue'</strong> field, it overrides
+ the normal <em>stringtovalue</em> function for the validator. The
+ <em>stringtovalue</em> function is described below.</p>
+
+<p> Other fields in the dictionary (apart from the core fields
+ mentioned above) are passed on to the <em>validator</em> and
+ <em>stringtovalue</em> functions as keyword arguments.</p>
+
+<p> If <strong>validate</strong> is not a dictionary, then it is equivalent to
+ specifying it as a dictionary with a single <strong>'validator'</strong> field.
+ For example, <code>validate = 'real'</code> is equivalent to /validate =
+ {'validator' : 'real'}/ and specifies real numbers without any
+ minimum or maximum limits and using <strong>'.'</strong> as the decimal point
+ character.</p>
+
+<p> The standard validators accepted in the <strong>'validator'</strong> field are:</p>
+
+<dl><dt><strong>'numeric'</strong></dt><dd>An integer greater than or equal to 0. Digits
+ only. No sign.<p></p>
+
+</dd>
+<dt><strong>'integer'</strong></dt><dd>Any integer (negative, 0 or positive) as accepted
+ by <code>string.atol()</code>.<p></p>
+
+</dd>
+<dt><strong>'hexadecimal'</strong></dt><dd>Hex number (with optional leading <strong>'0x'</strong>), as accepted
+ by <code>string.atol(text, 16)</code>.<p></p>
+
+</dd>
+<dt><strong>'real'</strong></dt><dd>A number, with or without a decimal point and optional
+ exponent (e or E), as accepted by <code>string.atof()</code>. This
+ validator accepts a <strong>'separator'</strong> argument, which specifies
+ the character used to represent the decimal point. The
+ default <strong>'separator'</strong> is <strong>'.'</strong>.<p></p>
+
+</dd>
+<dt><strong>'alphabetic'</strong></dt><dd>Consisting of the letters <strong>'a-z'</strong> and <strong>'A-Z'</strong>.
+ In this case, <strong>'min'</strong> and <strong>'max'</strong> specify limits on the length
+ of the text.<p></p>
+
+</dd>
+<dt><strong>'alphanumeric'</strong></dt><dd>Consisting of the letters <strong>'a-z'</strong>, <strong>'A-Z'</strong> and <strong>'0-9'</strong>.
+ In this case, <strong>'min'</strong> and <strong>'max'</strong> specify limits on the length
+ of the text.<p></p>
+
+</dd>
+<dt><strong>'time'</strong></dt><dd>Hours, minutes and seconds, in the format
+ <strong>'HH:MM:SS'</strong>, as accepted by <code>Pmw.timestringtoseconds()</code>.
+ This validator accepts a <strong>'separator'</strong> argument, which
+ specifies the character used to separate the three fields.
+ The default separator is <strong>':'</strong>. The time may be negative.<p></p>
+
+</dd>
+<dt><strong>'date'</strong></dt><dd>Day, month and year, as accepted by
+ <code>Pmw.datestringtojdn()</code>. This validator accepts a
+ <strong>'separator'</strong> argument, which specifies the character used to
+ separate the three fields. The default is <strong>':'</strong>. This
+ validator also accepts a <strong>'format'</strong> argument, which is passed to
+ <code>Pmw.datestringtojdn()</code> to specify the desired ordering of the
+ fields. The default is <strong>'ymd'</strong>.<p></p>
+
+</dd></dl>
+<p> If <strong>'validator'</strong> is a function, then it will be called whenever
+ the contents of the entry may have changed due to user action or
+ by a call to <code>setentry()</code>. The function is called with at least
+ one argument, the first one being the new text as modified by the
+ user or <code>setentry()</code>. The other arguments are keyword arguments
+ made up of the non-core fields of the <strong>validate</strong> dictionary.</p>
+
+<p> The <em>validator</em> function should return <strong>Pmw.OK</strong>, <strong>Pmw.ERROR</strong> or
+ <strong>Pmw.PARTIAL</strong> as described above. It should not perform minimum
+ and maximum checking. This is done after the call, if it returns
+ <strong>Pmw.OK</strong>.</p>
+
+<p> The <strong>'stringtovalue'</strong> field in the dictionary may be specified as
+ the name of one of the standard validators, the name of a
+ validator supplied by the <strong>extravalidators</strong> option, a function or
+ <strong>None</strong>.</p>
+
+<p> The <em>stringtovalue</em> function is used to convert the entry input
+ into a value which can then be compared with any minimum or
+ maximum values specified for the validator. If the <strong>'min'</strong> or
+ <strong>'max'</strong> fields are specified as strings, they are converted using
+ the <em>stringtovalue</em> function. The <em>stringtovalue</em> function is
+ called with the same arguments as the <em>validator</em> function. The
+ <em>stringtovalue</em> function for the standard number validators
+ convert the string to a number. Those for the standard alpha
+ validators return the length of the string. Those for the
+ standard <strong>'time'</strong> and <strong>'date'</strong> validators return the number of
+ seconds and the Julian Day Number, respectively. See
+ <code>Pmw.stringtoreal()</code>, <code>Pmw.timestringtoseconds()</code> and
+ <code>Pmw.datestringtojdn()</code>.</p>
+
+<p> If the validator has been specified as a function and no
+ <strong>'stringtovalue'</strong> field is given, then it defaults to the standard
+ python <code>len()</code> function.</p>
+
+<p> If <strong>'validator'</strong> is <strong>None</strong>, no validation is performed. However,
+ minimum and maximum checking may be performed, according to the
+ <em>stringtovalue</em> function. For example, to limit the entry text to
+ a maximum of five characters:</p>
+
+<dl><dd><pre> Pmw.EntryField(validate = {'max' : 5})</pre></dd></dl>
+
+<p> The validator functions for each of the standard validators can
+ be accessed as:</p>
+<dl><dd><pre> Pmw.numericvalidator
+ Pmw.integervalidator
+ Pmw.hexadecimalvalidator
+ Pmw.realvalidator
+ Pmw.alphabeticvalidator
+ Pmw.alphanumericvalidator
+ Pmw.timevalidator
+ Pmw.datevalidator</pre></dd></dl>
+
+
+<p> Whenever the <strong>validate</strong> option is configured, the text currently
+ displayed in the entry widget is revalidated. If it is not valid,
+ the <strong>errorbackground</strong> color is set and the <strong>invalidcommand</strong>
+ function is called. However, the displayed text is not modified.</p>
+
+<p> The default for <strong>validate</strong> is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.value></a>
+<dl><dt> <strong>value
+</strong></dt><dd>
+Initialisation option. Specifies the initial contents of the entry.
+ If this text is invalid, it will be displayed with the
+ <strong>errorbackground</strong> color and the <strong>invalidcommand</strong> function will be called.
+ If both <strong>value</strong> and <strong>entry_textvariable</strong> options are specified in
+ the constructor, <strong>value</strong> will take precedence. The default is <strong>''</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.entry></a>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+The widget where the user may enter text. Long text may be
+ scrolled horizontally by dragging with the middle mouse button. By default, this component is a Tkinter.Entry.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Entry</strong> class
+are forwarded by this megawidget to the
+<strong>entry</strong> component.
+<p></p>
+<a name=method.checkentry></a>
+<dl><dt> <strong>checkentry</strong>()</dt><dd>
+Check the validity of the current contents of the entry widget
+ and return the result.
+ If the text is not valid, set the background to <strong>errorbackground</strong> and
+ call the <strong>invalidcommand</strong> function. If there is a variable
+ specified by the <strong>entry_textvariable</strong> option, this method should be
+ called after the <code>set()</code> method of the variable is called. If this
+ is not done in this case, the entry widget background will not be
+ set correctly.</p>
+
+
+</dd></dl>
+<a name=method.clear></a>
+<dl><dt> <strong>clear</strong>()</dt><dd>
+Remove all text from the entry widget. Equivalent to <code>setentry('')</code>.</p>
+
+
+</dd></dl>
+<a name=method.getvalue></a>
+<dl><dt> <strong>getvalue</strong>()</dt><dd>
+Return the text displayed by the entry.</p>
+
+
+</dd></dl>
+<a name=method.invoke></a>
+<dl><dt> <strong>invoke</strong>()</dt><dd>
+Invoke the command specified by the <strong>command</strong> option as if the
+ <strong><Return></strong> key had been pressed and return the result.</p>
+
+
+</dd></dl>
+<a name=method.setentry></a>
+<dl><dt> <strong>setentry</strong>(<em>text</em>)</dt><dd>
+Same as <code>setvalue()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.setvalue></a>
+<dl><dt> <strong>setvalue</strong>(<em>text</em>)</dt><dd>
+Set the contents of the entry widget to <em>text</em> and carry out
+ validation as if the text had been entered by the user. If the
+ text is invalid, the entry widget will not be changed and the
+ <strong>invalidcommand</strong> function will be called. Return the validity
+ of <em>text</em>.</p>
+
+
+</dd></dl>
+<a name=method.valid></a>
+<dl><dt> <strong>valid</strong>()</dt><dd>
+Return true if the contents of the entry widget are valid.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the EntryFields.
+ self._any = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Any:',
+ validate = None,
+ command = self.execute)
+ self._real = Pmw.EntryField(parent,
+ labelpos = 'w',
+ value = '55.5',
+ label_text = 'Real (10.0 to 99.0):',
+ validate = {'validator' : 'real',
+ 'min' : 10, 'max' : 99, 'minstrict' : 0},
+ modifiedcommand = self.changed)
+ self._odd = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Odd length:',
+ validate = self.custom_validate,
+ value = 'ABC')
+ self._date = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Date (in 2000):',
+ value = '2000/2/29',
+ validate = {'validator' : 'date',
+ 'min' : '2000/1/1', 'max' : '2000/12/31',
+ 'minstrict' : 0, 'maxstrict' : 0,
+ 'format' : 'ymd'},
+ )
+ now = time.localtime(time.time())
+ self._date2 = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Date (d.m.y):',
+ value = '%d.%d.%d' % (now[2], now[1], now[0]),
+ validate = {'validator' : 'date',
+ 'format' : 'dmy', 'separator' : '.'},
+ )
+ self._time = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Time (24hr clock):',
+ value = '8:00:00',
+ validate = {'validator' : 'time',
+ 'min' : '00:00:00', 'max' : '23:59:59',
+ 'minstrict' : 0, 'maxstrict' : 0},
+ )
+ self._comma = Pmw.EntryField(parent,
+ labelpos = 'w',
+ label_text = 'Real (with comma):',
+ value = '123,456',
+ validate = {'validator' : 'real', 'separator' : ','},
+ )
+
+ entries = (self._any, self._real, self._odd, self._date, self._date2,
+ self._time, self._comma)
+
+ for entry in entries:
+ entry.pack(fill='x', expand=1, padx=10, pady=5)
+ Pmw.alignlabels(entries)
+
+ self._any.component('entry').focus_set()
+
+ def changed(self):
+ print 'Text changed, value is', self._real.getvalue()
+
+ def execute(self):
+ print 'Return pressed, value is', self._any.getvalue()
+
+ # This implements a custom validation routine. It simply checks
+ # if the string is of odd length.
+ def custom_validate(self, text):
+ print 'text:', text
+ if len(text) % 2 == 0:
+ return -1
+ else:
+ return 1
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 22 May 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+title = 'Pmw.EXAMPLE demonstration'
+
+# Import Pmw from this directory tree.
+import sys
+sys.path[:0] = ['../../..']
+
+import Tkinter
+import Pmw
+
+class Demo:
+ def __init__(self, parent):
+
+ # Create and pack the EXAMPLEs.
+ self.widget1 = Pmw.Counter(parent)
+ self.widget1.setentry('1')
+ self.widget1.pack()
+
+ self.widget2 = Pmw.Counter(parent, increment = 10)
+ self.widget2.setentry('100')
+ self.widget2.pack()
+
+######################################################################
+
+# Create demo in root window for testing.
+if __name__ == '__main__':
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ root.title(title)
+
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+ widget = Demo(root)
+ root.mainloop()
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.Group reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.Group</h1>
+
+<center><IMG SRC=Group.gif ALT="" WIDTH=428 HEIGHT=209></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.Group() -
+ frame with ring border and tag
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This megawidget consists of an interior frame with an exterior
+ ring border and an identifying tag displayed over the top edge of
+ the ring. The programmer can create other widgets within the
+ interior frame.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.collapsedsize></a>
+<dl><dt> <strong>collapsedsize
+</strong></dt><dd>
+Initialisation option. The distance from the bottom of the tag to the bottom of the ring
+ when the groupchildsite is collapsed. The default is <strong>6</strong>.</p>
+
+
+</dd></dl>
+<a name=option.tagindent></a>
+<dl><dt> <strong>tagindent
+</strong></dt><dd>
+Initialisation option. The distance from the left edge of the ring to the left side of
+ the tag component. The default is <strong>10</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.groupchildsite></a>
+<dl><dt> <strong>groupchildsite
+</strong></dt><dd>
+The frame which can contain other widgets to be grouped. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.ring></a>
+<dl><dt> <strong>ring
+</strong></dt><dd>
+This component acts as the enclosing ring around the
+ <strong>groupchildsite</strong>. The default <strong>borderwidth</strong> is <strong>2</strong> and the
+ default <strong>relief</strong> is <strong>'groove'</strong>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.tag></a>
+<dl><dt> <strong>tag
+</strong></dt><dd>
+The identifying tag displayed over the top edge of the enclosing
+ ring. If the pyclass for this component is <strong>None</strong>, (ie:
+ <code>tag_pyclass = None</code>, then no tag component is created. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.collapse></a>
+<dl><dt> <strong>collapse</strong>()</dt><dd>
+Do not display the groupchildsite component.</p>
+
+
+</dd></dl>
+<a name=method.expand></a>
+<dl><dt> <strong>expand</strong>()</dt><dd>
+Display the groupchildsite component.</p>
+
+
+</dd></dl>
+<a name=method.interior></a>
+<dl><dt> <strong>interior</strong>()</dt><dd>
+Return the frame within which the programmer may create widgets.
+ This is the same as <code>component('groupchildsite')</code>.</p>
+
+
+</dd></dl>
+<a name=method.toggle></a>
+<dl><dt> <strong>toggle</strong>()</dt><dd>
+Display the groupchildsite component if it is currently hidden and
+ hide it if it is currently displayed.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+
+ # Create and pack the Groups.
+ w = Pmw.Group(parent, tag_text='label')
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(),
+ text = 'A group with the\ndefault Label tag')
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ w = Pmw.Group(parent, tag_pyclass = None)
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(), text = 'A group\nwithout a tag')
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ radiogroups = []
+ self.var = Tkinter.IntVar()
+ self.var.set(0)
+ radioframe = Tkinter.Frame(parent)
+ w = Pmw.Group(radioframe,
+ tag_pyclass = Tkinter.Radiobutton,
+ tag_text='radiobutton 1',
+ tag_value = 0,
+ tag_variable = self.var)
+ w.pack(fill = 'both', expand = 1, side='left')
+ cw = Tkinter.Frame(w.interior(),width=200,height=20)
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+ radiogroups.append(w)
+
+ w = Pmw.Group(radioframe,
+ tag_pyclass = Tkinter.Radiobutton,
+ tag_text='radiobutton 2',
+ tag_font = Pmw.logicalfont('Helvetica', 4),
+ tag_value = 1,
+ tag_variable = self.var)
+ w.pack(fill = 'both', expand = 1, side='left')
+ cw = Tkinter.Frame(w.interior(),width=200,height=20)
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+ radiogroups.append(w)
+ radioframe.pack(padx = 6, pady = 6, expand='yes', fill='both')
+ Pmw.aligngrouptags(radiogroups)
+
+ w = Pmw.Group(parent,
+ tag_pyclass = Tkinter.Checkbutton,
+ tag_text='checkbutton',
+ tag_foreground='blue')
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Frame(w.interior(),width=150,height=20)
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ w = Pmw.Group(parent,
+ tag_pyclass = Tkinter.Button,
+ tag_text='Tkinter.Button')
+ w.configure(tag_command = w.toggle)
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(),
+ background = 'aliceblue',
+ text = 'A group with\na Button tag!?'
+ )
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+ w = Pmw.Group(parent,
+ tag_pyclass = Tkinter.Button,
+ tag_text='Show/Hide')
+ w.configure(tag_command = w.toggle)
+ w.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ cw = Tkinter.Label(w.interior(),
+ background = 'aliceblue',
+ text = 'Now you see me.\nNow you don\'t.'
+ )
+ cw.pack(padx = 2, pady = 2, expand='yes', fill='both')
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 15 November 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.HistoryText reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.HistoryText</h1>
+
+<center><IMG SRC=HistoryText.gif ALT="" WIDTH=551 HEIGHT=142></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.HistoryText() -
+ text widget with a course-grained form of history
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="ScrolledText.html">Pmw.ScrolledText</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A history text is a scrolled text widget with added functionality
+ to maintain a history of each screen and allow editing of prior
+ screens. Here, <em>screen</em> refers to the entire contents of the text
+ widget. This widget does not support a fine-grained history of
+ every change made to the text.</p>
+
+<p> Together with a few buttons and a scrolled text to display the
+ results, a history text can be used as the query-entry part of a
+ simple interactive text-based database query system. When the
+ user enters and executes a query, the query (the entire contents
+ of the text widget) is added to the history list. The user may
+ view previous queries and either execute them again or modify them
+ and execute the new query. If a previously executed query is
+ modified, the user may undo or redo all changes made to the query
+ <em>before the query is executed</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+Initialisation option. If true, the <strong>borderframe</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.columnheader></a>
+<dl><dt> <strong>columnheader
+</strong></dt><dd>
+Initialisation option. If true, the <strong>columnheader</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.compressany></a>
+<dl><dt> <strong>compressany
+</strong></dt><dd>
+See <code>addhistory()</code>. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.compresstail></a>
+<dl><dt> <strong>compresstail
+</strong></dt><dd>
+See <code>addhistory()</code>. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.historycommand></a>
+<dl><dt> <strong>historycommand
+</strong></dt><dd>
+This is a callback to indicate whether the currently displayed
+ entry in the history list has a previous or next entry. The
+ callback is given two arguments, <em>prevstate</em> and <em>nextstate</em>. If
+ the currently displayed entry is first in the history list, then
+ <em>prevstate</em> is <strong>'disabled'</strong>, otherwise it is <strong>'normal'</strong>. If the
+ currently displayed entry is last in the history list, then
+ <em>nextstate</em> is <strong>'disabled'</strong>, otherwise it is <strong>'normal'</strong>. These
+ values can be used, for example, to modify the state of <strong>Next</strong> and
+ <strong>Previous</strong> buttons that call the <code>next()</code> and <code>prev()</code> methods. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.hscrollmode></a>
+<dl><dt> <strong>hscrollmode
+</strong></dt><dd>
+The horizontal scroll mode. If <strong>'none'</strong>, the horizontal scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.rowcolumnheader></a>
+<dl><dt> <strong>rowcolumnheader
+</strong></dt><dd>
+Initialisation option. If true, the <strong>rowcolumnheader</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.rowheader></a>
+<dl><dt> <strong>rowheader
+</strong></dt><dd>
+Initialisation option. If true, the <strong>rowheader</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.scrollmargin></a>
+<dl><dt> <strong>scrollmargin
+</strong></dt><dd>
+Initialisation option. The distance between the scrollbars and the text widget. The default is <strong>2</strong>.</p>
+
+
+</dd></dl>
+<a name=option.usehullsize></a>
+<dl><dt> <strong>usehullsize
+</strong></dt><dd>
+Initialisation option. If true, the size of the megawidget is determined solely by the
+ width and height options of the <strong>hull</strong> component.</p>
+<p> Otherwise, the size of the megawidget is determined by the width
+ and height of the <strong>text</strong> component, along with the size and/or
+ existence of the other components, such as the label, the
+ scrollbars and the scrollmargin option. All these affect the
+ overall size of the megawidget. The default is <strong>0</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.vscrollmode></a>
+<dl><dt> <strong>vscrollmode
+</strong></dt><dd>
+The vertical scroll mode. If <strong>'none'</strong>, the vertical scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+A frame widget which snuggly fits around the text widget, to give
+ the appearance of a text border. It is created with a border so
+ that the text widget, which is created without a border, looks
+ like it has a border. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.columnheader></a>
+<dl><dt> <strong>columnheader
+</strong></dt><dd>
+A text widget with a default height of 1 displayed above the main
+ text widget and which scrolls horizontally in sync with the
+ horizontal scrolling of the main text widget. By default, this component is a Tkinter.Text. Its component group is <strong>Header</strong>.</p>
+
+
+</dd></dl>
+<a name=component.horizscrollbar></a>
+<dl><dt> <strong>horizscrollbar
+</strong></dt><dd>
+The horizontal scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.rowcolumnheader></a>
+<dl><dt> <strong>rowcolumnheader
+</strong></dt><dd>
+A text widget displayed to the top left of the main text widget,
+ above the row header and to the left of the column header if they
+ exist. The widget is not scrolled automatically. By default, this component is a Tkinter.Text. Its component group is <strong>Header</strong>.</p>
+
+
+</dd></dl>
+<a name=component.rowheader></a>
+<dl><dt> <strong>rowheader
+</strong></dt><dd>
+A text widget displayed to the left of the main text widget and
+ which scrolls vertically in sync with the vertical scrolling of
+ the main text widget. By default, this component is a Tkinter.Text. Its component group is <strong>Header</strong>.</p>
+
+
+</dd></dl>
+<a name=component.text></a>
+<dl><dt> <strong>text
+</strong></dt><dd>
+The text widget which is scrolled by the scrollbars. If the
+ <strong>borderframe</strong> option is true, this is created with a borderwidth
+ of <strong>0</strong> to overcome a known problem with text widgets: if a widget
+ inside a text widget extends across one of the edges of the text
+ widget, then the widget obscures the border of the text widget.
+ Therefore, if the text widget has no border, then this overlapping
+ does not occur. By default, this component is a Tkinter.Text.</p>
+
+
+</dd></dl>
+<a name=component.vertscrollbar></a>
+<dl><dt> <strong>vertscrollbar
+</strong></dt><dd>
+The vertical scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="ScrolledText.html#methods">Pmw.ScrolledText</a></strong>.
+<p></p>
+<a name=method.addhistory></a>
+<dl><dt> <strong>addhistory</strong>()</dt><dd>
+Append the currently displayed text to the history list.</p>
+<p> If <strong>compressany</strong> is true, a new entry will be added to the history
+ list only if the currently displayed entry has changed.</p>
+
+<p> If <strong>compresstail</strong> is true, a new entry will be added to the
+ history list only if the currently displayed entry has changed
+ <em>or</em> if it is not the last entry in the history list.</p>
+
+
+
+</dd></dl>
+<a name=method.gethistory></a>
+<dl><dt> <strong>gethistory</strong>()</dt><dd>
+Return the history list. Each entry in the list is a 3-tuple.
+ The first item in a history entry is the original text as added by
+ <code>addhistory()</code>. The second item is the edited text (if the user
+ has modified the entry but <code>addhistory()</code> has not yet been called
+ on the text). The third item specifies whether the entry should
+ currently display the original or modified text.</p>
+
+
+</dd></dl>
+<a name=method.next></a>
+<dl><dt> <strong>next</strong>()</dt><dd>
+Display the next screen in the history list.</p>
+
+
+</dd></dl>
+<a name=method.prev></a>
+<dl><dt> <strong>prev</strong>()</dt><dd>
+Display the previous screen in the history list.</p>
+
+
+</dd></dl>
+<a name=method.redo></a>
+<dl><dt> <strong>redo</strong>()</dt><dd>
+Reverse the effect of <code>undo()</code>.</p>
+
+
+</dd></dl>
+<a name=method.undo></a>
+<dl><dt> <strong>undo</strong>()</dt><dd>
+Undo all changes made since this entry was added to the history
+ list.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the PanedWidget to hold the query and result
+ # windows.
+ # !! panedwidget should automatically size to requested size
+ panedWidget = Pmw.PanedWidget(parent,
+ orient = 'vertical',
+ hull_height = 400,
+ hull_width = 550)
+ panedWidget.add('query', min = 0.05, size = 0.2)
+ panedWidget.add('buttons', min = 0.1, max = 0.1)
+ panedWidget.add('results', min = 0.05)
+ panedWidget.pack(fill = 'both', expand = 1)
+
+ # Create and pack the HistoryText.
+ self.historyText = Pmw.HistoryText(panedWidget.pane('query'),
+ text_wrap = 'none',
+ text_width = 60,
+ text_height = 10,
+ historycommand = self.statechange,
+ )
+ self.historyText.pack(fill = 'both', expand = 1)
+ self.historyText.component('text').focus()
+
+ buttonList = (
+ [20, None],
+ ['Clear', self.clear],
+ ['Undo', self.historyText.undo],
+ ['Redo', self.historyText.redo],
+ [20, None],
+ ['Prev', self.historyText.prev],
+ ['Next', self.historyText.next],
+ [30, None],
+ ['Execute', Pmw.busycallback(self.executeQuery)],
+ )
+ self.buttonDict = {}
+
+ buttonFrame = panedWidget.pane('buttons')
+ for text, cmd in buttonList:
+ if type(text) == type(69):
+ frame = Tkinter.Frame(buttonFrame, width = text)
+ frame.pack(side = 'left')
+ else:
+ button = Tkinter.Button(buttonFrame, text = text, command = cmd)
+ button.pack(side = 'left')
+ self.buttonDict[text] = button
+
+ for text in ('Prev', 'Next'):
+ self.buttonDict[text].configure(state = 'disabled')
+
+ self.results = Pmw.ScrolledText(panedWidget.pane('results'), text_wrap = 'none')
+ self.results.pack(fill = 'both', expand = 1)
+
+ def statechange(self, prevstate, nextstate):
+ self.buttonDict['Prev'].configure(state = prevstate)
+ self.buttonDict['Next'].configure(state = nextstate)
+
+ def clear(self):
+ self.historyText.delete('1.0', 'end')
+
+ def addnewlines(self, text):
+ if len(text) == 1:
+ text = text + '\n'
+ if text[-1] != '\n':
+ text = text + '\n'
+ if text[-2] != '\n':
+ text = text + '\n'
+ return text
+
+ def executeQuery(self):
+ sql = self.historyText.get()
+ self.results.insert('end', 'Query:\n' + self.addnewlines(sql))
+ self.results.see('end')
+ self.results.update_idletasks()
+ self.historyText.addhistory()
+ results = 'Results:\nfoo'
+ if len(results) > 0:
+ self.results.insert('end', self.addnewlines(results))
+ self.results.see('end')
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 20 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.LabeledWidget reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.LabeledWidget</h1>
+
+<center><IMG SRC=LabeledWidget.gif ALT="" WIDTH=163 HEIGHT=116></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.LabeledWidget() -
+ frame with label
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This megawidget consists of an interior frame with an associated
+ label which can be positioned on any side of the frame. The
+ programmer can create other widgets within the interior frame.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.sticky></a>
+<dl><dt> <strong>sticky
+</strong></dt><dd>
+Initialisation option. The default is <strong>'nsew'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.labelchildsite></a>
+<dl><dt> <strong>labelchildsite
+</strong></dt><dd>
+The frame which can contain other widgets to be labelled. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.interior></a>
+<dl><dt> <strong>interior</strong>()</dt><dd>
+Return the frame within which the programmer may create widgets.
+ This is the same as <code>component('labelchildsite')</code>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+
+ # Create a frame to put the LabeledWidgets into
+ frame = Tkinter.Frame(parent, background = 'grey90')
+ frame.pack(fill = 'both', expand = 1)
+
+ # Create and pack the LabeledWidgets.
+ column = 0
+ row = 0
+ for pos in ('n', 'nw', 'wn', 'w'):
+ lw = Pmw.LabeledWidget(frame,
+ labelpos = pos,
+ label_text = pos + ' label')
+ lw.component('hull').configure(relief='sunken', borderwidth=2)
+ lw.grid(column=column, row=row, padx=10, pady=10)
+ cw = Tkinter.Button(lw.interior(), text='child\nsite')
+ cw.pack(padx=10, pady=10, expand='yes', fill='both')
+
+ # Get ready for next grid position.
+ column = column + 1
+ if column == 2:
+ column = 0
+ row = row + 1
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 8 November 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.MainMenuBar reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.MainMenuBar</h1>
+
+<center><IMG SRC=MainMenuBar.gif ALT="" WIDTH=474 HEIGHT=29></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.MainMenuBar() -
+ manager for toplevel native menus
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaArchetype.html">Pmw.MegaArchetype</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This class is a wrapper for the Tkinter.Menu class. It should be
+ used as the main menu of toplevel windows. The class is similar
+ to <a href="MenuBar.html">Pmw.MenuBar</a>, but should be used when native menus are required.
+ See the Tkinter.Menu documentation for full details.</p>
+
+<p> This class should be created as the child of a Tkinter.Toplevel
+ and should then be specified as the menu associated with the
+ toplevel, using the toplevel's <code>configure()</code> method. For example:</p>
+<dl><dd><pre> # Create a Pmw.MegaToplevel.
+ megaToplevel = Pmw.MegaToplevel()
+ # Get the Tkinter.Toplevel from Pmw.MegaToplevel.
+ toplevel = megaToplevel.interior()
+ # Create the menu bar for the toplevel.
+ menuBar = Pmw.MainMenuBar(toplevel)
+ # Configure the toplevel to use the menuBar.
+ toplevel.configure(menu = menuBar)</pre></dd></dl>
+
+
+<p> There are methods to add menus, both as toplevel menus and
+ sub-menus, and for adding menu items to the menus. Each menu item
+ may have help text to be displayed by a <a href="Balloon.html">Pmw.Balloon</a>. Each menu and
+ cascaded menu (sub-menu) is referenced by name which is supplied
+ on creation.</p>
+
+<p> This megawidget is derived from <a href="MegaArchetype.html">Pmw.MegaArchetype</a> (not <a href="MegaWidget.html">Pmw.MegaWidget</a>
+ like most other megawidgets), with the hull class being
+ Tkinter.Menu.</p>
+
+<p> (Note that due to bugs in Tk's menubar functionality, balloon help
+ has not been implemented and status help does not work properly.)</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.balloon></a>
+<dl><dt> <strong>balloon
+</strong></dt><dd>
+Specifies a <a href="Balloon.html">Pmw.Balloon</a> to display the help text for menu items. If
+ <strong>None</strong>, no help is displayed. If the balloon has an associated
+ <a href="MessageBar.html">Pmw.MessageBar</a>, the help text will also be displayed there.</p>
+<p> Due to a bug in some versions of Tk (8.0 and possible others),
+ help text will not be displayed by the balloon. However, help
+ text will be displayed in the balloon's associated messagebar. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.hotkeys></a>
+<dl><dt> <strong>hotkeys
+</strong></dt><dd>
+Initialisation option. If true, keyboard accelerators will be assigned to each menu item.
+ Keyboard accelerators can be used to access the menus without
+ using the mouse. The accelerator character is always one of the
+ alphanumeric characters in the text label of the menu item and is
+ indicated by an underline.</p>
+<p> To select a menu, simultaneously press the <strong><Alt></strong> key and the
+ accelerator character indicated on a toplevel menu item. The
+ arrows keys can then be used to select other menus and menu items.
+ To invoke a menu item, press <strong><Return></strong> or press the accelerator
+ character indicated on the menu item.</p>
+
+<p> Each accelerator character will be assigned automatically unless
+ <em>traverseSpec</em> is supplied to the <code>addmenu()</code>, <code>addmenuitem()</code> or
+ <code>addcascademenu()</code> methods. The automatically selected
+ accelerator character for a menu item is the first character in
+ the label text that has not already been used as an accelerator in
+ the menu containing the menu item.</p>
+
+<p> If <em>traverseSpec</em> is given, it must be either an integer or a
+ character. If an integer, it specifies the index of the character
+ in the label text to use as the accelerator character. If a
+ character, it specifies the character to use as the accelerator
+ character. The default is <strong>1</strong>.</p>
+
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+The toplevel menu widget. By default, this component is a Tkinter.Menu.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Dynamic components</h3></dt><dd>
+<p>
+ Menu components are created dynamically by the <code>addmenu()</code> and
+ <code>addcascademenu()</code> methods. By default, these are of type
+ Tkinter.Menu and are created with a component group of <strong>Menu</strong>.</p>
+<p> </p>
+
+
+
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaArchetype.html#methods">Pmw.MegaArchetype</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Menu</strong> class
+are forwarded by this megawidget to the
+<strong>hull</strong> component.
+<p></p>
+<a name=method.addcascademenu></a>
+<dl><dt> <strong>addcascademenu</strong>(<em>parentMenuName</em>, <em>menuName</em>, <em>statusHelp</em> = <strong>''</strong>, <em>traverseSpec</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+Add a cascade menu (sub-menu) to the menu <em>parentMenuName</em>. The
+ <em>menuName</em> argument must not be the same as any menu already
+ created using the <code>addmenu()</code> or <code>addcascademenu()</code> methods.</p>
+<p> A menu item in the parent menu is created (with the
+ <code>add_cascade()</code> method of the parent menu) using all keyword
+ arguments except <strong>tearoff</strong> and <strong>name</strong>.</p>
+
+<p> If the <strong>label</strong> keyword argument is not given, the <strong>label</strong> option
+ of the menu item defaults to <em>menuName</em>. If the <strong>underline</strong>
+ keyword argument is not given (and the <strong>hotkeys</strong> megawidget option
+ is true) the <strong>underline</strong> option is determined as described under
+ <strong>hotkeys</strong> and is used to specify the keyboard accelerator.</p>
+
+<p> The <em>statusHelp</em> argument is used as the help string for the menu
+ item. This is displayed using the <code>showstatus()</code> method of the
+ balloon.</p>
+
+<p> The <strong>tearoff</strong> and <strong>name</strong> keyword arguments, if present, are passed
+ to the constructor of the menu. See Tkinter.Menu for details of
+ these options. The menu is created as a component named
+ <em>menuName</em>.</p>
+
+
+
+</dd></dl>
+<a name=method.addmenu></a>
+<dl><dt> <strong>addmenu</strong>(<em>menuName</em>, <em>balloonHelp</em>, <em>statusHelp</em> = <strong>None</strong>, <em>traverseSpec</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+Add a cascade menu to the toplevel menu. The <em>menuName</em> argument
+ must not be the same as any menu already created using the
+ <code>addmenu()</code> or <code>addcascademenu()</code> methods.</p>
+<p> A menu item in the toplevel menu is created (with the
+ <code>add_cascade()</code> method) using all keyword arguments except
+ <strong>tearoff</strong> and <strong>name</strong>.</p>
+
+<p> If the <strong>label</strong> keyword argument is not given, the <strong>label</strong> option
+ of the menu button defaults to <em>menuName</em>. If the <strong>underline</strong>
+ keyword argument is not given (and the <strong>hotkeys</strong> megawidget option
+ is true) the <strong>underline</strong> option is determined as described under
+ <strong>hotkeys</strong> and is used to specify the keyboard accelerator.</p>
+
+<p> The <em>statusHelp</em> argument is used as the help string for the menu
+ item. This is displayed using the <code>showstatus()</code> method of the
+ balloon. Currently <em>balloonHelp</em> is not used, due to a bug in Tk
+ version 8.0.</p>
+
+<p> The <strong>tearoff</strong> and <strong>name</strong> keyword arguments, if present, are passed
+ to the constructor of the menu. See Tkinter.Menu for details of
+ these options. The menu is created as a component named
+ <em>menuName</em>.</p>
+
+
+
+</dd></dl>
+<a name=method.addmenuitem></a>
+<dl><dt> <strong>addmenuitem</strong>(<em>menuName</em>, <em>itemType</em>, <em>statusHelp</em> = <strong>''</strong>, <em>traverseSpec</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+Add a menu item to the menu <em>menuName</em>. The kind of menu item is
+ given by <em>itemType</em> and may be one of <strong>command</strong>, <strong>separator</strong>,
+ <strong>checkbutton</strong>, <strong>radiobutton</strong> or <strong>cascade</strong> (although cascade menus
+ are better added using the <code>addcascademenu()</code> method). Any
+ keyword arguments present will be passed to the menu when creating
+ the menu item. See Tkinter.Menu for the valid options for each
+ item type. In addition, a keyboard accelerator may be
+ automatically given to the item, as described under <strong>hotkeys</strong>. </p>
+<p> When the mouse is moved over the menu item, the <em>helpString</em> will
+ be displayed by the <strong>balloon</strong>'s <strong>statuscommand</strong>.</p>
+
+
+
+</dd></dl>
+<a name=method.deletemenu></a>
+<dl><dt> <strong>deletemenu</strong>(<em>menuName</em>)</dt><dd>
+Delete the menu <em>menuName</em> and all its items. The menu may either
+ be a toplevel menu or a cascade menu.</p>
+
+
+</dd></dl>
+<a name=method.deletemenuitems></a>
+<dl><dt> <strong>deletemenuitems</strong>(<em>menuName</em>, <em>start</em>, <em>end</em> = <strong>None</strong>)</dt><dd>
+Delete menu items from the menu <em>menuName</em>. If <em>end</em> is not
+ given, the <em>start</em> item is deleted. Otherwise all items from
+ <em>start</em> to <em>end</em> are deleted.</p>
+
+
+</dd></dl>
+<a name=method.disableall></a>
+<dl><dt> <strong>disableall</strong>()</dt><dd>
+Disable all toplevel menus.</p>
+
+
+</dd></dl>
+<a name=method.enableall></a>
+<dl><dt> <strong>enableall</strong>()</dt><dd>
+Enable all toplevel menus.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create button to launch the toplevel with main menubar.
+ w = Tkinter.Button(parent, text = 'Show Pmw.MainMenuBar demo',
+ command = lambda parent=parent: MainMenuBarToplevel(parent))
+ w.pack(padx = 8, pady = 8)
+
+class MainMenuBarToplevel:
+ def __init__(self, parent):
+ # Create the toplevel to contain the main menubar.
+ megaToplevel = Pmw.MegaToplevel(parent, title = title)
+ toplevel = megaToplevel.interior()
+
+ # Create the Balloon for this toplevel.
+ self.balloon = Pmw.Balloon(toplevel)
+
+ # Create and install the MenuBar.
+ menuBar = Pmw.MainMenuBar(toplevel,
+ balloon = self.balloon)
+ toplevel.configure(menu = menuBar)
+ self.menuBar = menuBar
+
+ # Add some buttons to the MainMenuBar.
+ menuBar.addmenu('File', 'Close this window or exit')
+ menuBar.addmenuitem('File', 'command', 'Close this window',
+ command = PrintOne('Action: close'),
+ label = 'Close')
+ menuBar.addmenuitem('File', 'separator')
+ menuBar.addmenuitem('File', 'command', 'Exit the application',
+ command = PrintOne('Action: exit'),
+ label = 'Exit')
+
+ menuBar.addmenu('Edit', 'Cut, copy or paste')
+ menuBar.addmenuitem('Edit', 'command', 'Delete the current selection',
+ command = PrintOne('Action: delete'),
+ label = 'Delete')
+
+ menuBar.addmenu('Options', 'Set user preferences')
+ menuBar.addmenuitem('Options', 'command', 'Set general preferences',
+ command = PrintOne('Action: general options'),
+ label = 'General...')
+
+ # Create a checkbutton menu item.
+ self.toggleVar = Tkinter.IntVar()
+ # Initialise the checkbutton to 1:
+ self.toggleVar.set(1)
+ menuBar.addmenuitem('Options', 'checkbutton', 'Toggle me on/off',
+ label = 'Toggle',
+ command = self._toggleMe,
+ variable = self.toggleVar)
+ self._toggleMe()
+
+ menuBar.addcascademenu('Options', 'Size',
+ 'Set some other preferences', traverseSpec = 'z', tearoff = 1)
+ for size in ('tiny', 'small', 'average', 'big', 'huge'):
+ menuBar.addmenuitem('Size', 'command', 'Set size to ' + size,
+ command = PrintOne('Action: size ' + size),
+ label = size)
+
+ menuBar.addmenu('Help', 'User manuals', name = 'help')
+ menuBar.addmenuitem('Help', 'command', 'About this application',
+ command = PrintOne('Action: about'),
+ label = 'About...')
+
+ # Create and pack the main part of the window.
+ self.mainPart = Tkinter.Label(toplevel,
+ text = 'This is the\nmain part of\nthe window',
+ background = 'black',
+ foreground = 'white',
+ padx = 30,
+ pady = 30)
+ self.mainPart.pack(fill = 'both', expand = 1)
+
+ # Create and pack the MessageBar.
+ self.messageBar = Pmw.MessageBar(toplevel,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ self.messageBar.pack(fill = 'x', padx = 10, pady = 10)
+ self.messageBar.message('state',
+ 'Balloon/status help not working properly - Tk menubar bug')
+
+ buttonBox = Pmw.ButtonBox(toplevel)
+ buttonBox.pack(fill = 'x')
+ buttonBox.add('Disable\nall', command = menuBar.disableall)
+ buttonBox.add('Enable\nall', command = menuBar.enableall)
+ buttonBox.add('Create\nmenu', command = self.add)
+ buttonBox.add('Delete\nmenu', command = self.delete)
+ buttonBox.add('Create\nitem', command = self.additem)
+ buttonBox.add('Delete\nitem', command = self.deleteitem)
+
+ # Configure the balloon to displays its status messages in the
+ # message bar.
+ self.balloon.configure(statuscommand = self.messageBar.helpmessage)
+
+ self.testMenuList = []
+
+ def _toggleMe(self):
+ print 'Toggle value:', self.toggleVar.get()
+
+ def add(self):
+ if len(self.testMenuList) == 0:
+ num = 0
+ else:
+ num = self.testMenuList[-1]
+ num = num + 1
+ name = 'Menu%d' % num
+ self.testMenuList.append(num)
+
+ self.menuBar.addmenu(name, 'This is ' + name)
+
+ def delete(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[0]
+ name = 'Menu%d' % num
+ del self.testMenuList[0]
+ self.menuBar.deletemenu(name)
+
+ def additem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName)
+ if menu.index('end') is None:
+ label = 'item X'
+ else:
+ label = menu.entrycget('end', 'label') + 'X'
+ self.menuBar.addmenuitem(menuName, 'command', 'Help for ' + label,
+ command = PrintOne('Action: ' + menuName + ': ' + label),
+ label = label)
+
+ def deleteitem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName)
+ if menu.index('end') is None:
+ self.menuBar.bell()
+ else:
+ self.menuBar.deletemenuitems(menuName, 0)
+
+class PrintOne:
+ def __init__(self, text):
+ self.text = text
+
+ def __call__(self):
+ print self.text
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 22 April 2000
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.MegaArchetype reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.MegaArchetype</h1>
+
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.MegaArchetype() -
+ abstract base class for all Pmw megawidgets
+</p>
+
+
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This class is the basis for all Pmw megawidgets. It provides
+ methods to manage options and component widgets.</p>
+
+<p> This class is normally used as a base class for other classes. If
+ the <em>hullClass</em> argument is specified, such as in the <a href="MegaWidget.html">Pmw.MegaWidget</a>
+ and <a href="MegaToplevel.html">Pmw.MegaToplevel</a> classes, a container widget is created to act
+ as the parent of all other component widgets. Classes derived
+ from these sub classes create other component widgets and options
+ to implement megawidgets that can be used in applications. </p>
+
+<p> If no <em>hullClass</em> argument is given to the constructor, no
+ container widget is created and only the option configuration
+ functionality is available.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+<p>
+ A megawidget is generally made up of other widgets packed
+ within the megawidget's containing widget. These sub-widgets
+ are called the <em>components</em> of the megawidget and are given
+ logical names for easy reference. The component mechanism
+ allows the user of a megawidget to gain controlled access to
+ some of the internals of the megawidget, for example to call a
+ method of a component or to set a component's configuration
+ options.</p>
+
+<p> <strong>Sub components:</strong> If a component is itself a megawidget containing
+ sub-components, then these sub-components can be referred to
+ using the notation <em>component_subcomponent</em>. For example,
+ <a href="ComboBox.html">Pmw.ComboBox</a> has a component named <strong>entryfield</strong> which is an
+ instance of <a href="EntryField.html">Pmw.EntryField</a>, which itself has a Tkinter.Entry
+ component named <strong>entry</strong>. In the context of the combobox, this
+ entry widget can be referred to as <strong>entryfield_entry</strong>.</p>
+
+<p> <strong>Component aliases:</strong> Because the sub-component notation may
+ make component names inconveniently long, components and
+ sub-components can be aliased to simpler names. For example,
+ the <strong>entryfield_entry</strong> sub-component of <a href="ComboBox.html">Pmw.ComboBox</a> is aliased
+ to simply <strong>entry</strong>. If there is no conflict in component
+ names, sub-component names are usually aliased to the name of
+ the "leaf" component.</p>
+
+<p> <strong>Component groups:</strong> Similar components of a megawidget can be
+ given a <em>group</em> name, which allows all components of a group
+ to be referenced using the one group name. For example, the
+ two arrow components of <a href="Counter.html">Pmw.Counter</a> have a group name of <strong>Arrow</strong>.
+ Also, megawidgets that can create an unlimited number of
+ similar components, such as <a href="ButtonBox.html">Pmw.ButtonBox</a>, create each of these
+ components with the same group name. By convention, group
+ names begin with a capital letter.</p>
+<p> </p>
+
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+<p>
+ A megawidget defines options which allow the megawidget user
+ to modify the appearance and behaviour of the megawidget.
+ Using the same technique as Tkinter widgets, the values of
+ megawidget options may be set in calls to the constructor and
+ to <code>configure()</code> and the values may be queried by calls to
+ <code>cget()</code> and <code>configure()</code>. Like Tkinter widgets, megawidget
+ options are initialised with default values. Also, if the
+ <em>useTkOptionDb</em> option to <code>Pmw.initialise()</code> has been set,
+ then the Tk option database will be queried to get the initial
+ values. Strings found in the option database are converted
+ to python objects (integer, float, tuple, dictionary, etc)
+ using a restricted <code>eval()</code> call. Anything that is not accepted by
+ <code>eval()</code> is treated as a string.</p>
+
+<p> <strong>Inherited options:</strong> As well as the options defined in a class,
+ a derived class inherits all options of its base classes. The
+ default value of an option defined by a base class may be
+ modified by the derived class.</p>
+
+<p> <strong>Initialisation options:</strong> Some megawidget options can only be
+ set in the call to the constructor. These are called
+ <em>initialisation options</em>. Unlike normal configuration
+ options, they cannot be set by calling the <code>configure()</code>
+ method.</p>
+
+<p> <strong>Component options:</strong> Options of the components of a megawidget
+ can be referred to using the notation <em>component_option</em>.
+ Like the megawidget options, component options can be used in
+ calls to the constructor and to the <code>cget()</code> and <code>configure()</code>
+ methods. For example, the <strong>state</strong> option of the Tkinter.Text
+ <strong>text</strong> component of <a href="ScrolledText.html">Pmw.ScrolledText</a> may be set by calling</p>
+<dl><dd><pre> widget.configure(text_state = 'disabled')</pre></dd></dl>
+
+
+<p> Sub-components, component aliases and component groups may
+ also be combined with options. For example, the <strong>state</strong>
+ option of the <strong>entryfield_entry</strong> component of <a href="ComboBox.html">Pmw.ComboBox</a>
+ may be set by calling</p>
+<dl><dd><pre> combobox.configure(entryfield_entry_state = 'normal')</pre></dd></dl>
+
+
+<p> Since it has an alias, it is more convenient to use the
+ equivalent form</p>
+<dl><dd><pre> combobox.configure(entry_state = 'normal')</pre></dd></dl>
+
+
+<p> Also, the background color of both arrows of <a href="Counter.html">Pmw.Counter</a>
+ can be set using the <strong>Arrow</strong> component group.</p>
+<dl><dd><pre> counter.configure(Arrow_background = 'aliceblue')</pre></dd></dl>
+
+
+<p> </p>
+
+
+</dd>
+<dt> <h3>The pyclass component option</h3></dt><dd>
+<p>
+ The <strong>pyclass</strong> component option is a special notation that can
+ be used to specify a non-default python class for a component.
+ This can only be used when the component is being constructed.
+ For a component created during the construction of its parent
+ megawidget, this option must be given to the constructor in
+ the form <em>component_pyclass</em>. For example, to change the
+ python class of the <strong>text</strong> sub-component of <a href="TextDialog.html">Pmw.TextDialog</a>
+ to a class <strong>FontText.Text</strong></p>
+<dl><dd><pre> dialog = Pmw.TextDialog(text_pyclass = FontText.Text)</pre></dd></dl>
+
+
+<p> For components created after the construction of the parent
+ megawidget, the <strong>pyclass</strong> option must be passed into the
+ method which constructs the component. For example, to set
+ the python class of a button in <a href="ButtonBox.html">Pmw.ButtonBox</a> to a class
+ <strong>MyButton</strong>:</p>
+<dl><dd><pre> buttonBox.add('special', pyclass = MyButton)</pre></dd></dl>
+
+
+<p> The new python class of the component must support all methods
+ and options that are used by the megawidget when operating on
+ the component. The exact interface required for each
+ component is not documented. You will have to examine the Pmw
+ source code. However, any class derived from the default
+ class of a component can be used as the new class of the
+ component, as long as all of the original methods and options
+ are still supported. For example, any class derived from
+ <strong>Tkinter.Text</strong> can be used as the class of the <strong>text</strong>
+ sub-component of <a href="TextDialog.html">Pmw.TextDialog</a>. </p>
+
+<p> The <strong>pyclass</strong> component option should not be confused with the
+ <strong>class</strong> option that some of the Tk widgets support. The
+ <strong>class</strong> option sets the Tk option database class for the
+ widget and is used by Tk to query the database for the default
+ values of the widget's other options. The name <strong>pyclass</strong> was
+ chosen so that it did not conflict with any known Tk options.</p>
+
+<p> </p>
+
+
+</dd>
+<dt> <h3>Construction</h3></dt><dd>
+<p>
+ The constructors of classes derived from this class all accept
+ the same arguments: one positional argument and any number of
+ keyword arguments. The positional argument defaults to <strong>None</strong>
+ (meaning the root window) and specifies the widget to use as
+ the parent when creating the
+ megawidget's <strong>hull</strong> component. The keyword arguments define
+ initial values for options. The format for the constructors
+ of derived classes is:</p>
+
+<dl><dd><pre> def __init__(self, parent = None, **kw):</pre></dd></dl>
+<p> </p>
+
+
+
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+<a name=method.addoptions></a>
+<dl><dt> <strong>addoptions</strong>(<em>optionDefs</em>)</dt><dd>
+Add additional options for this megawidget. The <em>optionDefs</em>
+ argument is treated in the same way as for the <code>defineoptions()</code>
+ method.</p>
+<p> This method is for use by derived classes. It is only used if a
+ megawidget should conditionally define some options, perhaps
+ depending on the value of other options. Usually, megawidgets
+ unconditionally define all their options in the call to
+ <code>defineoptions()</code> and do not need to use <code>addoptions()</code>. This
+ method must be called after the call to <code>defineoptions()</code> and
+ before the call to <code>initialiseoptions()</code>.</p>
+
+
+
+</dd></dl>
+<a name=method.cget></a>
+<dl><dt> <strong>cget</strong>(<em>option</em>)</dt><dd>
+Return the current value of <em>option</em> (which should be in the
+ format described in the <strong>Options</strong> section). This method is also
+ available using object subscripting, for example
+ <code>myWidget['font']</code>. Unlike Tkinter's cget(), which always returns
+ a string, this method returns the same value and type as used when
+ the option was set (except where <em>option</em> is a component option
+ and the component is a Tkinter widget, in which case it returns
+ the string returned by Tcl/Tk).</p>
+
+
+</dd></dl>
+<a name=method.component></a>
+<dl><dt> <strong>component</strong>(<em>name</em>)</dt><dd>
+Return the component widget whose name is <em>name</em>. This
+ allows the user of a megawidget to access and configure component
+ widgets directly.</p>
+
+
+</dd></dl>
+<a name=method.componentaliases></a>
+<dl><dt> <strong>componentaliases</strong>()</dt><dd>
+Return the list of aliases for components. Each item in the list
+ is a tuple whose first item is the name of the alias and whose
+ second item is the name of the component or sub-component it
+ refers to.</p>
+
+
+</dd></dl>
+<a name=method.componentgroup></a>
+<dl><dt> <strong>componentgroup</strong>(<em>name</em>)</dt><dd>
+Return the group of the component whose name is <em>name</em> or <strong>None</strong>
+ if it does not have a group.</p>
+
+
+</dd></dl>
+<a name=method.components></a>
+<dl><dt> <strong>components</strong>()</dt><dd>
+Return a sorted list of names of the components of this
+ megawidget.</p>
+
+
+</dd></dl>
+<a name=method.configure></a>
+<dl><dt> <strong>configure</strong>(<em>option</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+Query or configure the megawidget options.</p>
+<p> If no arguments are given, return a tuple consisting of all
+ megawidget options and values, each as a 5-element tuple
+ (<em>name</em>, <em>resourceName</em>, <em>resourceClass</em>, <em>default</em>, <em>value</em>).
+ This is in the same format as the value returned by the standard
+ Tkinter <code>configure()</code> method, except that the resource name is
+ always the same as the option name and the resource class is the
+ option name with the first letter capitalised.</p>
+
+<p> If one argument is given, return the 5 element tuple for <em>option</em>.</p>
+
+<p> Otherwise, set the configuration options specified by the keyword
+ arguments. Each key should be in the format described in the
+ <strong>Options</strong> section.</p>
+
+
+
+</dd></dl>
+<a name=method.createcomponent></a>
+<dl><dt> <strong>createcomponent</strong>(<em>componentName</em>, <em>componentAliases</em>, <em>componentGroup</em>, <em>widgetClass</em>, *<em>widgetArgs</em>, **<em>kw</em>)</dt><dd>
+Create a component widget by calling <em>widgetClass</em> with the
+ arguments given by <em>widgetArgs</em> and any keyword arguments. The
+ <em>componentName</em> argument is the name by which the component will
+ be known and must not contain the underscore, <strong>'_'</strong>, character.
+ The <em>componentGroup</em> argument specifies the group of the
+ component. The <em>componentAliases</em> argument is a sequence of
+ 2-element tuples, whose first item is an alias name and whose
+ second item is the name of the component or sub-component it is to
+ refer to.</p>
+<p> If this method is called during megawidget construction, any
+ component options supplied to the megawidget constructor which
+ refer to this component (by <em>componentName</em> or <em>componentGroup</em>)
+ are added to the <em>kw</em> dictionary before calling <em>widgetClass</em>. If
+ the dictionary contains a <strong>'pyclass'</strong> key, then this item is
+ removed from the dictionary and the value is used instead of
+ <em>widgetClass</em>. For more details see <strong>The pyclass component option</strong>
+ section.</p>
+
+<p> This method may be called by derived classes during or after
+ megawidget construction. It returns the instance of the class
+ created.</p>
+
+
+
+</dd></dl>
+<a name=method.createlabel></a>
+<dl><dt> <strong>createlabel</strong>(<em>parent</em>, <em>childCols</em> = <strong>1</strong>, <em>childRows</em> = <strong>1</strong>)</dt><dd>
+Create a <strong>Tkinter.Label</strong> component named <strong>'label'</strong> in the <em>parent</em>
+ widget. This is a convenience method used by several megawidgets
+ that require an optional label. The widget must have options
+ named <strong>labelpos</strong> and <strong>labelmargin</strong>. If <strong>labelpos</strong> is <strong>None</strong>, no
+ label is created. Otherwise, a label is created and positioned
+ according to the value of <strong>labelpos</strong> and <strong>labelmargin</strong>. The label
+ is added to the parent using the <code>grid()</code> method, with <em>childCols</em>
+ and <em>childRows</em> indicating how many rows and columns the label
+ should span. Note that all other child widgets of the parent
+ <em>must</em> be added to the parent using the <code>grid()</code> method. The
+ <code>createlabel()</code> method may be called by derived classes during
+ megawidget construction.</p>
+
+
+</dd></dl>
+<a name=method.defineoptions></a>
+<dl><dt> <strong>defineoptions</strong>(<em>keywords</em>, <em>optionDefs</em>, <em>dynamicGroups</em> = <strong>()</strong>)</dt><dd>
+Create options for this megawidget. The <em>optionDefs</em> argument
+ defines the options. It is a sequence of 3-element tuples,
+ (<em>name</em>, <em>default</em>, <em>callback</em>), where <em>name</em> is the name of the
+ option, <em>default</em> is its default value and <em>callback</em> is the
+ function to call when the value of the option is set by a call to
+ <code>configure()</code>. The <em>keywords</em> argument should be the keyword
+ arguments passed in to the constructor of the megawidget. The user
+ may override the default value of an option by supplying a keyword
+ argument to the constructor.</p>
+<p> If any option created by a base class is also defined by
+ <em>optionDefs</em>, then the derived class's default value will take
+ precedence over the base class's. If the <em>callback</em> field is not
+ <strong>None</strong>, then this will also override the callback set by the base
+ class.</p>
+
+<p> If <em>callback</em> is <strong>Pmw.INITOPT</strong>, then the option is an
+ <em>initialisation option</em>.</p>
+
+<p> The <em>dynamicGroups</em> argument contains a list of the groups of the
+ components created dynamically by this megawidget. If a group is
+ included in this list, then it not an error if a keyword argument
+ for the group is given to the constructor or to <code>configure()</code>,
+ even when no components with this group have been created.</p>
+
+<p> If <code>defineoptions()</code> is called, it must be called once in the
+ megawidget constructor before the call to the constructor of the
+ base class and there must be a matching call to
+ <code>initialiseoptions()</code> at the end of the constructor.</p>
+
+
+
+</dd></dl>
+<a name=method.destroy></a>
+<dl><dt> <strong>destroy</strong>()</dt><dd>
+Destroy the <strong>hull</strong> component widget, if it exists, including all
+ of its children.</p>
+
+
+</dd></dl>
+<a name=method.destroycomponent></a>
+<dl><dt> <strong>destroycomponent</strong>(<em>name</em>)</dt><dd>
+Remove the megawidget component called <em>name</em>. This method may be
+ called by derived classes to destroy a megawidget component. It
+ destroys the component widget and then removes all record of the
+ component from the megawidget.</p>
+
+
+</dd></dl>
+<a name=method.hulldestroyed></a>
+<dl><dt> <strong>hulldestroyed</strong>()</dt><dd>
+Return true if the Tk widget corresponding to the <strong>hull</strong> component
+ has been destroyed.</p>
+
+
+</dd></dl>
+<a name=method.initialiseoptions></a>
+<dl><dt> <strong>initialiseoptions</strong>(<em>dummy</em> = <strong>None</strong>)</dt><dd>
+Check keyword arguments and call option callback functions. This
+ method must be called, at the end of a megawidget constructor, if
+ and only if <code>defineoptions()</code> was also called in the constructor.
+ The <em>dummy</em> argument is not required, but is retained for
+ backwards compatibility.</p>
+<p> It checks that all keyword arguments given to the constructor have
+ been used. If not, it raises an error indicating which arguments
+ were unused. A keyword is defined to be used if, during the
+ construction of a megawidget, it is defined in a call to
+ <code>defineoptions()</code> or <code>addoptions()</code> (by the megawidget or one of
+ its base classes), or it references, by name, a component of the
+ megawidget, or it references, by group, at least one component.
+ It also calls the configuration callback function for all options
+ that have a callback.</p>
+
+<p> This method is only effective when called by the constructor of
+ the <em>leaf</em> class, that is, the class in the class hierarchy which
+ first called <code>defineoptions()</code>. For all other classes in the
+ class hierarchy (base classes), the method returns immediately.</p>
+
+
+
+</dd></dl>
+<a name=method.interior></a>
+<dl><dt> <strong>interior</strong>()</dt><dd>
+Return the widget framing the interior space in which any children
+ of this megawidget should be created. By default, this returns
+ the <strong>hull</strong> component widget, if one was created, or <strong>None</strong>
+ otherwise. A subclass should use the widget returned by
+ <code>interior()</code> as the parent of any components or sub-widgets it
+ creates. Megawidgets which can be further subclassed, such as
+ <a href="Dialog.html">Pmw.Dialog</a>, should redefine this method to return the widget in
+ which subclasses should create children. The overall containing
+ widget is always available as the <strong>hull</strong> component.</p>
+
+
+</dd></dl>
+<a name=method.isinitoption></a>
+<dl><dt> <strong>isinitoption</strong>(<em>option</em>)</dt><dd>
+If <em>option</em> is an initialisation option, return true. Otherwise,
+ return false (the option is a configuration option). The <em>option</em>
+ argument must be an option of this megawidget, not an option of a
+ component. Otherwise an exception is raised.</p>
+
+
+</dd></dl>
+<a name=method.options></a>
+<dl><dt> <strong>options</strong>()</dt><dd>
+Return a sorted list of this megawidget's options. Each item in
+ the list is a 3-element tuple, (<em>option</em>, <em>default</em>, <em>isinit</em>),
+ where <em>option</em> is the name of the option, <em>default</em> is its default
+ value and <em>isinit</em> is true if the option is an initialisation
+ option.</p>
+
+
+</dd></dl>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 22 May 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.MegaToplevel reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.MegaToplevel</h1>
+
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.MegaToplevel() -
+ base class for megawidgets within a toplevel
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaArchetype.html">Pmw.MegaArchetype</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This class creates a megawidget contained within a toplevel
+ window. It may be used directly to create a toplevel megawidget
+ or it may be used as a base class for more specialised toplevel
+ megawidgets, such as <a href="Dialog.html">Pmw.Dialog</a>. It creates a Tkinter.Toplevel
+ component, named <strong>hull</strong>, to act as the container of the megawidget.
+ The window class name for the <strong>hull</strong> widget is set to the
+ most-specific class name for the megawidget. Derived classes
+ specialise this class by creating other widget components as
+ children of the <strong>hull</strong> widget.</p>
+
+<p> The megawidget may be used as either a normal toplevel window or
+ as a modal dialog. Use <code>show()</code> and <code>withdraw()</code> for normal use
+ and <code>activate()</code> and <code>deactivate()</code> for modal dialog use. If the
+ window is deleted by the window manager while being shown
+ normally, the default behaviour is to destroy the window. If the
+ window is deleted by the window manager while the window is active
+ (ie: when used as a modal dialog), the window is deactivated.
+ Use the <code>userdeletefunc()</code> and <code>usermodaldeletefunc()</code> methods to
+ override these behaviours. Do not call <code>protocol()</code> to set the
+ <strong>WM_DELETE_WINDOW</strong> window manager protocol directly if you want to
+ use this window as a modal dialog.</p>
+
+<p> The currently active windows form a stack with the most recently
+ activated window at the top of the stack. All mouse and
+ keyboard events are sent to this top window. When it
+ deactivates, the next window in the stack will start to receive
+ events.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaArchetype.html#methods">Pmw.MegaArchetype</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Toplevel</strong> class
+are forwarded by this megawidget to the
+<strong>hull</strong> component.
+<p></p>
+<a name=method.activate></a>
+<dl><dt> <strong>activate</strong>(<em>globalMode</em> = <strong>0</strong>, <em>geometry</em> = <strong>'centerscreenfirst'</strong>)</dt><dd>
+Display the window as a modal dialog. This means that all mouse
+ and keyboard events go to this window and no other windows can
+ receive any events. If you do not want to restrict mouse and
+ keyboard events to this window, use the <code>show()</code> method instead.</p>
+<p> If the BLT extension to Tk is present, a busy cursor will be
+ displayed on other toplevel windows, using <code>Pmw.showbusycursor()</code>.</p>
+
+<p> The <code>activate()</code> method does not return until the <code>deactivate()</code>
+ method is called, when the window is withdrawn, the grab released
+ and the result returned.</p>
+
+<p> If <em>globalMode</em> is false, the window will grab control of the
+ pointer and keyboard, preventing any events from being delivered
+ to any other toplevel windows within the application. If
+ <em>globalMode</em> is true, the grab will prevent events from being
+ delivered to any other toplevel windows regardless of application.
+ Global grabs should be used sparingly, if at all.</p>
+
+<p> If <em>globalMode</em> is <strong>'nograb'</strong>, then no grab is performed. If BLT
+ is present, this will allow mouse and keyboard events to be
+ received by other windows whose <strong>exclude</strong> busycursor attribute has
+ been set to true by a call to <code>Pmw.setbusycursorattributes()</code>.
+ Note that if <strong>'nograb'</strong> is used and BLT is not present, then <em>all</em>
+ other windows will receive mouse and keyboard events. This is
+ because, in plain Tk, there is no way to specify that two windows
+ (only) receive events. If your application may be used without
+ BLT, then do not use <strong>'nograb'</strong>.</p>
+
+<p> When the window is displayed, it is positioned on the screen
+ according to <em>geometry</em> which may be one of:</p>
+
+<dl><dt><strong>centerscreenfirst</strong></dt><dd>The window will be centered the first time it is activated.
+ On subsequent activations it will be positioned in the same
+ position as the last time it was displayed, even if it has
+ been moved by the user.<p></p>
+
+</dd>
+<dt><strong>centerscreenalways</strong></dt><dd>The window will be be centered on the screen (halfway across
+ and one third down).<p></p>
+
+</dd>
+<dt><strong>first</strong> + <em>spec</em></dt><dd>It is assumed that the rest of the argument (after <strong>'first'</strong>)
+ is a standard geometry specification. The window will be
+ positioned using this specification the first time it is
+ activated. On subsequent activations it will be positioned in
+ the same position as the last time it was displayed, even if
+ it has been moved by the user. For example,
+ <code>geometry = first+100+100</code> will initially display the window
+ at position (100,100). Other calls to <code>activate()</code> will not
+ change the previous position of the window.<p></p>
+
+</dd>
+<dt><em>spec</em></dt><dd>This is a standard geometry specification. The window will be
+ be positioned using this specification.<p></p>
+
+</dd></dl>
+<p> If the <strong>BLT</strong> Tcl extension library is present, a <strong>clock</strong> cursor
+ will be displayed until the window is deactivated.</p>
+
+<p> If the <strong>activatecommand</strong> option is callable, it is called just
+ before the window begins to wait for the result.</p>
+
+<p> If the <strong>master</strong> option is not <strong>None</strong>, the window will become a
+ transient window of <strong>master</strong>, which should be a toplevel window.
+ If <strong>master</strong> has the special value of <strong>'parent'</strong>, the master is the
+ toplevel window of the window's parent.</p>
+
+
+
+</dd></dl>
+<a name=method.active></a>
+<dl><dt> <strong>active</strong>()</dt><dd>
+Return true if the megawidget is currently active (that is,
+ <code>activate()</code> is currently waiting for a result to be passed to it
+ by a call to <code>deactivate()</code>).</p>
+
+
+</dd></dl>
+<a name=method.deactivate></a>
+<dl><dt> <strong>deactivate</strong>(<em>result</em> = <strong>None</strong>)</dt><dd>
+This should be called while a call to <code>activate()</code> is waiting. It
+ will withdraw the window, release the grab and cause the
+ <code>activate()</code> call to return with the value of <em>result</em>.</p>
+<p> If the <strong>deactivatecommand</strong> option is callable, it is called just
+ before the <code>deactivate()</code> method returns.</p>
+
+
+
+</dd></dl>
+<a name=method.destroy></a>
+<dl><dt> <strong>destroy</strong>()</dt><dd>
+Destroy the <strong>hull</strong> component widget, including all of its
+ children. If the megawidget is currently active, deactivate it.</p>
+
+
+</dd></dl>
+<a name=method.show></a>
+<dl><dt> <strong>show</strong>(<em>master</em> = <strong>None</strong>)</dt><dd>
+Make the window visible. This raises or deiconifies the toplevel
+ window. If the window has previously been shown it will remain in
+ the same position. This means that calling <code>withdraw()</code> then
+ <code>show()</code> will not move the window, whereas calling <code>withdraw()</code>
+ then <code>deiconify()</code> may change the window's position. (This may
+ depend on the behaviour of the window manager.)</p>
+
+
+</dd></dl>
+<a name=method.userdeletefunc></a>
+<dl><dt> <strong>userdeletefunc</strong>(<em>func</em> = <strong>None</strong>)</dt><dd>
+If <em>func</em> is <strong>None</strong>, return the function that will be called
+ when the window is deleted by the window manager while being
+ displayed normally. If <em>func</em> is not <strong>None</strong>, set this function to
+ <em>func</em>. By default, the function is <code>self.destroy</code>.</p>
+
+
+</dd></dl>
+<a name=method.usermodaldeletefunc></a>
+<dl><dt> <strong>usermodaldeletefunc</strong>(<em>func</em> = <strong>None</strong>)</dt><dd>
+If <em>func</em> is <strong>None</strong>, return the function that will be called
+ when the window is deleted by the window manager while it is
+ active (ie: when being used as a modal dialog). If <em>func</em> is not
+ <strong>None</strong>, set this function to <em>func</em>. By default, the function is
+ <code>self.deactivate</code>.</p>
+
+
+</dd></dl>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 22 May 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.MegaWidget reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.MegaWidget</h1>
+
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.MegaWidget() -
+ base class for megawidgets within a frame
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaArchetype.html">Pmw.MegaArchetype</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ This class creates a megawidget contained within a Tkinter.Frame
+ window. The class acts as the base class for megawidgets that are
+ not contained in their own toplevel window, such as <a href="ButtonBox.html">Pmw.ButtonBox</a> and
+ <a href="ComboBox.html">Pmw.ComboBox</a>. It creates a Tkinter.Frame component, named <strong>hull</strong>,
+ to act as the container of the megawidget. The window class name
+ for the <strong>hull</strong> widget is set to the most-specific class name for
+ the megawidget. Derived classes specialise this class by
+ creating other widget components as children of the <strong>hull</strong> widget.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+This megawidget has no methods of its own.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaArchetype.html#methods">Pmw.MegaArchetype</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Frame</strong> class
+are forwarded by this megawidget to the
+<strong>hull</strong> component.
+<p></p>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 22 May 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.MenuBar reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.MenuBar</h1>
+
+<center><IMG SRC=MenuBar.gif ALT="" WIDTH=474 HEIGHT=29></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.MenuBar() -
+ manager megawidget for menu buttons and menus
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A menu bar is a container megawidget which manages a number of
+ menu buttons and dropdown menus. There
+ are methods to add menu buttons and menus to the menu bar and for
+ adding menu items to the menus. Menu buttons may be added to the
+ left or right of the megawidget. Each menu button and menu item may
+ have help text to be displayed by a <a href="Balloon.html">Pmw.Balloon</a>. Each menu
+ and cascaded menu (sub-menu) is referenced by name which is
+ supplied on creation.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.balloon></a>
+<dl><dt> <strong>balloon
+</strong></dt><dd>
+Specifies a <a href="Balloon.html">Pmw.Balloon</a> to display the help text for menu
+ buttons and menu items. If <strong>None</strong>, no help is displayed. If the
+ balloon has an associated <a href="MessageBar.html">Pmw.MessageBar</a>, the help text will also be
+ displayed there. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.hotkeys></a>
+<dl><dt> <strong>hotkeys
+</strong></dt><dd>
+Initialisation option. If true, keyboard accelerators will be assigned to each menu
+ button and menu item. Keyboard accelerators can be used to access
+ the menus without using the mouse. The accelerator character is
+ always one of the alphanumeric characters in the text label of the
+ menu or menu item and is indicated by an underline.</p>
+<p> To select a menu, simultaneously press the <strong><Alt></strong> key and the
+ accelerator character indicated on a menu button. The arrows keys
+ can then be used to select other menus and menu items. To invoke a
+ menu item, press <strong><Return></strong> or press the accelerator character
+ indicated on the menu item.</p>
+
+<p> Each accelerator character will be assigned automatically unless
+ <em>traverseSpec</em> is supplied to the <code>addmenu()</code>, <code>addmenuitem()</code> or
+ <code>addcascademenu()</code> methods. The automatically selected
+ accelerator character for a menu button (or menu item) is the
+ first character in the label text that has not already been used
+ as an accelerator for a menu button (or in the menu containing the
+ menu item).</p>
+
+<p> If <em>traverseSpec</em> is given, it must be either an integer or a
+ character. If an integer, it specifies the index of the character
+ in the label text to use as the accelerator character. If a
+ character, it specifies the character to use as the accelerator
+ character. The default is <strong>1</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.padx></a>
+<dl><dt> <strong>padx
+</strong></dt><dd>
+Initialisation option. Specifies a padding distance to leave between each menu button in
+ the x direction and also between the menu buttons and the outer
+ edge of the menu bar. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Dynamic components</h3></dt><dd>
+<p>
+ Menu button components are created dynamically by the
+ <code>addmenu()</code> method. By default, these are of type
+ Tkinter.Menubutton and are created with a component group of
+ <strong>Button</strong>.</p>
+
+<p> Menu components are created dynamically by the <code>addmenu()</code> and
+ <code>addcascademenu()</code> methods. By default, these are of type
+ Tkinter.Menu and are created with a component group of <strong>Menu</strong>.</p>
+<p> </p>
+
+
+
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.addcascademenu></a>
+<dl><dt> <strong>addcascademenu</strong>(<em>parentMenuName</em>, <em>menuName</em>, <em>statusHelp</em> = <strong>''</strong>, <em>traverseSpec</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+Add a cascade menu (sub-menu) to the menu <em>parentMenuName</em>. The
+ <em>menuName</em> argument must not be the same as any menu already
+ created using the <code>addmenu()</code> or <code>addcascademenu()</code> methods.</p>
+<p> A menu item in the parent menu is created (with the
+ <code>add_cascade()</code> method of the parent menu) using all keyword
+ arguments except <strong>tearoff</strong>.</p>
+
+<p> If the <strong>label</strong> keyword argument is not given, the <strong>label</strong> option
+ of the menu item defaults to <em>menuName</em>. If the <strong>underline</strong>
+ keyword argument is not given (and the <strong>hotkeys</strong> megawidget option
+ is true) the <strong>underline</strong> option is determined as described under
+ <strong>hotkeys</strong> and is used to specify the keyboard accelerator.</p>
+
+<p> The <em>statusHelp</em> argument is used as the help string for the menu
+ item. This is displayed using the <code>showstatus()</code> method of the
+ balloon.</p>
+
+<p> The <strong>tearoff</strong> keyword argument, if present, is passed to the
+ constructor of the menu. The menu is created as a component named
+ <em>menuName</em>-<strong>menu</strong>.</p>
+
+
+
+</dd></dl>
+<a name=method.addmenu></a>
+<dl><dt> <strong>addmenu</strong>(<em>menuName</em>, <em>balloonHelp</em>, <em>statusHelp</em> = <strong>None</strong>, <em>side</em> = <strong>'left'</strong>, <em>traverseSpec</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+Add a menu button and its associated menu to the menu bar. The
+ <em>menuName</em> argument must not be the same as any menu already
+ created using the <code>addmenu()</code> or <code>addcascademenu()</code> methods.</p>
+<p> Any keyword arguments present (except <strong>tearoff</strong>) will be passed to
+ the constructor of the menu button. If the <strong>text</strong> keyword
+ argument is not given, the <strong>text</strong> option of the menu button
+ defaults to <em>menuName</em>. If the <strong>underline</strong> keyword argument is
+ not given (and the <strong>hotkeys</strong> megawidget option is true) the
+ <strong>underline</strong> option is determined as described under <strong>hotkeys</strong> and
+ is used to specify the keyboard accelerator. Each menu button is
+ packed into the menu bar using the given <em>side</em>, which should be
+ either <strong>left</strong> or <strong>right</strong>. The menu button is created as a
+ component named <em>menuName</em>-<strong>button</strong>.</p>
+
+<p> If the <strong>balloon</strong> option has been defined, <em>balloonHelp</em> and
+ <em>statusHelp</em> are passed to the balloon as the help strings for the
+ menu button. See the <code>bind()</code> method of <a href="Balloon.html">Pmw.Balloon</a> for how these
+ strings may be displayed.</p>
+
+<p> The <strong>tearoff</strong> keyword argument, if present, is passed to the
+ constructor of the menu. The menu is created as a component named
+ <em>menuName</em>-<strong>menu</strong>.</p>
+
+
+
+</dd></dl>
+<a name=method.addmenuitem></a>
+<dl><dt> <strong>addmenuitem</strong>(<em>menuName</em>, <em>itemType</em>, <em>statusHelp</em> = <strong>''</strong>, <em>traverseSpec</em> = <strong>None</strong>, **<em>kw</em>)</dt><dd>
+Add a menu item to the menu <em>menuName</em>. The kind of menu item is
+ given by <em>itemType</em> and may be one of <strong>command</strong>, <strong>separator</strong>,
+ <strong>checkbutton</strong>, <strong>radiobutton</strong> or <strong>cascade</strong> (although cascade menus
+ are better added using the <code>addcascademenu()</code> method). Any
+ keyword arguments present will be passed to the menu when creating
+ the menu item. See Tkinter.Menu for the valid options for each
+ item type. In addition, a keyboard accelerator may be
+ automatically given to the item, as described under <strong>hotkeys</strong>. </p>
+<p> When the mouse is moved over the menu item, the <em>helpString</em> will
+ be displayed by the <strong>balloon</strong>'s <strong>statuscommand</strong>.</p>
+
+
+
+</dd></dl>
+<a name=method.deletemenu></a>
+<dl><dt> <strong>deletemenu</strong>(<em>menuName</em>)</dt><dd>
+Delete the menu <em>menuName</em> and all its items. The menu may either
+ be a toplevel menu (in which case the corresponding menu button is
+ also deleted) or a cascade menu.</p>
+
+
+</dd></dl>
+<a name=method.deletemenuitems></a>
+<dl><dt> <strong>deletemenuitems</strong>(<em>menuName</em>, <em>start</em>, <em>end</em> = <strong>None</strong>)</dt><dd>
+Delete menu items from the menu <em>menuName</em>. If <em>end</em> is not
+ given, the <em>start</em> item is deleted. Otherwise all items from
+ <em>start</em> to <em>end</em> are deleted.</p>
+
+
+</dd></dl>
+<a name=method.disableall></a>
+<dl><dt> <strong>disableall</strong>()</dt><dd>
+Disable all toplevel menus.</p>
+
+
+</dd></dl>
+<a name=method.enableall></a>
+<dl><dt> <strong>enableall</strong>()</dt><dd>
+Enable all toplevel menus.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the Balloon.
+ self.balloon = Pmw.Balloon(parent)
+
+ # Create and pack the MenuBar.
+ menuBar = Pmw.MenuBar(parent,
+ hull_relief = 'raised',
+ hull_borderwidth = 1,
+ balloon = self.balloon)
+ menuBar.pack(fill = 'x')
+ self.menuBar = menuBar
+
+ # Add some buttons to the MenuBar.
+ menuBar.addmenu('File', 'Close this window or exit')
+ menuBar.addmenuitem('File', 'command', 'Close this window',
+ command = PrintOne('Action: close'),
+ label = 'Close')
+ menuBar.addmenuitem('File', 'separator')
+ menuBar.addmenuitem('File', 'command', 'Exit the application',
+ command = PrintOne('Action: exit'),
+ label = 'Exit')
+
+ menuBar.addmenu('Edit', 'Cut, copy or paste')
+ menuBar.addmenuitem('Edit', 'command', 'Delete the current selection',
+ command = PrintOne('Action: delete'),
+ label = 'Delete')
+
+ menuBar.addmenu('Options', 'Set user preferences')
+ menuBar.addmenuitem('Options', 'command', 'Set general preferences',
+ command = PrintOne('Action: general options'),
+ label = 'General...')
+
+ # Create a checkbutton menu item.
+ self.toggleVar = Tkinter.IntVar()
+ # Initialise the checkbutton to 1:
+ self.toggleVar.set(1)
+ menuBar.addmenuitem('Options', 'checkbutton', 'Toggle me on/off',
+ label = 'Toggle',
+ command = self._toggleMe,
+ variable = self.toggleVar)
+ self._toggleMe()
+
+ menuBar.addcascademenu('Options', 'Size',
+ 'Set some other preferences', traverseSpec = 'z', tearoff = 1)
+ for size in ('tiny', 'small', 'average', 'big', 'huge'):
+ menuBar.addmenuitem('Size', 'command', 'Set size to ' + size,
+ command = PrintOne('Action: size ' + size),
+ label = size)
+
+ menuBar.addmenu('Help', 'User manuals', side = 'right')
+ menuBar.addmenuitem('Help', 'command', 'About this application',
+ command = PrintOne('Action: about'),
+ label = 'About...')
+
+ # Create and pack the main part of the window.
+ self.mainPart = Tkinter.Label(parent,
+ text = 'This is the\nmain part of\nthe window',
+ background = 'black',
+ foreground = 'white',
+ padx = 30,
+ pady = 30)
+ self.mainPart.pack(fill = 'both', expand = 1)
+
+ # Create and pack the MessageBar.
+ self.messageBar = Pmw.MessageBar(parent,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ self.messageBar.pack(fill = 'x', padx = 10, pady = 10)
+ self.messageBar.message('state', 'OK')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(fill = 'x')
+ buttonBox.add('Disable\nall', command = menuBar.disableall)
+ buttonBox.add('Enable\nall', command = menuBar.enableall)
+ buttonBox.add('Create\nmenu', command = self.add)
+ buttonBox.add('Delete\nmenu', command = self.delete)
+ buttonBox.add('Create\nitem', command = self.additem)
+ buttonBox.add('Delete\nitem', command = self.deleteitem)
+
+ # Configure the balloon to displays its status messages in the
+ # message bar.
+ self.balloon.configure(statuscommand = self.messageBar.helpmessage)
+
+ self.testMenuList = []
+
+ def _toggleMe(self):
+ print 'Toggle value:', self.toggleVar.get()
+
+ def add(self):
+ if len(self.testMenuList) == 0:
+ num = 0
+ else:
+ num = self.testMenuList[-1]
+ num = num + 1
+ name = 'Menu%d' % num
+ self.testMenuList.append(num)
+
+ self.menuBar.addmenu(name, 'This is ' + name)
+
+ def delete(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[0]
+ name = 'Menu%d' % num
+ del self.testMenuList[0]
+ self.menuBar.deletemenu(name)
+
+ def additem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName + '-menu')
+ if menu.index('end') is None:
+ label = 'item X'
+ else:
+ label = menu.entrycget('end', 'label') + 'X'
+ self.menuBar.addmenuitem(menuName, 'command', 'Help for ' + label,
+ command = PrintOne('Action: ' + menuName + ': ' + label),
+ label = label)
+
+ def deleteitem(self):
+ if len(self.testMenuList) == 0:
+ self.menuBar.bell()
+ else:
+ num = self.testMenuList[-1]
+ menuName = 'Menu%d' % num
+ menu = self.menuBar.component(menuName + '-menu')
+ if menu.index('end') is None:
+ self.menuBar.bell()
+ else:
+ self.menuBar.deletemenuitems(menuName, 0)
+
+class PrintOne:
+ def __init__(self, text):
+ self.text = text
+
+ def __call__(self):
+ print self.text
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 22 April 2000
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.MessageBar reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.MessageBar</h1>
+
+<center><IMG SRC=MessageBar.gif ALT="" WIDTH=398 HEIGHT=44></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.MessageBar() -
+ information line for displaying short messages
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A message bar contains a single-line message display area. Messages
+ of several different types may displayed. Messages are cleared
+ after a period defined for each message type. Each message type
+ has a priority so that if the application attempts to display more
+ than one message at a time, the message with the highest priority
+ will be displayed. Messages may be accompanied by a number of
+ audible bells.</p>
+
+<p> This megawidget can be used for both interactive help messages
+ (when the mouse enters certain widgets) and also for other general
+ messages.</p>
+
+<p> To perform the help function it can cooperate with the <a href="Balloon.html">Pmw.Balloon</a>
+ megawidget so that the programmer (or user) can choose either
+ balloon help, message bar help, both or neither.</p>
+
+<p> This megawidget supports a configurable number of message types.
+ The default types include <strong>'state'</strong>, <strong>'help'</strong>, <strong>'usererror'</strong> and
+ <strong>'systemerror'</strong>. The difference between these are the length of
+ time they are displayed, the number of bells that are rung and the
+ priority of the message. For example, the <strong>'help'</strong> message type
+ is lower in priority than the <strong>'usererror'</strong>, so that error
+ messages will always be displayed in preference to help messages
+ regardless of the order the messages are created. The <strong>'state'</strong>
+ message type is lowest in priority but has no timeout, so it
+ should contain messages describing the current state of the
+ application, such as <em>Waiting for database connection</em> or 'Waiting
+ for file to be unlocked'. Generally this should be set to the
+ empty string when the application is running normally. By default
+ the help messages (with message type <strong>'help'</strong>) time out after 5
+ seconds, so that if the cursor happens to be left over a widget,
+ the application state will be redisplayed after a short time.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.messagetypes></a>
+<dl><dt> <strong>messagetypes
+</strong></dt><dd>
+Initialisation option. This defines what message types are supported by the message bar
+ and the characteristics of those message types. It is a
+ dictionary where the key is a string specifying a message type and
+ the value is a tuple of four integers, (<em>priority</em>, <em>showtime</em>,
+ <em>bells</em>, <em>logmessage</em>), where <em>priority</em> is the rank of the
+ message type, <em>showtime</em> is the number of seconds to display
+ messages of this message type, <em>bells</em> is the number of audible
+ bells to ring and <em>logmessage</em> is a boolean
+ specifying whether this message should be logged for retrieval
+ later. Messages with a higher priority are displayed in
+ preference to those with lower priority. If a high priority
+ message times out (because it has been displayed for <em>showtime</em>
+ seconds), then a lower priority message may be displayed. A
+ <em>showtime</em> of <strong>0</strong> means that the message will never time out and
+ is useful for displaying messages describing the current state of
+ the application as opposed to messages describing events. Logging
+ is not currently implemented. The default is</p>
+<dl><dd><pre> {
+ 'systemerror' : (5, 10, 2, 1),
+ 'usererror' : (4, 5, 1, 0),
+ 'busy' : (3, 0, 0, 0),
+ 'systemevent' : (2, 5, 0, 0),
+ 'userevent' : (2, 5, 0, 0),
+ 'help' : (1, 5, 0, 0),
+ 'state' : (0, 0, 0, 0),
+ }</pre></dd></dl>
+
+
+
+</dd></dl>
+<a name=option.silent></a>
+<dl><dt> <strong>silent
+</strong></dt><dd>
+If true, no audible bells will sound, regardless of the value for
+ <em>bells</em> defined in the <strong>messagetypes</strong> option. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.sticky></a>
+<dl><dt> <strong>sticky
+</strong></dt><dd>
+Initialisation option. The default is <strong>'ew'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.entry></a>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+The widget where the messages are displayed. Long messages may be
+ scrolled horizontally by dragging with the middle mouse button. By default, this component is a Tkinter.Entry.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Entry</strong> class
+are forwarded by this megawidget to the
+<strong>entry</strong> component.
+<p></p>
+<a name=method.helpmessage></a>
+<dl><dt> <strong>helpmessage</strong>(<em>text</em>)</dt><dd>
+A convenience method to display <em>text</em> in the message bar
+ according to the characteristics defined by the <strong>help</strong> message type.
+ Equivalent to <code>message('help', text)</code>.</p>
+
+
+</dd></dl>
+<a name=method.message></a>
+<dl><dt> <strong>message</strong>(<em>type</em>, <em>text</em>)</dt><dd>
+Display <em>text</em> in the message bar according to the characteristics
+ defined by the <em>type</em> message type, as discussed under
+ <strong>messagetypes</strong>.</p>
+
+
+</dd></dl>
+<a name=method.resetmessages></a>
+<dl><dt> <strong>resetmessages</strong>(<em>type</em>)</dt><dd>
+Clear the <em>type</em> message and all message types with a lower
+ priority, except permanent messages, such as <strong>state</strong>. This is
+ useful to clear the <strong>busy</strong> message and any outstanding event and
+ help messages.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the MessageBar.
+ self._messagebar = Pmw.MessageBar(parent,
+ entry_width = 40,
+ entry_relief='groove',
+ labelpos = 'w',
+ label_text = 'Status:')
+ self._messagebar.pack(side = 'bottom', fill = 'x',
+ expand = 1, padx = 10, pady = 10)
+
+ # Create and pack the ScrolledListBox to change the MessageBar.
+ self.box = Pmw.ScrolledListBox(parent,
+ listbox_selectmode='single',
+ items=('state', 'help', 'userevent', 'systemevent',
+ 'usererror', 'systemerror', 'busy',),
+ label_text='Message type',
+ labelpos='n',
+ selectioncommand=self.selectionCommand)
+ self.box.pack(fill = 'both', expand = 'yes', padx = 10, pady = 10)
+
+ self._index = 0
+ self._stateCounter = 0
+
+ def selectionCommand(self):
+ sels = self.box.getcurselection()
+ if len(sels) > 0:
+ self._index = self._index + 1
+ messagetype = sels[0]
+ if messagetype == 'state':
+ self._stateCounter = (self._stateCounter + 1) % 3
+ text = stateMessages[self._stateCounter]
+ if text != '':
+ text = text + ' (' + messagetype + ')'
+ self._messagebar.message('state', text)
+ else:
+ text = messages[messagetype]
+ text = text + ' (' + messagetype + ')'
+ self._messagebar.message(messagetype, text)
+ if messagetype == 'busy':
+ Pmw.showbusycursor()
+ self.box.after(2000)
+ Pmw.hidebusycursor()
+ self._messagebar.resetmessages('busy')
+ text = 'All files successfully removed'
+ text = text + ' (userevent)'
+ self._messagebar.message('userevent', text)
+
+
+messages = {
+ 'help': 'Save current file',
+ 'userevent': 'Saving file "foo"',
+ 'busy': 'Busy deleting all files from file system ...',
+ 'systemevent': 'File "foo" saved',
+ 'usererror': 'Invalid file name "foo/bar"',
+ 'systemerror': 'Failed to save file: file system full',
+}
+
+stateMessages = {
+ 0: '',
+ 1: 'Database is down',
+ 2: 'Waiting for reply from database',
+}
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.MessageDialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.MessageDialog</h1>
+
+<center><IMG SRC=MessageDialog.gif ALT="" WIDTH=309 HEIGHT=220></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.MessageDialog() -
+ a dialog displaying a text message and an icon
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="Dialog.html">Pmw.Dialog</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A message dialog is a dialog window which displays a simple
+ message to the user along with one or more buttons to press.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderx></a>
+<dl><dt> <strong>borderx
+</strong></dt><dd>
+Initialisation option. The padding to the left and right of the text message and icon. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.bordery></a>
+<dl><dt> <strong>bordery
+</strong></dt><dd>
+Initialisation option. The padding above and below the text message and icon. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('OK',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.iconmargin></a>
+<dl><dt> <strong>iconmargin
+</strong></dt><dd>
+Initialisation option. The padding between the text message and icon. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.iconpos></a>
+<dl><dt> <strong>iconpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the text message to place the icon.
+ Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.icon></a>
+<dl><dt> <strong>icon
+</strong></dt><dd>
+If the <strong>iconpos</strong> option is not <strong>None</strong>, this component is created
+ to contain the icon label for the dialog. To display a bitmap as
+ an icon, set the <strong>icon_bitmap</strong> component option to any of the
+ forms acceptable to Tk, such as <strong>'warning'</strong> or <strong>'error'</strong>. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.message></a>
+<dl><dt> <strong>message
+</strong></dt><dd>
+The label to contain the text message for the dialog. To set
+ the text, use the <strong>message_text</strong> component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+This megawidget has no methods of its own.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="Dialog.html#methods">Pmw.Dialog</a></strong>.
+<p></p>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ self.parent = parent
+
+ # Create dialog 1.
+ self.dialog1 = Pmw.MessageDialog(parent,
+ title = 'Simple message dialog',
+ defaultbutton = 0,
+ message_text = 'A simple message dialog\nwith no callback.')
+ self.dialog1.iconname('Simple message dialog')
+ self.dialog1.withdraw()
+
+ # Create dialog 2.
+ self.dialog2 = Pmw.MessageDialog(parent,
+ title = 'Bell ringing dialog',
+ message_text = 'This message dialog\nwill ring the bell ' +
+ 'when\nyou click on the buttons.',
+ iconpos = 'w',
+ icon_bitmap = 'error',
+ command = self.execute2,
+ buttons = ('One', 'Two', 'Three', 'Close'))
+ self.dialog2.iconname('Bell ringing dialog')
+ self.dialog2.withdraw()
+
+ # Create dialog 3.
+ self.dialog3 = Pmw.MessageDialog(parent,
+ title = 'Vertical button dialog',
+ message_text = 'This message dialog\nhas the buttons on the\n' +
+ 'right hand side.',
+ buttonboxpos = 'e',
+ iconpos = 'n',
+ icon_bitmap = 'warning',
+ buttons = ('Goodbye', 'Au revoir', 'Sayonara', 'Close'),
+ defaultbutton = 'Close')
+ self.dialog3.iconname('Vertical button dialog')
+ self.dialog3.withdraw()
+
+ # Create some buttons to launch the dialogs.
+ w = Tkinter.Button(parent, text = 'Simple dialog',
+ command = lambda self = self:
+ self.dialog1.activate(geometry = 'first+100+100'))
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Bell ringing dialog',
+ command = self.dialog2.activate)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'Vertical buttons',
+ command = self.dialog3.activate)
+ w.pack(padx = 8, pady = 8)
+
+ w = Tkinter.Button(parent, text = 'On the fly dialog',
+ command = self._createOnTheFly)
+ w.pack(padx = 8, pady = 8)
+
+ def execute2(self, result):
+ print 'You clicked on', result
+ if result is None:
+ self.dialog2.deactivate(result)
+ elif result == 'Close':
+ self.dialog2.deactivate(result)
+ else:
+ for count in range({'One': 1, 'Two': 2, 'Three': 3}[result]):
+ if count != 0:
+ self.dialog2.after(200)
+ self.dialog2.bell()
+
+ def _createOnTheFly(self):
+ dialog = Pmw.MessageDialog(self.parent,
+ title = 'On the fly dialog',
+ defaultbutton = 0,
+ buttons = ('OK', 'Apply', 'Cancel', 'Help'),
+ message_text = 'This dialog was created when you clicked ' +
+ 'on the button.')
+ dialog.iconname('Simple message dialog')
+ result = dialog.activate()
+
+ print 'You selected', result
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.NoteBook reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.NoteBook</h1>
+
+<center><IMG SRC=NoteBook.gif ALT="" WIDTH=400 HEIGHT=219></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.NoteBook() -
+ a set of tabbed pages
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaArchetype.html">Pmw.MegaArchetype</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A notebook contains a set of tabbed pages. At any one time only
+ one of these pages (the <em>selected</em> page) is visible, with the
+ other pages being hidden "beneath" it. Another page in the
+ notebook may be displayed by clicking on the tab attached to the
+ page. The tabs are displayed along the top edge.</p>
+
+<p> Optionally, the notebook may be displayed without tabs. In this
+ case, another selection widget, such as <a href="OptionMenu.html">Pmw.OptionMenu</a>, may be used
+ to select the pages.</p>
+
+<p> This megawidget is derived from <a href="MegaArchetype.html">Pmw.MegaArchetype</a> (not <a href="MegaWidget.html">Pmw.MegaWidget</a>
+ like most other megawidgets), with the hull class being
+ Tkinter.Canvas.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.arrownavigation></a>
+<dl><dt> <strong>arrownavigation
+</strong></dt><dd>
+Initialisation option. If true and a tab button has the keyboard focus, then the Left and
+ Right arrow keys can be used to select the page before or after
+ the tab button with the focus. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderwidth></a>
+<dl><dt> <strong>borderwidth
+</strong></dt><dd>
+Initialisation option. The width of the border drawn around each tab and around the
+ selected page. The default is <strong>2</strong>.</p>
+
+
+</dd></dl>
+<a name=option.createcommand></a>
+<dl><dt> <strong>createcommand
+</strong></dt><dd>
+Specifies a function to call when a page is selected for the first
+ time. The function is called with a single argument, which is the
+ name of the selected page, and is called before the <strong>raisecommand</strong>
+ function. This allows the creation of the page contents to be
+ deferred until the page is first displayed. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.lowercommand></a>
+<dl><dt> <strong>lowercommand
+</strong></dt><dd>
+Specifies a function to call when the selected page is replaced
+ with a new selected page. The function is called with a single
+ argument, which is the name of the previously selected page, and
+ is called before the <strong>createcommand</strong> or <strong>raisecommand</strong> functions. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.pagemargin></a>
+<dl><dt> <strong>pagemargin
+</strong></dt><dd>
+Initialisation option. The margin (in pixels) around the selected page inside the
+ notebook's page border. The default is <strong>4</strong>.</p>
+
+
+</dd></dl>
+<a name=option.raisecommand></a>
+<dl><dt> <strong>raisecommand
+</strong></dt><dd>
+Specifies a function to call when a new page is selected. The
+ function is called with a single argument, which is the name of
+ the selected page. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.tabpos></a>
+<dl><dt> <strong>tabpos
+</strong></dt><dd>
+Initialisation option. Specifies the location of the tabs. If <strong>'n'</strong>, tabs are created
+ for each page and positioned at the top of the notebook. If
+ <strong>None</strong>, no tabs are created, in which case another selection
+ widget can be used to select pages by calling the <code>selectpage()</code>
+ method. The default is <strong>'n'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the megawidget. The contents of the
+ megawidget are created as canvas items and positioned in the
+ hull using the canvas coordinate system. By default, this component is a Tkinter.Canvas.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Dynamic components</h3></dt><dd>
+<p>
+ Page and tab components are created dynamically by the <code>add()</code>
+ and <code>insert()</code> methods. By default, the pages are of type
+ Tkinter.Frame and are created with a component group of <strong>Page</strong>
+ and the tabs are of type Tkinter.Button and are created with a
+ component group of <strong>Tab</strong>.</p>
+<p> </p>
+
+
+
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaArchetype.html#methods">Pmw.MegaArchetype</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Canvas</strong> class
+are forwarded by this megawidget to the
+<strong>hull</strong> component.
+<p></p>
+<a name=method.add></a>
+<dl><dt> <strong>add</strong>(<em>pageName</em>, **<em>kw</em>)</dt><dd>
+Add a page at the end of the notebook. See the <code>insert()</code> method
+ for full details.</p>
+
+
+</dd></dl>
+<a name=method.delete></a>
+<dl><dt> <strong>delete</strong>(*<em>pageNames</em>)</dt><dd>
+Delete the pages given by <em>pageNames</em> from the notebook. Each of
+ the <em>pageNames</em> may have any of the forms accepted by the
+ <code>index()</code> method.</p>
+<p> If the currently selected page is deleted, then the next page, in
+ index order, is selected. If the <strong>end</strong> page is deleted, then the
+ previous page is selected.</p>
+
+
+
+</dd></dl>
+<a name=method.getcurselection></a>
+<dl><dt> <strong>getcurselection</strong>()</dt><dd>
+Return the name of the currently selected page.</p>
+
+
+</dd></dl>
+<a name=method.index></a>
+<dl><dt> <strong>index</strong>(<em>index</em>, <em>forInsert</em> = <strong>0</strong>)</dt><dd>
+Return the numerical index of the page corresponding to <em>index</em>.
+ This may be specified in any of the following forms:</p>
+<dl><dt><em>name</em></dt><dd>Specifies the page labelled <em>name</em>.<p></p>
+
+</dd>
+<dt><em>number</em></dt><dd>Specifies the page numerically, where <strong>0</strong> corresponds to
+ the first page.<p></p>
+
+</dd>
+<dt><strong>Pmw.END</strong></dt><dd>Specifies the last page.<p></p>
+
+</dd>
+<dt><strong>Pmw.SELECT</strong></dt><dd>Specifies the currently selected page.<p></p>
+
+</dd></dl>
+<p> If <em>forInsert</em> is true, <strong>Pmw.END</strong> returns the number of pages
+ rather than the index of the last page.</p>
+
+
+
+</dd></dl>
+<a name=method.insert></a>
+<dl><dt> <strong>insert</strong>(<em>pageName</em>, <em>before</em> = <strong>0</strong>, **<em>kw</em>)</dt><dd>
+Add a page to the notebook as a component named <em>pageName</em>. The
+ page is added just before the page specified by <em>before</em>, which
+ may have any of the forms accepted by the <code>index()</code> method. If
+ <strong>tabpos</strong> is not <strong>None</strong>, also create a tab as a component named
+ <em>pageName</em>-<strong>tab</strong>. Keyword arguments prefixed with <strong>page_</strong> or
+ <strong>tab_</strong> are passed to the respective constructors when creating the
+ page or tab. If the <strong>tab_text</strong> keyword argument is not given, the
+ <strong>text</strong> option of the tab defaults to <em>pageName</em>. If a page is
+ inserted into an empty notebook, the page is selected. To add a
+ page to the end of the notebook, use <code>add()</code>. The method returns
+ the <em>pageName</em> component widget.</p>
+
+
+</dd></dl>
+<a name=method.nextpage></a>
+<dl><dt> <strong>nextpage</strong>(<em>pageIndex</em> = <strong>None</strong>)</dt><dd>
+If <em>pageIndex</em> is <strong>None</strong>, then select the page after the
+ currently selected page. Otherwise select the page after
+ <em>pageIndex</em>, which may have any of the forms accepted by the
+ <code>index()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.page></a>
+<dl><dt> <strong>page</strong>(<em>pageIndex</em>)</dt><dd>
+Return the frame component widget of the page <em>pageIndex</em>, where
+ <em>pageIndex</em> may have any of the forms accepted by the <code>index()</code>
+ method.</p>
+
+
+</dd></dl>
+<a name=method.pagenames></a>
+<dl><dt> <strong>pagenames</strong>()</dt><dd>
+Return a list of the names of the pages, in display order.</p>
+
+
+</dd></dl>
+<a name=method.previouspage></a>
+<dl><dt> <strong>previouspage</strong>(<em>pageIndex</em> = <strong>None</strong>)</dt><dd>
+If <em>pageIndex</em> is <strong>None</strong>, then select the page before the
+ currently selected page. Otherwise select the page before
+ <em>pageIndex</em>, which may have any of the forms accepted by the
+ <code>index()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.recolorborders></a>
+<dl><dt> <strong>recolorborders</strong>()</dt><dd>
+Change the color of the page and tab borders. This method is
+ required because the borders are created as canvas polygons and
+ hence do not respond to normal color changing techniques, such as
+ <code>Pmw.Color.changecolor()</code>.</p>
+
+
+</dd></dl>
+<a name=method.selectpage></a>
+<dl><dt> <strong>selectpage</strong>(<em>page</em>)</dt><dd>
+Select <em>page</em> to be the currently selected page. The page will be
+ raised and the previous selected page will be lowered.</p>
+
+
+</dd></dl>
+<a name=method.setnaturalsize></a>
+<dl><dt> <strong>setnaturalsize</strong>(<em>pageNames</em> = <strong>None</strong>)</dt><dd>
+Set the width and height of the notebook to be the maximum
+ requested width and height of the pages specified by <em>pageNames</em>.
+ If <em>pageNames</em> is <strong>None</strong>, the size of all pages are used to
+ determine the size of the notebook. Otherwise, <em>pageNames</em> must
+ be a list of page names whose sizes are to be used to determine
+ the size of the notebook. This method should be called after all
+ pages and their contents have been created. It calls
+ <code>update_idletasks()</code> so that the width and height of the pages can
+ be determined. This may cause the notebook to flash onto the
+ screen at the default size before resizing to the natural size.</p>
+
+
+</dd></dl>
+<a name=method.tab></a>
+<dl><dt> <strong>tab</strong>(<em>pageIndex</em>)</dt><dd>
+Return the tab component widget of the page <em>pageIndex</em>, where
+ <em>pageIndex</em> may have any of the forms accepted by the <code>index()</code>
+ method. If <strong>tabpos</strong> is <strong>None</strong>, return <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the NoteBook.
+ notebook = Pmw.NoteBook(parent)
+ notebook.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+
+ # Add the "Appearance" page to the notebook.
+ page = notebook.add('Appearance')
+ notebook.tab('Appearance').focus_set()
+
+ # Create the "Toolbar" contents of the page.
+ group = Pmw.Group(page, tag_text = 'Toolbar')
+ group.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+ b1 = Tkinter.Checkbutton(group.interior(), text = 'Show toolbar')
+ b1.grid(row = 0, column = 0)
+ b2 = Tkinter.Checkbutton(group.interior(), text = 'Toolbar tips')
+ b2.grid(row = 0, column = 1)
+
+ # Create the "Startup" contents of the page.
+ group = Pmw.Group(page, tag_text = 'Startup')
+ group.pack(fill = 'both', expand = 1, padx = 10, pady = 10)
+ home = Pmw.EntryField(group.interior(), labelpos = 'w',
+ label_text = 'Home page location:')
+ home.pack(fill = 'x', padx = 20, pady = 10)
+
+ # Add two more empty pages.
+ page = notebook.add('Helpers')
+ page = notebook.add('Images')
+
+ notebook.setnaturalsize()
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 30 October 1999
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.OptionMenu reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.OptionMenu</h1>
+
+<center><IMG SRC=OptionMenu.gif ALT="" WIDTH=298 HEIGHT=170></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.OptionMenu() -
+ single item selection megawidget
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ An option menu consists of a menu button
+ and an associated menu which pops up when the button is pressed.
+ The text displayed in the menu button is updated whenever an item
+ is selected in the menu. The currently selected value can be
+ retrieved from the megawidget.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a menu item is selected or
+ the <code>invoke()</code> method is called. The function is called with the
+ currently selected value as its single argument. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.initialitem></a>
+<dl><dt> <strong>initialitem
+</strong></dt><dd>
+Initialisation option. Specifies the initial selected value. This option is treated in
+ the same way as the <em>index</em> argument of the <code>setitems()</code> method. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.items></a>
+<dl><dt> <strong>items
+</strong></dt><dd>
+Initialisation option. A sequence of strings containing the initial items to be displayed
+ in the <strong>menu</strong> component. The default is <strong>()</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.sticky></a>
+<dl><dt> <strong>sticky
+</strong></dt><dd>
+Initialisation option. The default is <strong>'ew'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.menu></a>
+<dl><dt> <strong>menu
+</strong></dt><dd>
+The popup menu displayed when the <strong>menubutton</strong> is pressed. By default, this component is a Tkinter.Menu.</p>
+
+
+</dd></dl>
+<a name=component.menubutton></a>
+<dl><dt> <strong>menubutton
+</strong></dt><dd>
+The menu button displaying the currently selected value. By default, this component is a Tkinter.Menubutton.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.getcurselection></a>
+<dl><dt> <strong>getcurselection</strong>()</dt><dd>
+Same as <code>getvalue()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.getvalue></a>
+<dl><dt> <strong>getvalue</strong>()</dt><dd>
+Return the currently selected value.</p>
+
+
+</dd></dl>
+<a name=method.index></a>
+<dl><dt> <strong>index</strong>(<em>index</em>)</dt><dd>
+Return the numerical index of the menu item corresponding to
+ <em>index</em>. This may be specified in any of the following forms:</p>
+<dl><dt><em>name</em></dt><dd>Specifies the menu item labelled <em>name</em>.<p></p>
+
+</dd>
+<dt><em>number</em></dt><dd>Specifies the menu item numerically, where <strong>0</strong> corresponds to
+ the first menu item.<p></p>
+
+</dd>
+<dt><strong>Pmw.END</strong></dt><dd>Specifies the last menu item.<p></p>
+
+</dd>
+<dt><strong>Pmw.SELECT</strong></dt><dd>Specifies the currently selected menu item.<p></p>
+
+</dd></dl>
+
+
+</dd></dl>
+<a name=method.invoke></a>
+<dl><dt> <strong>invoke</strong>(<em>index</em> = <strong>Pmw.SELECT</strong>)</dt><dd>
+Calling this method is the same as selecting the menu item
+ specified by <em>index</em>: the text displayed by the
+ <strong>menubutton</strong> component is updated and the function specified by
+ the <strong>command</strong> option is called. <em>index</em> may have any of the
+ forms accepted by the <code>index()</code> method. The value returned by
+ <strong>command</strong> is returned.</p>
+
+
+</dd></dl>
+<a name=method.setitems></a>
+<dl><dt> <strong>setitems</strong>(<em>items</em>, <em>index</em> = <strong>None</strong>)</dt><dd>
+Replace all the items in the <strong>menu</strong> component with those specified
+ by <em>items</em>, which must be a sequence of strings.</p>
+<p> If <em>index</em> is not <strong>None</strong>, set the selected value to <em>index</em>, which
+ may have any of the forms accepted by the <code>index()</code> method.</p>
+
+<p> If <em>index</em> is <strong>None</strong> and the <strong>textvariable</strong> option of the
+ <strong>menubutton</strong> component is the empty string, then if
+ the previous selected value is one of the <em>items</em>, then do not
+ change the selection. If the previous selected value is no longer
+ in <em>items</em>, then set the selected value to the first value in
+ <em>items</em>. If <em>items</em> is empty, set the selected value to the empty
+ string.</p>
+
+<p> If <em>index</em> is <strong>None</strong> and the <strong>textvariable</strong> option of the
+ <strong>menubutton</strong> component is not the empty string, then do not set
+ the selected value. This assumes that the variable is already (or
+ will be) set to the desired value.</p>
+
+
+
+</dd></dl>
+<a name=method.setvalue></a>
+<dl><dt> <strong>setvalue</strong>(<em>text</em>)</dt><dd>
+Set the text displayed by the <strong>menubutton</strong> component to <em>text</em>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the OptionMenu megawidgets.
+ # The first one has a textvariable.
+ self.var = Tkinter.StringVar()
+ self.var.set('steamed')
+ self.method_menu = Pmw.OptionMenu(parent,
+ labelpos = 'w',
+ label_text = 'Choose method:',
+ menubutton_textvariable = self.var,
+ items = ['baked', 'steamed', 'stir fried', 'boiled', 'raw'],
+ menubutton_width = 10,
+ )
+ self.method_menu.pack(anchor = 'w', padx = 10, pady = 10)
+
+ self.vege_menu = Pmw.OptionMenu (parent,
+ labelpos = 'w',
+ label_text = 'Choose vegetable:',
+ items = ('broccoli', 'peas', 'carrots', 'pumpkin'),
+ menubutton_width = 10,
+ command = self._printOrder,
+ )
+ self.vege_menu.pack(anchor = 'w', padx = 10, pady = 10)
+
+ self.direction_menu = Pmw.OptionMenu (parent,
+ labelpos = 'w',
+ label_text = 'Menu direction:',
+ items = ('flush', 'above', 'below', 'left', 'right'),
+ menubutton_width = 10,
+ command = self._changeDirection,
+ )
+ self.direction_menu.pack(anchor = 'w', padx = 10, pady = 10)
+
+ menus = (self.method_menu, self.vege_menu, self.direction_menu)
+ Pmw.alignlabels(menus)
+
+ def _printOrder(self, vege):
+ # Can use 'self.var.get()' instead of 'getcurselection()'.
+ print 'You have chosen %s %s.' % \
+ (self.method_menu.getcurselection(), vege)
+
+ def _changeDirection(self, direction):
+ for menu in (self.method_menu, self.vege_menu, self.direction_menu):
+ menu.configure(menubutton_direction = direction)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 23 October 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.PanedWidget reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.PanedWidget</h1>
+
+<center><IMG SRC=PanedWidget.gif ALT="" WIDTH=400 HEIGHT=128></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.PanedWidget() -
+ frame subdivided into several resizable panes
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A paned widget is a container megawidget which manages a number of
+ resizable frames, known as panes. Each pane may act as the container for
+ other widgets. The user may interactively resize the panes by
+ dragging a small rectangle (the handle) or the line between the
+ panes (the separator). The panes may be arranged horizontally or
+ vertically. Each pane may have maximum and minimum limits of its
+ size.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to be called whenever the size of any of the
+ panes changes. The function is called with a single argument,
+ being a list of the sizes of the panes, in order. For <strong>vertical</strong>
+ orientation, the size is the height of the panes. For
+ <strong>horizontal</strong> orientation, the size is the width of the panes. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.handlesize></a>
+<dl><dt> <strong>handlesize
+</strong></dt><dd>
+Initialisation option. Specifies the size in pixels of the square handle which appears on
+ the lines separating the panes. The default is <strong>8</strong>.</p>
+
+
+</dd></dl>
+<a name=option.orient></a>
+<dl><dt> <strong>orient
+</strong></dt><dd>
+Initialisation option. Specifies the orientation of the paned widget. This may be
+ <strong>'horizontal'</strong> or <strong>'vertical'</strong>. If <strong>'vertical'</strong>, the panes are
+ stacked above and below each other, otherwise the panes are laid
+ out side by side. The default is <strong>'vertical'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorrelief></a>
+<dl><dt> <strong>separatorrelief
+</strong></dt><dd>
+Initialisation option. Specifies the relief of the lines separating the panes. The default is <strong>'sunken'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorthickness></a>
+<dl><dt> <strong>separatorthickness
+</strong></dt><dd>
+Initialisation option. Specifies the thickness of the lines separating the panes. The default is <strong>2</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Pane options</h3></dt><dd>
+<p>
+ Each pane has the following options. These may be set when
+ creating or configuring a pane. The value of each option may
+ be an integer, which specifies a pane size in pixels, or a
+ real number between 0.0 and 1.0, which specifies a pane size
+ proportional to the size of the entire paned widget.</p>
+
+<dl><dt><strong>size</strong></dt><dd>Specifies the initial size of the pane. The default is <strong>0</strong>.<p></p>
+
+</dd>
+<dt><strong>min</strong></dt><dd>Specifies the minimum size of the pane. The default is <strong>0</strong>.<p></p>
+
+</dd>
+<dt><strong>max</strong></dt><dd>Specifies the maximum size of the pane. The default is a
+ very large number.<p></p>
+<p> </p>
+
+
+</dd></dl>
+
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Dynamic components</h3></dt><dd>
+<p>
+ Frame, separator and handle components are created dynamically
+ by the <code>add()</code> and <code>insert()</code> methods. The components are of type
+ Tkinter.Frame and are created with component groups of
+ <strong>Frame</strong>, <strong>Separator</strong> and <strong>Handle</strong> respectively.</p>
+<p> </p>
+
+
+
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.add></a>
+<dl><dt> <strong>add</strong>(<em>name</em>, **<em>kw</em>)</dt><dd>
+Add a pane to the end of the paned widget as a component named
+ <em>name</em>. This is equivalent to calling <code>insert()</code> with <em>before</em>
+ set to the current number of panes. The method returns the <em>name</em>
+ component widget.</p>
+
+
+</dd></dl>
+<a name=method.configurepane></a>
+<dl><dt> <strong>configurepane</strong>(<em>name</em>, **<em>kw</em>)</dt><dd>
+Configure the pane specified by <em>name</em>, where <em>name</em> is either an
+ integer, specifying the index of the pane, or a string, specifying
+ the name of the pane. The keyword arguments specify the new
+ values for the options for the pane. These options are described
+ in the <strong>Pane options</strong> section.</p>
+
+
+</dd></dl>
+<a name=method.delete></a>
+<dl><dt> <strong>delete</strong>(<em>name</em>)</dt><dd>
+Delete the pane specified by <em>name</em>, where <em>name</em> is either an
+ integer, specifying the index of the pane, or a string, specifying
+ the name of the pane.</p>
+<p> If the pane deleted was not the only pane in the paned widget,
+ also delete the separator and handle components named
+ <strong>separator</strong>-<em>n</em> and <strong>handle</strong>-<em>n</em>, where <em>n</em> is the number of
+ panes remaining.</p>
+
+
+
+</dd></dl>
+<a name=method.insert></a>
+<dl><dt> <strong>insert</strong>(<em>name</em>, <em>before</em> = <strong>0</strong>, **<em>kw</em>)</dt><dd>
+Add a pane to the paned widget as a component named <em>name</em>. The
+ pane is added just before the pane specified by <em>before</em>, where
+ <em>before</em> may be either an integer, specifying the index of the
+ pane, or a string, specifying the name of the pane. The keyword
+ arguments specify the initial values for the options for the new
+ pane. These options are described in the <strong>Pane options</strong> section.
+ To add a pane to the end of the paned widget, use <code>add()</code>.</p>
+<p> The new pane is created as a Tkinter.Frame component named <em>name</em>.
+ If this is not the only pane, a separator and handle are also
+ created as components named <strong>separator</strong>-<em>n</em> and <strong>handle</strong>-<em>n</em>,
+ where <em>n</em> is one less than the number of panes. The method
+ returns the <em>name</em> component widget.</p>
+
+
+
+</dd></dl>
+<a name=method.move></a>
+<dl><dt> <strong>move</strong>(<em>name</em>, <em>newPos</em>, <em>newPosOffset</em> = <strong>0</strong>)</dt><dd>
+Move the pane specified by <em>name</em> to the new position specified by
+ <em>newPos</em>. The first two arguments may be either an integer,
+ specifying the index of the pane, or a string, specifying the name
+ of the pane. If <em>newPosOffset</em> is specified, it is added to the
+ <em>newPos</em> index. For example, to move a horizontal pane one pane
+ to the left, specify the name or index of the pane for both <em>name</em>
+ and <em>newPos</em> and specify <strong>-1</strong> for <em>newPosOffset</em>.</p>
+
+
+</dd></dl>
+<a name=method.pane></a>
+<dl><dt> <strong>pane</strong>(<em>name</em>)</dt><dd>
+Return the Tkinter.Frame pane widget for the pane specified by
+ <em>name</em>, where <em>name</em> is either an integer, specifying the index of
+ the pane, or a string, specifying the name of the pane.</p>
+
+
+</dd></dl>
+<a name=method.panes></a>
+<dl><dt> <strong>panes</strong>()</dt><dd>
+Return a list of the names of the panes, in display order.</p>
+
+
+</dd></dl>
+<a name=method.setnaturalsize></a>
+<dl><dt> <strong>setnaturalsize</strong>()</dt><dd>
+If oriented horizontally, set the width of the paned widget to the
+ sum of the requested widths of all panes and set the height to the
+ maximum requested height of all panes.</p>
+<p> If oriented vertically, set the height of the paned widget to the
+ sum of the requested heights of all panes and set the width to the
+ maximum requested width of all panes.</p>
+
+
+
+</dd></dl>
+<a name=method.updatelayout></a>
+<dl><dt> <strong>updatelayout</strong>()</dt><dd>
+Recalculate size and position of panes. This method must be
+ called after adding or deleting one or more panes. However it
+ does not need to be called when panes are first added to a newly
+ created paned widget, before it has been displayed.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+
+ # Create a main PanedWidget with a few panes.
+ self.pw = Pmw.PanedWidget(parent,
+ orient='vertical',
+ hull_borderwidth = 1,
+ hull_relief = 'sunken',
+ hull_width=300,
+ hull_height=400)
+ for self.numPanes in range(4):
+ if self.numPanes == 1:
+ name = 'Fixed size'
+ pane = self.pw.add(name, min = .1, max = .1)
+ else:
+ name = 'Pane ' + str(self.numPanes)
+ pane = self.pw.add(name, min = .1, size = .25)
+ label = Tkinter.Label(pane, text = name)
+ label.pack(side = 'left', expand = 1)
+ button = Tkinter.Button(pane, text = 'Delete',
+ command = lambda s=self, n=name: s.deletePane(n))
+ button.pack(side = 'left', expand = 1)
+ # TODO: add buttons to invoke self.moveOneUp and self.moveOneUp.
+
+ self.pw.pack(expand = 1, fill='both')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(fill = 'x')
+ buttonBox.add('Add pane', command = self.addPane)
+ buttonBox.add('Move pane', command = self.move)
+ self.moveSrc = 0
+ self.moveNewPos = 1
+ self.moveBack = 0
+
+ def move(self):
+ numPanes = len(self.pw.panes())
+ if numPanes == 0:
+ print 'No panes to move!'
+ return
+
+ if self.moveSrc >= numPanes:
+ self.moveSrc = numPanes - 1
+ if self.moveNewPos >= numPanes:
+ self.moveNewPos = numPanes - 1
+ print 'Moving pane', self.moveSrc, 'to new position', self.moveNewPos
+ self.pw.move(self.moveSrc, self.moveNewPos)
+
+ self.moveSrc, self.moveNewPos = self.moveNewPos, self.moveSrc
+ if self.moveBack:
+ if self.moveNewPos == numPanes - 1:
+ self.moveNewPos = 0
+ if self.moveSrc == numPanes - 1:
+ self.moveSrc = 0
+ else:
+ self.moveSrc = self.moveSrc + 1
+ else:
+ self.moveNewPos = self.moveNewPos + 1
+ self.moveBack = not self.moveBack
+
+ def addPane(self):
+ self.numPanes = self.numPanes + 1
+ name = 'Pane ' + str(self.numPanes)
+ print 'Adding', name
+ pane = self.pw.add(name, min = .1, size = .25)
+ label = Tkinter.Label(pane, text = name)
+ label.pack(side = 'left', expand = 1)
+ button = Tkinter.Button(pane, text = 'Delete',
+ command = lambda s=self, n=name: s.deletePane(n))
+ button.pack(side = 'left', expand = 1)
+ self.pw.updatelayout()
+
+ def deletePane(self, name):
+ print 'Deleting', name
+ self.pw.delete(name)
+ self.pw.updatelayout()
+
+ def moveOneUp(self, name):
+ self.pw.move(name, name, -1)
+
+ def moveOneDown(self, name):
+ self.pw.move(name, name, 1)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 14 April 2001
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw functions reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw functions</h1>
+
+<dl>
+<dt> <strong>Pmw.aboutcontact</strong>(<em>value</em>)</dt><dd>
+<p>
+ The value passed to this function is used to construct the text
+ displayed by <a href="AboutDialog.html">Pmw.AboutDialog</a> megawidgets created subsequently.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.aboutcopyright</strong>(<em>value</em>)</dt><dd>
+<p>
+ The value passed to this function is used to construct the text
+ displayed by <a href="AboutDialog.html">Pmw.AboutDialog</a> megawidgets created subsequently.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.aboutversion</strong>(<em>value</em>)</dt><dd>
+<p>
+ The value passed to this function is used to construct the text
+ displayed by <a href="AboutDialog.html">Pmw.AboutDialog</a> megawidgets created subsequently.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.aligngrouptags</strong>(<em>groups</em>)</dt><dd>
+<p>
+ This function takes a sequence of <a href="Group.html">Pmw.Group</a>s and adjusts the
+ vertical position of the tags in each group so that they all have
+ the height of the tallest tag. This can be used when groups are
+ positioned side-by-side but the natural height of the tags are
+ different because, for example, different fonts with different
+ sizes are used.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.alignlabels</strong>(<em>widgets</em>, <em>sticky</em> = <strong>None</strong>)</dt><dd>
+<p>
+ Adjust the size of the labels of all the <em>widgets</em> to be equal, so
+ that the body of each widget lines up vertically. This assumes
+ that each widget is a megawidget with a <strong>label</strong> component in
+ column 0 (ie, the <strong>labelpos</strong> option was set to <strong>'w'</strong>, <strong>'wn'</strong> or
+ <strong>'ws'</strong>). If <em>sticky</em> is set to a combination of <strong>'n'</strong>, <strong>'s'</strong>,
+ <strong>'e'</strong> and <strong>'w'</strong>, the label will be positioned within its cell
+ accordingly. For example to make labels right justified, set
+ <em>sticky</em> to <strong>'e'</strong>, <strong>'ne'</strong> or <strong>'se'</strong>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.alphabeticvalidator</strong>(<em>text</em>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>alphabetic</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.alphanumericvalidator</strong>(<em>text</em>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>alphanumeric</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.busycallback</strong>(<em>command</em>, <em>updateFunction</em> = <strong>None</strong>)</dt><dd>
+<p>
+ Create a wrapper function which displays a busy cursor while
+ executing <em>command</em> and return the wrapper. When the wrapper
+ function is called, it first calls <code>Pmw.showbusycursor()</code>, then
+ the <em>command</em> (passing any arguments to it), then <code>Pmw.hidebusycursor()</code>.
+ The return value of <em>command</em> is returned from the wrapper.</p>
+
+<p> If <em>updateFunction</em> is specified, it is called just before the
+ call to <code>Pmw.hidebusycursor()</code>. This is intended to be the
+ Tkinter <code>update()</code> method, in which case it will clear any events
+ that may have occurred while <em>command</em> was executing. An example
+ of this usage is in the <code>ShowBusy</code> demonstration: run the
+ demonstration, click on the entry widget then click on the button
+ and type some characters while the busy cursor is displayed. No
+ characters should appear in the entry widget.</p>
+
+<p> Note that the Tkinter <code>update()</code> method should only be called when
+ it is known that it can be safely called. One case where a
+ problem has been found is when a filehandler has been created (on
+ a non-blocking Oracle database connection), but the filehandler
+ does not read from the connection. The connection is read (by a
+ call to the Oracle fetch function <em>ofen</em>) in a loop which also
+ contains a call to <code>_tkinter.dooneevent()</code>. If <code>update()</code> is
+ called from <code>dooneevent()</code> and there is data to be read on the
+ connection, then the filehandler will be called continuously, thus
+ hanging the application.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.clearbusycursor</strong>()</dt><dd>
+<p>
+ Unconditionally remove the event block and busy cursor from all
+ windows. This undoes all outstanding calls to
+ <code>Pmw.showbusycursor()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.datestringtojdn</strong>(<em>text</em>, <em>format</em> = <strong>'ymd'</strong>, <em>separator</em> = <strong>'/'</strong>)</dt><dd>
+<p>
+ Return the Julian Day Number corresponding to the date in <em>text</em>.
+ A Julian Day Number is defined as the number of days since 1 Jan 4713
+ BC. The date must be specified as three integers separated by the
+ <em>separator</em> character. The integers must be in the order specified by
+ <em>format</em>, which must be a combination of <strong>'d'</strong>, <strong>'m'</strong> and <strong>'y'</strong> in
+ any order. These give the order of the day, month and year
+ fields. Examples of valid input are:</p>
+<dl><dd><pre> 'dmy': 31/01/99 31/1/1999 31/1/99
+ 'mdy': 01/31/99 1/31/1999 1/31/99
+ 'ymd': 99/01/31 1999/1/31 99/1/31</pre></dd></dl>
+
+
+<p> If the application's
+ <em>pivot</em> year (default 50) is not <strong>None</strong> and the year specified
+ in <em>text</em> has only one or two digits, then the year is
+ converted to a four digit year. If it is less than or equal to
+ the pivot year, then it is incremented by the application's
+ <em>century</em> value (default 2000). If it is more than the pivot year
+ then it is incremented by the <em>century</em> value less 100.</p>
+
+<p> The function <code>Pmw.setyearpivot()</code> can be used to change the
+ default values for the application's
+ <em>pivot</em> and <em>century</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.datevalidator</strong>(<em>text</em>, <em>format</em> = <strong>'ymd'</strong>, <em>separator</em> = <strong>'/'</strong>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>date</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.displayerror</strong>(<em>text</em>)</dt><dd>
+<p>
+ This is a general purpose method for displaying background errors
+ to the user. The errors would normally be programming errors and
+ may be caused by errors in Tk callbacks or functions called by other
+ asynchronous events.</p>
+
+<p> If the global error report file (set by calling
+ <code>Pmw.reporterrorstofile()</code>) is <strong>None</strong>, the error message `text` is
+ written to standard error and also shown in a text window. If
+ <code>displayerror</code> is called while previous error messages are being
+ displayed, the window is raised and the new error is queued. The
+ queued errors may be viewed by the user or ignored by dismissing
+ the window.</p>
+
+<p> If the global error report file is not <strong>None</strong>, `text` is written
+ to the file. <em>file</em> may be any object with a <code>write()</code> method,
+ such as <code>sys.stderr</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.drawarrow</strong>(<em>canvas</em>, <em>color</em>, <em>direction</em>, <em>tag</em>, <em>baseOffset</em> = <strong>0.25</strong>, <em>edgeOffset</em> = <strong>0.15</strong>)</dt><dd>
+<p>
+ Draw a triangle in the Tkinter.Canvas <em>canvas</em> in the given
+ <em>color</em>. The value of <em>direction</em> may be <strong>'up'</strong>, <strong>'down'</strong>,
+ <strong>'left'</strong> or <strong>'right'</strong> and specifies which direction the arrow
+ should point. The values of <em>baseOffset</em> and <em>edgeOffset</em> specify
+ how far from the edges of the canvas the points of the triangles
+ are as a fraction of the size of the canvas.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.forwardmethods</strong>(<em>fromClass</em>, <em>toClass</em>, <em>toPart</em>, <em>exclude</em> = <strong>()</strong>)</dt><dd>
+<p>
+ Forward methods from one class to another.</p>
+
+<p> This function adds methods to the class <em>fromClass</em>. The names of
+ the methods added are the names of the methods of the class
+ <em>toClass</em> (and its base classes) except those which are already
+ defined by <em>fromClass</em> or are found in the <em>exclude</em> list.
+ Special methods with one or more leading or trailing underscores
+ are also excluded.</p>
+
+<p> When one of the added methods is called, the method of the same
+ name is called on an instance defined by <em>toPart</em> and the return
+ value passed back. If <em>toPart</em> is a string, then it specifies the
+ name of an attribute (<em>not</em> a component) of the <em>fromClass</em>
+ object. The class of this attribute should be <em>toClass</em>. If
+ <em>toPart</em> is not a string, it must be a function taking a
+ <em>fromClass</em> object and returning a <em>toClass</em> object.</p>
+
+<p> This function must be called outside of and after the definition
+ of <em>fromClass</em>.</p>
+
+<p> For example:</p>
+
+<dl><dd><pre>class MyClass:
+ def __init__(self):
+ ...
+ self.__target = TargetClass()
+ ...
+
+ def foo(self):
+ pass
+
+ def findtarget(self):
+ return self.__target
+
+Pmw.forwardmethods(MyClass, TargetClass, '__target',
+ ['dangerous1', 'dangerous2'])
+
+# ...or...
+
+Pmw.forwardmethods(MyClass, TargetClass,
+ MyClass.findtarget, ['dangerous1', 'dangerous2'])</pre></dd></dl>
+
+<p> In both cases, all <code>TargetClass</code> methods will be forwarded from
+ <code>MyClass</code> except for <code>dangerous1</code>, <code>dangerous2</code>, special methods like
+ <code>__str__</code>, and pre-existing methods like <code>foo</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.grabstacktopwindow</strong>()</dt><dd>
+<p>
+ Return the window at the top of the grab stack (the window
+ currently with the grab) or <strong>None</strong> if the grab stack is empty (no
+ window has the grab). See also <code>pushgrab()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.hexadecimalvalidator</strong>(<em>text</em>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>hexadecimal</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.hidebusycursor</strong>(<em>forceFocusRestore</em> = <strong>0</strong>)</dt><dd>
+<p>
+ Undo one call to <code>Pmw.showbusycursor()</code>. If there are no
+ outstanding calls to <code>Pmw.showbusycursor()</code>, remove the event
+ block and busy cursor.</p>
+
+<p> If the focus window has not been changed since the corresponding
+ call to <code>Pmw.showbusycursor()</code>, or if <em>forceFocusRestore</em> is true,
+ then the focus is restored to that saved by <code>Pmw.showbusycursor()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.initialise</strong>(<em>root</em> = <strong>None</strong>, <em>size</em> = <strong>None</strong>, <em>fontScheme</em> = <strong>None</strong>, <em>useTkOptionDb</em> = <strong>0</strong>, <em>noBltBusy</em> = <strong>0</strong>, <em>disableKeyboardWhileBusy</em> = <strong>None</strong>)</dt><dd>
+<p>
+ Initialise Pmw. This performs several functions:</p>
+<ul><li><p>Set up a trap in the Tkinter Toplevel constructor so that a
+ list of Toplevels can be maintained. A list of all Toplevel
+ windows needs to be kept so that <code>Pmw.showbusycursor()</code> can
+ create busy cursors for them.</p>
+
+</li>
+<li><p>Set up a trap in the Tkinter Toplevel and Frame destructors
+ so that Pmw is notified when these widgets are destroyed.
+ This allows Pmw to destroy megawidgets when their hull
+ widget is destroyed and to prune the list of Toplevels.</p>
+
+</li>
+<li><p>Modify Tkinter's CallWrapper class to improve the display of
+ errors which occur in callbacks. If an error occurs, the
+ new CallWrapper class calls <code>Pmw.clearbusycursor()</code> to
+ remove any outstanding busy cursors and calls
+ <code>Pmw.displayerror()</code> to display the error.</p>
+
+</li>
+<li><p>Using the window given by <em>root</em>, set the <strong>WM_DELETE_WINDOW</strong>
+ root window protocol to destroy the root window. This means
+ that the root window is destroyed if the window manager
+ deletes it. This is only done if the protocol has not been
+ set before the call to <code>Pmw.initialise()</code>. This protocol is
+ required if there is a modal dialog displayed and the window
+ manager deletes the root window. Otherwise the application
+ will not exit, even though there are no windows.</p>
+
+</li>
+<li><p>Set the base font size for the application to <em>size</em>. This
+ is used by <code>Pmw.logicalfont()</code> as the default point size for
+ fonts. If this is not given, the default is <strong>14</strong>, except
+ under NT where it is <strong>16</strong>. These are reasonable default
+ sizes for most screens, but for unusually high or low screen
+ resolutions, an appropriate size should be supplied. Note
+ that Tk's definition of <em>point size</em>, is somewhat
+ idiosyncratic.</p>
+
+</li>
+<li><p>Set the Tk option database for <em>root</em> according to
+ <em>fontScheme</em>. This changes the default fonts set by Tk.
+ <em>fontScheme</em> may be one of</p>
+<dl><dt><strong>None</strong> </dt><dd>Do not change the Tk defaults.<p></p>
+
+</dd>
+<dt><strong>'pmw1'</strong> </dt><dd>If running under posix (Unix), set the default font to
+ be Helvetica with bold italic menus, italic scales and
+ a special balloon font 6 points smaller than the base
+ font size and with the <strong>'pixel'</strong> field set to <strong>'12'</strong>.
+ For other operating systems (such as NT or Macintosh),
+ simply set the default font to be Helvetica. All
+ fonts are as returned by calls to <code>Pmw.logicalfont()</code>.<p></p>
+
+</dd>
+<dt><strong>'pmw2'</strong> </dt><dd>This is the same as <strong>'pmw1'</strong> except that under posix
+ the balloon font is 2 points smaller than the base
+ font size and the <strong>'pixel'</strong> field is not set.<p></p>
+
+</dd>
+<dt><strong>'default'</strong> </dt><dd>This sets the default fonts using the Tk font naming
+ convention, rather than that returned by
+ <code>Pmw.logicalfont()</code>. The default font is bold
+ Helvetica. The font for entry widgets is Helvetica.
+ The font for text widgets is Courier The size of all
+ fonts is the application base font size as described
+ above.<p></p>
+
+</dd></dl>
+
+</li>
+<li><p>If <em>root</em> is <strong>None</strong>, use the Tkinter default root window as the
+ root, if it has been created, or create a new Tk root window.
+ The <code>initialise()</code> method returns this <em>root</em>.</p>
+
+</li>
+<li><p>If <em>useTkOptionDb</em> is true, then, when a megawidget is
+ created, the Tk option database will be queried to get the
+ initial values of the options which have not been set in
+ the call to the constructor. The resource name used in the
+ query is the same as the option name and the resource class
+ is the option name with the first letter capitalised. If
+ <em>useTkOptionDb</em> is false, then options for newly created
+ megawidgets will be initialised to default values.</p>
+
+</li>
+<li><p>If <em>noBltBusy</em> is true, then <code>Pmw.showbusycursor()</code> will not
+ display a busy cursor, even if the BLT busy command is
+ present.</p>
+
+</li>
+<li><p>If <em>disableKeyboardWhileBusy</em> is false, then do not disable
+ keyboard input while displaying the busy cursor. Normally,
+ Pmw ignores keyboard input while displaying the busy cursor
+ by setting the focus for each toplevel window to the Blt
+ busy window. However, under NT, this may cause the toplevel
+ windows to be raised. If this is not acceptable, programs
+ running on NT can request show/hidebusycursor to not ignore
+ keyboard input by setting <em>disableKeyboardWhileBusy</em> to true
+ in <code>Pmw.initialise()</code>.</p>
+
+</li></ul>
+
+<p> It is not absolutely necessary to call this function to be able to use
+ Pmw. However, some functionality will be lost. Most importantly,
+ Pmw megawidgets will not be notified when their hull widget is
+ destroyed. This may prevent the megawidget from cleaning up
+ timers which will try to access the widget, hence causing a
+ background error to occur.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.installedversions</strong>(<em>alpha</em> = <strong>0</strong>)</dt><dd>
+<p>
+ If <em>alpha</em> is false, return the list of base versions of Pmw
+ that are currently installed and available for use. If <em>alpha</em> is
+ true, return the list of alpha versions.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.integervalidator</strong>(<em>text</em>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>integer</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.jdntoymd</strong>(<em>jdn</em>, <em>julian</em> = <strong>-1</strong>, <em>papal</em> = <strong>1</strong>)</dt><dd>
+<p>
+ Return the year, month and day of the Julian Day Number <em>jdn</em>. If
+ <em>julian</em> is <strong>1</strong>, then the date returned will be in the Julian
+ calendar. If <em>julian</em> is <strong>0</strong>, then the date returned will be in
+ the modern calendar. If <em>julian</em> is <strong>-1</strong>, then which calendar to
+ use will be automatically determined by the value of <em>jdn</em> and
+ <em>papal</em>. If <em>papal</em> is true, then the date set by Pope Gregory
+ XIII's decree (4 October 1582) will be used as the last day to use
+ the Julian calendar. If <em>papal</em> is false, then the last day to
+ use the Julian calendar will be according to British-American
+ usage (2 September 1752).</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.logicalfont</strong>(<em>name</em> = <strong>'Helvetica'</strong>, <em>sizeIncr</em> = <strong>0</strong>, **<em>kw</em>)</dt><dd>
+<p>
+ Return the full name of a Tk font, being a hyphen-separated list
+ of font properties. The <em>logical</em> name of the font is given by
+ <em>name</em> and may be one of <strong>'Helvetica'</strong>, <strong>'Times'</strong>, <strong>'Fixed'</strong>,
+ <strong>'Courier'</strong> or <strong>'Typewriter'</strong>. Pmw uses this name to define the
+ default values of many of the font properties. The size of the
+ font is the base font size for the application specified in the
+ call to <code>Pmw.initialise()</code> increased or decreased by the value of
+ <em>sizeIncr</em>. The other properties of the font may be specified by
+ other named arguments. These may be <strong>'registry'</strong>, <strong>'foundry'</strong>,
+ <strong>'family'</strong>, <strong>'weight'</strong>, <strong>'slant'</strong>, <strong>'width'</strong>, <strong>'style'</strong>,
+ <strong>'pixel'</strong>, <strong>'size'</strong>, <strong>'xres'</strong>, <strong>'yres'</strong>, <strong>'spacing'</strong>,
+ <strong>'avgwidth'</strong>, <strong>'charset'</strong> and <strong>'encoding'</strong>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.logicalfontnames</strong>()</dt><dd>
+<p>
+ Return the list of known logical font names that can be given
+ to <code>Pmw.logicalfont()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.numericvalidator</strong>(<em>text</em>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>numeric</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.popgrab</strong>(<em>window</em>)</dt><dd>
+<p>
+ Remove <em>window</em> from the grab stack. If there are not more
+ windows in the grab stack, release the grab. Otherwise set the
+ grab and the focus to the next window in the grab stack. See also
+ <code>pushgrab()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.pushgrab</strong>(<em>grabWindow</em>, <em>globalMode</em>, <em>deactivateFunction</em>)</dt><dd>
+<p>
+ The grab functions (<code>pushgrab()</code>, <code>popgrab()</code>, <code>releasegrabs()</code>
+ and <code>grabstacktopwindow()</code>) are an interface to the Tk <strong>grab</strong>
+ command which implements simple pointer and keyboard grabs. When
+ a grab is set for a particular window, Tk restricts all pointer
+ events to the grab window and its descendants in Tk's window
+ hierarchy. The functions are used by the <code>activate()</code> and
+ <code>deactivate()</code> methods to implement modal dialogs.</p>
+
+<p> Pmw maintains a stack of grabbed windows, where the window on the
+ top of the stack is the window currently with the grab. The grab
+ stack allows nested modal dialogs, where one modal dialog can be
+ activated while another modal dialog is activated. When the
+ second dialog is deactivated, the first dialog becomes active
+ again.</p>
+
+<p> Use <code>pushgrab()</code> to add <em>grabWindow</em> to the grab stack. This
+ releases the grab by the window currently on top of the stack (if
+ there is one) and gives the grab and focus to the <em>grabWindow</em>.
+ If <em>globalMode</em> is true, perform a global grab, otherwise perform
+ a local grab. The value of <em>deactivateFunction</em> specifies a
+ function to call (usually grabWindow.deactivate) if popgrab() is
+ called (usually from a deactivate() method) on a window which is
+ not at the top of the stack (that is, does not have the grab or
+ focus). For example, if a modal dialog is deleted by the window
+ manager or deactivated by a timer. In this case, all dialogs
+ above and including this one are deactivated, starting at the top
+ of the stack.</p>
+
+<p> For more information, see the Tk grab manual page.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.realvalidator</strong>(<em>text</em>, <em>separator</em> = <strong>'.'</strong>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>real</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.releasegrabs</strong>()</dt><dd>
+<p>
+ Release grab and clear the grab stack. This should normally not
+ be used, use <code>popgrab()</code> instead. See also <code>pushgrab()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.reporterrorstofile</strong>(<em>file</em> = <strong>None</strong>)</dt><dd>
+<p>
+ Sets the global error report file, which is initially <strong>None</strong>. See
+ <code>Pmw.displayerror()</code></p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.setalphaversions</strong>(*<em>alpha_versions</em>)</dt><dd>
+<p>
+ Set the list of alpha versions of Pmw to use for this session to
+ the arguments. When searching for Pmw classes and functions,
+ these alpha versions will be searched, in the order given, before
+ the base version. This must be called before any other Pmw class
+ or function, except functions setting or querying versions.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.setbusycursorattributes</strong>(<em>window</em>, **<em>kw</em>)</dt><dd>
+<p>
+ Use the keyword arguments to set attributes controlling the effect
+ on <em>window</em> (which must be a <strong>Tkinter.Toplevel</strong>) of future calls
+ to <code>Pmw.showbusycursor()</code>. The attributes are:</p>
+
+<dl><dt><strong>exclude</strong></dt><dd>a boolean value which specifies whether the window
+ will be affected by calls to <code>Pmw.showbusycursor()</code>. If a window
+ is excluded, then the cursor will not be changed to a busy cursor
+ and events will still be delivered to the window. By default,
+ windows are affected by calls to <code>Pmw.showbusycursor()</code>.<p></p>
+
+</dd>
+<dt><strong>cursorName</strong></dt><dd>the name of the cursor to use when displaying the
+ busy cursor. If <strong>None</strong>, then the default cursor is used.<p></p>
+
+</dd></dl>
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.setgeometryanddeiconify</strong>(<em>window</em>, <em>geom</em>)</dt><dd>
+<p>
+ Deiconify and raise the toplevel <em>window</em> and set its position and
+ size according to <em>geom</em>. This overcomes some problems with the
+ window flashing under X and correctly positions the window under
+ NT (caused by Tk bugs).</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.setversion</strong>(<em>version</em>)</dt><dd>
+<p>
+ Set the version of Pmw to use for this session to <em>version</em>. If
+ <code>Pmw.setversion()</code> is not called, the latest installed version of
+ Pmw will be used. This must be called before any other Pmw class
+ or function, except functions setting or querying versions.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.setyearpivot</strong>(<em>pivot</em>, <em>century</em> = <strong>None</strong>)</dt><dd>
+<p>
+ Set the pivot year and century for the application's date
+ processing. These values are used in the <code>datestringtojdn()</code>
+ method, which is used by <a href="Counter.html">Pmw.Counter</a> and <a href="EntryField.html">Pmw.EntryField</a>
+ and derived classes. The initial values of <em>pivot</em> and <em>century</em>
+ are <strong>50</strong> and <strong>2000</strong> repectively. Return a tuple containing the
+ old values of <em>pivot</em> and <em>century</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.showbusycursor</strong>()</dt><dd>
+<p>
+ Block events to and display a busy cursor over all windows in this
+ application that are in the state <strong>'normal'</strong> or <strong>'iconic'</strong>, except
+ those windows whose <strong>exclude</strong> busycursor attribute has been set to
+ true by a call to <code>Pmw.setbusycursorattributes()</code>.</p>
+
+<p> If a window and its contents have just been created,
+ <code>update_idletasks()</code> may have to be called before
+ <code>Pmw.showbusycursor()</code> so that the window is mapped to the screen.
+ Windows created or deiconified after calling
+ <code>Pmw.showbusycursor()</code> will not be blocked.</p>
+
+<p> To unblock events and remove the busy cursor, use
+ <code>Pmw.hidebusycursor()</code>. Nested calls to <code>Pmw.showbusycursor()</code>
+ may be made. In this case, a matching number of calls to
+ <code>Pmw.hidebusycursor()</code> must be made before the event block and
+ busy cursor are removed.</p>
+
+<p> If the BLT extension to Tk is not present, this function has no
+ effect other than to save the value of the current focus window,
+ to be later restored by <code>Pmw.hidebusycursor()</code>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.stringtoreal</strong>(<em>text</em>, <em>separator</em> = <strong>'.'</strong>)</dt><dd>
+<p>
+ Return the real number represented by <em>text</em>. This is similar to
+ <code>string.atof()</code> except that the character representing the decimal
+ point in <em>text</em> is given by <em>separator</em>.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.timestringtoseconds</strong>(<em>text</em>, <em>separator</em> = <strong>':'</strong>)</dt><dd>
+<p>
+ Return the number of seconds corresponding to the time in <em>text</em>.
+ The time must be specified as three integers separated by the
+ <em>separator</em> character and must be in the order hours, minutes and
+ seconds. The first number may be negative, indicating a negative
+ time.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.timevalidator</strong>(<em>text</em>, <em>separator</em> = <strong>':'</strong>)</dt><dd>
+<p>
+ Validator function for <a href="EntryField.html">Pmw.EntryField</a> <strong>time</strong> standard validator.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.tracetk</strong>(<em>root</em> = <strong>None</strong>, <em>on</em> = <strong>1</strong>, <em>withStackTrace</em> = <strong>0</strong>, <em>file</em> = <strong>None</strong>)</dt><dd>
+<p>
+ Print debugging trace of calls to, and callbacks from, the Tk
+ interpreter associated with the <em>root</em> window . If <em>root</em> is
+ <strong>None</strong>, use the Tkinter default root. If <em>on</em> is true, start
+ tracing, otherwise stop tracing. If <em>withStackTrace</em> is true,
+ print a python function call stacktrace after the trace for each
+ call to Tk. If <em>file</em> is <strong>None</strong>, print to standard error,
+ otherwise print to the file given by <em>file</em>.</p>
+
+<p> For each call to Tk, the Tk command and its options are printed as
+ a python tuple, followed by the return value of the command (if
+ not the empty string). For example:</p>
+
+<dl><dd><pre>python executed:
+ button = Tkinter.Button()
+ button.configure(text = 'Hi')
+
+tracetk output:
+ CALL TK> 1: ('button', '.3662448') -> '.3662448'
+ CALL TK> 1: ('.3662448', 'configure', '-text', 'Hi')</pre></dd></dl>
+
+<p> Some calls from python to Tk (such as <strong>update</strong>, <strong>tkwait</strong>,
+ <strong>invoke</strong>, etc) result in the execution of callbacks from Tk to
+ python. These python callbacks can then recursively call into Tk.
+ When displayed by <strong>tracetk()</strong>, these recursive calls are indented
+ proportionally to the depth of recursion. The depth is also
+ printed as a leading number. The return value of a call to Tk
+ which generated recursive calls is printed on a separate line at
+ the end of the recursion. For example:</p>
+
+<dl><dd><pre>python executed:
+ def callback():
+ button.configure(text = 'Bye')
+ return 'Got me!'
+ button = Tkinter.Button()
+ button.configure(command = callback)
+ button.invoke()</pre></dd></dl>
+
+<dl><dd><pre>tracetk output:
+ CALL TK> 1: ('button', '.3587144') -> '.3587144'
+ CALL TK> 1: ('.3587144', 'configure', '-command', '3638368callback')
+ CALL TK> 1: ('.3587144', 'invoke')
+ CALLBACK> 2: callback()
+ CALL TK> 2: ('.3587144', 'configure', '-text', 'Bye')
+ CALL RTN> 1: -> 'Got me!'</pre></dd></dl>
+
+<p> <strong>Pmw.initialise()</strong> must be called before <strong>tracetk()</strong> so that hooks
+ are put into the Tkinter CallWrapper class to trace callbacks from
+ Tk to python and also to handle recursive calls correctly.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.version</strong>(<em>alpha</em> = <strong>0</strong>)</dt><dd>
+<p>
+ If <em>alpha</em> is false, return the base version of Pmw being used
+ for this session. If <code>Pmw.setversion()</code> has not been called, this
+ will be the latest installed version of Pmw. If <em>alpha</em> is true,
+ return the list of alpha versions of Pmw being used for this
+ session, in search order. If <code>Pmw.setalphaversions()</code> has not
+ been called, this will be the empty list.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <strong>Pmw.ymdtojdn</strong>(<em>year</em>, <em>month</em>, <em>day</em>, <em>julian</em> = <strong>-1</strong>, <em>papal</em> = <strong>1</strong>)</dt><dd>
+<p>
+ Return the Julian Day Number corresponding to <em>year</em>, <em>month</em> and
+ <em>day</em>. See <code>jdntoymd()</code> for description of other arguments)</p>
+
+<p></p>
+
+
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.PromptDialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.PromptDialog</h1>
+
+<center><IMG SRC=PromptDialog.gif ALT="" WIDTH=218 HEIGHT=164></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.PromptDialog() -
+ selection dialog displaying an entry field
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="Dialog.html">Pmw.Dialog</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ The prompt dialog is a dialog window which displays an entry field
+ which can be used to prompt the user for a value.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderx></a>
+<dl><dt> <strong>borderx
+</strong></dt><dd>
+Initialisation option. The padding to the left and right of the entry field. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.bordery></a>
+<dl><dt> <strong>bordery
+</strong></dt><dd>
+Initialisation option. The padding above and below the entry field. The default is <strong>20</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('OK',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.entryfield></a>
+<dl><dt> <strong>entryfield
+</strong></dt><dd>
+The entry field for the user to enter a value. By default, this component is a <a href="EntryField.html">Pmw.EntryField</a>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+Alias for <strong>entryfield_entry</strong>.
+</dd></dl>
+<dl><dt> <strong>label
+</strong></dt><dd>
+Alias for <strong>entryfield_label</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="Dialog.html#methods">Pmw.Dialog</a></strong>.
+In addition, methods from the
+<strong><a href="EntryField.html#methods">Pmw.EntryField</a></strong> class
+are forwarded by this megawidget to the
+<strong>entryfield</strong> component.
+<p></p>
+<a name=method.deleteentry></a>
+<dl><dt> <strong>deleteentry</strong>(<em>first</em>, <em>last</em> = <strong>None</strong>)</dt><dd>
+Delete text from the entry field's entry widget. An alias for
+ <code>component('entry').delete()</code>.</p>
+
+
+</dd></dl>
+<a name=method.indexentry></a>
+<dl><dt> <strong>indexentry</strong>(<em>index</em>)</dt><dd>
+An alias for <code>component('entry').index()</code>.</p>
+
+
+</dd></dl>
+<a name=method.insertentry></a>
+<dl><dt> <strong>insertentry</strong>(<em>index</em>, <em>text</em>)</dt><dd>
+Insert text into the entry field's entry widget. An alias for
+ <code>component('entry').insert()</code>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog to prompt for the password.
+ self.dialog = Pmw.PromptDialog(parent,
+ title = 'Password',
+ label_text = 'Password:',
+ entryfield_labelpos = 'n',
+ entry_show = '*',
+ defaultbutton = 0,
+ buttons = ('OK', 'Cancel'),
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Create the confirmation dialog.
+ self.confirm = Pmw.MessageDialog(
+ title = 'Are you sure?',
+ message_text = 'Are you really sure?',
+ defaultbutton = 0,
+ buttons = ('OK', 'Cancel'))
+ self.confirm.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show prompt dialog',
+ command = self.dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self, result):
+ if result is None or result == 'Cancel':
+ print 'Password prompt cancelled'
+ self.dialog.deactivate(result)
+ else:
+ result = self.confirm.activate()
+ if result == 'OK':
+ print 'Password entered ' + self.dialog.get()
+ self.dialog.deactivate()
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.RadioSelect reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.RadioSelect</h1>
+
+<center><IMG SRC=RadioSelect.gif ALT="" WIDTH=466 HEIGHT=272></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.RadioSelect() -
+ a set of buttons, some of which may be selected
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A radio select is a container megawidget which manages a number of
+ buttons. The buttons may be laid out either horizontally or
+ vertically. In single selection mode, only one button may be
+ selected at any one time. In multiple selection mode, several
+ buttons may be selected at the same time and clicking on a
+ selected button will deselect it. </p>
+
+<p> The buttons displayed can be either standard buttons, radio
+ buttons or check buttons. When selected, standard buttons are
+ displayed sunken and radio and check buttons are displayed with
+ the appropriate indicator color and relief.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.buttontype></a>
+<dl><dt> <strong>buttontype
+</strong></dt><dd>
+Initialisation option. Specifies the default type of buttons created by the <code>add()</code>
+ method. If <strong>'button'</strong>, the default type is Tkinter.Button. If
+ <strong>'radiobutton'</strong>, the default type is Tkinter.Radiobutton. If
+ <strong>'checkbutton'</strong>, the default type is Tkinter.Checkbutton.</p>
+<p> If <strong>'radiobutton'</strong>, single selection mode is automatically set.
+ If <strong>'checkbutton'</strong>, multiple selection mode is automatically set. The default is <strong>'button'</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call when one of the buttons is clicked on
+ or when <code>invoke()</code> is called.</p>
+<p> In single selection mode, the function is called with a single
+ argument, which is the name of the selected button.</p>
+
+<p> In multiple selection mode, the function is called with the first
+ argument being the name of the button and the second argument
+ being true if the button is now selected or false if it is now
+ deselected. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.orient></a>
+<dl><dt> <strong>orient
+</strong></dt><dd>
+Initialisation option. Specifies the direction in which the buttons are laid out. This
+ may be <strong>'horizontal'</strong> or <strong>'vertical'</strong>. The default is <strong>'horizontal'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.padx></a>
+<dl><dt> <strong>padx
+</strong></dt><dd>
+Initialisation option. Specifies a padding distance to leave between each button in the x
+ direction and also between the buttons and the outer edge of the
+ radio select. The default is <strong>5</strong>.</p>
+
+
+</dd></dl>
+<a name=option.pady></a>
+<dl><dt> <strong>pady
+</strong></dt><dd>
+Initialisation option. Specifies a padding distance to leave between each button in the y
+ direction and also between the buttons and the outer edge of the
+ radio select. The default is <strong>5</strong>.</p>
+
+
+</dd></dl>
+<a name=option.selectmode></a>
+<dl><dt> <strong>selectmode
+</strong></dt><dd>
+Initialisation option. Specifies the selection mode: whether a single button or multiple
+ buttons can be selected at one time. If <strong>'single'</strong>, clicking on
+ an unselected button selects it and deselects all other buttons.
+ If <strong>'multiple'</strong>, clicking on an unselected button selects it and
+ clicking on a selected button deselects it. This option is
+ ignored if <strong>buttontype</strong> is <strong>'radiobutton'</strong> or <strong>'checkbutton'</strong>. The default is <strong>'single'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.frame></a>
+<dl><dt> <strong>frame
+</strong></dt><dd>
+If the <strong>label</strong> component has been created (that is, the <strong>labelpos</strong>
+ option is not <strong>None</strong>), the <strong>frame</strong> component is created to act as
+ the container of the buttons created by the <code>add()</code> method. If
+ there is no <strong>label</strong> component, then no <strong>frame</strong> component is
+ created and the <strong>hull</strong> component acts as the container. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Dynamic components</h3></dt><dd>
+<p>
+ Button components are created dynamically by the <code>add()</code>
+ method. The default type of the buttons depends on the value
+ of the <strong>buttontype</strong> option.</p>
+
+<p> Button components are created with a component group of <strong>Button</strong>.</p>
+<p> </p>
+
+
+
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.add></a>
+<dl><dt> <strong>add</strong>(<em>componentName</em>, **<em>kw</em>)</dt><dd>
+Add a button to the end of the radio select as a component
+ named <em>componentName</em>. with a default type as specified by
+ <strong>buttontype</strong>. Any keyword arguments present (except <strong>command</strong>)
+ will be passed to the constructor when creating the button. If
+ the <strong>text</strong> keyword argument is not given, the <strong>text</strong> option of the
+ button defaults to <em>componentName</em>. The method returns the
+ component widget.</p>
+
+
+</dd></dl>
+<a name=method.button></a>
+<dl><dt> <strong>button</strong>(<em>buttonIndex</em>)</dt><dd>
+Return the button specified by <em>buttonIndex</em>, which may have any
+ of the forms accepted by the <code>index()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.deleteall></a>
+<dl><dt> <strong>deleteall</strong>()</dt><dd>
+Delete all buttons and clear the current selection.</p>
+
+
+</dd></dl>
+<a name=method.getcurselection></a>
+<dl><dt> <strong>getcurselection</strong>()</dt><dd>
+Same as <code>getvalue()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.getvalue></a>
+<dl><dt> <strong>getvalue</strong>()</dt><dd>
+In single selection mode, return the name of the currently
+ selected button, or <strong>None</strong> if no buttons have been selected yet.</p>
+<p> In multiple selection mode, return a list of the names of the
+ currently selected buttons.</p>
+
+
+
+</dd></dl>
+<a name=method.index></a>
+<dl><dt> <strong>index</strong>(<em>index</em>)</dt><dd>
+Return the numerical index of the button corresponding to <em>index</em>.
+ This may be specified in any of the following forms:</p>
+<dl><dt><em>name</em></dt><dd>Specifies the button named <em>name</em>.<p></p>
+
+</dd>
+<dt><em>number</em></dt><dd>Specifies the button numerically, where <strong>0</strong> corresponds to
+ the left (or top) button.<p></p>
+
+</dd>
+<dt><strong>Pmw.END</strong></dt><dd>Specifies the right (or bottom) button.<p></p>
+
+</dd></dl>
+
+
+</dd></dl>
+<a name=method.invoke></a>
+<dl><dt> <strong>invoke</strong>(<em>index</em>)</dt><dd>
+Calling this method is the same as clicking on the button
+ specified by <em>index</em>: the buttons are displayed selected or
+ deselected according to the selection mode and <strong>command</strong> is
+ called. <em>index</em> may have any of the forms accepted by the
+ <code>index()</code> method. The value returned by <strong>command</strong> is returned.</p>
+
+
+</dd></dl>
+<a name=method.numbuttons></a>
+<dl><dt> <strong>numbuttons</strong>()</dt><dd>
+Return the number of buttons in the radio select.</p>
+
+
+</dd></dl>
+<a name=method.setvalue></a>
+<dl><dt> <strong>setvalue</strong>(<em>textOrList</em>)</dt><dd>
+Set the current selection for the radio select to <em>textOrList</em>,
+ but do not invoke <strong>command</strong>.</p>
+<p> In single selection mode, select only the button specified by the
+ string <em>textOrList</em>.</p>
+
+<p> In multiple selection mode, select only the buttons specified by
+ the list <em>textOrList</em>.</p>
+
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack a horizontal RadioSelect widget.
+ horiz = Pmw.RadioSelect(parent,
+ labelpos = 'w',
+ command = self.callback,
+ label_text = 'Horizontal',
+ frame_borderwidth = 2,
+ frame_relief = 'ridge'
+ )
+ horiz.pack(fill = 'x', padx = 10, pady = 10)
+
+ # Add some buttons to the horizontal RadioSelect.
+ for text in ('Fruit', 'Vegetables', 'Cereals', 'Legumes'):
+ horiz.add(text)
+ horiz.invoke('Cereals')
+
+ # Create and pack a multiple selection RadioSelect widget.
+ self.multiple = Pmw.RadioSelect(parent,
+ labelpos = 'w',
+ command = self.multcallback,
+ label_text = 'Multiple\nselection',
+ frame_borderwidth = 2,
+ frame_relief = 'ridge',
+ selectmode = 'multiple',
+ )
+ self.multiple.pack(fill = 'x', padx = 10)
+
+ # Add some buttons to the multiple selection RadioSelect.
+ for text in ('Apricots', 'Eggplant', 'Rice', 'Lentils'):
+ self.multiple.add(text)
+ self.multiple.invoke('Rice')
+
+ # Create and pack a vertical RadioSelect widget, with checkbuttons.
+ self.checkbuttons = Pmw.RadioSelect(parent,
+ buttontype = 'checkbutton',
+ orient = 'vertical',
+ labelpos = 'w',
+ command = self.checkbuttoncallback,
+ label_text = 'Vertical,\nusing\ncheckbuttons',
+ hull_borderwidth = 2,
+ hull_relief = 'ridge',
+ )
+ self.checkbuttons.pack(side = 'left', expand = 1, padx = 10, pady = 10)
+
+ # Add some buttons to the checkbutton RadioSelect.
+ for text in ('Male', 'Female'):
+ self.checkbuttons.add(text)
+ self.checkbuttons.invoke('Male')
+ self.checkbuttons.invoke('Female')
+
+ # Create and pack a RadioSelect widget, with radiobuttons.
+ radiobuttons = Pmw.RadioSelect(parent,
+ buttontype = 'radiobutton',
+ orient = 'vertical',
+ labelpos = 'w',
+ command = self.callback,
+ label_text = 'Vertical,\nusing\nradiobuttons',
+ hull_borderwidth = 2,
+ hull_relief = 'ridge',
+ )
+ radiobuttons.pack(side = 'left', expand = 1, padx = 10, pady = 10)
+
+ # Add some buttons to the radiobutton RadioSelect.
+ for text in ('Male', 'Female', 'Both', 'Neither'):
+ radiobuttons.add(text)
+ radiobuttons.invoke('Both')
+
+ def callback(self, tag):
+ # This is called whenever the user clicks on a button
+ # in a single select RadioSelect widget.
+ print 'Button', tag, 'was pressed.'
+
+ def multcallback(self, tag, state):
+ # This is called whenever the user clicks on a button
+ # in the multiple select RadioSelect widget.
+ if state:
+ action = 'pressed.'
+ else:
+ action = 'released.'
+
+ print 'Button', tag, 'was', action, \
+ 'Selection:', self.multiple.getcurselection()
+
+ def checkbuttoncallback(self, tag, state):
+ # This is called whenever the user clicks on a button
+ # in the checkbutton RadioSelect widget.
+ if state:
+ action = 'pressed.'
+ else:
+ action = 'released.'
+
+ print 'Button', tag, 'was', action, \
+ 'Selection:', self.checkbuttons.getcurselection()
+
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 6 June 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ScrolledCanvas reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ScrolledCanvas</h1>
+
+<center><IMG SRC=ScrolledCanvas.gif ALT="" WIDTH=382 HEIGHT=240></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ScrolledCanvas() -
+ canvas with optional scrollbars
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A scrolled canvas consists of a standard canvas widget with optional
+ scrollbars which can be used to scroll the canvas. The scrollbars
+ can be <em>dynamic</em>, which means that a scrollbar will only be
+ displayed if it is necessary, that is, if the scrollregion of the
+ canvas is larger than the canvas.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+Initialisation option. If true, the <strong>borderframe</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.canvasmargin></a>
+<dl><dt> <strong>canvasmargin
+</strong></dt><dd>
+Initialisation option. The margin around the items in the canvas. Used by the
+ <strong>resizescrollregion()</strong> method. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.hscrollmode></a>
+<dl><dt> <strong>hscrollmode
+</strong></dt><dd>
+The horizontal scroll mode. If <strong>'none'</strong>, the horizontal scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.scrollmargin></a>
+<dl><dt> <strong>scrollmargin
+</strong></dt><dd>
+Initialisation option. The distance between the scrollbars and the enclosing canvas
+ widget. The default is <strong>2</strong>.</p>
+
+
+</dd></dl>
+<a name=option.usehullsize></a>
+<dl><dt> <strong>usehullsize
+</strong></dt><dd>
+Initialisation option. If true, the size of the megawidget is determined solely by the
+ width and height options of the <strong>hull</strong> component.</p>
+<p> Otherwise, the size of the megawidget is determined by the width
+ and height of the <strong>canvas</strong> component, along with the size and/or
+ existence of the other components, such as the label, the
+ scrollbars and the scrollmargin option. All these affect the
+ overall size of the megawidget. The default is <strong>0</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.vscrollmode></a>
+<dl><dt> <strong>vscrollmode
+</strong></dt><dd>
+The vertical scroll mode. If <strong>'none'</strong>, the vertical scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+A frame widget which snuggly fits around the canvas, to give the
+ appearance of a canvas border. It is created with a border so
+ that the canvas, which is created without a border, looks like it
+ has a border. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.canvas></a>
+<dl><dt> <strong>canvas
+</strong></dt><dd>
+The canvas widget which is scrolled by the scrollbars. If the
+ <strong>borderframe</strong> option is true, this is created with a borderwidth
+ of <strong>0</strong> to overcome a known problem with canvas widgets: if a
+ widget inside a canvas extends across one of the edges of the
+ canvas, then the widget obscures the border of the canvas.
+ Therefore, if the canvas has no border, then this overlapping does
+ not occur. By default, this component is a Tkinter.Canvas.</p>
+
+
+</dd></dl>
+<a name=component.horizscrollbar></a>
+<dl><dt> <strong>horizscrollbar
+</strong></dt><dd>
+The horizontal scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.vertscrollbar></a>
+<dl><dt> <strong>vertscrollbar
+</strong></dt><dd>
+The vertical scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Canvas</strong> class
+are forwarded by this megawidget to the
+<strong>canvas</strong> component.
+<p></p>
+<a name=method.bbox></a>
+<dl><dt> <strong>bbox</strong>(*<em>args</em>)</dt><dd>
+This method is explicitly forwarded to the <strong>canvas</strong> component's
+ <code>bbox()</code> method. Without this explicit forwarding, the <code>bbox()</code>
+ method (aliased to <code>grid_bbox()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+<a name=method.interior></a>
+<dl><dt> <strong>interior</strong>()</dt><dd>
+Return the canvas widget within which the programmer should create
+ graphical items and child widgets. This is the same as
+ <code>component('canvas')</code>.</p>
+
+
+</dd></dl>
+<a name=method.resizescrollregion></a>
+<dl><dt> <strong>resizescrollregion</strong>()</dt><dd>
+Resize the scrollregion of the <strong>canvas</strong> component to be the
+ bounding box covering all the items in the canvas plus a margin on
+ all sides, as specified by the <strong>canvasmargin</strong> option.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the ScrolledCanvas.
+ self.sc = Pmw.ScrolledCanvas(parent,
+ borderframe = 1,
+ labelpos = 'n',
+ label_text = 'ScrolledCanvas',
+ usehullsize = 1,
+ hull_width = 400,
+ hull_height = 300,
+ )
+
+ # Create a group widget to contain the scrollmode options.
+ w = Pmw.Group(parent, tag_text='Scroll mode')
+ w.pack(side = 'bottom', padx = 5, pady = 5)
+
+ hmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.sethscrollmode,
+ menubutton_width = 8,
+ )
+ hmode.pack(side = 'left', padx = 5, pady = 5)
+ hmode.invoke('dynamic')
+
+ vmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.setvscrollmode,
+ menubutton_width = 8,
+ )
+ vmode.pack(side = 'left', padx = 5, pady = 5)
+ vmode.invoke('dynamic')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(side = 'bottom')
+ buttonBox.add('yview', text = 'Show\nyview', command = self.showYView)
+ buttonBox.add('scroll', text = 'Page\ndown', command = self.pageDown)
+ buttonBox.add('center', text = 'Center', command = self.centerPage)
+
+ # Pack this last so that the buttons do not get shrunk when
+ # the window is resized.
+ self.sc.pack(padx = 5, pady = 5, fill = 'both', expand = 1)
+
+ self.sc.component('canvas').bind('<1>', self.addcircle)
+
+ testEntry = Tkinter.Entry(parent)
+ self.sc.create_line(20, 20, 100, 100)
+ self.sc.create_oval(100, 100, 200, 200, fill = 'green')
+ self.sc.create_text(100, 20, anchor = 'nw',
+ text = 'Click in the canvas\nto draw ovals',
+ font = testEntry.cget('font'))
+ button = Tkinter.Button(self.sc.interior(),
+ text = 'Hello,\nWorld!\nThis\nis\na\nbutton.')
+ self.sc.create_window(200, 200,
+ anchor='nw',
+ window = button)
+
+ # Set the scroll region of the canvas to include all the items
+ # just created.
+ self.sc.resizescrollregion()
+
+ self.colours = ('red', 'green', 'blue', 'yellow', 'cyan', 'magenta',
+ 'black', 'white')
+ self.oval_count = 0
+ self.rand = 12345
+
+ def sethscrollmode(self, tag):
+ self.sc.configure(hscrollmode = tag)
+
+ def setvscrollmode(self, tag):
+ self.sc.configure(vscrollmode = tag)
+
+ def addcircle(self, event):
+ x = self.sc.canvasx(event.x)
+ y = self.sc.canvasy(event.y)
+ width = 10 + self.random() % 100
+ height = 10 + self.random() % 100
+ self.sc.create_oval(
+ x - width, y - height, x + width, y + height,
+ fill = self.colours[self.oval_count])
+ self.oval_count = (self.oval_count + 1) % len(self.colours)
+ self.sc.resizescrollregion()
+
+ # Simple random number generator.
+ def random(self):
+ self.rand = (self.rand * 125) % 2796203
+ return self.rand
+
+ def showYView(self):
+ print self.sc.yview()
+
+ def pageDown(self):
+ self.sc.yview('scroll', 1, 'page')
+
+ def centerPage(self):
+ top, bottom = self.sc.yview()
+ size = bottom - top
+ middle = 0.5 - size / 2
+ self.sc.yview('moveto', middle)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 20 September 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ScrolledField reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ScrolledField</h1>
+
+<center><IMG SRC=ScrolledField.gif ALT="" WIDTH=268 HEIGHT=37></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ScrolledField() -
+ single line scrollable output field
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A scrolled field displays a single line of text. If the text is
+ too wide to display in the megawidget it can be scrolled to the
+ left and right by the user by dragging with the middle mouse
+ button. The text is also selectable by clicking or dragging with
+ the left mouse button.</p>
+
+<p> It can be used instead of a Tkinter.Label widget when displaying
+ text of unknown width such as application status messages.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.sticky></a>
+<dl><dt> <strong>sticky
+</strong></dt><dd>
+Initialisation option. The default is <strong>'ew'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.text></a>
+<dl><dt> <strong>text
+</strong></dt><dd>
+Specifies the text to display in the scrolled field. The default is <strong>''</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.entry></a>
+<dl><dt> <strong>entry
+</strong></dt><dd>
+This is used to display the text and allows the user to scroll and
+ select the text. The <strong>state</strong> of this component is set to
+ <strong>'readonly'</strong> (or <strong>'disabled'</strong> in earlier versions of Tcl/Tk which do
+ not support <strong>'readonly'</strong>), so that the user is unable to modify the text. By default, this component is a Tkinter.Entry.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+This megawidget has no methods of its own.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Entry</strong> class
+are forwarded by this megawidget to the
+<strong>entry</strong> component.
+<p></p>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create and pack the ScrolledField.
+ self._field = Pmw.ScrolledField(parent, entry_width = 30,
+ entry_relief='groove', labelpos = 'n',
+ label_text = 'Scroll the field using the\nmiddle mouse button')
+ self._field.pack(fill = 'x', expand = 1, padx = 10, pady = 10)
+
+ # Create and pack a button to change the ScrolledField.
+ self._button = Tkinter.Button(parent, text = 'Change field',
+ command = self.execute)
+ self._button.pack(padx = 10, pady = 10)
+
+ self._index = 0
+ self.execute()
+
+ def execute(self):
+ self._field.configure(text = lines[self._index % len(lines)])
+ self._index = self._index + 1
+
+lines = (
+ 'Alice was beginning to get very tired of sitting by her sister',
+ 'on the bank, and of having nothing to do: once or twice she had',
+ 'peeped into the book her sister was reading, but it had no',
+ 'pictures or conversations in it, "and what is the use of a book,"',
+ 'thought Alice "without pictures or conversation?"',
+ 'Alice\'s Adventures in Wonderland',
+ 'Lewis Carroll',
+)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 23 August 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ScrolledFrame reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ScrolledFrame</h1>
+
+<center><IMG SRC=ScrolledFrame.gif ALT="" WIDTH=404 HEIGHT=174></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ScrolledFrame() -
+ frame with optional scrollbars
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A scrolled frame consists of a scrollable interior frame within a
+ clipping frame. The programmer can create other widgets within
+ the interior frame. If the frame becomes larger than the
+ surrounding clipping frame, the user can position the frame using
+ the horizontal and vertical scrollbars.</p>
+
+<p> The scrollbars can be <em>dynamic</em>, which means that a scrollbar will
+ only be displayed if it is necessary. That is, if the frame is
+ smaller than the surrounding clipping frame, the scrollbar will be
+ hidden.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+Initialisation option. If true, the <strong>borderframe</strong> component will be created. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.horizflex></a>
+<dl><dt> <strong>horizflex
+</strong></dt><dd>
+Specifies how the width of the scrollable interior frame should be
+ resized relative to the clipping frame.</p>
+<p> If <strong>'fixed'</strong>, the interior frame is set to the <em>natural</em> width, as
+ requested by the child widgets of the frame. If <strong>'expand'</strong> and
+ the requested width of the interior frame is less than the width
+ of the clipping frame, the interior frame expands to fill the
+ clipping frame. If <strong>'shrink'</strong> and the requested width of the
+ interior frame is more than the width of the clipping frame, the
+ interior frame shrinks to the width of the clipping frame. If
+ <strong>'elastic'</strong>, the width of the interior frame is always set to the
+ width of the clipping frame. The default is <strong>'fixed'</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.horizfraction></a>
+<dl><dt> <strong>horizfraction
+</strong></dt><dd>
+Initialisation option. The fraction of the width of the clipper frame to scroll the
+ interior frame when the user clicks on the horizontal scrollbar
+ arrows. The default is <strong>0.05</strong>.</p>
+
+
+</dd></dl>
+<a name=option.hscrollmode></a>
+<dl><dt> <strong>hscrollmode
+</strong></dt><dd>
+The horizontal scroll mode. If <strong>'none'</strong>, the horizontal scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.scrollmargin></a>
+<dl><dt> <strong>scrollmargin
+</strong></dt><dd>
+Initialisation option. The distance between the scrollbars and the clipping frame. The default is <strong>2</strong>.</p>
+
+
+</dd></dl>
+<a name=option.usehullsize></a>
+<dl><dt> <strong>usehullsize
+</strong></dt><dd>
+Initialisation option. If true, the size of the megawidget is determined solely by the
+ width and height options of the <strong>hull</strong> component.</p>
+<p> Otherwise, the size of the megawidget is determined by the width
+ and height of the <strong>clipper</strong> component, along with the size and/or
+ existence of the other components, such as the label, the
+ scrollbars and the scrollmargin option. All these affect the
+ overall size of the megawidget. The default is <strong>0</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.vertflex></a>
+<dl><dt> <strong>vertflex
+</strong></dt><dd>
+Specifies how the height of the scrollable interior frame should
+ be resized relative to the clipping frame.</p>
+<p> If <strong>'fixed'</strong>, the interior frame is set to the <em>natural</em> height,
+ as requested by the child widgets of the frame. If <strong>'expand'</strong> and
+ the requested height of the interior frame is less than the height
+ of the clipping frame, the interior frame expands to fill the
+ clipping frame. If <strong>'shrink'</strong> and the requested height of the
+ interior frame is more than the height of the clipping frame, the
+ interior frame shrinks to the height of the clipping frame. If
+ <strong>'elastic'</strong>, the height of the interior frame is always set to the
+ height of the clipping frame. The default is <strong>'fixed'</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.vertfraction></a>
+<dl><dt> <strong>vertfraction
+</strong></dt><dd>
+Initialisation option. The fraction of the height of the clipper frame to scroll the
+ interior frame when the user clicks on the vertical scrollbar
+ arrows. The default is <strong>0.05</strong>.</p>
+
+
+</dd></dl>
+<a name=option.vscrollmode></a>
+<dl><dt> <strong>vscrollmode
+</strong></dt><dd>
+The vertical scroll mode. If <strong>'none'</strong>, the vertical scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+A frame widget which snuggly fits around the clipper, to give the
+ appearance of a border. It is created with a border so that the
+ clipper, which is created without a border, looks like it has a
+ border. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.clipper></a>
+<dl><dt> <strong>clipper
+</strong></dt><dd>
+The frame which is used to provide a clipped view of the <strong>frame</strong>
+ component. If the <strong>borderframe</strong> option is true, this is created
+ with a borderwidth of <strong>0</strong> to overcome a known problem with using
+ <code>place</code> to position widgets: if a widget (in this case the
+ <strong>frame</strong> component) is <code>placed</code> inside a frame (in this case the
+ <strong>clipper</strong> component) and it extends across one of the edges of the
+ frame, then the widget obscures the border of the frame.
+ Therefore, if the clipper has no border, then this overlapping
+ does not occur. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.frame></a>
+<dl><dt> <strong>frame
+</strong></dt><dd>
+The frame within the clipper to contain the widgets to be scrolled. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.horizscrollbar></a>
+<dl><dt> <strong>horizscrollbar
+</strong></dt><dd>
+The horizontal scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.vertscrollbar></a>
+<dl><dt> <strong>vertscrollbar
+</strong></dt><dd>
+The vertical scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.interior></a>
+<dl><dt> <strong>interior</strong>()</dt><dd>
+Return the frame within which the programmer may create widgets to
+ be scrolled. This is the same as <code>component('frame')</code>.</p>
+
+
+</dd></dl>
+<a name=method.reposition></a>
+<dl><dt> <strong>reposition</strong>()</dt><dd>
+Update the position of the <strong>frame</strong> component in the <strong>clipper</strong> and
+ update the scrollbars.</p>
+<p> Usually, this method does not need to be called explicitly, since
+ the position of the <strong>frame</strong> component and the scrollbars are
+ automatically updated whenever the size of the <strong>frame</strong> or
+ <strong>clipper</strong> components change or the user clicks in the scrollbars.
+ However, if <strong>horizflex</strong> or <strong>vertflex</strong> is <strong>'expand'</strong>, the
+ megawidget cannot detect when the requested size of the <strong>frame</strong>
+ increases to greater than the size of the <strong>clipper</strong>. Therefore,
+ this method should be called when a new widget is added to the
+ <strong>frame</strong> (or a widget is increased in size) <em>after</em> the initial
+ megawidget construction.</p>
+
+
+
+</dd></dl>
+<a name=method.xview></a>
+<dl><dt> <strong>xview</strong>(<em>mode</em> = <strong>None</strong>, <em>value</em> = <strong>None</strong>, <em>units</em> = <strong>None</strong>)</dt><dd>
+Query or change the horizontal position of the scrollable interior
+ frame. If <em>mode</em> is <strong>None</strong>, return a tuple of two numbers, each
+ between 0.0 and 1.0. The first is the position of the left edge
+ of the visible region of the contents of the scrolled frame,
+ expressed as a fraction of the total width of the contents. The
+ second is the position of the right edge of the visible region.</p>
+<p> If <em>mode</em> == <strong>'moveto'</strong>, adjust the view of the interior so that
+ the fraction <em>value</em> of the total width of the contents is
+ off-screen to the left. The <em>value</em> must be between <em>0.0</em> and
+ <em>1.0</em>.</p>
+
+<p> If <em>mode</em> == <strong>'scroll'</strong>, adjust the view of the interior left or
+ right by a fixed amount. If <em>what</em> is <strong>'units'</strong>, move the view in
+ units of <strong>horizfraction</strong>. If <em>what</em> is <em>pages</em>, move the view in
+ units of the width of the scrolled frame. If <em>value</em> is positive,
+ move to the right, otherwise move to the left.</p>
+
+
+
+</dd></dl>
+<a name=method.yview></a>
+<dl><dt> <strong>yview</strong>(<em>mode</em> = <strong>None</strong>, <em>value</em> = <strong>None</strong>, <em>units</em> = <strong>None</strong>)</dt><dd>
+Query or change the vertical position of the scrollable interior
+ frame. If <em>mode</em> is <strong>None</strong>, return a tuple of two numbers, each
+ between 0.0 and 1.0. The first is the position of the top edge
+ of the visible region of the contents of the scrolled frame,
+ expressed as a fraction of the total height of the contents. The
+ second is the position of the bottom edge of the visible region.</p>
+<p> If <em>mode</em> == <strong>'moveto'</strong>, adjust the view of the interior so that
+ the fraction <em>value</em> of the total height of the contents is
+ off-screen to the top. The <em>value</em> must be between <em>0.0</em> and
+ <em>1.0</em>.</p>
+
+<p> If <em>mode</em> == <strong>'scroll'</strong>, adjust the view of the interior up or
+ down by a fixed amount. If <em>what</em> is <strong>'units'</strong>, move the view in
+ units of <strong>vertfraction</strong>. If <em>what</em> is <em>pages</em>, move the view in
+ units of the height of the scrolled frame. If <em>value</em> is
+ positive, move to down, otherwise move up.</p>
+
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the ScrolledFrame.
+ self.sf = Pmw.ScrolledFrame(parent,
+ labelpos = 'n', label_text = 'ScrolledFrame',
+ usehullsize = 1,
+ hull_width = 400,
+ hull_height = 220,
+ )
+
+ # Create a group widget to contain the flex options.
+ w = Pmw.Group(parent, tag_text='Flex')
+ w.pack(side = 'bottom', padx = 5, pady = 3)
+
+ hflex = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['fixed', 'expand', 'shrink', 'elastic'],
+ command = self.sethflex,
+ menubutton_width = 8,
+ )
+ hflex.pack(side = 'left', padx = 5, pady = 3)
+ hflex.invoke('fixed')
+
+ vflex = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['fixed', 'expand', 'shrink', 'elastic'],
+ command = self.setvflex,
+ menubutton_width = 8,
+ )
+ vflex.pack(side = 'left', padx = 5, pady = 3)
+ vflex.invoke('fixed')
+
+ # Create a group widget to contain the scrollmode options.
+ w = Pmw.Group(parent, tag_text='Scroll mode')
+ w.pack(side = 'bottom', padx = 5, pady = 0)
+
+ hmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.sethscrollmode,
+ menubutton_width = 8,
+ )
+ hmode.pack(side = 'left', padx = 5, pady = 3)
+ hmode.invoke('dynamic')
+
+ vmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.setvscrollmode,
+ menubutton_width = 8,
+ )
+ vmode.pack(side = 'left', padx = 5, pady = 3)
+ vmode.invoke('dynamic')
+
+ self.radio = Pmw.RadioSelect(parent, selectmode = 'multiple',
+ command = self.radioSelected)
+ self.radio.add('center', text = 'Keep centered vertically')
+ self.radio.pack(side = 'bottom')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(side = 'bottom')
+ buttonBox.add('add', text = 'Add a button', command = self.addButton)
+ buttonBox.add('yview', text = 'Show yview', command = self.showYView)
+ buttonBox.add('scroll', text = 'Page down', command = self.pageDown)
+
+ # Pack this last so that the buttons do not get shrunk when
+ # the window is resized.
+ self.sf.pack(padx = 5, pady = 3, fill = 'both', expand = 1)
+
+ self.frame = self.sf.interior()
+
+ self.row = 0
+ self.col = 0
+
+ for count in range(15):
+ self.addButton()
+
+ def sethscrollmode(self, tag):
+ self.sf.configure(hscrollmode = tag)
+
+ def setvscrollmode(self, tag):
+ self.sf.configure(vscrollmode = tag)
+
+ def sethflex(self, tag):
+ self.sf.configure(horizflex = tag)
+
+ def setvflex(self, tag):
+ self.sf.configure(vertflex = tag)
+
+ def addButton(self):
+ button = Tkinter.Button(self.frame,
+ text = '(%d,%d)' % (self.col, self.row))
+ button.grid(row = self.row, column = self.col, sticky = 'nsew')
+
+ self.frame.grid_rowconfigure(self.row, weight = 1)
+ self.frame.grid_columnconfigure(self.col, weight = 1)
+ if self.sf.cget('horizflex') == 'expand' or \
+ self.sf.cget('vertflex') == 'expand':
+ self.sf.reposition()
+
+ if 'center' in self.radio.getcurselection():
+ self.sf.update_idletasks()
+ self.centerPage()
+
+ if self.col == self.row:
+ self.col = 0
+ self.row = self.row + 1
+ else:
+ self.col = self.col + 1
+
+ def showYView(self):
+ print self.sf.yview()
+
+ def pageDown(self):
+ self.sf.yview('scroll', 1, 'page')
+
+ def radioSelected(self, name, state):
+ if state:
+ self.centerPage()
+
+ def centerPage(self):
+ # Example of how to use the yview() method of Pmw.ScrolledFrame.
+ top, bottom = self.sf.yview()
+ size = bottom - top
+ middle = 0.5 - size / 2
+ self.sf.yview('moveto', middle)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 February 2001
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ScrolledListBox reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ScrolledListBox</h1>
+
+<center><IMG SRC=ScrolledListBox.gif ALT="" WIDTH=210 HEIGHT=168></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ScrolledListBox() -
+ listbox with optional scrollbars
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A scrolled listbox consists of a standard listbox widget with optional
+ scrollbars which can be used to scroll the listbox. The
+ scrollbars can be <em>dynamic</em>, which means that a scrollbar will
+ only be displayed if it is necessary. That is, if the listbox
+ does not contain enough entries, the vertical scrollbar will be
+ automatically hidden and if the entries are not wide enough, the
+ horizontal scrollbar will be automatically hidden.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.dblclickcommand></a>
+<dl><dt> <strong>dblclickcommand
+</strong></dt><dd>
+This specifies a function to call when mouse button 1 is double
+ clicked over an entry in the <strong>listbox</strong> component. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.hscrollmode></a>
+<dl><dt> <strong>hscrollmode
+</strong></dt><dd>
+The horizontal scroll mode. If <strong>'none'</strong>, the horizontal scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.items></a>
+<dl><dt> <strong>items
+</strong></dt><dd>
+Initialisation option. A tuple containing the initial items to be displayed by the
+ <strong>listbox</strong> component. The default is <strong>()</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.scrollmargin></a>
+<dl><dt> <strong>scrollmargin
+</strong></dt><dd>
+Initialisation option. The distance between the scrollbars and the listbox widget. The default is <strong>2</strong>.</p>
+
+
+</dd></dl>
+<a name=option.selectioncommand></a>
+<dl><dt> <strong>selectioncommand
+</strong></dt><dd>
+This specifies a function to call when mouse button 1 is single
+ clicked over an entry in the <strong>listbox</strong> component or if the <strong><Space></strong>
+ or <strong><Return></strong> key is hit while the <strong>listbox</strong> has focus. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.usehullsize></a>
+<dl><dt> <strong>usehullsize
+</strong></dt><dd>
+Initialisation option. If true, the size of the megawidget is determined solely by the
+ width and height options of the <strong>hull</strong> component.</p>
+<p> Otherwise, the size of the megawidget is determined by the width
+ and height of the <strong>listbox</strong> component, along with the size and/or
+ existence of the other components, such as the label, the
+ scrollbars and the scrollmargin option. All these affect the
+ overall size of the megawidget. The default is <strong>0</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.vscrollmode></a>
+<dl><dt> <strong>vscrollmode
+</strong></dt><dd>
+The vertical scroll mode. If <strong>'none'</strong>, the vertical scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.horizscrollbar></a>
+<dl><dt> <strong>horizscrollbar
+</strong></dt><dd>
+The horizontal scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.listbox></a>
+<dl><dt> <strong>listbox
+</strong></dt><dd>
+The listbox widget which is scrolled by the scrollbars. By default, this component is a Tkinter.Listbox.</p>
+
+
+</dd></dl>
+<a name=component.vertscrollbar></a>
+<dl><dt> <strong>vertscrollbar
+</strong></dt><dd>
+The vertical scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Listbox</strong> class
+are forwarded by this megawidget to the
+<strong>listbox</strong> component.
+<p></p>
+<a name=method.bbox></a>
+<dl><dt> <strong>bbox</strong>(<em>index</em>)</dt><dd>
+This method is explicitly forwarded to the <strong>listbox</strong> component's
+ <code>bbox()</code> method. Without this explicit forwarding, the <code>bbox()</code>
+ method (aliased to <code>grid_bbox()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+<a name=method.clear></a>
+<dl><dt> <strong>clear</strong>()</dt><dd>
+Delete all items from the scrolled listbox. Equivalent to
+ <code>setlist(())</code>.</p>
+
+
+</dd></dl>
+<a name=method.get></a>
+<dl><dt> <strong>get</strong>(<em>first</em> = <strong>None</strong>, <em>last</em> = <strong>None</strong>)</dt><dd>
+This is the same as the <code>get()</code> method of the <strong>listbox</strong> component,
+ except that if <em>first</em> is <strong>None</strong> all list
+ elements are returned.</p>
+
+
+</dd></dl>
+<a name=method.getcurselection></a>
+<dl><dt> <strong>getcurselection</strong>()</dt><dd>
+Same as <code>getvalue()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.getvalue></a>
+<dl><dt> <strong>getvalue</strong>()</dt><dd>
+Return a list of the currently selected items of the listbox.</p>
+
+
+</dd></dl>
+<a name=method.setlist></a>
+<dl><dt> <strong>setlist</strong>(<em>items</em>)</dt><dd>
+Replace all the items of the <strong>listbox</strong> component with those
+ specified by the <em>items</em> sequence.</p>
+
+
+</dd></dl>
+<a name=method.setvalue></a>
+<dl><dt> <strong>setvalue</strong>(<em>textOrList</em>)</dt><dd>
+Set the current selection for the scrolled list to <em>textOrList</em>.</p>
+<p> If <em>textOrList</em> is a string, select only the list item specified.</p>
+
+<p> Otherwise, select only the list items specified by <em>textOrList</em>,
+ which must be a sequence of strings.</p>
+
+
+
+</dd></dl>
+<a name=method.size></a>
+<dl><dt> <strong>size</strong>()</dt><dd>
+This method is explicitly forwarded to the <strong>listbox</strong> component's
+ <code>size()</code> method. Without this explicit forwarding, the <code>size()</code>
+ method (aliased to <code>grid_size()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the ScrolledListBox.
+ self.box = Pmw.ScrolledListBox(parent,
+ items=('Sydney', 'Melbourne', 'Brisbane'),
+ labelpos='nw',
+ label_text='Cities',
+ listbox_height = 6,
+ selectioncommand=self.selectionCommand,
+ dblclickcommand=self.defCmd,
+ usehullsize = 1,
+ hull_width = 200,
+ hull_height = 200,
+ )
+
+ # Create a group widget to contain the scrollmode options.
+ w = Pmw.Group(parent, tag_text='Scroll mode')
+ w.pack(side = 'bottom', padx = 5, pady = 5)
+
+ hmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Horizontal:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.sethscrollmode,
+ menubutton_width = 8,
+ )
+ hmode.pack(side = 'top', padx = 5, pady = 5)
+ hmode.invoke('dynamic')
+
+ vmode = Pmw.OptionMenu(w.interior(),
+ labelpos = 'w',
+ label_text = 'Vertical:',
+ items = ['none', 'static', 'dynamic'],
+ command = self.setvscrollmode,
+ menubutton_width = 8,
+ )
+ vmode.pack(side = 'top', padx = 5, pady = 5)
+ vmode.invoke('dynamic')
+
+ buttonBox = Pmw.ButtonBox(parent)
+ buttonBox.pack(side = 'bottom')
+ buttonBox.add('yview', text = 'Show\nyview', command = self.showYView)
+ buttonBox.add('scroll', text = 'Page\ndown', command = self.pageDown)
+ buttonBox.add('center', text = 'Center', command = self.centerPage)
+
+ # Pack this last so that the buttons do not get shrunk when
+ # the window is resized.
+ self.box.pack(fill = 'both', expand = 1, padx = 5, pady = 5)
+
+ # Do this after packing the scrolled list box, so that the
+ # window does not resize as soon as it appears (because
+ # alignlabels has to do an update_idletasks).
+ Pmw.alignlabels((hmode, vmode))
+
+ # Add some more entries to the listbox.
+ items = ('Andamooka', 'Coober Pedy', 'Innamincka', 'Oodnadatta')
+ self.box.setlist(items)
+ self.box.insert(2, 'Wagga Wagga', 'Perth', 'London')
+ self.box.insert('end', 'Darwin', 'Auckland', 'New York')
+ index = list(self.box.get(0, 'end')).index('London')
+ self.box.delete(index)
+ self.box.delete(7, 8)
+ self.box.insert('end', 'Bulli', 'Alice Springs', 'Woy Woy')
+ self.box.insert('end', 'Wallumburrawang', 'Willandra Billabong')
+
+ def sethscrollmode(self, tag):
+ self.box.configure(hscrollmode = tag)
+
+ def setvscrollmode(self, tag):
+ self.box.configure(vscrollmode = tag)
+
+ def selectionCommand(self):
+ sels = self.box.getcurselection()
+ if len(sels) == 0:
+ print 'No selection'
+ else:
+ print 'Selection:', sels[0]
+
+ def defCmd(self):
+ sels = self.box.getcurselection()
+ if len(sels) == 0:
+ print 'No selection for double click'
+ else:
+ print 'Double click:', sels[0]
+
+ def showYView(self):
+ print self.box.yview()
+
+ def pageDown(self):
+ self.box.yview('scroll', 1, 'page')
+
+ def centerPage(self):
+ top, bottom = self.box.yview()
+ size = bottom - top
+ middle = 0.5 - size / 2
+ self.box.yview('moveto', middle)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 30 August 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.ScrolledText reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.ScrolledText</h1>
+
+<center><IMG SRC=ScrolledText.gif ALT="" WIDTH=409 HEIGHT=310></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.ScrolledText() -
+ text widget with optional scrollbars
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A scrolled text consists of a standard text widget with optional
+ scrollbars which can be used to scroll the text. The
+ scrollbars can be <em>dynamic</em>, which means that a scrollbar will
+ only be displayed if it is necessary. That is, if the text widget
+ does not contain enough text (either horizontally or vertically),
+ the scrollbar will be automatically hidden. If it is displayed,
+ the horizontal scrollbar is under the text widget. Similarly, if
+ it is displayed, the vertical scrollbar is to the right of the
+ text widget.</p>
+
+<p> Row and column headers may also be displayed, which scroll in sync
+ with the text widget and may be useful when displaying tabular
+ data. To assist in ensuring that columns line up when using a
+ column header, a fixed width font should be used.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+Initialisation option. If true, the <strong>borderframe</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.columnheader></a>
+<dl><dt> <strong>columnheader
+</strong></dt><dd>
+Initialisation option. If true, the <strong>columnheader</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.hscrollmode></a>
+<dl><dt> <strong>hscrollmode
+</strong></dt><dd>
+The horizontal scroll mode. If <strong>'none'</strong>, the horizontal scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.rowcolumnheader></a>
+<dl><dt> <strong>rowcolumnheader
+</strong></dt><dd>
+Initialisation option. If true, the <strong>rowcolumnheader</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.rowheader></a>
+<dl><dt> <strong>rowheader
+</strong></dt><dd>
+Initialisation option. If true, the <strong>rowheader</strong> component will be created. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.scrollmargin></a>
+<dl><dt> <strong>scrollmargin
+</strong></dt><dd>
+Initialisation option. The distance between the scrollbars and the text widget. The default is <strong>2</strong>.</p>
+
+
+</dd></dl>
+<a name=option.usehullsize></a>
+<dl><dt> <strong>usehullsize
+</strong></dt><dd>
+Initialisation option. If true, the size of the megawidget is determined solely by the
+ width and height options of the <strong>hull</strong> component.</p>
+<p> Otherwise, the size of the megawidget is determined by the width
+ and height of the <strong>text</strong> component, along with the size and/or
+ existence of the other components, such as the label, the
+ scrollbars and the scrollmargin option. All these affect the
+ overall size of the megawidget. The default is <strong>0</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.vscrollmode></a>
+<dl><dt> <strong>vscrollmode
+</strong></dt><dd>
+The vertical scroll mode. If <strong>'none'</strong>, the vertical scrollbar
+ will never be displayed. If <strong>'static'</strong>, the scrollbar will always
+ be displayed. If <strong>'dynamic'</strong>, the scrollbar will be displayed
+ only if necessary. The default is <strong>'dynamic'</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.borderframe></a>
+<dl><dt> <strong>borderframe
+</strong></dt><dd>
+A frame widget which snuggly fits around the text widget, to give
+ the appearance of a text border. It is created with a border so
+ that the text widget, which is created without a border, looks
+ like it has a border. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.columnheader></a>
+<dl><dt> <strong>columnheader
+</strong></dt><dd>
+A text widget with a default height of 1 displayed above the main
+ text widget and which scrolls horizontally in sync with the
+ horizontal scrolling of the main text widget. By default, this component is a Tkinter.Text. Its component group is <strong>Header</strong>.</p>
+
+
+</dd></dl>
+<a name=component.horizscrollbar></a>
+<dl><dt> <strong>horizscrollbar
+</strong></dt><dd>
+The horizontal scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.rowcolumnheader></a>
+<dl><dt> <strong>rowcolumnheader
+</strong></dt><dd>
+A text widget displayed to the top left of the main text widget,
+ above the row header and to the left of the column header if they
+ exist. The widget is not scrolled automatically. By default, this component is a Tkinter.Text. Its component group is <strong>Header</strong>.</p>
+
+
+</dd></dl>
+<a name=component.rowheader></a>
+<dl><dt> <strong>rowheader
+</strong></dt><dd>
+A text widget displayed to the left of the main text widget and
+ which scrolls vertically in sync with the vertical scrolling of
+ the main text widget. By default, this component is a Tkinter.Text. Its component group is <strong>Header</strong>.</p>
+
+
+</dd></dl>
+<a name=component.text></a>
+<dl><dt> <strong>text
+</strong></dt><dd>
+The text widget which is scrolled by the scrollbars. If the
+ <strong>borderframe</strong> option is true, this is created with a borderwidth
+ of <strong>0</strong> to overcome a known problem with text widgets: if a widget
+ inside a text widget extends across one of the edges of the text
+ widget, then the widget obscures the border of the text widget.
+ Therefore, if the text widget has no border, then this overlapping
+ does not occur. By default, this component is a Tkinter.Text.</p>
+
+
+</dd></dl>
+<a name=component.vertscrollbar></a>
+<dl><dt> <strong>vertscrollbar
+</strong></dt><dd>
+The vertical scrollbar. By default, this component is a Tkinter.Scrollbar. Its component group is <strong>Scrollbar</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+In addition, methods from the
+<strong>Tkinter.Text</strong> class
+are forwarded by this megawidget to the
+<strong>text</strong> component.
+<p></p>
+<a name=method.appendtext></a>
+<dl><dt> <strong>appendtext</strong>(<em>text</em>)</dt><dd>
+Add <em>text</em> to the end of the <strong>text</strong> component. Scroll to the
+ bottom of the text, but only if it was already visible before the
+ new text was added.</p>
+
+
+</dd></dl>
+<a name=method.bbox></a>
+<dl><dt> <strong>bbox</strong>(<em>index</em>)</dt><dd>
+This method is explicitly forwarded to the <strong>text</strong> component's
+ <code>bbox()</code> method. Without this explicit forwarding, the <code>bbox()</code>
+ method (aliased to <code>grid_bbox()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+<a name=method.clear></a>
+<dl><dt> <strong>clear</strong>()</dt><dd>
+Delete all text from the <strong>text</strong> component.</p>
+
+
+</dd></dl>
+<a name=method.exportfile></a>
+<dl><dt> <strong>exportfile</strong>(<em>fileName</em>)</dt><dd>
+Write the contents of the <strong>text</strong> component to the file <em>fileName</em>.</p>
+
+
+</dd></dl>
+<a name=method.get></a>
+<dl><dt> <strong>get</strong>(<em>first</em> = <strong>None</strong>, <em>last</em> = <strong>None</strong>)</dt><dd>
+This is the same as the <code>get()</code> method of the <strong>text</strong> component,
+ except that if <em>first</em> is <strong>None</strong> the entire
+ contents of the text widget are returned.</p>
+
+
+</dd></dl>
+<a name=method.getvalue></a>
+<dl><dt> <strong>getvalue</strong>()</dt><dd>
+Return the entire contents of the text widget.</p>
+
+
+</dd></dl>
+<a name=method.importfile></a>
+<dl><dt> <strong>importfile</strong>(<em>fileName</em>, <em>where</em> = <strong>'end'</strong>)</dt><dd>
+Read the contents of the file <em>fileName</em> and insert into the
+ <strong>text</strong> component at the position given by <em>where</em>.</p>
+
+
+</dd></dl>
+<a name=method.settext></a>
+<dl><dt> <strong>settext</strong>(<em>text</em>)</dt><dd>
+Same as <code>setvalue()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.setvalue></a>
+<dl><dt> <strong>setvalue</strong>(<em>text</em>)</dt><dd>
+Replace the entire contents of the <strong>text</strong> component with <em>text</em>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+
+ # Create the ScrolledText with headers.
+ fixedFont = Pmw.logicalfont('Fixed')
+ self.st = Pmw.ScrolledText(parent,
+ # borderframe = 1,
+ labelpos = 'n',
+ label_text='ScrolledText with headers',
+ columnheader = 1,
+ rowheader = 1,
+ rowcolumnheader = 1,
+ usehullsize = 1,
+ hull_width = 400,
+ hull_height = 300,
+ text_wrap='none',
+ text_font = fixedFont,
+ Header_font = fixedFont,
+ Header_foreground = 'blue',
+ rowheader_width = 3,
+ rowcolumnheader_width = 3,
+ text_padx = 4,
+ text_pady = 4,
+ Header_padx = 4,
+ rowheader_pady = 4,
+ )
+
+ self.st.pack(padx = 5, pady = 5, fill = 'both', expand = 1)
+
+ funcs = 'atan cos cosh exp log log10 sin sinh sqrt tan tanh'
+ funcs = string.split(funcs)
+
+ # Create the header for the row headers
+ self.st.component('rowcolumnheader').insert('end', 'x')
+
+ # Create the column headers
+ headerLine = ''
+ for column in range(len(funcs)):
+ headerLine = headerLine + ('%-7s ' % (funcs[column],))
+ headerLine = headerLine[:-3]
+ self.st.component('columnheader').insert('0.0', headerLine)
+
+ self.st.tag_configure('yellow', background = 'yellow')
+
+ # Create the data rows and the row headers
+ numRows = 50
+ tagList = []
+ for row in range(1, numRows):
+ dataLine = ''
+ x = row / 5.0
+ for column in range(len(funcs)):
+ value = eval('math.' + funcs[column] + '(' + str(x) + ')')
+ data = str(value)[:7]
+ if value < 0:
+ tag1 = '%d.%d' % (row, len(dataLine))
+ tag2 = '%d.%d' % (row, len(dataLine) + len(data))
+ tagList.append(tag1)
+ tagList.append(tag2)
+ data = '%-7s' % (data,)
+ dataLine = dataLine + data + ' '
+ dataLine = dataLine[:-3]
+ header = '%.1f' % (x,)
+ if row < numRows - 1:
+ dataLine = dataLine + '\n'
+ header = header + '\n'
+ self.st.insert('end', dataLine)
+ self.st.component('rowheader').insert('end', header)
+ apply(self.st.tag_add, ('yellow',) + tuple(tagList))
+
+ # Prevent users' modifying text and headers
+ self.st.configure(
+ text_state = 'disabled',
+ Header_state = 'disabled',
+ )
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 30 August 1998
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+# Based on iwidgets2.2.0/tests/scrolledtext.test code.
+
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.ScrolledText
+
+def _testYView(doBottom):
+ w = Test.currentWidget()
+ top, bottom = w.yview()
+ if type(top) != type(0.0) or type(bottom) != type(0.0):
+ return 'bad type ' + str(top) + ' ' + str(bottom)
+ if doBottom:
+ if bottom != 1.0:
+ return 'bottom is ' + str(bottom)
+ else:
+ if top != 0.0:
+ return 'top is ' + str(top)
+
+kw_1 = {'labelpos': 'n', 'label_text': 'ScrolledText'}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 10),
+ (c.importfile, 'ScrolledText_test.py'),
+ ('hull_background', 'aliceblue'),
+ ('text_borderwidth', 3),
+ ('Scrollbar_borderwidth', 3),
+ ('hull_cursor', 'gumby'),
+ ('text_exportselection', 0),
+ ('text_exportselection', 1),
+ ('text_foreground', 'Black'),
+ ('text_height', 10),
+ ('text_width', 20),
+ ('text_insertbackground', 'Black'),
+ ('text_insertborderwidth', 1),
+ ('text_insertofftime', 200),
+ ('text_insertontime', 500),
+ ('text_insertwidth', 3),
+ ('label_text', 'Label'),
+ ('text_relief', 'raised'),
+ ('text_relief', 'sunken'),
+ ('Scrollbar_repeatdelay', 200),
+ ('Scrollbar_repeatinterval', 105),
+ ('vscrollmode', 'none'),
+ ('vscrollmode', 'static'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'none'),
+ ('hscrollmode', 'static'),
+ ('hscrollmode', 'dynamic'),
+ ('Scrollbar_width', 20),
+ ('text_selectborderwidth', 2),
+ ('text_state', 'disabled'),
+ ('text_state', 'normal'),
+ ('text_background', 'GhostWhite'),
+ ('text_wrap', 'char'),
+ ('text_wrap', 'none'),
+ ('vscrollmode', 'bogus', 'ValueError: bad vscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ ('hscrollmode', 'bogus', 'ValueError: bad hscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ (c.cget, 'vscrollmode', 'bogus'),
+ (c.cget, 'hscrollmode', 'bogus'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'dynamic'),
+ (c.insert, ('end', 'Hello there\n')),
+ (_testYView, 0),
+ (c.yview, ('moveto', 0.02)),
+ (c.yview, ('moveto', 0.04)),
+ (c.yview, ('moveto', 0.06)),
+ (c.yview, ('moveto', 0.08)),
+ (c.yview, ('moveto', 0.10)),
+ (c.yview, ('moveto', 0.12)),
+ (c.yview, ('moveto', 0.14)),
+ (c.yview, ('moveto', 0.16)),
+ (c.yview, ('moveto', 0.18)),
+ (c.yview, ('moveto', 0.20)),
+ (c.yview, ('moveto', 0.22)),
+ (c.yview, ('moveto', 0.24)),
+ (c.yview, ('moveto', 0.26)),
+ (c.yview, ('moveto', 0.28)),
+ (c.yview, ('moveto', 0.98)),
+ (_testYView, 1),
+ (c.yview, ('scroll', -1, 'page')),
+ (c.yview, ('scroll', -50, 'page')),
+ (_testYView, 0),
+ (c.yview, ('scroll', 1, 'page')),
+ (c.yview, ('scroll', 50, 'page')),
+ (_testYView, 1),
+ (c.clear, ()),
+ (c.get, (), '\n'),
+)
+
+kw_2 = {
+ 'hscrollmode' : 'dynamic',
+ 'label_text' : 'Label',
+ 'labelpos' : 'n',
+ 'scrollmargin': 20,
+}
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (c.importfile, 'ScrolledText_test.py'),
+ ('text_relief', 'raised'),
+ ('text_relief', 'sunken'),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((Pmw.ScrolledText, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.SelectionDialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.SelectionDialog</h1>
+
+<center><IMG SRC=SelectionDialog.gif ALT="" WIDTH=206 HEIGHT=278></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.SelectionDialog() -
+ selection dialog displaying a scrolled list
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="Dialog.html">Pmw.Dialog</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ The selection dialog is a dialog window which displays a scrolled
+ list which can be used to prompt the user for a value.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderx></a>
+<dl><dt> <strong>borderx
+</strong></dt><dd>
+Initialisation option. The padding to the left and right of the scrolled list. The default is <strong>10</strong>.</p>
+
+
+</dd></dl>
+<a name=option.bordery></a>
+<dl><dt> <strong>bordery
+</strong></dt><dd>
+Initialisation option. The padding above and below the scrolled list. The default is <strong>10</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('OK',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.scrolledlist></a>
+<dl><dt> <strong>scrolledlist
+</strong></dt><dd>
+The scrolled list for the user to enter a value. By default, this component is a <a href="ScrolledListBox.html">Pmw.ScrolledListBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>label
+</strong></dt><dd>
+Alias for <strong>scrolledlist_label</strong>.
+</dd></dl>
+<dl><dt> <strong>listbox
+</strong></dt><dd>
+Alias for <strong>scrolledlist_listbox</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="Dialog.html#methods">Pmw.Dialog</a></strong>.
+In addition, methods from the
+<strong><a href="ScrolledListBox.html#methods">Pmw.ScrolledListBox</a></strong> class
+are forwarded by this megawidget to the
+<strong>scrolledlist</strong> component.
+<p></p>
+<a name=method.bbox></a>
+<dl><dt> <strong>bbox</strong>(<em>index</em>)</dt><dd>
+This method is explicitly forwarded to the <strong>listbox</strong> component's
+ <code>bbox()</code> method. Without this explicit forwarding, the <code>bbox()</code>
+ method (aliased to <code>grid_bbox()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+<a name=method.size></a>
+<dl><dt> <strong>size</strong>()</dt><dd>
+This method is explicitly forwarded to the <strong>listbox</strong> component's
+ <code>size()</code> method. Without this explicit forwarding, the <code>size()</code>
+ method (aliased to <code>grid_size()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog.
+ self.dialog = Pmw.SelectionDialog(parent,
+ title = 'My SelectionDialog',
+ buttons = ('OK', 'Cancel'),
+ defaultbutton = 'OK',
+ scrolledlist_labelpos = 'n',
+ label_text = 'What do you think of Pmw?',
+ scrolledlist_items = ('Cool man', 'Cool', 'Good', 'Bad', 'Gross'),
+ command = self.execute)
+ self.dialog.withdraw()
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show selection dialog',
+ command = self.dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+ def execute(self, result):
+ sels = self.dialog.getcurselection()
+ if len(sels) == 0:
+ print 'You clicked on', result, '(no selection)'
+ else:
+ print 'You clicked on', result, sels[0]
+ self.dialog.deactivate(result)
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.TextDialog reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.TextDialog</h1>
+
+<center><IMG SRC=TextDialog.gif ALT="" WIDTH=362 HEIGHT=287></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.TextDialog() -
+ a dialog displaying a scrolled text
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="Dialog.html">Pmw.Dialog</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A text dialog is a dialog window which displays a text message to
+ the user along with one or more buttons to press.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.activatecommand></a>
+<dl><dt> <strong>activatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ activated by a call to <code>activate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.borderx></a>
+<dl><dt> <strong>borderx
+</strong></dt><dd>
+Initialisation option. The padding to the left and right of the scrolled text. The default is <strong>10</strong>.</p>
+
+
+</dd></dl>
+<a name=option.bordery></a>
+<dl><dt> <strong>bordery
+</strong></dt><dd>
+Initialisation option. The padding above and below the scrolled text. The default is <strong>10</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonboxpos></a>
+<dl><dt> <strong>buttonboxpos
+</strong></dt><dd>
+Initialisation option. Specifies on which side of the dialog window to place the button
+ box. Must be one of <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> or <strong>'w'</strong>. The default is <strong>'s'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttons></a>
+<dl><dt> <strong>buttons
+</strong></dt><dd>
+This must be a tuple or a list and specifies the names on the
+ buttons in the button box. The default is <strong>('OK',)</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+Specifies a function to call whenever a button in the button box
+ is invoked or the window is deleted by the window manager. The
+ function is called with a single argument, which is the name of
+ the button which was invoked, or <strong>None</strong> if the window was deleted
+ by the window manager.</p>
+<p> If the value of <strong>command</strong> is not callable, the default behaviour
+ is to deactivate the window if it is active, or withdraw the
+ window if it is not active. If it is deactivated, <code>deactivate()</code>
+ is called with the button name or <strong>None</strong> as described above. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.deactivatecommand></a>
+<dl><dt> <strong>deactivatecommand
+</strong></dt><dd>
+If this is callable, it will be called whenever the megawidget is
+ deactivated by a call to <code>deactivate()</code>. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.defaultbutton></a>
+<dl><dt> <strong>defaultbutton
+</strong></dt><dd>
+Specifies the default button in the button box. If the <strong><Return></strong>
+ key is hit when the dialog has focus, the default button will be
+ invoked. If <strong>defaultbutton</strong> is <strong>None</strong>, there will be no default
+ button and hitting the <strong><Return></strong> key will have no effect. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.master></a>
+<dl><dt> <strong>master
+</strong></dt><dd>
+This is used by the <code>activate()</code> method to control whether the
+ window is made <em>transient</em> during modal dialogs. See the
+ <code>activate()</code> method. The default is <strong>'parent'</strong>.</p>
+
+
+</dd></dl>
+<a name=option.separatorwidth></a>
+<dl><dt> <strong>separatorwidth
+</strong></dt><dd>
+Initialisation option. If this is greater than <strong>0</strong>, a separator line with the specified
+ width will be created between the button box and the child site,
+ as a component named <strong>separator</strong>. Since the default border of the
+ button box and child site is <strong>raised</strong>, this option does not
+ usually need to be set for there to be a visual separation between
+ the button box and child site. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.title></a>
+<dl><dt> <strong>title
+</strong></dt><dd>
+This is the title that the window manager displays in the title
+ bar of the window. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.buttonbox></a>
+<dl><dt> <strong>buttonbox
+</strong></dt><dd>
+This is the button box containing the buttons for the dialog. By
+ default it is created with the options
+ <code>(hull_borderwidth = 1, hull_relief = 'raised')</code>. By default, this component is a <a href="ButtonBox.html">Pmw.ButtonBox</a>.</p>
+
+
+</dd></dl>
+<a name=component.dialogchildsite></a>
+<dl><dt> <strong>dialogchildsite
+</strong></dt><dd>
+This is the child site for the dialog, which may be used to
+ specialise the megawidget by creating other widgets within it. By
+ default it is created with the options
+ <code>(borderwidth = 1, relief = 'raised')</code>. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Toplevel.</p>
+
+
+</dd></dl>
+<a name=component.scrolledtext></a>
+<dl><dt> <strong>scrolledtext
+</strong></dt><dd>
+The scrolled text to contain the text for the dialog. By default, this component is a <a href="ScrolledText.html">Pmw.ScrolledText</a>.</p>
+
+
+</dd></dl>
+<a name=component.separator></a>
+<dl><dt> <strong>separator
+</strong></dt><dd>
+If the <strong>separatorwidth</strong> initialisation option is non-zero, the
+ <strong>separator</strong> component is the line dividing the area between the
+ button box and the child site. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>label
+</strong></dt><dd>
+Alias for <strong>scrolledtext_label</strong>.
+</dd></dl>
+<dl><dt> <strong>text
+</strong></dt><dd>
+Alias for <strong>scrolledtext_text</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="Dialog.html#methods">Pmw.Dialog</a></strong>.
+In addition, methods from the
+<strong><a href="ScrolledText.html#methods">Pmw.ScrolledText</a></strong> class
+are forwarded by this megawidget to the
+<strong>scrolledtext</strong> component.
+<p></p>
+<a name=method.bbox></a>
+<dl><dt> <strong>bbox</strong>(<em>index</em>)</dt><dd>
+This method is explicitly forwarded to the <strong>text</strong> component's
+ <code>bbox()</code> method. Without this explicit forwarding, the <code>bbox()</code>
+ method (aliased to <code>grid_bbox()</code>) of the <strong>hull</strong> would be invoked,
+ which is probably not what the programmer intended.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ # Create the dialog.
+ dialog = Pmw.TextDialog(parent, scrolledtext_labelpos = 'n',
+ title = 'My TextDialog',
+ defaultbutton = 0,
+ label_text = 'Lawyer jokes')
+ dialog.withdraw()
+ dialog.insert('end', jokes)
+ dialog.configure(text_state = 'disabled')
+
+ # Create button to launch the dialog.
+ w = Tkinter.Button(parent, text = 'Show text dialog',
+ command = dialog.activate)
+ w.pack(padx = 8, pady = 8)
+
+jokes = """
+Q: What do you call 5000 dead lawyers at the bottom of the ocean?
+A: A good start!
+
+Q: How can you tell when a lawyer is lying?
+A: His lips are moving.
+
+Q: Why won't sharks attack lawyers?
+A: Professional courtesy.
+
+Q: What do have when a lawyer is buried up to his neck in sand?
+A: Not enough sand.
+
+Q: How do you get a lawyer out of a tree?
+A: Cut the rope.
+
+Q: What is the definition of a shame (as in "that's a shame")?
+A: When a bus load of lawyers goes off a cliff.
+
+Q: What is the definition of a "crying shame"?
+A: There was an empty seat.
+
+Q: What do you get when you cross the Godfather with a lawyer?
+A: An offer you can't understand.
+
+Q. What do lawyers use as contraceptives?
+A. Their personalities.
+
+Q. What's brown and black and looks good on a lawyer?
+A. A doberman.
+
+Q. Why are lawyers buried 12 feet underground?
+A. Deep down their good.
+
+Q. What's the difference between a catfish and a lawyer?
+A. One's a slimy scum-sucking scavenger, the other is just a fish.
+
+"""
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 18 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw.TimeCounter reference manual</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw.TimeCounter</h1>
+
+<center><IMG SRC=TimeCounter.gif ALT="" WIDTH=170 HEIGHT=85></center>
+<dl>
+<dt> <h3>Name</h3></dt><dd>
+<p>Pmw.TimeCounter() -
+ counter for display and input of time
+</p>
+
+
+</dd>
+<dt> <h3>Inherits</h3></dt><dd>
+<a href="MegaWidget.html">Pmw.MegaWidget</a><br>
+</dd>
+<dt> <h3>Description</h3></dt><dd>
+<p>
+ A time counter is similar to a regular <a href="Counter.html">Pmw.Counter</a> except that the
+ user may increment and decrement the hours, minutes and seconds
+ individually.</p>
+
+<p></p>
+
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+Options for this megawidget and its base
+classes are described below.<p></p>
+<a name=option.autorepeat></a>
+<dl><dt> <strong>autorepeat
+</strong></dt><dd>
+If true, the counter will continue to count up or down while an
+ arrow button is held pressed down. The default is <strong>1</strong>.</p>
+
+
+</dd></dl>
+<a name=option.buttonaspect></a>
+<dl><dt> <strong>buttonaspect
+</strong></dt><dd>
+Initialisation option. Specifies the width of the arrow buttons as a proportion of their
+ height. Values less than <strong>1.0</strong> will produce thin arrow buttons.
+ Values greater than <strong>1.0</strong> will produce fat arrow buttons. The default is <strong>1.0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.command></a>
+<dl><dt> <strong>command
+</strong></dt><dd>
+This specifies a function to call whenever the <strong><Return></strong> key is
+ pressed in one of the entry fields or <code>invoke()</code> is called. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.initwait></a>
+<dl><dt> <strong>initwait
+</strong></dt><dd>
+Specifies the initial delay (in milliseconds) before a depressed
+ arrow button automatically starts to repeat counting. The default is <strong>300</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelmargin></a>
+<dl><dt> <strong>labelmargin
+</strong></dt><dd>
+Initialisation option. If the <strong>labelpos</strong> option is not <strong>None</strong>, this specifies the
+ distance between the <strong>label</strong> component and the rest of the
+ megawidget. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.labelpos></a>
+<dl><dt> <strong>labelpos
+</strong></dt><dd>
+Initialisation option. Specifies where to place the <strong>label</strong> component. If not
+ <strong>None</strong>, it should be a concatenation of one or two of the
+ letters <strong>'n'</strong>, <strong>'s'</strong>, <strong>'e'</strong> and <strong>'w'</strong>. The first letter
+ specifies on which side of the megawidget to place the label.
+ If a second letter is specified, it indicates where on that
+ side to place the label. For example, if <strong>labelpos</strong> is <strong>'w'</strong>,
+ the label is placed in the center of the left hand side; if
+ it is <strong>'wn'</strong>, the label is placed at the top of the left
+ hand side; if it is <strong>'ws'</strong>, the label is placed at the
+ bottom of the left hand side.</p>
+<p> If <strong>None</strong>, a label component is not created. The default is <strong>None</strong>.</p>
+
+
+
+</dd></dl>
+<a name=option.max></a>
+<dl><dt> <strong>max
+</strong></dt><dd>
+Specifies the maximum acceptable time in the form "HH:MM:SS", or
+ <strong>None</strong> if no maximum checking should be performed. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.min></a>
+<dl><dt> <strong>min
+</strong></dt><dd>
+Specifies the minimum acceptable time in the form "HH:MM:SS", or
+ <strong>None</strong> if no minimum checking should be performed. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+<a name=option.padx></a>
+<dl><dt> <strong>padx
+</strong></dt><dd>
+Initialisation option. Specifies how much wider to make each column than the default
+ width (where a column consists of two arrows and an entry field).
+ The entry fields expand to fill the extra space, but the arrow
+ buttons are centered in the available space. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.pady></a>
+<dl><dt> <strong>pady
+</strong></dt><dd>
+Initialisation option. Specifies how much higher to make each row of arrow buttons than
+ the default hight. The arrow buttons are centered in the
+ available space. The default is <strong>0</strong>.</p>
+
+
+</dd></dl>
+<a name=option.repeatrate></a>
+<dl><dt> <strong>repeatrate
+</strong></dt><dd>
+Specifies the delay (in milliseconds) between automatic counts
+ while an arrow button is held pressed down. The default is <strong>50</strong>.</p>
+
+
+</dd></dl>
+<a name=option.value></a>
+<dl><dt> <strong>value
+</strong></dt><dd>
+Initialisation option. Specifies the initial contents of the time counter, in the form
+ "HH:MM:SS". If this is <strong>None</strong>, the current time is used as the
+ initial contents. The default is <strong>None</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+Components created by this megawidget and its base
+classes are described below.<p></p>
+<a name=component.downhourarrow></a>
+<dl><dt> <strong>downhourarrow
+</strong></dt><dd>
+The arrow button used for decrementing the hour field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+<a name=component.downminutearrow></a>
+<dl><dt> <strong>downminutearrow
+</strong></dt><dd>
+The arrow button used for decrementing the minute field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+<a name=component.downsecondarrow></a>
+<dl><dt> <strong>downsecondarrow
+</strong></dt><dd>
+The arrow button used for decrementing the second field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+<a name=component.frame></a>
+<dl><dt> <strong>frame
+</strong></dt><dd>
+If the <strong>label</strong> component has been created (that is, the <strong>labelpos</strong>
+ option is not <strong>None</strong>), the <strong>frame</strong> component is created to act as
+ the container of the entry fields and arrow buttons. If there is
+ no <strong>label</strong> component, then no <strong>frame</strong> component is created and the
+ <strong>hull</strong> component acts as the container. In either case the border
+ around the container of the entry fields and arrow buttons will be
+ raised (but not around the label). By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.hourentryfield></a>
+<dl><dt> <strong>hourentryfield
+</strong></dt><dd>
+The entry field where the hours are entered and displayed. By default, this component is a <a href="EntryField.html">Pmw.EntryField</a>.</p>
+
+
+</dd></dl>
+<a name=component.hull></a>
+<dl><dt> <strong>hull
+</strong></dt><dd>
+This acts as the body for the entire megawidget. Other components
+ are created as children of the hull to further specialise this
+ class. By default, this component is a Tkinter.Frame.</p>
+
+
+</dd></dl>
+<a name=component.label></a>
+<dl><dt> <strong>label
+</strong></dt><dd>
+If the <strong>labelpos</strong> option is not <strong>None</strong>, this component is
+ created as a text label for the megawidget. See the
+ <strong>labelpos</strong> option for details. Note that to set, for example,
+ the <strong>text</strong> option of the label, you need to use the <strong>label_text</strong>
+ component option. By default, this component is a Tkinter.Label.</p>
+
+
+</dd></dl>
+<a name=component.minuteentryfield></a>
+<dl><dt> <strong>minuteentryfield
+</strong></dt><dd>
+The entry field where the minutes are entered and displayed. By default, this component is a <a href="EntryField.html">Pmw.EntryField</a>.</p>
+
+
+</dd></dl>
+<a name=component.secondentryfield></a>
+<dl><dt> <strong>secondentryfield
+</strong></dt><dd>
+The entry field where the seconds are entered and displayed. By default, this component is a <a href="EntryField.html">Pmw.EntryField</a>.</p>
+
+
+</dd></dl>
+<a name=component.uphourarrow></a>
+<dl><dt> <strong>uphourarrow
+</strong></dt><dd>
+The arrow button used for incrementing the hour field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+<a name=component.upminutearrow></a>
+<dl><dt> <strong>upminutearrow
+</strong></dt><dd>
+The arrow button used for incrementing the minute field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+<a name=component.upsecondarrow></a>
+<dl><dt> <strong>upsecondarrow
+</strong></dt><dd>
+The arrow button used for incrementing the second field. By default, this component is a Tkinter.Canvas. Its component group is <strong>Arrow</strong>.</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Component aliases</h3></dt><dd>
+Sub-components of components of this megawidget
+may be accessed via the following aliases.<p></p>
+<dl><dt> <strong>hourentry
+</strong></dt><dd>
+Alias for <strong>hourentryfield_entry</strong>.
+</dd></dl>
+<dl><dt> <strong>minuteentry
+</strong></dt><dd>
+Alias for <strong>minuteentryfield_entry</strong>.
+</dd></dl>
+<dl><dt> <strong>secondentry
+</strong></dt><dd>
+Alias for <strong>secondentryfield_entry</strong>.
+</dd></dl>
+</dd>
+<a name=methods></a>
+<dt> <h3>Methods</h3></dt><dd>
+Only methods specific to this megawidget are described below.
+For a description of its inherited methods, see the
+manual for its base class
+<strong><a href="MegaWidget.html#methods">Pmw.MegaWidget</a></strong>.
+<p></p>
+<a name=method.decrement></a>
+<dl><dt> <strong>decrement</strong>(<em>seconds</em> = <strong>1</strong>)</dt><dd>
+Decrement the time by <em>seconds</em> seconds.</p>
+
+
+</dd></dl>
+<a name=method.getint></a>
+<dl><dt> <strong>getint</strong>()</dt><dd>
+Return the currently displayed time as a number of seconds.</p>
+
+
+</dd></dl>
+<a name=method.getstring></a>
+<dl><dt> <strong>getstring</strong>()</dt><dd>
+Same as <code>getvalue()</code> method.</p>
+
+
+</dd></dl>
+<a name=method.getvalue></a>
+<dl><dt> <strong>getvalue</strong>()</dt><dd>
+Return the currently displayed time as a string in the form
+ "HH:MM:SS".</p>
+
+
+</dd></dl>
+<a name=method.increment></a>
+<dl><dt> <strong>increment</strong>(<em>seconds</em> = <strong>1</strong>)</dt><dd>
+Increment the time by <em>seconds</em> seconds.</p>
+
+
+</dd></dl>
+<a name=method.invoke></a>
+<dl><dt> <strong>invoke</strong>()</dt><dd>
+Invoke the command specified by the <strong>command</strong> option as if the
+ <strong><Return></strong> key had been pressed.</p>
+
+
+</dd></dl>
+<a name=method.setvalue></a>
+<dl><dt> <strong>setvalue</strong>(<em>text</em>)</dt><dd>
+Set the contents of the time counter, where <em>text</em> must be in the
+ form "HH:MM:SS".</p>
+
+
+</dd></dl>
+</dd>
+<dt> <h3>Example</h3></dt><dd>
+The image at the top of this manual is a snapshot
+of the window (or part of the window) produced
+by the following code.<p></p>
+<pre>
+class Demo:
+ def __init__(self, parent):
+ self._time = Pmw.TimeCounter(parent,
+ labelpos = 'w',
+ label_text = 'HH:MM:SS',
+ min = '00:00:00',
+ max = '23:59:59')
+ self._time.pack(padx=10, pady=5)
+
+ button = Tkinter.Button(parent, text = 'Show', command = self.show)
+ button.pack()
+
+ def show(self):
+ stringVal = self._time.getstring()
+ intVal = self._time.getint()
+ print stringVal + ' (' + str(intVal) + ')'
+
+</pre>
+</dd>
+</dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+ <br>Manual page last reviewed: 25 May 2002
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>List of known bugs</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">List of known bugs</h1>
+
+<p>
+This is a list of some of the known bugs in Pmw. If you fix any of
+these, please let the maintainer (<em>gregm@iname.com</em>) know.</p>
+<ul><li><p>Under the Enlightenment window manager, if show() is called when
+ a window is already displayed (and is not obscured by other
+ windows), then the application will hang for two seconds. This
+ is either a bug in Tcl/Tk or in Enlightenment. See the comment
+ in the Tk function WaitForConfigureNotify() in the Tk source
+ file tk8.3.2/unix/tkUnixWm.c:</p>
+<dl><dd><pre> /*
+ * One more tricky detail about this procedure. In some cases the
+ * window manager will decide to ignore a configure request (e.g.
+ * because it thinks the window is already in the right place).
+ * To avoid hanging in this situation, only wait for a few seconds,
+ * then give up.
+ */</pre></dd></dl>
+
+
+</li>
+<li><p>On NT, Pmw.MenuBar does not display message bar help for menu
+ items. It seems that Tk menu widgets do not support <Motion>
+ events on MS. This probably is an issue that should be taken up
+ with the Tcl/Tk people. (Reported by Stefan Schone. Pmw.0.7)</p>
+
+</li>
+<li><p>Run the CounterDialog.py demo, select the show dialog button and
+ press ok. Now exit the dialog (either with the exit button or
+ the close box). The following error appears:</p>
+<dl><dd><pre> Menu ID 256 is already in use!Fatal Python Error: Tcl/Tk panic</pre></dd></dl>
+
+<p> This may be a problem with Mac version of Tk. (Reported by
+ Anthony Wilson.)</p>
+
+
+</li>
+<li><p>Pmw.Balloons bind to widgets and canvas items. This means that
+ bindings made by other users are deleted when the balloon makes
+ its bindings. (For example, the "Delete" canvas item in the
+ Balloon demo overrides that <ButtonPress> binding and so that
+ balloon is not withdrawn when the mouse button is pressed over
+ the item.)</p>
+<p> The obvious solution is for Pmw.Balloon to add its bindings with
+ a <em>+</em>. But this would make the unbind and tagunbind methods
+ inconsistent - they would remove all bindings, not just the ones
+ added by the balloon. A better way would be for the balloon to
+ add a bindtag to each widget`s bindtag list - then it would not
+ upset any other bindings and it could be deleted cleanly.
+ (Reported by Joe Saltiel)</p>
+
+
+</li></ul>
+
+<dl><dd><pre> import Tkinter
+ import Pmw
+
+ def foo(event):
+ print '<Enter> event on text'
+
+ root = Pmw.initialise()
+ balloon = Pmw.Balloon()
+
+ canvas = Tkinter.Canvas()
+ canvas.pack()
+
+ text1 = canvas.create_text(50, 50, text = 'hello
+there')
+
+ # As is, the balloon does not appear over the text, but foo
+ # is called. Swap the following two lines and the balloon
+ # appears but foo will not be called.
+ canvas.tag_bind(text1, "<Enter>", foo)
+ balloon.tagbind(canvas, text1, 'text 1 help')
+
+ root.mainloop()</pre></dd></dl>
+<ul><li><p>In Pmw.Balloon, the balloon should not be withdrawn when the
+ pointer leaves a widget or item and it immediatly enters another
+ widget or item with balloon help. Instead, the balloon should
+ be moved and its contents changed immediately.</p>
+
+</li>
+<li><p>When a Pmw.Balloon is bound to a canvas item, moving the item
+ becomes very slow. (Reported by Joe Saltiel)</p>
+<dl><dd><pre> > Second, after I fixed my ordering problem I noticed, there
+ > is a pretty big delay in updating widgets that have balloon
+ > messages bound to them. (For example dragging a box across
+ > a screen, the box has a delayed reaction.) I believe this is
+ > due to some of the timing functions used in PmwBalloon, I am
+ > not sure if there is a way around it. I set all timers to
+ > zero, and still had the problem.</pre></dd></dl>
+
+
+</li>
+<li><p>When running Pmw demos under ptui the busy cursor does not
+ appear.</p>
+
+</li>
+<li><p>If a combobox has a horizontal scrollbar and it displays its
+ listbox above the entry, then it is misplaced.</p>
+
+</li>
+<li><p>Bug in Pmw.PanedWidget: repeat by creating new panes in Demo -
+ existing panes jump to the right 1 or 2 pixels.</p>
+
+</li>
+<li><p>Bug in Pmw.PanedWidget: repeat by setting hull_borderwidth to
+ 20 in demo - initial drag jumps to right by about 20 pixels.
+ Also right hand side border is missing. (Fix may be similar to
+ method used in Pmw.ScrolledFrame to give canvas border.)</p>
+
+</li>
+<li><p>Fix ButtonRelease events so they do not trigger without a
+ corresponding ButtonPress event.</p>
+<p> From Joe Saltiel: I was playing around with a scrolledlistbox
+ and tkFileDialog. When I have the dialog open above the list
+ box and I doubleclick on it, I invoke the selectioncmd of the
+ listbox as well as the tkFileDialog box, should this be
+ happening?</p>
+
+<p> Attached is small sample program you can try. To get the bug to
+ show you must do two things. First, when you open the file
+ dialog box, make sure the item you are going to select if
+ over(above) the scrolledlistbox. Second, you have to double
+ click on that item. If you single click and hit "Open" you do
+ not get the bug. Nor do you get it unless the file you click on
+ is directly over the clickable region of the scrolledlist box.</p>
+<dl><dd><pre> import Tkinter
+ import Pmw
+ import tkFileDialog
+ import string
+
+ def askOpen():
+ file = tkFileDialog.askopenfile(filetypes=[("all files", "*")])
+ print file
+
+ def printMe():
+ print "Me"
+
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+
+ frame1 = Tkinter.Frame(root)
+ lst = string.split("abc def ghi jkl mno pqr stu vwx yz")
+ lstbox = Pmw.ScrolledListBox(frame1, items=lst, selectioncommand=printMe)
+ lstbox.grid(row=0, column=0, columnspan=2)
+ Tkinter.Button(frame1, text='open', command=askOpen).grid(row=1, column=0)
+ Tkinter.Button(frame1, text='exit', command=root.destroy).grid(row=1, column=1)
+ frame1.pack()
+
+ root.mainloop()</pre></dd></dl>
+
+
+<p> Response: I have found where the problem is but I am not sure
+ how to fix it. It appears that the tkFileDialog box closes on a
+ ButtonPress event. The corresponding ButtonRelease event is
+ then sent to whichever widget is under the cursor at the time of
+ the Release. I have reproduced the problem with a Tcl-only
+ script:</p>
+<dl><dd><pre> listbox .l
+ .l insert 0 1 2 3 4
+ bind .l <ButtonRelease-1> {puts AAAGGHHH!}
+
+ button .b -text open -command tk_getOpenFile
+ pack .l .b</pre></dd></dl>
+
+
+<p> If you do a quick Press-Release-Press over the file dialog, it
+ is withdrawn. If you then keep the mouse button down and move
+ the mouse around, you will see that the button and the listbox
+ still respond to it. If you do the final button Release over
+ the listbox, its <ButtonRelease-1> binding is invoked.</p>
+
+<p> I think the correct solution is to modify Pmw to be very careful
+ when to accept ButtonRelease events. It will need to also bind
+ to ButtonPress events and make sure that it gets a Press before
+ it accepts the Release. I'll try to do the change as soon as
+ possible, but the code involved is fairly complex so I it may
+ take a little time.</p>
+
+
+</li>
+<li><p>Investigate bug in Tk8.0: When a dialog pops up over the
+ pointer then the keyboard focus is not set and so <Return> does
+ not invoke default button.</p>
+
+</li>
+<li><p>Under both X and NT, the arrows in the timecounter, counter and
+ combobox do not match the scrollbar arrows.</p>
+
+</li>
+<li><p>Pmw.Group does not work correctly when the tag is a compound
+ widget. The tag is placed such that the top of the tag is cut
+ off. (Reported by Peter Stoehr.)</p>
+<dl><dd><pre> import Tkinter
+ import Pmw
+
+ root = Tkinter.Tk()
+ Pmw.initialise(root, fontScheme = 'pmw1')
+ exitButton = Tkinter.Button(root, text = 'Exit', command = root.destroy)
+ exitButton.pack(side = 'bottom')
+
+ def makeGroup(tagClassName):
+ tagClass = eval(tagClassName)
+ group = Pmw.Group(
+ tag_pyclass = tagClass,
+ hull_background = 'red',
+ groupchildsite_background = 'blue',
+ )
+ group.pack(fill = 'both', expand = 1, padx = 6, pady = 6)
+ child = Tkinter.Label(group.interior(),
+ text = 'Group with tag ' + tagClassName,
+ background = 'aliceblue',
+ )
+ child.pack(padx = 10, pady = 5, expand = 1, fill = 'both')
+
+ return group
+
+ grp1 = makeGroup('Pmw.EntryField')
+ grp2 = makeGroup('Pmw.ComboBox')
+ grp3 = makeGroup('Tkinter.Entry')
+
+ root.mainloop()</pre></dd></dl>
+
+<p> Also, Pmw.Group does not resize correctly if the simple widget
+ changes size. For example:</p>
+<dl><dd><pre> grp3.configure(tag_font = ('Helveltica', '-160'))</pre></dd></dl>
+
+
+
+</li>
+<li><p>Bug(s) in PmwScrolledCanvas. There is a bug in 0.8.1
+ PmwScrolledCanvas._setRegion. If there are no objects in the
+ canvas, then error occurs on len(region) because region is None.
+ Below is an attempt to fix it. Click on Show, then on Delete.
+ The window then continuously resizes. If the ScrolledCanvas is
+ created with canvasmargin = 0, the problem goes away. (Reported
+ by Anders Henja.)</p>
+<dl><dd><pre> import Tkinter
+ import Pmw
+
+ def _setRegion(self):
+ # Attempt to fix PmwScrolledCanvas._setRegion.
+ self.setregionTimer = None
+
+ region = self._canvas.bbox('all')
+ canvasmargin = self['canvasmargin']
+ if region is None:
+ region = (0, 0, 0, 0)
+ region = (region[0] - canvasmargin, region[1] - canvasmargin,
+ region[2] + canvasmargin, region[3] + canvasmargin)
+ self._canvas.configure(scrollregion = region)
+
+ def show():
+ canvas.component('canvas').delete('all')
+ canvas.create_oval(0, 0, 800, 600, fill = 'red')
+ canvas.configure(canvas_width = 600, canvas_height = 450)
+ canvas.resizescrollregion()
+
+ def delete():
+ canvas.component('canvas').delete('all')
+ canvas.configure(canvas_width = 0, canvas_height = 0)
+ canvas.resizescrollregion()
+
+ root=Tkinter.Tk()
+ Pmw.initialise(root)
+
+ buttonbox=Pmw.ButtonBox()
+ buttonbox.pack(fill='x',side='bottom',padx=5,pady=5)
+ buttonbox.add('Show',command=show)
+ buttonbox.add('Delete',command=delete)
+ buttonbox.alignbuttons()
+
+ canvas=Pmw.ScrolledCanvas(canvasmargin=2)
+ canvas.__class__._setRegion = _setRegion
+ canvas.pack(fill='both',side='right',expand=1)
+
+ root.mainloop()</pre></dd></dl>
+
+
+</li>
+<li><p>Bug in Pmw.Dialog: if <strong>defaultbutton</strong> is configured before
+ <strong>buttons</strong> during <strong>self.initialiseoptions()</strong> (that is if
+ <strong>self._constructorKeywords.keys()</strong> returns a different order),
+ then <strong>setdefault()</strong> fails.</p>
+
+</li>
+<li><p>Bugs in Tk which affect Pmw.MainMenuBar:</p>
+<ul><li><p>Extra bindings assigned to a Tkinter.Menu widget using
+ bindtags have no effect. Hence the method used in
+ Pmw.MenuBar for status help (bind_class followed by
+ bindtags) does not work and therefore binding to the menu
+ widget is used instead.</p>
+
+</li>
+<li><p>The <strong>'active'</strong> tag for the <code>index()</code> method of Tkinter.Menu
+ always returns <strong>None</strong>. Hence, in the menu widget motion
+ binding, <code>event.y</code> and the <strong>'@'</strong> format is used instead, for
+ all menus except the toplevel main menu.</p>
+
+</li>
+<li><p>For the toplevel main menu, <code>event.x</code> must be used for the
+ <code>index()</code> method, but it returns the wrong index. It
+ appears that the Tk widget is assuming vertical layout
+ to calculate distances, rather than horizontal.</p>
+
+</li>
+<li><p>For toplevel main menus, several Tk commands, such as
+ <code>winfo_height()</code>, do not work. This prevents the use of
+ balloon help for Pmw.MainMenuBar.</p>
+
+</li></ul>
+
+</li>
+<li><p>Bug in Pmw.ComboBox: Tab to combobox arrow, use up/down arrow
+ keys to change selection, hit return, nothing happens, <Shift
+ Tab> to entry window, hit return, combobox changes</p>
+<ul><li><p>actually, it would be better if you could not tab to
+ the arrow, only the entry field, like the Pmw.Counter.</p>
+
+</li>
+<li><p>the problem is if the entry field is not editable, what to
+ do then?</p>
+
+</li></ul>
+
+</li>
+<li><p>Bug in TimeCounter: Arrow keys don't work when focus is on entry.</p>
+
+</li>
+<li><p>Bug in Pmw.NoteBook: The size of the tab does not change when
+ the text value changes</p>
+
+</li>
+<li><p>Bug in Pmw.NoteBook: The name of the tab components has a "-" sign
+ in it, which means that component options can not be used in the
+ configure command. Eg:</p>
+<dl><dd><pre> n = Pmw.NoteBook()
+ p = n.add('page1')
+ n.configure(page1_background = 'red') # works
+ n.configure(page1-tab_background = 'red') # fail, must do this:
+ n.component('page1-tab').configure(background = 'red') # works</pre></dd></dl>
+
+
+</li></ul>
+<p></p>
+
+
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Changes to Pmw</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Changes to Pmw</h1>
+
+<p>
+ 6 January 1997</p>
+
+<ul><li><p>Release of version 0.1</p>
+
+</li></ul>
+<p> 14 February 1997</p>
+
+<ul><li><p>Fixed bug in Counter demo for the Macintosh - the maximum size of an
+ integer is smaller than the value returned by time.time().</p>
+
+</li>
+<li><p>Fixed bug in Grid demo for Tk 4.2 - grid_bbox returns garbage if it is
+ called without update_idletasks. Also, grid_bbox can only have two
+ arguments in Tk 4.1.</p>
+
+</li>
+<li><p>Modified ScrolledText demo so that the text widget contains enough text
+ to require a vertical scrollbar.</p>
+
+</li>
+<li><p>Changes to PmwBase:</p>
+<ul><li><p>Prefixed the name of several private variables with a double underscore.</p>
+
+</li>
+<li><p>Added symbolic constants for the indexes into an optionInfo list.</p>
+
+</li>
+<li><p>Changed names of several methods and variables to be more descriptive.</p>
+
+</li>
+<li><p>Removed options() method.</p>
+
+</li>
+<li><p>Simplified configuration option data structures. Modified option
+ handling code so that default options are set correctly. If an
+ option is created before initialise() is called then initialise()
+ checks if the option is set by the keyword arguments to
+ initialise(). If not, then it is given the value found in the
+ Tk option database, if a value exists, or the default value. If an
+ option is created after initialise() is called, then it is given the
+ value found in the Tk option database, if a value exists, or the
+ default value.</p>
+
+</li></ul>
+
+</li>
+<li><p>Replaced usage of self._hull in megawidgets by interior() method.</p>
+
+</li>
+<li><p>Added autoclear option to ComboBox.</p>
+
+</li>
+<li><p>Fixed bug in ComboBox - fast clicking on the arrow button could result
+ in an attempt to grab a window that was not yet visible.</p>
+
+</li>
+<li><p>Added "sys.exc_traceback = None" to the except clauses of all try
+ statements so that references to objects in the stack trace would not
+ be left.</p>
+
+</li>
+<li><p>Added takefocus option to PushButton.</p>
+
+</li>
+<li><p>Modified the getcurselection() method of ScrolledListBox so that it
+ returns a string if the selection mode is <strong>'single'</strong> or <strong>'browse'</strong>, rather
+ than a tuple with one element. This also affects methods forwarded and
+ derived from ScrolledListBox.</p>
+
+</li>
+<li><p>Modified ScrolledListBox so that it avoids unnecessary updates by
+ using idle timer.</p>
+
+</li>
+<li><p>Modified ScrolledText to use grid instead of pack.</p>
+
+</li>
+<li><p>Added shutdown() function to Tk module to clean up all references to
+ the Tcl interpreter and then delete it.</p>
+
+</li>
+<li><p>Fixed bug in Tk module for the Macintosh - update() was being called in
+ initialise() before the Tcl interpreter was created.</p>
+
+</li></ul>
+<p> 14 February 1997</p>
+
+<ul><li><p>Version 0.1.1 completed and released internally.</p>
+
+</li></ul>
+<p> 6 March 1997</p>
+
+<ul><li><p>Pmw now uses the standard Tkinter module. The Tk module has been
+ dropped. This means that the Tk module functions such as after,
+ bell, bind, update, etc, are no longer available and the equivalent
+ Tkinter methods should be used.</p>
+
+</li>
+<li><p>To restore some of the features of the Tk module, Pmw.initialise()
+ now adds run-time hooks into Tkinter to get notification of when Tk
+ widgets are created and destroyed. It also modifies the CallWrapper
+ class so that errors during callbacks and bindings can be displayed
+ in a window. If Pmw.initialise() is not called, Tkinter is not
+ modified and these features are not available.</p>
+
+</li>
+<li><p>If a Tk widget which is acting as the hull of a megawidget is
+ destroyed, then the megawidget is destroyed as well. This can
+ only happen if Pmw.initialise() is called.</p>
+
+</li>
+<li><p>Pmw.initialise() now takes the Tkinter root as its argument.</p>
+
+</li>
+<li><p>The parent of megawidgets now defaults to the Tk root. Previously,
+ the parent of non-toplevel megawidgets had to be given.</p>
+
+</li>
+<li><p>Added PmwBase.tracetk() function to get trace of calls to the Tcl
+ interpreter for debugging.</p>
+
+</li>
+<li><p>Added functions to PmwBase to display a busy cursor over the
+ application such as when a modal dialog is displayed or it is
+ blocked doing a long calculation. Uses busy command of the blt
+ extension, if present.</p>
+
+</li>
+<li><p>Created a nifty new demo which demonstrates most of the megawidgets
+ in a convenient way.</p>
+
+</li>
+<li><p>Added a TextDialog.</p>
+
+</li>
+<li><p>Added functionality to handle the grabbing of nested modal dialogs
+ correctly.</p>
+
+</li>
+<li><p>Added an activatecommand option to Dialog which allows, for example,
+ the PromptDialog widget to set the keyboard focus when it is
+ activated.</p>
+
+</li>
+<li><p>Added tests for Counter and logicalfont.</p>
+
+</li>
+<li><p>The ScrolledListBox selectioncommand is no longer given the widget
+ as its first argument.</p>
+
+</li>
+<li><p>Several method, function and component names were changed, to be
+ consistent with the coding conventions.</p>
+
+</li>
+<li><p>Some of the effects of moving from the Tk module to Tkinter are:</p>
+<ul><li><p>The Tk module used to exit if there were no non-root toplevel
+ windows shown. This is no longer the case and so the application
+ must handle this explicitly, particularly if the root window is
+ withdrawn and the last non-root toplevel is deleted by the window
+ manager.</p>
+
+</li>
+<li><p>The Tk module bind functions and methods used to take a noEvent
+ argument to indicate that the Tk event should not be passed to the
+ callback. Tkinter does not support this.</p>
+
+</li>
+<li><p>The Tk module initialise() function should be replaced by
+ "root = Tkinter.Tk()" and root should be used instead of "Tk.Root()"</p>
+
+</li>
+<li><p>The Tk module quit() function should be replace by "root.destroy()".</p>
+
+</li>
+<li><p>Toplevels are not hidden when created. To be consistent,
+ MegaToplevels are not hidden either.</p>
+
+</li>
+<li><p>The hide and show methods are not available for Tkinter Toplevels,
+ only MegaToplevels</p>
+
+</li>
+<li><p>There is no grid_configure method.</p>
+
+</li>
+<li><p>Tkinter.Canvas.coords() returns a python list, not a tuple.</p>
+
+</li>
+<li><p>The Tkinter cget and configure widget methods always return
+ strings for the option values. The Tk module used to convert the
+ string to the appropriate python type (such as string, integer,
+ float, Variable, Image, callback function).</p>
+
+</li>
+<li><p>Tkinter Menu and Toplevel classes incorrectly have a pack method.</p>
+
+</li>
+<li><p>Menu class has no geometry method.</p>
+
+</li>
+<li><p>Canvas focus returns <strong>''</strong> rather than None.</p>
+
+</li>
+<li><p>Text mark_gravity returns <strong>''</strong> rather than None.</p>
+
+</li></ul>
+
+</li></ul>
+<p> 13 March 1997</p>
+
+<ul><li><p>Release of version 0.2</p>
+
+</li></ul>
+<p> 17 March 1997</p>
+
+<ul><li><p>Set default WM_DELETE_WINDOW protocol of Tkinter.Toplevel to
+ destroy() and removed duplicated protocol request from all demos.</p>
+
+</li>
+<li><p>Modified text of ShowBusy demo to indicate that busy cursor will
+ only be seen if the BLT extension is present.</p>
+
+</li>
+<li><p>Replaced call to update() in PmwLabeledWidget.py with update_idletasks().</p>
+
+</li>
+<li><p>Changed name of PromptDialog component from <strong>'entry'</strong> to <strong>'entryfield'</strong>.</p>
+
+</li></ul>
+<p> 28 April 1997</p>
+
+<ul><li><p>Version 0.3 released internally</p>
+
+</li></ul>
+<p> 19 August 1997</p>
+
+<ul><li><p>Many changes made (see the version 0.4 porting guide for
+ more details).</p>
+
+</li>
+<li><p>The option propagation mechanism that iwidgets uses is too
+ cumbersome, too hard to understand and, in python, too slow.
+ Developed a new mechanism which is more explicit in naming
+ options. This resulted in most options which were simply
+ propagated to components being removed. Removed keep(), rename()
+ and ignore() methods and "usual" options.</p>
+
+</li>
+<li><p>For speed, Pmw no longer queries the Tk option database for
+ default values for megawidget options. Hence, resource names and
+ classes do not need to be supplied when creating options and
+ <strong>None</strong> is returned for the resource name and class when using
+ <code>configure()</code> to query the options. Option "types" no longer
+ used.</p>
+
+</li>
+<li><p>Changed method and component names to be more consistent.</p>
+
+</li>
+<li><p>Replaced most uses of pack() with grid().</p>
+
+</li>
+<li><p>Megawidgets no longer inherit from LabeledWidget. Instead they
+ call createlabel() to optionally create the label component.</p>
+
+</li>
+<li><p>Removed child site from EntryField and rewrote ComboBox
+ accordingly.</p>
+
+</li>
+<li><p>Wrote lots more documentation, including automatically generated
+ reference manuals.</p>
+
+</li>
+<li><p>Removed PushButton and rewrote ButtonBox to directly create
+ Tkinter.Buttons rather than PushButtons.</p>
+
+</li>
+<li><p>Added initialisation options - options which can be set at
+ creation time but not later using configure().</p>
+
+</li>
+<li><p>Added aliases for components.</p>
+
+</li>
+<li><p>Modified the base classes so that during option configuration,
+ components are configured <em>before</em> configuration called functions
+ are called.</p>
+
+</li>
+<li><p>Added several more megawidgets.</p>
+
+</li>
+<li><p>Added interface to BLT graph and vector commands.</p>
+
+</li>
+<li><p>Created PmwLazy module for lazy importing of Pmw - avoids loading
+ megawidgets which are not used.</p>
+
+</li>
+<li><p>Added several more functions for handling color and fonts.</p>
+
+</li>
+<li><p>Replaced Counter and EntryField <em>time</em> with <em>timeN</em> and <em>time24</em></p>
+
+</li>
+<li><p>Pmw.initialise() will now create Tkinter.Tk if not given root.</p>
+
+</li></ul>
+<p> 1 September 1997</p>
+
+<ul><li><p>Release of version 0.4</p>
+
+</li></ul>
+<p> 5 September 1997</p>
+
+<ul><li><p>Modified the base classes so that the Tk option database resource
+ class of megawidgets can be overridden in the call to the
+ constructor using the <strong>hull_class</strong> option.</p>
+
+</li>
+<li><p>The separators in Pmw.PanedWidget are now active - they can be
+ grabbed, like the handles, and moved around. The cursor now
+ changes to the correct left/right or up/down cursor when over a
+ separator or handle. (Clemens Hintze)</p>
+
+</li>
+<li><p>Fixed bug in MessageInfo demo Dismiss button. If it is invoked,
+ an error occurs saying "not enough arguments". (Mark Colclough)</p>
+
+</li></ul>
+<p> 9 September 1997</p>
+
+<ul><li><p>Added the <strong>useTkOptionDb</strong> argument to Pmw.initialise which
+ specifies that the initial values of megawidget options are to be
+ set by querying the Tk option database.</p>
+
+</li>
+<li><p>When used to query options, the configure() method now returns the
+ resource class and name of the options.</p>
+
+</li></ul>
+<p> 19 September 1997</p>
+
+<ul><li><p>Changed functions datestringtoint() and timestringtoint() to
+ datestringtojdn() and timestringtoseconds(). Changed return value
+ of datestringtojdn() to be Julian Day Numbers rather than seconds
+ since the epoch.</p>
+
+</li>
+<li><p>Fixed a bug in the date Counter due to use of time.timezone, by
+ replacing, when calculating date increments, calls to the time
+ module with calls to datestringtojdn().</p>
+
+</li>
+<li><p>Added century pivot year (setyearpivot function) to Counter date
+ datatypes to handle two-digit years.</p>
+
+</li>
+<li><p>Added date_dmy4, date_mdy4 and date_y4md datatypes to Counter.</p>
+
+</li>
+<li><p>Modified demos All.py and ScrolledText.py so that demos can be called
+ from directories other than the demos directory. (Case Roole and
+ Guido van Rossum)</p>
+
+</li>
+<li><p>Changed the default for the Pmw.Balloon <em>label_justify</em> option to
+ <em>left</em> to improve appearance of multi-line balloons. Pmw.Balloon
+ now replaces newlines with spaces in the statusHelp string so that
+ the strings look better when displayed in a Pmw.MessageBar.
+ (Andreas Kostyrka)</p>
+
+</li>
+<li><p>Pmw.Blt now calls <em>package require BLT</em> when checking for the
+ existence of Blt, so that it can be loaded if it is not statically
+ linked. (Clemens Hintze, Matthias Klose)</p>
+
+</li>
+<li><p>Copied earthris.gif and flagup.bmp files from Tcl distribution to
+ test directory, just in case they have not been installed.
+ (Jonathan Kelly)</p>
+
+</li>
+<li><p>Lots of improvements to the documentation and documenting recent
+ changes.</p>
+
+</li></ul>
+<p> 16 October 1997</p>
+
+<ul><li><p>Modified Pmw.Balloon and Pmw.ComboBox to work around a bug in the
+ Windows95 version of Tk which caused the popup windows to appear
+ in the wrong place. (Fredrik Lundh and Jerome Gay)</p>
+
+</li>
+<li><p>Added Pmw.maxfontwidth() function. (Rob Pearson)</p>
+
+</li></ul>
+<p> 24 October 1997</p>
+
+<ul><li><p>Changed PmwBase._reporterror to handle the class exceptions of
+ python 1.5. (Case Roole)</p>
+
+</li></ul>
+<p> 29 October 1997</p>
+
+<ul><li><p>Fixed a bug in forwardmethods() function which occurred if the
+ <em>toClass</em> class had a method called <strong>type</strong>.</p>
+
+</li></ul>
+<p> 7 November 1997</p>
+
+<ul><li><p>Changed tests/Test._getErrorValue to handle the class exceptions of
+ python 1.5. (Michael McLay)</p>
+
+</li>
+<li><p>Changed bug fix in forwardmethods() function to use the
+ <code>exec execString in d</code> construct. (Guido van Rossum)</p>
+
+</li>
+<li><p>Can now use Pmw.MegaArchetype as a base class just to get option
+ handling; it will not create the hull component unless requested.
+ Moved __str__() and interior() methods from Pmw.MegaToplevel and
+ Pmw.MegaWidget to Pmw.MegaArchetype class.</p>
+
+</li></ul>
+<p> 10 November 1997</p>
+
+<ul><li><p>Added <em>textclass</em> option to Pmw.ScrolledText and <em>listboxclass</em>
+ option for Pmw.ScrolledListBox to allow embedding of custom
+ widgets.</p>
+
+</li>
+<li><p>Added Mitch Chapman's <strong>FontText</strong> module to the <code>demos</code> directory
+ and used it to display the demo source code in color.</p>
+
+</li>
+<li><p>Added two notebook megawwidgets, Pmw.NoteBookR and Pmw.NoteBookS.
+ (Case Roole and Joe Saltiel)</p>
+
+</li>
+<li><p>Added Pmw.ScrolledCanvas megawidget. (Joe Saltiel)</p>
+
+</li>
+<li><p>Added Pmw.TreeBrowse megawidget. (Michael McLay)</p>
+
+</li>
+<li><p>Added Pmw.Group megawidget and modified to use <code>grid()</code> instead
+ of <code>pack()</code>. (Case Roole)</p>
+
+</li>
+<li><p>Release of version 0.5</p>
+
+</li></ul>
+<p> 12 November 1997</p>
+
+<ul><li><p>Added <em>pyclass</em> option to components and removed <em>textclass</em>
+ option from Pmw.ScrolledText and <em>listboxclass</em> option from
+ Pmw.ScrolledListBox. (Suggested by Shen Wang)</p>
+
+</li>
+<li><p>Added label component to Pmw.ButtonBox megawidget.</p>
+
+</li>
+<li><p>Fixed mis-spelling of PmwTreeBrowse in Pmw.py.</p>
+
+</li>
+<li><p>Release of version 0.5.1</p>
+
+</li></ul>
+<p> 5 December 1997</p>
+
+<ul><li><p>The pyclass option can now be None. If so, createcomponent
+ returns None.</p>
+
+</li>
+<li><p>Removed tagtype option from Pmw.Group. Can now use the more
+ general tag_pyclass instead.</p>
+
+</li>
+<li><p>Added tcl call to <code>load {} Blt</code> when testing for presence of Blt.</p>
+
+</li>
+<li><p>Added julian and papal options to Pmw.ymdtojulian and
+ Pmw.juliantoymd functions and made sure divisions give the same
+ result as C even when operands are negative.</p>
+
+</li>
+<li><p>Exported ymdtojulian and juliantoymd functions.</p>
+
+</li>
+<li><p>Fixed bug in activate method. Did not prepend TclError with Tkinter.</p>
+
+</li>
+<li><p>When the Blt busy hold command is called from showbusycursor, the
+ bindtags on the busy window are set so that no events cause
+ callbacks to occur for the toplevel or all bindings. Also, while
+ a busy window is up, the focus is changed to the busy window so
+ that no keyboard events are accepted. This fixes a bug where the
+ Tkinter._nametowidget function could crash with a <code>KeyError: _Busy</code>
+ if there was a binding on a toplevel window and the mouse
+ was pressed while the busy cursor was up.</p>
+
+</li></ul>
+<p> 9 December 1997</p>
+
+<ul><li><p>Fixed bug in Pmw.datestringtojdn() when dealing with century year,
+ such as 2000.</p>
+
+</li></ul>
+<p> 10 December 1997</p>
+
+<ul><li><p>Added <em>where</em> option to <code>Pmw.ScrolledText.importfile()</code>. (Graham
+ Matthews)</p>
+
+</li></ul>
+<p> 16 December 1997</p>
+
+<ul><li><p>Modified Pmw.RadioSelect and Pmw.ButtonBox so that you can no
+ longer index their buttons using regular expressions. This
+ feature seemed to have little use and caused problems with buttons
+ labeled for example <em>a*</em> and <em>b*</em>. (Problem reported by Rob
+ Hooft)</p>
+
+</li>
+<li><p>Added updateFunction option to Pmw.busycallback(). If set, the
+ function will be called just after the command given to
+ Pmw.busycallback(). If the function is set the Tkinter update()
+ method, then this will clear any events that may have occurred
+ while the command was executing.</p>
+
+</li></ul>
+<p> 30 December 1997</p>
+
+<ul><li><p>Changed ymdtojulian and juliantoymd functions to jdntoymd and
+ ymdtojdn, because the meaning of "julian" is ambiguous, whereas
+ the meaning of "Julian Day Number" is not (maybe).</p>
+
+</li>
+<li><p>Converted Pmw to use python 1.5 package mechanism. (Michael McLay
+ and Case Roole)</p>
+
+</li>
+<li><p>Removed Pmw.py and PmwLazy files. Added __init__.py, PmwLoader.py
+ and Pmw.def files. (Case Roole)</p>
+
+</li>
+<li><p>Applications can now specify at runtime which version of Pmw to
+ use and also which alpha versions, if any. (Case Roole)</p>
+
+</li>
+<li><p>Modified Pmw code for the version of Tkinter released with python
+ 1.5.</p>
+
+</li>
+<li><p>Release of version 0.6</p>
+
+</li></ul>
+<p> 5 January 1998</p>
+
+<ul><li><p>Fixed alpha version handling so that alpha versions do not have to
+ supply PmwBase.py and PmwUtils.py. (Case Roole)</p>
+
+</li>
+<li><p>Added example alpha directory and documentation. (Case Roole)</p>
+
+</li></ul>
+<p> 7 January 1998</p>
+
+<ul><li><p>Added selectmode option to Pmw.RadioSelect megawidget. (Roman
+ Sulzhyk)</p>
+
+</li>
+<li><p>Added some changes to Pmw.ScrolledCanvas to get around some bugs.
+ (Joe Saltiel)</p>
+
+</li>
+<li><p>Release of version 0.6.1</p>
+
+</li></ul>
+<p> 8 January 1998</p>
+
+<ul><li><p>Added some more changes to Pmw.ScrolledCanvas. (from Joe Saltiel)</p>
+
+</li></ul>
+<p> 12 January 1998</p>
+
+<ul><li><p>Added Pmw.OptionMenu megawidget. (Roman Sulzhyk)</p>
+
+</li></ul>
+<p> 20 February 1998</p>
+
+<ul><li><p>Added new Pmw.MenuBar features to delete menus and menuitems,
+ enable and disable menu bar and to add cascade menus. (Rob Pearson)</p>
+
+</li>
+<li><p>Added extra arguments to Pmw.Color.spectrum for more control over
+ color choice.</p>
+
+</li></ul>
+<p> 23 February 1998</p>
+
+<ul><li><p>Added canvasbind() method to Pmw.Balloon.</p>
+
+</li>
+<li><p>Fixed demos/All.py so that it will correctly determine which Pmw
+ version to use even if it is in a directory symlinked to the demos
+ directory.</p>
+
+</li>
+<li><p>Removed "import DemoVersion" from all demos, except All.py, so
+ that they will work unchanged when copied outside of the Pmw
+ distribution.</p>
+
+</li>
+<li><p>Release of version 0.6.2</p>
+
+</li></ul>
+<p> 26 February 1998</p>
+
+<ul><li><p>Fixed PmwLoader so that it works on Macintoshes. (Jack Jansen)</p>
+
+</li></ul>
+<p> 2 March 1998</p>
+
+<ul><li><p>Fixed PmwBase and PmwBlt so that an attempt is made to dynamically
+ load Blt before it is used. Previously only attempted to load Blt
+ when calling showbusycursor.</p>
+
+</li></ul>
+<p> 16 March 1998</p>
+
+<ul><li><p>Added hulldestroyed() method.</p>
+
+</li>
+<li><p>Modified displayerror() function to use value given to
+ reporterrorstofile() if it is set.</p>
+
+</li>
+<li><p>Fixed bug in Pmw.EntryField which occurred when the <em>command</em>
+ option destroyed the megawidget.</p>
+
+</li>
+<li><p>Pmw.EntryField invoke method now passes on the value returned by
+ the <em>command</em> function.</p>
+
+</li></ul>
+<p> 3 April 1998</p>
+
+<ul><li><p>Added Pmw.ScrolledFrame megawidget. (Joe Saltiel)</p>
+
+</li>
+<li><p>Color.rgb2hsi() now uses the built-in <code>min()</code> and <code>max()</code> functions.</p>
+
+</li></ul>
+<p> 20 April 1998</p>
+
+<ul><li><p>Moved time and date functions from PmwCounter.py to new file,
+ PmwTimeFuncs.py.</p>
+
+</li>
+<li><p>Added optional <em>separator</em> argument to <code>timestringtoseconds</code> and
+ <code>datestringtojdn</code> functions. These functions are now stricter
+ when checking if a string is a valid date or time. For example,
+ it now checks for correct day in month, month in year, etc. These
+ changes also affect the Pmw.Counter date and time validators.</p>
+
+</li>
+<li><p>The <code>datestringtojdn</code> function now accepts all combinations of
+ <strong>'d'</strong>, <strong>'m'</strong>, <strong>'y'</strong> as format string.</p>
+
+</li>
+<li><p>Moved functions to bottom of file and class to top of file in
+ PmwEntryField.py and PmwCounter.py.</p>
+
+</li>
+<li><p>The validation for Pmw.EntryField <em>integer</em>, <em>hexadecimal</em> and
+ <em>real</em> types now use string.atol or string.atof rather than
+ regular expressions.</p>
+
+</li>
+<li><p>The validation for the Pmw.EntryField <em>real</em> type accepts a
+ <em>separator</em> argument, for those who prefer a comma instead of a
+ full stop/period/point as the decimal dividing symbol.</p>
+
+</li>
+<li><p>The Pmw.EntryField <em>time*</em> and <em>date_*</em> validators have been
+ removed. The functionality can be replaced by using the new
+ <em>time</em> and <em>date</em> validators with <em>min</em> and <em>max</em> fields.</p>
+
+</li>
+<li><p>The Pmw.EntryField <em>maxwidth</em> option has been removed. The
+ functionality can be replaced by using the <em>max</em> field of the
+ validator.</p>
+
+</li>
+<li><p>Added an <em>extravalidators</em> option to Pmw.EntryField. This allows
+ new types of validation to be added, particularly in classes
+ derived from Pmw.EntryField. It also allows the use of different
+ names for the same validation, by using aliases. Added
+ SpecialEntry demo to show <em>extravalidators</em> option, based on work
+ by Joachim Schmitz.</p>
+
+</li>
+<li><p>Fixed a bug in Pmw.EntryField when combining use of <em>value</em> and
+ <em>entry_textvariable</em> options.</p>
+
+</li>
+<li><p>The Pmw.EntryField <em>validate</em> option now also accepts a dictionary
+ to handle minimum and maximum validation and to allow the passing
+ of other arguments to the validating functions, such as date, time
+ and number formats and separators.</p>
+
+</li>
+<li><p>Fixed bug in Pmw.EntryField where the entry would scroll to the
+ start of the text if an invalid character was typed.</p>
+
+</li>
+<li><p>Added checkentry() method to Pmw.EntryField, so that it can be
+ updated if the entry widget is tied to a textvariable.</p>
+
+</li></ul>
+<p> 10 May 1998</p>
+
+<ul><li><p>The activate() method now takes a geometry option to allow more
+ flexible positioning of the modal dialog.</p>
+
+</li>
+<li><p>Fixed rarely occurring bug in deactivate() method if it is called
+ (perhaps from a timer) during the call to wait_visibility() in the
+ activate() method. This bug used to generate an error and the
+ application would not exit properly.</p>
+
+</li>
+<li><p>Fixed another rarely occurring bug in deactivate() method if it is
+ called while another application has the grab.</p>
+
+</li>
+<li><p>Removed "sys.exc_traceback = None" for except clauses which used
+ to be required by python 1.4 so that references to objects in the
+ stack trace would not be left.</p>
+
+</li>
+<li><p>Now uses sys.exc_info() function when displaying exception
+ traceback.</p>
+
+</li>
+<li><p>The <strong>state</strong> option of Pmw.Balloon and the <strong>orient</strong> option of
+ several others now generate an exception if they have a bad value.</p>
+
+</li>
+<li><p>Added a deactivatecommand option to Pmw.MegaToplevel which can be
+ used, for example, to cancel timers.</p>
+
+</li>
+<li><p>Made changes to Pmw.Counter so that the entry display continuously
+ changes when arrow key presses are repeated quickly.</p>
+
+</li>
+<li><p>Made changes to Pmw.Counter so that the insertion cursor is maintained
+ while counting and the entry scrolls to the end if the value is long.</p>
+
+</li>
+<li><p>Pmw.Counter now behaves correctly when counting past the maximum
+ and minimum values of the EntryField.</p>
+
+</li></ul>
+<p> 28 May 1998</p>
+
+<ul><li><p>Made all Pmw.EntryField standard validators publicly available
+ as <code>Pmw.numericvalidator</code>, etc.</p>
+
+</li>
+<li><p>Now uses faster <code>string.replace()</code> instead of <code>regsub.gsub()</code> when
+ applicable.</p>
+
+</li>
+<li><p>If the <em>balloonHelp</em> argument of the Pmw.Balloon bind methods is
+ <strong>None</strong>, no balloon is displayed.</p>
+
+</li>
+<li><p>Merged the code from the PmwUtils module (forwardmethods()) into
+ PmwBase, since it was always used, was used nowhere else, and made
+ freezing a little more complicated.</p>
+
+</li>
+<li><p>Added a short delay between calling Tkinter bell() method (sounds nicer).</p>
+
+</li>
+<li><p>The functions <code>datestringtojdn()</code> and <code>timestringtoseconds()</code> now
+ return ValueError on invalid input.</p>
+
+</li>
+<li><p>Created bundlepmw.py, to help when freezing in Pmw. Placed in bin
+ directory.</p>
+
+</li></ul>
+<p> 29 May 1998</p>
+
+<ul><li><p>Fixed rare bug in Pmw.Counter which occured if the counter was
+ unmapped while the mouse button was held down over an arrow button.</p>
+
+</li>
+<li><p>Created contrib directory and placed PmwVerticalGuage.py in it.
+ (Chris Wright)</p>
+
+</li>
+<li><p>Patched PmwNoteBookR.py. (Siggy Brentrup)</p>
+
+</li>
+<li><p>Added addoptions() method to Pmw.MegaArchetype class. (Dieter Maurer)</p>
+
+</li>
+<li><p>By default, MenuBar creates hotkeys for menus and menu items for
+ keyboard traversal. Added traversSpec argument to MenuBar add
+ methods. (Michael McLay)</p>
+
+</li></ul>
+<p> 31 May 1998</p>
+
+<ul><li><p>Cleaned up bbox() methods in Pmw.ScrolledCanvas and
+ Pmw.ScrolledListBox.</p>
+
+</li>
+<li><p>The createcomponent() method now disallows the creation of
+ component names containing an underscore, since the query
+ functions would not be able to find them.</p>
+
+</li></ul>
+<p> 2 June 1998</p>
+
+<ul><li><p>Release of version 0.7</p>
+
+</li></ul>
+<p> 3 June 1998</p>
+
+<ul><li><p>Moved Pmw.TreeBrowse megawidget to contrib directory.</p>
+
+</li></ul>
+<p> 17 June 1998</p>
+
+<ul><li><p>Added PmwFullTimeCounter.py to contrib directory (Daniel Michelson)</p>
+
+</li></ul>
+<p> 1 July 1998</p>
+
+<ul><li><p>Changed mispelt file PmwVerticalGuage.py to PmwVerticalGauge.py
+ in contrib directory.</p>
+
+</li></ul>
+<p> 7 July 1998</p>
+
+<ul><li><p>Fixed bug in Pmw.Counter real datatype. Sometimes incorrectly
+ counted negative decimal fractions. (Reported by David Ascher)</p>
+
+</li></ul>
+<p> 12 July 1998</p>
+
+<ul><li><p>The <em>format</em> argument of Pmw.datestringtojdn() now defaults to
+ <strong>'ymd'</strong>.</p>
+
+</li>
+<li><p>Removed Tkinter_test.py from tests since it does not test any Pmw
+ functionality (only Tkinter) and it fails under MS-Windows 95.</p>
+
+</li></ul>
+<p> 23 August 1998</p>
+
+<ul><li><p>Changed several exception types to be more consistent.</p>
+
+</li>
+<li><p>Made the interface to Pmw.Blt.Vector more like the builtin python
+ list type.</p>
+
+</li>
+<li><p>It is no longer an error to call Pmw.setversion() or
+ Pmw.setalphaversions() after initialisation, as long as the
+ requested version matches the actual version.</p>
+
+</li>
+<li><p>Fixed Pmw.NoteBookR so that it behaves better when the
+ highlightthickness is changed.</p>
+
+</li>
+<li><p>The setyearpivot() function now returns a tuple containing the old
+ values of <em>pivot</em> and <em>century</em>.</p>
+
+</li>
+<li><p>Added PmwFileDialog.py to contrib directory (Rob Hooft)</p>
+
+</li>
+<li><p>Modified demos so that full tracebacks are displayed if an error
+ occurs when importing a module.</p>
+
+</li>
+<li><p>Removed justify() method from Pmw.ScrolledListBox, since it is
+ just a wrapper around the xview and yview methods of the listbox.
+ Also, it was not a permanent justification, as the name implied.</p>
+
+</li></ul>
+<p> 20 September 1998</p>
+
+<ul><li><p>Changed implementation of Pmw.ScrolledCanvas.</p>
+
+</li>
+<li><p>Added <strong>borderframe</strong> option to Pmw.ScrolledText and Pmw.ScrolledCanvas.</p>
+
+</li></ul>
+<p> 18 October 1998</p>
+
+<ul><li><p>Major overhaul of all scrolled widgets. Modified all to use
+ similar structure, given the peculiarities of each. Fixed several
+ subtle bugs.</p>
+
+</li>
+<li><p>Pmw.ScrolledFrame: now uses a frame positioned within a clipping
+ frame using the place geometry manager. Added borderframe,
+ horizflex, horizfraction, usehullsize, vertflex, vertfraction
+ options. Added reposition() method. Removed getFrame() method;
+ use interior() method instead.</p>
+
+</li>
+<li><p>Pmw.ScrolledListBox: added usehullsize option.</p>
+
+</li>
+<li><p>Pmw.ScrolledText: added borderframe and usehullsize options.</p>
+
+</li>
+<li><p>Pmw.ScrolledCanvas: simplified widget structure. Added
+ borderframe, canvasmargin, scrollmargin and usehullsize options.
+ Added label.</p>
+
+</li>
+<li><p>Modified Pmw.OptionMenu to use standard widgets rather than call
+ tcl procedure. Added <strong>initialitem</strong> option. Now handles
+ <strong>menubutton_textvariable</strong> component option correctly.</p>
+
+</li></ul>
+<p> 1 November 1998</p>
+
+<ul><li><p>Documented more Pmw functions and Pmw.ComboBox.</p>
+
+</li></ul>
+<p> 15 November 1998</p>
+
+<ul><li><p>Fixed some bugs, cleaned up code and wrote documentation for
+ Pmw.Group. Removed <strong>ringpadx</strong> and <strong>ringpady</strong> options, since this
+ functionality is more generally available by padding the
+ megawidget itself and by padding the children of the megawidget.
+ Modified Pmw.aligngrouptags so that it takes into account the
+ borderwidth and highlightthickness of the ring and so that it
+ works when there is no tag widget. Added <strong>tagindent</strong> option.</p>
+
+</li></ul>
+<p> 18 November 1998</p>
+
+<ul><li><p>Renamed canvasbind() and canvasunbind() methods of Pmw.Balloon to
+ tagbind() and tagunbind() and modified so that they work with both
+ Tkinter.Canvas items and Tkinter.Text tagged items.</p>
+
+</li></ul>
+<p> 19 November 1998</p>
+
+<ul><li><p>Added havebltbusy() method to Pmw.Blt. (Robin Becker)</p>
+
+</li></ul>
+<p> 21 November 1998</p>
+
+<ul><li><p>Modified contrib/PmwFileDialog.py so that when a file is selected
+ with the mouse, the highlight (in the file list) persists and the
+ file list does not scroll to the top. (Rob Hooft)</p>
+
+</li>
+<li><p>Modified Pmw.Balloon so that it can be bound to a tag associated
+ with several Canvas or Text items. (Magnus Kessler)</p>
+
+</li></ul>
+<p> 21 November 1998</p>
+
+<ul><li><p>Cleaned up appearance and colors of Pmw.NoteBookR tabs. (Georg
+ Mischler)</p>
+
+</li>
+<li><p>Added <strong>buttontype</strong> option to Pmw.RadioSelect to support
+ radiobuttons and checkbuttons. (Georg Mischler)</p>
+
+</li></ul>
+<p> 23 November 1998</p>
+
+<ul><li><p>Updated usage of <code>bind_class(tag)</code> due to change in return value
+ in Tkinter module in python 1.5.2. (Magnus Kessler, Fredrik Lundh)</p>
+
+</li>
+<li><p>The default time displayed in Pmw.TimeCounter is now the current
+ local time, not GMT as before.</p>
+
+</li>
+<li><p>The times displayed in the Counter demonstration are now the
+ current local time, not GMT as before.</p>
+
+</li></ul>
+<p> 7 December 1998</p>
+
+<ul><li><p>Modified Pmw.ComboBox to take advantage of the fix to the Tkinter
+ <code>bind()</code> method callback handling of <code>Event.widget</code> in python
+ 1.5.2. It works even if the <strong>selectioncommand</strong> destroys the
+ combobox. For simple comboboxes, the invoke() method now returns
+ the return value of the <strong>selectioncommand</strong>.</p>
+
+</li>
+<li><p>Modified Pmw.EntryField to take advantage of the fix to the
+ Tkinter <code>bind()</code> method callback handling of <code>Event.widget</code> in
+ python 1.5.2. It works even if a user-supplied callback
+ (<strong>command</strong>, <strong>invalidcommand</strong>, <strong>validator</strong> or <strong>stringtovalue</strong>)
+ destroys the entryfield. Cleans up correctly when destroyed. The
+ invoke() method now returns the return value of the <strong>command</strong>.</p>
+
+</li>
+<li><p>The invoke() method of Pmw.TimeCounter now returns the return
+ value of the <strong>command</strong>.</p>
+
+</li>
+<li><p>Modified Pmw.ButtonBox to use the new (in Tk8.0) <strong>default</strong> option
+ of the Tkinter <strong>Button</strong> widget instead of a separate frame.
+ Changed default padding to be more compact. Removed "ring" frame
+ component and "ringborderwidth", "ringpadx" and "ringpady"
+ options. (Georg Mischler)</p>
+
+</li>
+<li><p>Changed <strong>'pmw1'</strong> fontScheme to set default fonts only when running
+ under posix, since the default fonts on other systems look better.</p>
+
+</li></ul>
+<p> 10 December 1998</p>
+
+<ul><li><p>Release of version 0.8</p>
+
+</li></ul>
+<p> 20 January 1999</p>
+
+<ul><li><p>Added <strong>master</strong> option to Pmw.MegaToplevel and removed <strong>master</strong>
+ argument from the activate method.</p>
+
+</li>
+<li><p>Replaced rand module in demos with a simple random number
+ generator (since rand is not built-in on all versions of python).</p>
+
+</li></ul>
+<p> 22 February 1999</p>
+
+<ul><li><p>Modified <code>__init__.py</code> so that it only accepts directories whose
+ names begin with <strong>Pmw_M_N</strong> and which have a /lib/PmwLoader.py/
+ file.</p>
+
+</li></ul>
+<p> 13 May 1999</p>
+
+<ul><li><p>Changed Pmw.ScrolledCanvas, Pmw.ScrolledText and Pmw.ScrolledListBox
+ to speed up scrolling if the scrollmodes are not both dynamic.</p>
+
+</li>
+<li><p>Changed busy cursor and activate/deactivate code so that it works
+ correctly under fast mouse clicking or fast keyboarding (using
+ accelerators). Also fixed so that grab is correctly restored
+ after a Pmw.ComboBox popup list is unmapped inside a modal dialog.
+ (Clemens Hintze)</p>
+
+</li>
+<li><p>Several dialogs now give focus to one of their components (listbox
+ or entry widget) when activated. (Clemens Hintze)</p>
+
+</li>
+<li><p>Fixed Pmw.ComboBox so that it unposts popup if the combobox is
+ unmapped and returns grab and focus correctly if destroyed.</p>
+
+</li>
+<li><p>Improved tracetk() output to be more readable. Also displays
+ nested calls to the Tk mainloop better and shows callbacks from
+ tcl to python.</p>
+
+</li>
+<li><p>Upgraded Blt support to blt2.4i. Graph widget is not backwards
+ compatible with blt2.1.</p>
+
+</li></ul>
+<p> 19 May 1999</p>
+
+<ul><li><p>Fixed bug in Pmw.Balloon in placement of balloons over canvas
+ items when the canvas was scrolled. (Tessa Lau)</p>
+
+</li></ul>
+<p> 20 May 1999</p>
+
+<ul><li><p>Added new Tk event types (new in Tk 8.0 and 8.0.5) to PmwBase
+ error display method. Also added check for unknown event types to
+ safeguard against future changes. (Magnus Kessler)</p>
+
+</li>
+<li><p>Added <strong>exclude</strong> argument to <code>showbusycursor()</code>. (Rob Hooft)</p>
+
+</li></ul>
+<p> 1 June 1999</p>
+
+<ul><li><p>Added wrappers for Blt Stripchart and Tabset widgets. (Nick Belshaw)</p>
+
+</li>
+<li><p>Changed createcomponent() so that arguments to the constructor of
+ the component can now be specified as either multiple trailing
+ arguments to createcomponent() or as a single tuple argument.</p>
+
+</li></ul>
+<p> 7 June 1999</p>
+
+<ul><li><p>Added call to update_idletasks() in Pmw.ScrolledCanvas,
+ Pmw.ScrolledFrame, Pmw.ScrolledText and Pmw.ScrolledListBox to
+ avoid endless mapping/unmapping of two dynamic scrollbars when the
+ window is first mapped and only one scrollbar is needed.
+ (Reported by Mark C Favas, solution suggested by Dieter Maurer.)</p>
+
+</li></ul>
+<p> 10 June 1999</p>
+
+<ul><li><p>Fixed bug in bundlepmw.py when called with -noblt option.
+ (Reported by Kevin O'Connor)</p>
+
+</li>
+<li><p>Pmw.ComboBox now unposts the dropdown listbox before the selection
+ callback is invoked, to avoid problems when the callback takes a
+ long time to run. (Reported by Randall Hopper)</p>
+
+</li></ul>
+<p> 11 June 1999</p>
+
+<ul><li><p>Release of version 0.8.1</p>
+
+</li></ul>
+<p> 29 June 1999</p>
+
+<ul><li><p>PmwMessageBar.message() now replaces newlines with spaces before
+ displaying message. Also applies to helpmessage().</p>
+
+</li></ul>
+<p> 2 July 1999</p>
+
+<ul><li><p>Improved toplevel window positioning under NT, and stopped most of
+ the ugly flashing.</p>
+
+</li></ul>
+<p> 5 July 1999</p>
+
+<ul><li><p>The <strong>pmw1</strong> fontScheme is now supported under NT, as is the <em>size</em>
+ option to <code>Pmw.initialise()</code>.</p>
+
+</li></ul>
+<p> 6 July 1999</p>
+
+<ul><li><p>Changed the names of positional arguments in the following
+ methods, so that they have less chance of conflicting with keyword
+ arguments: MegaArchetype.createcomponent(), ButtonBox.insert(),
+ ButtonBox.add(), MenuBar.addcascademenu(), MenuBar.addmenuitem()
+ and RadioSelect.add().</p>
+
+</li></ul>
+<p> 9 July 1999</p>
+
+<ul><li><p>Added images and example code to the megawidget reference manuals.
+ (Suggested by Joerg Henrichs)</p>
+
+</li>
+<li><p>Fixed showbusycursor() under NT. It now calls update() instead of
+ update_idletasks() to force display of cursor. (Solution
+ suggested by George Howlett)</p>
+
+</li>
+<li><p>Improved display of arrows in ComboBox, Counter and TimeCounter.</p>
+
+</li></ul>
+<p> 16 July 1999</p>
+
+<ul><li><p>Removed Pmw.maxfontwidth() function, since better functionality is
+ now supplied by the Tk "font measure" command.</p>
+
+</li>
+<li><p>Removed Pmw.fontexists() function, since in Tk8.0 all fonts exist.</p>
+
+</li></ul>
+<p> 28 July 1999</p>
+
+<ul><li><p>Fixed bug in date counter with separator other than <strong>'/'</strong> and time
+ counter with separator other than <strong>':'</strong>. (David M. Cooke, Alan
+ Robinson)</p>
+
+</li>
+<li><p>Under NT, the font named <strong>'fixed'</strong> is not fixed width, so added
+ alias from <strong>'Fixed'</strong> to <strong>'Courier'</strong>.</p>
+
+</li>
+<li><p>Changed the <code>bind()</code> and <code>tagbind()</code> methods of Pmw.Balloon to
+ remove a potential memory leak. The methods now store the
+ <em>funcids</em> of the callback functions, so that if the same widget or
+ tag is bound twice, the balloon can remove the old bindings.
+ (Peter Stoehr)</p>
+
+</li>
+<li><p>Changed NoteBookR so that lowercmd, creatcmd and raisecmd are
+ called in that order when a page is selected. Also fixed bug
+ which always raised page 0 when notebook is resized. (Scott
+ Evans, Charles Choi)</p>
+
+</li></ul>
+<p> 1 August 1999</p>
+
+<ul><li><p>Added <strong>dynamicGroups</strong> argument to <code>defineoptions()</code> method and
+ modified ButtonBox, MenuBar, PanedWidget, RadioSelect to register
+ their dynamic groups.</p>
+
+</li>
+<li><p><code>Pmw.initialise()</code> can now be called multiple times, with
+ different <em>root</em> arguments, but only sequentially. Pmw does not
+ (yet) support multiple simultaneous interpreters. Modified
+ Pmw.EntryField so that it recreates class bindings when
+ Tkinter.root changes.</p>
+
+</li></ul>
+<p> 4 August 1999</p>
+
+<ul><li><p>Added relmouse option to Pmw.Balloon. Fixed Pmw.Balloon so that
+ the balloon is not displayed off-screen. (Tessa Lau)</p>
+
+</li></ul>
+<p> 16 August 1999</p>
+
+<ul><li><p>Added disableKeyboardWhileBusy option to initialise(). To ignore
+ keyboard input while displaying the busy cursor, Pmw sets the
+ focus for each toplevel window to the Blt busy window. However,
+ under NT, this causes each window to be raised. If this is not
+ acceptable, programs running on NT can request show/hidebusycursor
+ not to ignore keyboard input. </p>
+
+</li></ul>
+<p> 25 August 1999</p>
+
+<ul><li><p>Added Pmw.Blt.busy_forget() and used it in Pmw.hidebusycursor()
+ when running under NT. There is a bug in the Blt busy release
+ command under NT where it sometimes fails to display the busy
+ cursor. Using busy forget avoids the problem.</p>
+
+</li></ul>
+<p> 27 September 1999</p>
+
+<ul><li><p>Added busyCursorName option to Pmw.initialise() and added cursor
+ argument to Pmw.Blt.busy_hold(). (Mark Favas)</p>
+
+</li></ul>
+<p> 20 October 1999</p>
+
+<ul><li><p>Replaced Pmw.NoteBookR and Pmw.NoteBookS with completely rewritten
+ Pmw.NoteBook.</p>
+
+</li>
+<li><p>Renamed Pmw.OptionMenu.get() to Pmw.OptionMenu.getcurselection()
+ and Pmw.PanedWidget.remove() to Pmw.PanedWidget.delete(), to be
+ more consistent with other megawidgets.</p>
+
+</li>
+<li><p>The index() method of several megawidgets now use Pmw.END,
+ Pmw.SELECT and Pmw.DEFAULT instead of strings, since these may
+ conflict with component names. </p>
+
+</li>
+<li><p>Pmw.OptionMenu.index() now uses Pmw.SELECT to return
+ index of the currently selected menu item, rather than None.</p>
+
+</li>
+<li><p>Added destroy() method to Pmw.MegaArchetype to handle cleaning up
+ of _hullToMegaWidget mapping. </p>
+
+</li>
+<li><p>Removed exclude argument from Pmw.showbusycursor() and added
+ Pmw.excludefrombusycursor() function instead. (Rob Hooft)</p>
+
+</li>
+<li><p>Fixed several bugs for Windows NT.</p>
+
+</li>
+<li><p>Added Pmw.ButtonBox.button() and Pmw.RadioSelect.button().</p>
+
+</li>
+<li><p>Added Pmw.Color.bordercolors().</p>
+
+</li></ul>
+<p> 21 October 1999</p>
+
+<ul><li><p>Release of version 0.8.3. (Version 0.8.2 was not released.)</p>
+
+</li></ul>
+<p> 30 October 1999</p>
+
+<ul><li><p>Added arrownavigation option and previouspage() and nextpage()
+ methods to Pmw.NoteBook. (Peter Funk)</p>
+
+</li>
+<li><p>Renamed the <code>setnaturalpagesize()</code> method of Pmw.NoteBook to
+ <code>setnaturalsize()</code> to be consistent with Pmw.PanedWidget.</p>
+
+</li>
+<li><p>Changed Pmw.excludefrombusycursor() to Pmw.setbusycursorattributes().
+ Removed busyCursorName option from Pmw.initialise() and added
+ cursorName attribute to Pmw.setbusycursorattributes().</p>
+
+</li>
+<li><p>Added documentation source and build scripts to ftp site.</p>
+
+</li></ul>
+<p> 6 November 1999</p>
+
+<ul><li><p>Fixed memory leaks when destroying megawidgets. Added automatic
+ check for memory leak to test script used by all tests.
+ Pmw.initialise() now uses a hook into Tkinter.Widget.destroy
+ rather than Tkinter.Frame.destroy to handle the case of
+ Pmw.NoteBook being destroyed (since the notebook hull is a canvas
+ and not a frame). Window manager delete protocol callbacks are
+ now cleaned up. Pmw.ScrolledListBox event bindings now do not
+ leak. (Reported by Jeff Weeks)</p>
+
+</li>
+<li><p>Removed key bindings for Pmw.ScrolledListBox except space and return keys.</p>
+
+</li></ul>
+<p> 20 November 1999</p>
+
+<ul><li><p>Fixed bug in Pmw.Balloon when the canvas or text item that
+ triggered the balloon is deleted before the balloon is displayed
+ by the <strong>initwait</strong> timer. (Magnus Kessler)</p>
+
+</li>
+<li><p>Added <strong>'nograb'</strong> to <em>globalMode</em> option of <code>activate()</code> method. (Rob Hooft)</p>
+
+</li>
+<li><p>Added __setitem__ method to Pmw.MegaArchetype, so that megawidget
+ options can be now set using <code>megawidget['option'] = value</code> style.
+ (Oliver Gathmann)</p>
+
+</li></ul>
+<p> 27 December 1999</p>
+
+<ul><li><p>Converted from <code>regex</code> module to <code>re</code> module, since <code>regex</code> is not
+ implemented for Jpython. (Finn Bock)</p>
+
+</li></ul>
+<p> 30 December 1999</p>
+
+<ul><li><p>Added <code>clear()</code> method to Pmw.ScrolledListBox (suggested by Carson
+ Fenimore).</p>
+
+</li></ul>
+<p> 15 March 2000</p>
+
+<ul><li><p>Fixed problem in PmwBase when deleting windows that were created
+ before Pmw was initialised (such as splash windows displayed while
+ the application is coming up). (Mark Favas)</p>
+
+</li>
+<li><p>Added splash window to Pmw demo. (Mark Favas)</p>
+
+</li></ul>
+<p> 30 April 2000</p>
+
+<ul><li><p>Added Pmw.MainMenuBar megawidget, which uses the menubar feature
+ of Tk to provide platform specific menu bars.</p>
+
+</li>
+<li><p>Fixed Pmw.Counter and several other megawidgets so that certain
+ <strong>hull</strong> constructor keywords, such as <strong>hull_relief</strong> and
+ <strong>hull_borderwidth</strong>, are not overriden in the constructor.</p>
+
+</li>
+<li><p>Thanks to Peter Cashin for his help on how to unpack gzipped tar
+ files on Microsoft Windows operating systems.</p>
+
+</li>
+<li><p>Added Pmw.HistoryText megawidget. This can be used as the basis
+ of an interactive text-based database query gui. It maintains a
+ history of each query and allows editing of prior queries.</p>
+
+</li>
+<li><p>Added references to the Pmw.Blt.Graph documentation by Bjørn Ove
+ Thue and Hans Petter Langtangen.</p>
+
+</li>
+<li><p>Searched for and fixed memory leaks. There are no more known memory leaks.</p>
+<ul><li><p>For commands created by <code>bind</code>: these are cleaned up by Tkinter
+ when the widget is destroyed. Pmw.Balloon, which repeatedly
+ binds to the same widget (or item, using <code>tag_bind</code>), has been
+ fixed by passing the old command into the call to <code>unbind</code> or
+ <code>tag_unbind</code> which is cleaned up by Tkinter.</p>
+
+</li>
+<li><p>For commands created by <code>class_bind</code>: most class bindings are
+ only created once (per Tk interpreter) and so do not need to be
+ cleaned up. The exception is adding and deleting menus in
+ Pmw.MenuBar. This has now been fixed to clean up <code>class_bind</code>
+ commands when deleting menus.</p>
+
+</li>
+<li><p>Callbacks given to command, xscrollcommand, yscrollcommand, etc
+ options are cleaned up by Tkinter when the widget is destroyed.
+ Cases where Pmw repeatedly sets such options have now been fixed
+ to clean up the old command before configuring the new one.
+ These are in <code>setitems</code> in Pmw.OptionMenu and when modifying the
+ scrollcommand options in several of the scrolled widgets.</p>
+
+</li>
+<li><p>Pmw now cleans up calbacks it registers with the
+ WM_DELETE_WINDOW protocol for toplevel windows.</p>
+
+</li></ul>
+
+</li>
+<li><p>Added ManualTests.py to tests directory for tests which need to be
+ run by hand.</p>
+
+</li></ul>
+<p> 12 May 2000</p>
+
+<ul><li><p>Release of version 0.8.4.</p>
+
+</li></ul>
+<p> 17 May 2000</p>
+
+<ul><li><p>Modified Pmw.Counter to deal with the presence (python up to
+ 1.5.2) or absence (python 1.6 and after) of an <strong>L</strong> at the end of
+ the ascii representation of a long. (Mark Favas)</p>
+
+</li>
+<li><p>Fixed bug in Pmw.ScrolledFrame when given invalid flex options.
+ (Stephen D Evans)</p>
+
+</li></ul>
+<p> 23 January 2001</p>
+
+<ul><li><p>Moved Pmw home from www.dscpl.com.au to pmw.sourceforge.net.</p>
+
+</li>
+<li><p>Added pmw2 font scheme, since the font used for balloon text with
+ pmw1 is too small on Linux.</p>
+
+</li>
+<li><p>Removed syntax coloring from code window in demos. It did not
+ look good and the pattern matching was not always correct.</p>
+
+</li>
+<li><p>Changed font size used for demos to 12 for Unix, since 14 looked
+ too big under Linux.</p>
+
+</li>
+<li><p>Minor fixes to tests for Tk 8.3.</p>
+
+</li></ul>
+<p> 8 February 2001</p>
+
+<ul><li><p>Release of version 0.8.5</p>
+
+</li></ul>
+<p> 18 February 2001</p>
+
+<ul><li><p>Added xview() and yview() methods to Pmw.ScrolledFrame (suggested
+ by Christer Fernstrom).</p>
+
+</li>
+<li><p>Made tktrace output more readable.</p>
+
+</li>
+<li><p>Added noBltBusy option to Pmw.initialise.</p>
+
+</li>
+<li><p>Fixed bug where combobox dropdown list could stay mapped after
+ entryfield was unmapped.</p>
+
+</li>
+<li><p>Improved scrolling in scrolled frame.</p>
+
+</li></ul>
+<p> 21 February 2001</p>
+
+<ul><li><p>Fixed tests for recent version of Blt graph (reported by
+ Venkatesh Prasad Ranganath).</p>
+
+</li>
+<li><p>Fixed problem in Pmw.ScrolledFrame in python 1.5 - string.atof
+ does not accept a number as argument, but it does in python 2.0.</p>
+
+</li></ul>
+<p> 24 February 2001</p>
+
+<ul><li><p>Modified Pmw.OptionMenu documentation to specify that list
+ elements must be strings (problem reported by Guy Middleton).</p>
+
+</li>
+<li><p>Fixed bug in Pmw.OptionMenu where the wrong item was displayed
+ when an integer item in the menu was selected with the mouse (even
+ though items should be strings).</p>
+
+</li>
+<li><p>Added work around to Pmw.ScrolledFrame for bug in Tk when
+ retrieving value from scrollbars soon after creation.</p>
+
+</li></ul>
+<p> 27 February 2001</p>
+
+<ul><li><p>Added HistoryText and MainMenuBar to bin/bundlepmw.py - accidently
+ left out.</p>
+
+</li></ul>
+<p> 13 April 2001</p>
+
+<ul><li><p>Changed default foreground (text) of Pmw.Balloown to black. (Eric
+ Pettersen)</p>
+
+</li>
+<li><p>Added default fontScheme to Pmw.initialise().</p>
+
+</li>
+<li><p>Added -fontscheme and -fontsize options to demo.</p>
+
+</li>
+<li><p>Added updatelayout() to Pmw.PanedWidget for use when dynamically
+ adding and deleting panes. (G Cash)</p>
+
+</li>
+<li><p>Added move() to Pmw.PanedWidget to move panes. (G Cash)</p>
+
+</li></ul>
+<p> 20 April 2001</p>
+
+<ul><li><p>Fixed bug in Pmw.Balloon where the balloon would reappear if the
+ mouse button was pressed down inside a widget and then, while the
+ mouse button was being held down, the mouse was moved outside of
+ the widget and then moved back over the widget.</p>
+
+</li>
+<li><p>Fixed bug in Pmw.Balloon when destroying widgets while the balloon
+ was up. In this case, the balloon remained displayed even though
+ the widget had been destroyed. (Reported by Stefan Schone.)</p>
+
+</li>
+<li><p>Fixed bug in Pmw.Balloon when destroying widgets during the
+ initwait period. In this case, an error occurred when the
+ initwait timer went off when it tried to access the destroyed
+ widget. (Reported by Stefan Schone.)</p>
+
+</li>
+<li><p>Fixed Pmw.Balloon so that unbinding withdraws the balloon if
+ the widget being unbound is the widget which triggered the balloon.</p>
+
+</li>
+<li><p>Modified Pmw.Balloon so that when deleting a canvas or text item,
+ <code>tagunbind()</code> can be called which will withdraw the balloon if it
+ was triggered by the item. Unfortunately this can not be
+ automated as for widgets since Tk does not support <Destroy>
+ bindings on canvas or text items, so there is no way that
+ Pmw.Balloon can be notified of the deletion of an item.</p>
+
+</li>
+<li><p>Updated tests for python 2.1.</p>
+
+</li></ul>
+<p> 21 May 2001</p>
+
+<ul><li><p>Pmw.OptionMenu now defaults to taking focus (on <Tab> key).</p>
+
+</li></ul>
+<p> 15 May 2002</p>
+
+<ul><li><p>Fixed bug in Pmw.Graph.element_closest() where element names
+ should follow option arguments. (Val Shkolnikov)</p>
+
+</li></ul>
+<p> 5 June 2002</p>
+
+<ul><li><p>Added command option to Pmw.TimeCounter.</p>
+
+</li>
+<li><p>Finished all documentation.</p>
+
+</li>
+<li><p>Fixed bug in documentation creation script which, since python
+ 2.0, printed default values of real options (such as the
+ horizfraction option of Pmw.ScrolledFrame) with too many digits
+ (such as 0.050000000000000003).</p>
+
+</li>
+<li><p>Fixed bug in setgeometryanddeiconify for cygwin python (John
+ Williams).</p>
+
+</li></ul>
+<p> 4 July 2002</p>
+
+<ul><li><p>Added master option to <code>MegaToplevel.show()</code></p>
+
+</li>
+<li><p>Improved <code>MegaToplevel.show()</code> so that tkraise is not called
+ unecessarily, thus avoiding 2 second delay under certain window
+ managers (such as sawfish) in most circumstances. There are still
+ problems with the Enlightenment window manager.</p>
+
+</li></ul>
+<p> 18 August 2002</p>
+
+<ul><li><p>Added columnheader, rowheader and rowcolumnheader components to
+ Pmw.ScrolledText. (Rob Pearson)</p>
+
+</li>
+<li><p>Added <code>getvalue()</code> and <code>setvalue()</code> methods to several megawidgets
+ as a consistent way to set and get the user-modifiable state.
+ (Cimarron Taylor)</p>
+
+</li>
+<li><p>Made sub-classing simpler when no new options or components are
+ being created. A sub-class of a Pmw megawidget does not need to
+ have an __init__() method. If it does, it does not need to call
+ defineoptions(). Also, initialiseoptions() no longer requires an
+ argument (for backwards compatibility it may take an argument, but
+ it is ignored).</p>
+
+</li></ul>
+<p> 24 August 2002</p>
+
+<ul><li><p>Release of version 1.0</p>
+
+</li></ul>
+<p> 26 August 2002</p>
+
+<ul><li><p>Minor fixes.</p>
+
+</li>
+<li><p>Release of version 1.1</p>
+
+</li></ul>
+<p> 4 September 2002</p>
+
+<ul><li><p>Added collapse, expand and toggle methods and collapsedsize option
+ to Pmw.Group. (Rob Pearson)</p>
+
+</li></ul>
+<p> 5 September 2002</p>
+
+<ul><li><p>Added sticky option to several megawidgets.</p>
+
+</li></ul>
+<p> 18 September 2002</p>
+
+<ul><li><p>Added appendtext method to Pmw.ScrolledText. (Graham Dumpleton)</p>
+
+</li></ul>
+<p> 26 September 2002</p>
+
+<ul><li><p>Modified Pmw.ScrolledListBox to call dblclickcommand on
+ <Double-ButtonRelease-1> rather than <Double-ButtonPress-1> which
+ caused problems if the double button press unmapped the
+ ScrolledListBox. In this case, the second button release of the
+ double click is given to another widget. (Eric Pettersen)</p>
+
+</li></ul>
+<p> 14 June 2003</p>
+
+<ul><li><p>Changes for python 2.3 and Tcl/Tk 8.4.2:</p>
+<ul><li><p>Wrapped calls to cget() for Tkinter widgets in a call to
+ str(). Before python 2.3 cget() always returned a string.
+ Under python 2.3, Tkinter sometimes returns non-string values
+ (such as int, or Tcl_Obj). Made similar change when using
+ configure() to retrieve values. Fixed tests to handle integer
+ and Tcl_Obj return value from cget(). (Charles Doutriaux)</p>
+
+</li>
+<li><p>Fixed uses of <code>col</code> field of grid command. Must use full
+ <code>column</code> under Tcl/Tk 8.4.2.</p>
+
+</li>
+<li><p>Fixed PmwEntryField.py, PmwMessageBar.py, PmwScrolledField.py
+ so that the text is not greyed out under Tcl/Tk 8.4.2. This
+ was caused by a change in behaviour of the <strong>'disabled'</strong> state
+ and the Tk entry widget. Now use new <strong>'readonly'</strong> state for
+ Tcl/Tk 8.4.2.</p>
+
+</li>
+<li><p>Test script now ignores Blt test for Tcl/Tk 8.4.2, since it
+ causes Blt 2.4z to core dump. Blt needs to be fixed.</p>
+
+</li>
+<li><p>Changed Dialog test to work around problem caused by Tk 8.4.2
+ enforcing transient behaviour of windows. When activate() is
+ called on a dialog whose parent is withdrawn, then the dialog
+ window is made transient. Under old versions of Tk, the
+ transient dialog was displayed, but under 8.4.2 the dialog is
+ not displayed. Work around is to deiconify parent of dialog.</p>
+
+</li></ul>
+
+</li></ul>
+<p> 5 August 2003</p>
+
+<ul><li><p>Release of version 1.2</p>
+
+</li></ul>
+<p></p>
+
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw copyright</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw copyright</h1>
+
+<p>
+ Copyright 1997-1999 Telstra Corporation Limited, Australia
+ Copyright 2000-2002 Really Good Software Pty Ltd, Australia</p>
+
+<p> 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:</p>
+
+<p> The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.</p>
+
+<p> 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.
+</p>
+
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw demonstrations and tests</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw demonstrations and tests</h1>
+
+<center><P ALIGN="CENTER">
+<IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+</p></center>
+
+<p>
+ Pmw comes with an extensive range of demonstrations and tests. The
+ demonstrations can be used to get a feel for what is provided by Pmw
+ and the demonstration code can be viewed to see examples of how to
+ use Pmw. The tests can be executed to check that there are no
+ problems with running Pmw in your environment.
+
+</p>
+
+<dl>
+<dt> <h2>Demonstrations</h2></dt><dd>
+<p>
+ The Pmw <code>demos</code> directory contains demonstration scripts
+ showing many of the features of Pmw megawidgets. To view a
+ comprehensive package of all the demonstrations, including a view of
+ the source code, run the <code>All.py</code> script. Run
+ <code>All.py -help</code> for a short description of the script's
+ options.
+
+<p>
+ All of the demonstrations may also be run separately. Most of the
+ demonstrations show some of the features of one of the Pmw
+ megawidgets. For example, to see a demonstration of the ButtonBox
+ megawidget, change into the <code>demos</code> directory and
+ run
+
+</p>
+<dl>
+<dd>
+<pre>
+python ButtonBox.py
+</pre>
+</dd>
+</dl>
+
+<p>
+ Other demonstrations, which show other features of Pmw include
+</p>
+<dl>
+<dd>
+<pre>
+BltGraph.py demonstrates the Pmw interface to
+ the BLT graph and vector commands
+BltTabset.py demonstrates the Pmw interface to
+ the BLT tabset command
+Colors.py how to set color schemes
+ConfigClass.py how to configure the python class
+ of a megawidger component
+ErrorHandling.py how Pmw displays run time errors
+ in a window
+ExampleDemo.py template for new demonstrations
+Grid.py the Tkinter Grid geometry manager
+LogicalFont.py how to use standard values for fonts
+MessageInfo.py how to extend the Pmw MegaToplevel
+ class
+NestedDialogs.py how nested modal dialogs behave
+Resources.py how to use the option database to
+ modify Tk widget option defaults
+Resources_Pmw.py how to use the option database to
+ modify megawidget option defaults
+ShowBusy.py demonstrates the Pmw interface to
+ the BLT busy command
+SpecialEntry.py deriving from Pmw.EntryField
+Spectrum.py some of the Pmw color handling
+ functions
+SpeedTest.py tests the speed of creating Pmw
+ megawidgets
+TextDisplay.py how to extend the Pmw MegaWidget
+ class
+WidgetDestroy.py megawidget destruction
+</pre>
+</dd>
+</dl>
+
+<b>Creating demonstrations of new megawidgets</b>
+<br>
+<p>
+If you create a new megawidget you can create a demonstration for it
+by using the file
+<a href="ExampleDemo.py"><code>ExampleDemo.py</code></a> as a
+template. This template allows the demonstration to be run
+individually or to be automatically included as part of the
+demonstration package <code>All.py</code>. You should take a copy of
+the template and name the new file after your megawidget. You should
+then replace each instance of the word <code>EXAMPLE</code> with the
+name of your megawidget and replace the code in the
+<code>__init__</code> method with code to create and initialise one or
+more instances of your megawidget, which should be a child of
+<code>parent</code>. You can add other methods as necessary.
+
+</p>
+
+</dd>
+<dt> <h2>Tests</h2></dt><dd>
+<p>
+ The Pmw <code>tests</code> directory contains a test framework
+ and a set of test scripts for Pmw.
+ The tests cover the standard Tkinter module and most of the Pmw megawidgets.
+ The tests make a great
+ demonstration of the flexibility of the megawidgets. Simply change
+ into the <code>tests</code> directory and run
+ <code>python All.py</code>.
+
+<p>
+ If all tests pass there should be no output printed to standard
+ output. If any of the tests fail, please send the test output to
+ the maintainer at
+ <a href="mailto:gregm@iname.com"><i>gregm@iname.com</i></a>.
+
+</p>
+
+<p>
+ All of the tests may be run separately. Most of the tests test the
+ features of one of the Pmw megawidgets. For example, to execute the
+ test for the ButtonBox megawidget, run
+
+</p>
+
+<dl>
+<dd>
+<pre>
+python ButtonBox_test.py
+</pre>
+</dd>
+</dl>
+
+<p>
+ The Test.py file contains general testing functions and is imported
+ by all test files.
+ Other files, which test other features of Pmw include
+</p>
+<dl>
+<dd>
+<pre>
+Blt_test.py BLT vector and graph interface
+Colors_test.py setting color schemes
+MegaWidget_test.py creation of megawidget classes
+Options_test.py option and component handling
+PmwBase_test.py more option and component handling
+Tkinter_test.py Tk widgets in the Tkinter module
+</pre>
+</dd>
+</dl>
+
+<b>Creating tests for new megawidgets</b>
+<br>
+<p>
+If you create a new megawidget you should create a test for it. There
+is no template file for creating tests, but by looking at the other
+Pmw tests (for example,
+<a href="ScrolledText_test.py"><code>ScrolledText_test.py</code></a>) you
+will get some idea of how to create a test for your megawidget.
+
+</p>
+
+<p>
+The test files are designed to be run both individually or
+automatically by the test package <code>All.py</code>. Each test file
+must define the <code>testData</code> tuple. This consists of a
+sequence of 2-element tuples, each tuple being a test specification
+for one megawidget. Usually a file tests only one megawidget and so
+there is only one test specification. The first element in the
+specification is the megawidget class and the second is a sequence of
+(yet more) 2-element tuples. In each of these tuples, the first
+element is a sequence of individual tests to perform on an instance of
+the megawidget and the second element is a dictionary to use for
+the keyword arguments when creating the instance. Each individual
+test is a tuple, the meaning of which depends on the type of the first
+element, which may be either a string, a function or a method of the
+megawidget class, as explained below.
+
+</p>
+
+<ul>
+<li>
+<p>
+If the first element is a string, then it is treated as an option of
+the megawidget and configure() is called to set the option to the
+value specified by the second element. After setting the option,
+cget() is called to query the option. If the test tuple has three
+elements, then the value returned by cget() must equal the value
+specified by the third element. Otherwise, the value returned must
+equal the value specified by the second element. For example,
+
+</p>
+<dl>
+<dd>
+<pre>
+('vscrollmode', 'static'),
+('text_relief', 'sunken'),
+('vscrollmode', 'bogus', 'ValueError: bad vscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+</pre>
+</dd>
+</dl>
+
+</li>
+<li>
+<p>
+If the first element is a function or method, then the function or
+method is called. The arguments to the call are given by the second
+element. (As a special case, if the second element is not a tuple, it
+is used as the only argument to the call.) The test tuple may have 2,
+3 or 4 elements.
+
+</p>
+<ul>
+<li>
+<p>
+If it has two elements, then the value returned by the call must be
+None. For example,
+
+</p>
+<dl>
+<dd>
+<pre>
+(c.exportfile, '/tmp/ScrolledText_test.py'),
+(os.unlink, '/tmp/ScrolledText_test.py'),
+</pre>
+</dd>
+</dl>
+
+</li>
+<li>
+<p>
+If it has four elements, then the third element is a dictionary to use
+for the keyword arguments in the call and the value returned by the
+call must equal the value specified by the fourth element. For
+example,
+
+</p>
+<dl>
+<dd>
+<pre>
+(c.search, ('abc', '0.0'), {'nocase': 1}, '2.24'),
+</pre>
+</dd>
+</dl>
+
+</li>
+<li>
+<p>
+If is has three elements and the third element is a dictionary, then
+it is used for the keyword arguments in the call and the value
+returned by the call must be None. For example
+
+</p>
+<dl>
+<dd>
+<pre>
+(c.configurepane, 'first', {'size' : 200}),
+</pre>
+</dd>
+</dl>
+
+</li>
+<li>
+<p>
+If is has three elements and the third element is not a dictionary,
+then the value returned by the call must equal the value specified by
+the third element. For example,
+
+</p>
+<dl>
+<dd>
+<pre>
+(c.components, (), ['hull', 'label']),
+(c.add, ('Legumes',),
+ 'ValueError: name "Legumes" already exists'),
+</pre>
+</dd>
+</dl>
+
+</li>
+</ul>
+</li>
+</ul>
+
+<p>
+Some special functions and values supplied by the Test module that may
+be used in the tests include:
+</p>
+<dl>
+<dd>
+<pre>
+Test.callback callback taking no arguments
+Test.callback1 callback taking one argument
+Test.callbackN callback taking any number of arguments
+
+Test.currentWidget returns the widget instance being tested
+Test.num_options returns number of options for the widget
+
+Test.earthris a sample Tkinter.PhotoImage
+Test.flagup a sample Tkinter.BitmapImage
+Test.floatvar a Tkinter.DoubleVar
+Test.stringvar a Tkinter.StringVar
+</pre>
+</dd>
+</dl>
+
+<p>
+ To slow down a test (to see what is being displayed), add the
+ following line which sets the delay between tests to (say) 1000
+ milliseconds:
+
+</p>
+<dl>
+<dd>
+<pre>
+Test.setdelay(1000)
+</pre>
+</dd>
+</dl>
+
+<p>
+ To print information about what is being tested, add the line:
+
+</p>
+<dl>
+<dd>
+<pre>
+Test.setverbose(1)
+</pre>
+</dd>
+</dl>
+
+</dd>
+</dl>
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Dynamic loader</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Dynamic loader</h1>
+
+<p>
+ There are two aspects of Pmw, unrelated to megawidgets, that
+ require special attention. Firstly, Pmw is made up of many
+ sub-modules, potentially making access to its various classes and
+ functions cumbersome for the user. Secondly, Pmw is regularly
+ being modified and added to, thus requiring the release of new
+ versions. Therefore, techniques for making access to the
+ sub-modules easy and efficient and for dealing with the different
+ versions have been developed. These techniques are incorporated
+ into the dynamic loader which Pmw creates when it is first
+ imported.</p>
+
+<p> The first purpose of the loader is to give access to all Pmw classes
+ and functions through a single entry point, the <strong>Pmw.</strong> prefix. For
+ example, to access the ComboBox class (which resides in one of the
+ sub-modules of Pmw), you just have to use <code>Pmw.ComboBox</code>. Without
+ the loader, this would be a more complicated reference, such as,
+ hypothetically, <code>Pmw.PmwComboBox.ComboBox</code>.</p>
+
+<p> The second purpose of the loader is to delay the importing of the
+ sub-modules until they are needed. This improves the startup time
+ of applications which only use a few Pmw megawidgets. It also
+ allows more megawidgets to be added to the library without slowing
+ down applications which do not use them.</p>
+
+<p> The third purpose of the loader is to allow a script using Pmw to
+ specify which version of Pmw it requires. This allows an
+ application to continue working correctly even after newer releases
+ of Pmw have been made which are not compatible with the version
+ expected by the application. Several versions of Pmw can be
+ installed at once, with the actual version used being specified by
+ each application. In addition, the loader can be configured to
+ search in one or more alpha versions of Pmw. These versions may
+ contain new megawidgets, or new versions of existing megawidgets,
+ that are currently not in the base releases.</p>
+
+<p> Several functions are available to set and query the version of
+ Pmw being used. These are <code>Pmw.setversion()</code> and
+ <code>Pmw.setalphaversions()</code> which specify the version and alpha
+ versions (if any) to use for this session; <code>Pmw.version()</code> which
+ returns the version(s) being used by this session; and
+ <code>Pmw.installedversions()</code> which returns the version(s) of Pmw
+ currently installed. These are described in the
+ <a href="PmwFunctions.html">Pmw functions reference manual</a>.</p>
+
+<p> When Pmw is first imported, an instance of PmwLoader is created
+ and placed into <code>sys.modules['Pmw']</code>. From that point on, any
+ reference to attributes of the Pmw 'module' is handled by the
+ loader. The real Pmw package is stored in <code>sys.modules['_Pmw']</code>.</p>
+
+<p> The loader searches the Pmw package base directory for
+ sub-directories with the prefixes <code>Pmw_</code> and <code>Alpha_</code>, which
+ contain Pmw base releases and alpha releases. The version numbers
+ are given by the part of the directory name following the prefix.
+ These versions are available for use and are those returned by the
+ <code>Pmw.installedversions</code> function. The initial version is set to
+ the base release with the greatest version number. When the first
+ reference to a Pmw class or function is made, the loader reads the
+ files named <strong>Pmw.def</strong> in the current base version directory and
+ also in the alpha directories (if any). These files list all the
+ classes and functions supported by the version. Pmw attributes
+ are first searched for in the alpha directories and then in the
+ base version directory. The first directory which supports the
+ reference is used. In this way, alpha versions override base
+ versions.</p>
+
+<p> The directory <code>Alpha_99_9_example</code> contains a simple example of
+ how to structure an alpha version. The following code can be used
+ to request that the alpha version be used and then creates an
+ instance of a new megawidget defined in the alpha version.</p>
+
+<dl><dd><pre> import Pmw
+ Pmw.setalphaversions('99.9.example')
+
+ # Create a standard message dialog using the base Pmw version.
+ ordinary = Pmw.MessageDialog(
+ message_text = 'Ordinary\nPmw Dialog')
+
+ # Create an example dialog using the alpha Pmw version.
+ alpha = Pmw.AlphaExample()</pre></dd></dl>
+
+<p> <strong>Freezing Pmw</strong></p>
+
+<p> Since the dynamic loader requires that Pmw be installed at run
+ time, it can not be used when <em>freezing</em> Pmw. In this case, a
+ single module containing all Pmw code is required, which can then
+ be frozen with the rest of the application's modules. The
+ <code>bundlepmw.py</code> script in the Pmw <code>bin</code> directory can be used to
+ create such a file. This script concatenates (almost) all Pmw
+ megawidget files into a single file, <code>Pmw.py</code>, which it writes to
+ the current directory. The script is called like this:</p>
+
+<dl><dd><pre> bundlepmw.py [-noblt] [-nocolor] /path/to/Pmw/Pmw_X_X_X/lib</pre></dd></dl>
+
+<p> The last argument should be the path to the <code>lib</code> directory of the
+ required version of Pmw. By default, the <code>Pmw.py</code> file imports
+ the <code>PmwBlt</code> and <code>PmwColor</code> modules and so, to freeze an
+ application using Pmw, you will need to copy the files <code>PmwBlt.py</code>
+ and <code>PmwColor.py</code> to the application directory before freezing.</p>
+
+<p> If you are sure that your application does not use any of the
+ <code>Pmw.Blt</code> or <code>Pmw.Color</code> functions, you can use the <code>-noblt</code> or
+ <code>-nocolor</code> options. In this case <code>Pmw.py</code> will be modified so
+ that it does not import these module(s) and so will not need to be
+ included when freezing the application.</p>
+
+<p> If your application only uses a few Pmw megawidgets, you can
+ remove the references to the usused ones in the <code>files</code> list in
+ the <code>bundlepmw.py</code> code. To make the change, take a copy of the
+ script and modify it. This will make the <code>Pmw.py</code> file smaller.
+ However, be sure that you do not delete megawidgets that are
+ components or base classes of megawidgets that you use.</p>
+
+<p></p>
+
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+import Tkinter
+import Pmw
+
+class ThresholdScale(Pmw.MegaWidget):
+ """ Megawidget containing a scale and an indicator.
+ """
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = (
+ ('colors', ('green', 'red'), None),
+ ('threshold', 50, None),
+ ('value', None, Pmw.INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise base class (after defining options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ # Create the indicator component.
+ self.indicator = self.createcomponent('indicator',
+ (), None,
+ Tkinter.Frame, interior,
+ width = 16,
+ height = 16,
+ borderwidth = 2,
+ relief = 'raised')
+ self.indicator.grid()
+
+ # Create the scale component.
+ self.scale = self.createcomponent('scale',
+ (), None,
+ Tkinter.Scale, interior,
+ command = self._doCommand,
+ tickinterval = 20,
+ length = 200,
+ from_ = 100,
+ to = 0,
+ showvalue = 0)
+ self.scale.grid()
+
+ value = self['value']
+ if value is not None:
+ self.scale.set(value)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _doCommand(self, valueStr):
+ if self.scale.get() > self['threshold']:
+ color = self['colors'][1]
+ else:
+ color = self['colors'][0]
+ self.indicator.configure(background = color)
+
+Pmw.forwardmethods(ThresholdScale, Tkinter.Scale, 'scale')
+
+# Initialise Tkinter and Pmw.
+root = Pmw.initialise()
+root.title('Pmw ThresholdScale demonstration')
+
+# Create and pack two ThresholdScale megawidgets.
+mega1 = ThresholdScale()
+mega1.pack(side = 'left', padx = 10, pady = 10)
+
+mega2 = ThresholdScale(
+ colors = ('green', 'yellow'),
+ threshold = 75,
+ value = 80,
+ indicator_width = 32,
+ scale_width = 25)
+mega2.pack(side = 'left', padx = 10, pady = 10)
+
+# Let's go.
+root.mainloop()
--- /dev/null
+import Tkinter
+import Pmw
+
+class ThresholdScale(Pmw.MegaWidget):
+ """ Megawidget containing a scale and an indicator.
+ """
+
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ optiondefs = (
+ ('colors', ('green', 'red'), None),
+ ('orient', 'vertical', Pmw.INITOPT),
+ ('labelmargin', 0, Pmw.INITOPT),
+ ('labelpos', None, Pmw.INITOPT),
+ ('threshold', (50,), None),
+ ('value', None, Pmw.INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise base class (after defining options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ # Create the indicator component.
+ self.indicator = self.createcomponent('indicator',
+ (), None,
+ Tkinter.Frame, interior,
+ width = 16,
+ height = 16,
+ borderwidth = 2,
+ relief = 'raised')
+
+ # Create the value component.
+ self.value = self.createcomponent('value',
+ (), None,
+ Tkinter.Label, interior,
+ width = 3)
+
+ # Create the scale component.
+ if self['orient'] == 'vertical':
+ # The default scale range seems to be
+ # the wrong way around - reverse it.
+ from_ = 100
+ to = 0
+ else:
+ from_ = 0
+ to = 100
+
+ self.scale = self.createcomponent('scale',
+ (), None,
+ Tkinter.Scale, interior,
+ orient = self['orient'],
+ command = self._doCommand,
+ tickinterval = 20,
+ length = 200,
+ from_ = from_,
+ to = to,
+ showvalue = 0)
+
+ value = self['value']
+ if value is not None:
+ self.scale.set(value)
+
+ # Use grid to position all components
+ if self['orient'] == 'vertical':
+ self.indicator.grid(row = 1, column = 1)
+ self.value.grid(row = 2, column = 1)
+ self.scale.grid(row = 3, column = 1)
+ # Create the label.
+ self.createlabel(interior, childRows=3)
+ else:
+ self.indicator.grid(row = 1, column = 1)
+ self.value.grid(row = 1, column = 2)
+ self.scale.grid(row = 1, column = 3)
+ # Create the label.
+ self.createlabel(interior, childCols=3)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _doCommand(self, valueStr):
+ valueInt = self.scale.get()
+ colors = self['colors']
+ thresholds = self['threshold']
+ color = colors[-1]
+ for index in range(len(colors) - 1):
+ if valueInt <= thresholds[index]:
+ color = colors[index]
+ break
+ self.indicator.configure(background = color)
+ self.value.configure(text = valueStr)
+
+Pmw.forwardmethods(ThresholdScale, Tkinter.Scale, 'scale')
+
+# Initialise Tkinter and Pmw.
+root = Pmw.initialise()
+root.title('Pmw ThresholdScale demonstration')
+
+# Create and pack two ThresholdScale megawidgets.
+mega1 = ThresholdScale(scale_showvalue = 1)
+mega1.pack(side = 'left', padx = 10, pady = 10)
+
+mega2 = ThresholdScale(
+ colors = ('green', 'yellow', 'red'),
+ threshold = (50, 75),
+ value = 80,
+ indicator_width = 32,
+ scale_width = 25)
+mega2.pack(side = 'left', padx = 10, pady = 10)
+
+# Create and pack two horizontal ThresholdScale megawidgets.
+mega3 = ThresholdScale(
+ orient = 'horizontal',
+ labelpos = 'n',
+ label_text = 'Horizontal')
+mega3.pack(side = 'top', padx = 10, pady = 10)
+mega4 = ThresholdScale(orient = 'horizontal')
+mega4.pack(side = 'top', padx = 10, pady = 10)
+
+# Let's go.
+root.mainloop()
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw features</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw features</h1>
+
+<p>
+ Pmw is a toolkit for building high-level compound widgets, or
+ <em>megawidgets</em>, constructed using other widgets as component parts.
+ It promotes consistent look and feel within and between graphical
+ applications, is highly configurable to your needs and is easy to
+ use.</p>
+
+<p> Pmw consists of:</p>
+<ul><li><p>A few base classes, providing a foundation for building
+ megawidgets.</p>
+
+</li>
+<li><p>A library of flexible and extensible megawidgets built on
+ the base classes, such as buttonboxes, notebooks,
+ comboboxes, selection widgets, paned widgets, scrolled
+ widgets and dialog windows.</p>
+
+</li>
+<li><p>A lazy importer/dynamic loader which is automatically
+ invoked when Pmw is first imported. This gives unified
+ access to all Pmw classes and functions through the <strong>Pmw.</strong>
+ prefix. It also speeds up module loading time by only
+ importing Pmw sub-modules when needed.</p>
+
+</li>
+<li><p>Complete reference documentation, covering all classes and
+ functions including all megawidgets and their options,
+ methods and components. Helpful tutorial material is also
+ available.</p>
+
+</li>
+<li><p>A test framework and tests for Pmw megawidgets.</p>
+
+</li>
+<li><p>A slick demonstration of the megawidgets.</p>
+
+</li>
+<li><p>An interface to the BLT busy, graph and vector commands.</p>
+
+</li></ul>
+
+<p> The interface to Pmw megawidgets is similar to basic Tk widgets, so it
+ is easy for developers to include both megawidgets and basic Tk
+ widgets in their graphical applications. In addition, Pmw
+ megawidgets may themselves be extended, using either inheritance or
+ composition.</p>
+
+<p> The use of the Pmw megawidgets replaces common widget combinations
+ with higher level abstractions. This simplifies code, making it
+ more readable and maintainable. The ability to extend Pmw
+ megawidgets enables developers to create new megawidgets based on
+ previous work.</p>
+
+<p></p>
+
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>How to build Pmw megawidgets</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">How to build Pmw megawidgets</h1>
+
+<center><P ALIGN="CENTER">
+<IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+</p></center>
+
+<dl>
+<dt> <h3>Introduction</h3></dt><dd>
+<p>
+ This document briefly describes how to design and code Pmw
+ megawidgets by inheriting from the Pmw base classes. It shows step
+ by step how to build a simple example megawidget. This megawidget
+ allows the user to select one of a range of numbers and it also
+ indicates if the selected number is greater than a given threshold.
+
+</p>
+
+</dd>
+<dt> <h3>Choosing the components</h3></dt><dd>
+
+<p>
+ The megawidget will be built using a Tkinter.Scale widget to allow
+ the user to select a number in a range, and a Tkinter.Frame widget
+ to act as an indicator, displaying red (say) if the selected number
+ exceeds the threshold. It will look something like this:
+
+</p>
+
+<center><P ALIGN="CENTER">
+ <IMG SRC = scale1.gif ALT = "Scale 2" WIDTH=70 HEIGHT=244>
+</p></center>
+
+<p>
+ The programmer using this megawidget will need access to the scale
+ widget, since they will need to set the scale's range. Therefore
+ the scale will be made a component of the megawidget. The
+ programmer will probably not need access to the indicator frame,
+ but, just in case the need arises to change the borderwidth or
+ relief of the indicator, we will make it a component too. This
+ illustrates a convention about components - for maximum
+ configurability, make all sub-widgets components.
+
+</p>
+
+</dd>
+<dt> <h3>Choosing the options</h3></dt><dd>
+
+<p>
+ Apart from the component options now available through the scale and indicator
+ components, the megawidget will need a few options of its own. It
+ will need a <strong>threshold</strong> option to set the threshold.
+ It may also need options to set the colors of the indicator when the
+ selected value is both above and below the threshold. Other options
+ could be <strong>orient</strong> or <strong>indicatorpos</strong> to
+ specify the relative position of components and
+ <strong>margin</strong>, <strong>padx</strong> or
+ <strong>pady</strong> to specify spacing between and around the
+ components. For this example, we will define three options -
+ <strong>threshold</strong>, <strong>colors</strong> and
+ <strong>value</strong>. The <strong>colors</strong> option will be
+ a 2-element sequence specifying two colors (below threshold, above
+ threshold). The <strong>value</strong> option will be the initial
+ value of the scale.
+
+</p>
+
+</dd>
+<dt> <h3>Coding the megawidget</h3></dt><dd>
+
+<p>
+ The first things to do are to decide on a name for the new
+ megawidget, decide which base class to inherit from and to begin to
+ write the constructor. Most Pmw megawidgets are derived from either
+ Pmw.MegaWidget, Pmw.MegaToplevel or Pmw.Dialog. In this case, since
+ the widget is not to be contained within its own toplevel window, we
+ will inherit from Pmw.MegaWidget. The constructors of megawidgets
+ take one argument (the widget to use as the parent of the
+ megawidget's hull, defaulting to the root window) and any number of
+ keyword arguments.
+
+</p>
+
+<pre>
+class ThresholdScale(Pmw.MegaWidget):
+ """ Megawidget containing a scale and an indicator.
+ """
+
+ def __init__(self, parent = None, **kw):
+</pre>
+
+<p>
+ Next, we need to define the options supplied by this megawidget.
+ Each option is specified by a 3-element sequence. The first element
+ is the option's name. The second element is the default value. The
+ third element is either a callback function,
+ <strong>Pmw.INITOPT</strong> or <strong>None</strong>. In the first
+ case, the function is called at the end of construction (during the
+ call to <code>self.inialiseoptions</code>) and also
+ whenever the option is set by a call to
+ <code>configure</code>. <strong>Pmw.INITOPT</strong> indicates that
+ the option is an initialisation option - it cannot be set by calling
+ <code>configure</code>. <strong>None</strong> indicates that the
+ option can be set by calling <code>configure</code>, but that there
+ is no callback function.
+
+</p>
+
+<p>
+ The call to <code>self.defineoptions</code> also includes the
+ keyword arguments passed in to the constructor. The value given to
+ any option specified in the keywords will override the default
+ value.
+
+</p>
+
+<pre>
+ # Define the megawidget options.
+ optiondefs = (
+ ('colors', ('green', 'red'), None),
+ ('threshold', 50, None),
+ ('value', None, Pmw.INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+</pre>
+
+<p>
+ After defining the options, the constructor of the base class should
+ be called. The options need to be defined first so that a derived
+ class can redefine the default value of an option defined in a base
+ class. This is because the value specified by the derived class
+ must be made available before the base class constructor is called.
+ The keyword
+ arguments should not be passed into the base class constructor since
+ they have already been dealt with in the previous step.
+
+</p>
+
+<pre>
+ # Initialise base class (after defining options).
+ Pmw.MegaWidget.__init__(self, parent)
+</pre>
+
+<p>
+ Now we should create the components. The components are created as
+ children (or grandchildren ...) of the megawidget's interior.
+
+</p>
+
+<pre>
+ # Create the components.
+ interior = self.interior()
+</pre>
+
+<p>
+ The first component to create is the indicator. The
+ <code>createcomponent</code> method creates the sub-widget and
+ registers the widget as a component of this megawidget. It takes
+ five arguments plus any number of keyword arguments. The arguments
+ are name, aliases, group, class and constructor arguments. See the
+ <a href="MegaArchetype.html">Pmw.MegaArchetype reference manual</a>)
+ for full details.
+
+</p>
+
+<pre>
+ # Create the indicator component.
+ self.indicator = self.createcomponent('indicator',
+ (), None,
+ Tkinter.Frame, (interior,),
+ width = 16,
+ height = 16,
+ borderwidth = 2,
+ relief = 'raised')
+ self.indicator.grid()
+</pre>
+
+<p>
+ The scale component is created in a similar way. In this case, the
+ initial value of the scale is also set to the value of the
+ <strong>value</strong> initialisation option.
+
+</p>
+
+<pre>
+ # Create the scale component.
+ self.scale = self.createcomponent('scale',
+ (), None,
+ Tkinter.Scale, (interior,),
+ command = self._doCommand,
+ tickinterval = 20,
+ length = 200,
+ from_ = 100,
+ to = 0,
+ showvalue = 0)
+ self.scale.grid()
+
+ value = self['value']
+ if value is not None:
+ self.scale.set(value)
+</pre>
+
+<p>
+ At the end of the constructor, the <code>initialiseoptions</code>
+ method is called to check that all keyword arguments have been used
+ (that is, the caller did not specify any unknown or misspelled
+ options) and to call the option callback functions.
+
+</p>
+
+<pre>
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+</pre>
+
+<p>
+ All other methods must now be defined. In this case, only one
+ method is required - a method called whenever the scale changes and
+ which sets the indicator color according to the threshold.
+
+</p>
+
+<pre>
+ def _doCommand(self, valueStr):
+ if self.scale.get() > self['threshold']:
+ color = self['colors'][1]
+ else:
+ color = self['colors'][0]
+ self.indicator.configure(background = color)
+</pre>
+
+<p>
+ To complete the megawidget, methods from other classes can be
+ copied into this class. In this case, all Tkinter.Scale methods
+ not already defined by the megawidget are made available as methods
+ of this class and are forwarded to the scale component. Note that
+ the third argument to <code>Pmw.forwardmethods</code> is the name of
+ the instance variable referring to the Tkinter.Scale widget and not
+ the name of the component. This function is called outside of and
+ after the class definition.
+
+</p>
+
+<pre>
+Pmw.forwardmethods(ThresholdScale, Tkinter.Scale, 'scale')
+</pre>
+
+<p>
+ <strong>Important note:</strong> If a megawidget defines options
+ using <code>defineoptions()</code>, then this method must be
+ called in the megawidget constructor before the call to the base
+ class constructor and a matching call to
+ <code>initialiseoptions()</code> must made at the end of the
+ constructor. For example:
+
+</p>
+<pre>
+ def __init__(self, parent = None, **kw):
+ optionDefs = ...
+ self.defineoptions(kw, optionDefs)
+ BaseClass.__init__(self, parent)
+ ...
+ self.initialiseoptions()
+</pre>
+
+</dd>
+<dt> <h3>Creating instances of the megawidget</h3></dt><dd>
+
+<p>
+ The code below creates two of our example megawidgets. The first is
+ created with default values for all options. The second is created
+ with new values for the options. It also redefines some of the
+ options of the components.
+
+</p>
+
+<dl>
+<dd>
+<pre>
+# Create and pack two ThresholdScale megawidgets.
+mega1 = ThresholdScale()
+mega1.pack(side = 'left', padx = 10, pady = 10)
+
+mega2 = ThresholdScale(
+ colors = ('green', 'yellow'),
+ threshold = 75,
+ value = 80,
+ indicator_width = 32,
+ scale_width = 25)
+mega2.pack(side = 'left', padx = 10, pady = 10)
+</pre>
+</dd>
+</dl>
+
+<center><P ALIGN="CENTER">
+ <IMG SRC = scale2.gif ALT = "Scale 1" WIDTH=150 HEIGHT=244>
+</p></center>
+
+</dd>
+<dt> <h3>The complete code</h3></dt><dd>
+
+<p>
+ The complete code for this example can be seen
+ <a href="example.py">here</a>.
+
+</p>
+
+</dd>
+<dt> <h3>Exercises</h3></dt><dd>
+
+<p>
+ These exercises build on the example presented so far.
+
+</p>
+
+<ol>
+ <li>
+ Change the call to create <code>mega1</code> so that the scale
+ widget displays the current value next to the slider. (You may
+ need to look at the Tk scale manual page to find which option to
+ the <strong>scale</strong> component to set.) You will be able to
+ do this without modifying the ThresholdScale class code.
+
+ </li>
+ <li>
+ Add a Tkinter.Label component between the indicator and scale
+ components. Modify the <code>_doCommand</code> method so that it
+ displays the current value of the scale in this label.
+
+ </li>
+ <li>
+ Modify the <strong>colors</strong> and <strong>threshold</strong>
+ options so that they both accept a tuple. Now implement multiple
+ thresholds, so that the indicator displays one of several colors,
+ depending on the value of the scale.
+
+ </li>
+ <li>
+ Add an <strong>orient</strong> initialisation option and lay out
+ the components horizontally or vertically depending on its value.
+
+ </li>
+ <li>
+ Read the description of the <code>createlabel()</code> method in
+ the <a href="MegaArchetype.html">Pmw.MegaArchetype reference
+ manual</a> and add <strong>labelpos</strong> and
+ <strong>labelmargin</strong> initialisation options which allow
+ the creation of a label for the megawidget.
+
+ </li>
+</ol>
+
+<p>
+ An example of how these changes can be made can be seen
+ <a href="exercises.py">here</a>.
+
+</p>
+
+</dd>
+<dt> <h3>Contributing your megawidgets to Pmw</h3></dt><dd>
+
+<p>
+ If you have completed a megawidget that may be useful to others, you
+ may like to consider contributing it to Pmw. See
+ <a href="starting.html#contributions">Contributions welcome</a> for
+ how to contribute.
+
+</p>
+
+</dd>
+<dt> <h3>Pmw coding conventions</h3></dt><dd>
+
+<p>
+As a final note, the Pmw code makes an attempt to follow these coding
+conventions.
+</p>
+
+<ul>
+ <li>
+ Class names: initial of each word is upper case (including first word).
+
+ </li>
+ <li>
+ Public method and function names: all in lower case.
+
+ </li>
+ <li>
+ Megawidget options: all in lower case.
+
+ </li>
+ <li>
+ Megawidget component names: all in lower case.
+
+ </li>
+ <li>
+ Function arguments: initial of each word is upper case (except first word).
+
+ </li>
+ <li>
+ Private names: initial of each word is upper case (except first
+ word if not a class)
+
+ </li>
+ <li>
+ Underscores as word separators are only used when overriding
+ Tkinter methods of same name.
+
+ </li>
+ <li>
+ Indent is four spaces.
+
+ </li>
+ <li>
+ Continuation lines are indented by eight spaces, so that they
+ won't be confused with a following nested code block.
+ Continuation lines should be used when a statement, which would
+ normally be written on one line, is longer than 80 characters.
+ Examples are "if" statements which contain many conditions and
+ function calls with many arguments.
+
+ </li>
+ <li>
+
+ Surround <code>=</code> with spaces when used with keyword
+ parameters in function calls.
+
+ </li>
+ <li>
+
+ Multi-line function calls should have one keyword parameter per
+ line.
+
+ </li>
+</ul>
+</dd>
+</dl>
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>How to use Pmw megawidgets</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">How to use Pmw megawidgets</h1>
+
+<center><P ALIGN="CENTER">
+<IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+</p></center>
+
+<dl>
+<dt> <h3>Introduction</h3></dt><dd>
+<p>
+ This document briefly describes the features of the Pmw megawidget
+ toolkit and how to use the megawidgets. Using examples, it
+ describes those features common to all Pmw megawidgets. For a
+ description of individual Pmw megawidgets see the
+ <a href="refindex.html">reference manuals</a>.
+ For complete information on general Pmw megawidget functionality see the
+ <a href="MegaArchetype.html">Pmw.MegaArchetype reference manual</a>.
+ For a lot more example code, run any of the files in the
+ Pmw <code>demos</code> directory.
+
+</p>
+
+<p>
+ A simple example of a megawidget is a counter. This widget
+ contains an entry field and two small arrow buttons. Users may
+ enter a value directly into the entry field or they may use the
+ buttons to increment and decrement the value displayed without
+ having to use the keyboard. Having this and other megawidgets in
+ your toolbox allows you to choose the best graphical interface for
+ your application.
+
+</p>
+</dd>
+<dt> <h3>Getting started</h3></dt><dd>
+
+<b>Initialisation of Pmw</b>
+<br>
+<p>
+ To run the examples in the tutorial, make sure that the
+ Pmw <code>lib</code> directory is in <code>sys.path</code>. You
+ should be able to cut and paste the examples into an interactive
+ python session, or you can copy them to a file and run the file with
+ python.
+
+</p>
+<p>
+ The following two lines should be entered before any of the
+ examples. These import and initialise Pmw.
+ For more information on <code>Pmw.initialise()</code> see the
+ <a href="PmwFunctions.html">Pmw functions reference manual</a>.
+
+</p>
+
+<dl>
+<dd>
+<pre>
+import Pmw
+root = Pmw.initialise()
+</pre>
+</dd>
+</dl>
+
+<p>
+ If necessary, you can have more control over how Tkinter and Pmw are
+ initialised by using this form of initialisation:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+import Tkinter
+root = Tkinter.Tk()
+import Pmw
+Pmw.initialise(root)
+</pre>
+</dd>
+</dl>
+
+</dd>
+<dt> <h3>Megawidget construction</h3></dt><dd>
+
+<b>Creating a counter</b>
+<br>
+<p>
+ Now that you have the formalities out of the way, you can create and
+ pack a counter megawidget (see
+ <a href="Counter.html">Pmw.Counter reference manual</a>) using
+ its default configuration like this:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter1 = Pmw.Counter()
+counter1.pack(padx = 10, pady = 10)
+</pre>
+</dd>
+</dl>
+
+<p>
+ Now enter a number and click on the arrow buttons to see the number
+ increment or decrement. The result looks something like this:
+
+</p>
+
+<center><P ALIGN="CENTER">
+ <IMG SRC = counter1.gif ALT = "Counter 1" WIDTH=220 HEIGHT=46>
+</p></center>
+
+<p>
+ The above example creates the counter as a child of the root window.
+ If you want to create it as a child of another window (for example,
+ a Tkinter.Frame widget called 'frame'), add the parent as an
+ argument to the constructor:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter1a = Pmw.Counter(frame)
+</pre>
+</dd>
+</dl>
+
+</dd>
+<dt> <h3>Methods</h3></dt><dd>
+<p>
+ Once a megawidget has been created, you can call any of its other
+ methods in a similar way to Tk widgets. The following sets the value
+ of the counter and then increments it:
+</p>
+
+<dl>
+<dd>
+<pre>
+counter1.setentry(41)
+counter1.increment()
+</pre>
+</dd>
+</dl>
+
+</dd>
+<dt> <h3>Options</h3></dt><dd>
+<p>
+ Like any widget, a megawidget may have options to allow it to be
+ configured for a particular use. Options allow the megawidget user
+ to modify the appearance and behaviour of the megawidget. The
+ counter megawidget has several such options. One of them,
+ <strong>datatype</strong>, specifies how the counter should count up
+ and down, such as, for example, by integers, reals, times or dates.
+ The default value is <strong>'numeric'</strong>, which means the
+ counter expects integers to be entered and will support
+ incrementing and decrementing by whole numbers.
+
+</p>
+
+<p>
+ Another option is
+ <strong>increment</strong>, which specifies how many units should be
+ added or subtracted when the counter is incremented or decremented.
+ Using these options, you can create a time counter, supporting the
+ format <strong>HH:MM:SS</strong>, and counting in minutes, like
+ this (note also the call to the <code>setentry</code> method to set
+ the contents of the entry field):
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter2 = Pmw.Counter(
+ datatype = 'time',
+ increment = 60)
+counter2.setentry('00:00:00')
+counter2.pack(padx = 10, pady = 10)
+</pre>
+</dd>
+</dl>
+
+<p>
+ Many megawidget options can be modified using the
+ <code>configure()</code> method. For example, you can change the
+ value of the <strong>increment</strong> option to 10 minutes like
+ this:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter2.configure(increment = 60 * 10)
+</pre>
+</dd>
+</dl>
+
+<b>Initialisation options</b>
+<br>
+<p>
+ Some megawidget options can only be set when creating the megawidget.
+ These options can not be set by calling the <code>configure()</code>
+ method, but they can be queried in all the usual ways. For example,
+ the counter has an <strong>orient</strong> initialisation option
+ which specifies whether the arrow buttons should appear to the
+ left and right of the entry field (<strong>'horizontal'</strong>)
+ or above and below (<strong>'vertical'</strong>). You can create a
+ numeric counter with arrow buttons above and below the entry
+ field like this:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter3 = Pmw.Counter(orient = 'vertical')
+counter3.pack(padx = 10, pady = 10)
+</pre>
+</dd>
+</dl>
+
+<b>Querying options</b>
+<br>
+<p>
+ You can query the value of megawidget options (initialisation or
+ not) in similar ways as for normal Tkinter widgets. For example,
+ the following code prints the values of some of the counter options.
+
+</p>
+
+<dl>
+<dd>
+<pre>
+print counter3.cget('increment')
+ --> 1
+print counter3.configure('orient')
+ --> ('orient', 'orient', 'Orient', 'horizontal', 'vertical')
+</pre>
+</dd>
+</dl>
+
+<p>
+ When a Tk widget option is queried, its value is always
+ returned as a string, regardless of the type used when setting the
+ option. However, when a Pmw megawidget option is queried, a
+ reference to the object used when setting the option is returned.
+ In other words it is not always a string. For example, the type
+ returned by <code>cget('increment')</code> above was integer.
+
+</p>
+
+</dd>
+<dt> <h3>Components</h3></dt><dd>
+<p>
+ Megawidgets are made up of other widgets, which we call
+ <em>components</em>. Each component is known by a logical name and
+ may be either a simple Tk widget, or may itself be a megawidget.
+ Pmw gives the megawidget user access to not only the functionality
+ supported directly by the megawidget through its options and methods,
+ but also to the components of the megawidget and their options and
+ methods. To access a component directly, use the
+ <code>component()</code> method. For example, to call method
+ <strong>doit</strong> of component <strong>comp</strong>
+ of megawidget <strong>mega</strong>:
+
+ </p>
+
+<dl>
+<dd>
+<pre>
+mega.component('comp').doit()
+</pre>
+</dd>
+</dl>
+
+<b>Component options</b>
+<br>
+<p>
+ There is a short-hand way to access the options of components, by
+ using the notation <em>component_option</em>. This allows, for
+ example, a counter megawidget to be configured with different
+ colored backgrounds for each of its arrow button components (these
+ components are called <strong>downarrow</strong> and
+ <strong>uparrow</strong>):
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter2.configure(
+ downarrow_background = 'green',
+ uparrow_background = 'red')
+</pre>
+</dd>
+</dl>
+
+<b>The hull</b>
+<br>
+<p>
+ All megawidgets are enclosed in a containing widget which is created
+ automatically by the Pmw base classes. For normal megawidgets the
+ container is a Tkinter Frame widget. For megawidgets which are
+ toplevel windows, the container is a Tkinter Toplevel widget. The
+ containing widget is accessible as the <strong>hull</strong>
+ component.
+
+</p>
+
+<p>
+ To access options of the containing widget use the form
+ <strong>hull_</strong><em>option</em>. For example to create a
+ counter megawidget with a wide sunken border around it:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter4 = Pmw.Counter(
+ hull_relief = 'sunken',
+ hull_borderwidth = 5
+)
+</pre>
+</dd>
+</dl>
+
+
+<b>The interior</b>
+<br>
+<p>
+ Some megawidgets, such as Dialog and LabeledWidget, also have a
+ frame into which users can pack other widgets. This frame may be a
+ component but can also be accessed with the <code>interior()</code>
+ method. For the Pmw.MegaToplevel and Pmw.MegaWidget classes, the
+ interior widget is the same as the hull widget. For other
+ megawidgets, the hull is the outer, containing widget and the
+ interior is the empty frame which can be used to extend the
+ megawidget by including extra internal widgets.
+
+</p>
+
+<b>Sub components and aliases</b>
+<br>
+<p>
+ Components may themselves be megawidgets and so their
+ (sub-)components can be referred to using the notation
+ <em>component_sub-component</em>. For example, the
+ <strong>entryfield</strong> component of the counter is a
+ Pmw.EntryField megawidget (which handles the input validation). In
+ turn, this has a Tkinter.Entry component named
+ <strong>entry</strong>. Therefore, you can change the background of
+ the counter's Tkinter.Entry widget with:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter2.configure(entryfield_entry_background = 'yellow')
+</pre>
+</dd>
+</dl>
+
+<p>
+ Most component path names (like <strong>entryfield_entry</strong>)
+ have a shorter <strong>alias</strong> defined for them. In this
+ case, you can use the equivalent:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter2.configure(entry_background = 'yellow')
+</pre>
+</dd>
+</dl>
+
+<b>Changing the python class of a component</b>
+<br>
+<p>
+ Each megawidget component is an instance of some python class. The
+ default class of each component is given in the reference manual.
+ By using the special <strong>pyclass</strong> component option, you
+ can specify a different python class to use when creating the
+ component. For example, to create a Pmw.Counter megawidget which
+ has a Tkinter.Button as its label, rather than the default
+ Tkinter.Label:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+counter5 = Pmw.Counter(
+ labelpos = 'w',
+ label_text = 'Hello',
+ label_pyclass = Tkinter.Button
+)
+
+</pre>
+</dd>
+</dl>
+
+</dd>
+<dt> <h3>Forwarding methods</h3></dt><dd>
+<p>
+ Since a Pmw megawidget is a normal python class, it both inherits
+ methods from its base classes and also may have other methods
+ defined for it in the usual way.
+ Pmw also supports a third way that a megawidget may gain methods -
+ by 'forwarding' methods to one or more of its subwidgets. This is
+ also known as 'delegating'.
+ For example, a Pmw.Counter megawidget delegates the methods related
+ to its Pmw.EntryField component, <strong>entryfield</strong>, to the
+ component. It does not have to explicitely define methods which
+ call the component methods.
+ This is why we can call <strong>counter2.setentry()</strong> - since
+ <strong>setentry()</strong> is a method of the Pmw.EntryField
+ component, it is available to the Pmw.Counter.
+
+</p>
+<p>
+ Methods already defined by a class or its base classes take
+ precedence over delegated methods. For example, Pmw.Counter
+ inherits a <strong>cget</strong> method from Pmw.MegaArchetype.
+ Therefore, this method is not delegated to the <strong>cget</strong>
+ method of Pmw.EntryField.
+
+</p>
+
+</dd>
+<dt> <h3>Extending Pmw megawidgets</h3></dt><dd>
+
+<p>
+ There are several ways of extending Pmw megawidgets. Firstly, the
+ flexibility of the options and components allows the widget's
+ appearance and behaviour to be greatly modified. Secondly, widgets
+ of the user's choice can be added inside some megawidgets by using
+ the interior() method. The Pmw classes MegaToplevel, MegaWidget,
+ Dialog and LabeledWidget are particularly designed to be extended in
+ this way. For example, to create a dialog window containing a
+ counter:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+dialog = Pmw.Dialog(
+ title = 'Counter dialog',
+ buttons = ('OK', 'Cancel'))
+interior = dialog.interior()
+counter = Pmw.Counter(interior)
+counter.pack(padx = 20, pady = 20)
+</pre>
+</dd>
+</dl>
+
+<center><P ALIGN="CENTER">
+ <IMG SRC = counter2.gif ALT = "Counter 2" WIDTH=266 HEIGHT=126>
+</p></center>
+
+<p>
+ A third way to extend megawidgets is to inherit from (or subclass)
+ them. See <a href="howtobuild.html">How to build Pmw
+ megawidgets</a> for information on how to use inheritance to extend
+ a megawidget by adding new options. For simpler cases, where new
+ methods are to be added to an existing megawidget and/or the default
+ values for some options are to be changed, normal subclassing can be
+ used. For example, to create new classes based on a Pmw.Counter,
+ one with a new method <code>getminutes()</code> and one with a
+ default datatype of 'time' and a white entry background:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+class MinuteCounter1(Pmw.Counter):
+
+ def getminutes(self):
+ return Pmw.timestringtoseconds(self.getvalue()) / 60
+
+class MinuteCounter2(Pmw.Counter):
+
+ def __init__(self, parent = None, **kw):
+ kw['datatype'] = 'time'
+ kw['entry_background'] = 'white'
+ kw['entryfield_value'] = '00:00:00'
+ kw['increment'] = 60
+ apply(Pmw.Counter.__init__, (self, parent), kw)
+</pre>
+</dd>
+</dl>
+
+</dd>
+<dt> <h2>A quick example</h2></dt><dd>
+<p>
+ The following code is a small example of how to use Pmw megawidgets.
+ It is a complete program which displays three ways for the user to
+ enter a value - using an up-down counter, an entry field with
+ validation and a dropdown combobox.
+
+</p>
+<dl>
+<dd>
+<pre>
+import Pmw
+root = Pmw.initialise(fontScheme = 'pmw1')
+
+counter = Pmw.Counter(
+ label_text = 'Counter:',
+ labelpos = 'w',
+ entryfield_value = '00:00:00',
+ entryfield_validate = 'time',
+ datatype='time',
+ increment=5*60,
+)
+counter.pack(fill = 'x', padx = 10, pady = 10)
+
+entry = Pmw.EntryField(
+ label_text = 'Real entry:',
+ labelpos = 'w',
+ value = '+2.9979e+8',
+ validate = 'real',
+)
+entry.pack(fill = 'x', padx = 10, pady = 10)
+
+combo = Pmw.ComboBox(
+ label_text = 'ComboBox:',
+ labelpos = 'w',
+ scrolledlist_items = map(str, range(20))
+)
+combo.pack(fill = 'x', padx = 10, pady = 10)
+
+# Make the labels line up neatly
+Pmw.alignlabels((counter, entry, combo))
+
+root.title('Pmw megawidgets example')
+root.mainloop()
+</pre>
+</dd>
+</dl>
+
+<center><P ALIGN="CENTER">
+ <IMG SRC = example1.gif ALT = "Example 1" WIDTH=321 HEIGHT=140>
+</p></center>
+
+</dd>
+<dt> <h3>Another example</h3></dt><dd>
+<p>
+ The following also shows how to use Pmw megawidgets. It displays a
+ RadioSelect megawidget and an exit button packed into the root
+ window.
+
+</p>
+
+<dl>
+<dd>
+<pre>
+import Tkinter
+import Pmw
+
+def callback(tag):
+ # This is called whenever the user clicks on a
+ # button in the RadioSelect widget.
+ print tag, 'was pressed.'
+
+# Initialise Tkinter and Pmw.
+root = Pmw.initialise(fontScheme = 'pmw1')
+root.title('Pmw RadioSelect demonstration')
+
+# Create and pack a RadioSelect widget.
+radio = Pmw.RadioSelect(
+ command = callback,
+ labelpos = 'w',
+ label_text = 'Food group:')
+radio.pack(padx = 20, pady = 20)
+
+# Add some buttons to the RadioSelect.
+for text in ('Fruit', 'Vegetables', 'Cereals', 'Legumes'):
+ radio.add(text)
+radio.invoke('Vegetables')
+
+# Create an exit button.
+exit = Tkinter.Button(text = 'Exit', command = root.destroy)
+exit.pack(pady = 20)
+
+# Let's go.
+root.mainloop()
+</pre>
+</dd>
+</dl>
+
+<center><P ALIGN="CENTER">
+ <IMG SRC = example2.gif ALT = "Example 2" WIDTH=503 HEIGHT=158>
+</p></center>
+
+</dd>
+<dt> <h3>Using the Tk option database</h3></dt><dd>
+<p>
+ There are several ways to use the Tk option database to customise a
+ Pmw application. Firstly you can customise all the basic Tk widgets
+ in the usual way. For example, to set the background of all
+ Tkinter.Label widgets (whether a megawidget component or not):
+
+</p>
+
+<dl>
+<dd>
+<pre>
+root.option_add('*Label.background', 'pink')
+</pre>
+</dd>
+</dl>
+
+<p>
+ To set the background of all Pmw.EntryField <strong>label</strong>
+ components:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+root.option_add('*EntryField.Label.background', 'green')
+</pre>
+</dd>
+</dl>
+
+<p>
+ To set the background of all Pmw.EntryField components, including
+ the <strong>hull</strong> component:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+root.option_add('*EntryField*background', 'blue')
+</pre>
+</dd>
+</dl>
+
+<p>
+ The above option settings affect basic Tk widgets and, since it is
+ built into the Tk widgets, this functionality is always available.
+ However, to be able to use the Tk option database to set the default
+ values for Pmw megawidget options, <code>Pmw.initialise()</code>
+ must be called with <code>useTkOptionDb = 1</code>. If this is not
+ done, Pmw does not query the Tk option database for megawidget
+ option defaults. This is the default behaviour because there is a
+ slight performance penalty for using the Tk option database.
+
+</p>
+
+<p>
+ Assuming <code>useTkOptionDb</code> has been set, the default
+ buttonbox position of all Pmw.Dialog megawidgets can be changed
+ with:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+root.option_add('*Dialog.buttonboxpos', 'e')
+</pre>
+</dd>
+</dl>
+
+<p>
+ To set the label position of all Pmw.EntryField megawidgets, thus giving
+ them a <strong>label</strong> component by default:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+root.option_add('*EntryField.labelpos', 'w')
+</pre>
+</dd>
+</dl>
+
+</dd>
+</dl>
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw megawidgets 1.2</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw 1.2</h1>
+
+<h2 ALIGN="CENTER">Python megawidgets</h2>
+
+<center><P ALIGN="CENTER">
+<IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+</p></center>
+
+<p>
+Pmw is a toolkit for building high-level compound widgets in Python
+using the Tkinter module.
+</p>
+
+<p>
+It consists of a set of base classes and a library of
+flexible and extensible megawidgets built on this foundation. These
+megawidgets include notebooks, comboboxes, selection widgets, paned
+widgets, scrolled widgets, dialog windows, etc.
+
+</p>
+
+<p>
+<b>Local documentation</b>
+</p>
+
+<dl>
+<dd>
+
+ <IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="features.html">Main features</a>
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="starting.html">Getting started</a> - including downloading
+ and installation
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="howtouse.html">How to use Pmw megawidgets</a> - creating
+ and configuring megawidgets
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="howtobuild.html">How to build Pmw megawidgets</a> - inheriting
+ (sub-classing) from Pmw megawidgets
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="demosandtests.html">Demonstrations and tests</a> - how to run
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="dynamicloader.html">Dynamic loader</a> - also discusses how
+ to "freeze" Pmw
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="refindex.html">Reference manuals</a> - complete documentation
+ of all Pmw classes and functions
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="porting.html">Porting between different versions of Pmw</a>
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="changes.html">Change log</a>
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="todo.html">Todo list</a> and <a href="bugs.html">list of known bugs</a>
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="copyright.html">Copyright</a>
+</dd>
+</dl>
+
+<p>
+<b>External links</b>
+</p>
+
+<dl>
+<dd>
+
+ <IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="http://sourceforge.net/projects/pmw/">Pmw project home page
+ on SourceForge</a> - contains CVS source repository, bug tracking,
+ release distributions, mailing list, etc
+
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="http://lists.sourceforge.net/lists/listinfo/pmw-general">Pmw-general
+ mailing list</a> - subscribe to this list to get announcements of
+ Pmw releases and general discussion on Pmw
+
+<br><IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14>
+ <a href="http://www.ifi.uio.no/~hpl/Pmw.Blt/doc/">A User's Guide
+ to Pmw.Blt</a>
+ - an excellent tutorial and reference covering the Pmw interface
+ to the powerful Blt graph widget, written by Bjørn Ove Thue
+ and Hans Petter Langtangen. You can also download <a
+ href="http://www.ifi.uio.no/~hpl/Pmw.Blt/Pmw.Blt.doc.tar.gz">the full
+ HTML document</a> for local viewing.
+
+</dd>
+</dl>
+
+<p>
+See the
+<a href="http://pmw.sourceforge.net/">Pmw megawidgets home page</a>
+for the latest information about Pmw.
+
+</p>
+
+<p>
+
+Comments, bugs, fixes to the <a
+href="http://lists.sourceforge.net/lists/listinfo/pmw-general">Pmw
+discussion and announcement mailing list</a>.
+
+</p>
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Porting between different versions of Pmw</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Porting between different versions of Pmw</h1>
+
+<p>
+ This document contains a brief guide to porting existing code
+ between different versions of Pmw. It includes significant
+ functionality changes but does not include bug fixes or compatible
+ enhancements. For details of all changes, see
+ <a href="changes.html">Changes to Pmw versions</a>.</p>
+
+<p> <strong>Porting from 0.8.5 to 1.0, 1.1 and 1.2</strong></p>
+
+<ul><li><p>Bug fix, documention and new features only. No
+ backwards-incompatible changes.</p>
+
+</li></ul>
+<p> <strong>Porting from 0.8.4 to 0.8.5</strong></p>
+
+<ul><li><p>Bug fix release only. No interface changes.</p>
+
+</li></ul>
+<p> <strong>Porting from 0.8.3 to 0.8.4</strong></p>
+
+<ul><li><p>Change the <code>setnaturalpagesize()</code> method of Pmw.NoteBook to
+ <code>setnaturalsize()</code> (to be consistent with Pmw.PanedWidget).</p>
+
+</li>
+<li><p>Change Pmw.excludefrombusycursor() to Pmw.setbusycursorattributes().
+ Replace busyCursorName option of Pmw.initialise() with
+ cursorName attribute of Pmw.setbusycursorattributes().</p>
+
+</li>
+<li><p>Several rarely used key bindings for Pmw.ScrolledListBox were
+ removed, changing the behaviour of the megawidget.</p>
+
+</li></ul>
+<p> <strong>Porting from 0.8.1 to 0.8.3</strong></p>
+
+<ul><li><p>The megawidgets Pmw.NoteBookR and Pmw.NoteBookS have been
+ replaced by a new Pmw.NoteBook. The interfaces are not
+ compatible, so see the Pmw.NoteBook reference manual for
+ details.</p>
+
+</li>
+<li><p>Change the get() method of Pmw.OptionMenu to getcurselection()
+ and the remove() method of Pmw.PanedWidget to delete().</p>
+
+</li>
+<li><p>If you use <strong>'end'</strong>, <strong>'default'</strong> or <strong>None</strong> in calls to the
+ index() method of several megawidgets, change these to
+ <strong>Pmw.END</strong>, <strong>Pmw.DEFAULT</strong> and <strong>Pmw.SELECT</strong>, respectively.</p>
+
+</li>
+<li><p>The exclude argument has been removed from Pmw.showbusycursor().
+ Use Pmw.excludefrombusycursor() instead.</p>
+
+</li>
+<li><p>The names of some of the positional arguments in the following
+ methods have changed: MegaArchetype.createcomponent(),
+ ButtonBox.insert(), ButtonBox.add(), MenuBar.addcascademenu(),
+ MenuBar.addmenuitem() and RadioSelect.add().</p>
+
+</li>
+<li><p>The Pmw.maxfontwidth() function has been removed. Use the
+ <code>font_measure()</code> Tkinter method, or if that has not yet been
+ implemented:</p>
+<dl><dd><pre> someWidget.tk.call('font', 'measure', someFont, 'W')</pre></dd></dl>
+
+
+</li>
+<li><p>The Pmw.fontexists() function has been removed. This is
+ because, since Tk8.0, all fonts exist, so it no longer has
+ any meaning.</p>
+
+</li></ul>
+<p> <strong>Porting from 0.8 to 0.8.1</strong></p>
+
+<ul><li><p>The Blt.Graph now supports blt2.4i which is not backwards
+ compatible with blt2.1.</p>
+
+</li></ul>
+<p> <strong>Porting from 0.7 to 0.8</strong></p>
+
+<ul><li><p>The <em>format</em> argument of Pmw.datestringtojdn() now defaults to
+ <strong>'ymd'</strong>. If you want to display dates with year, month and day
+ in a different order, add a <em>format</em> option to
+ Pmw.datestringtojdn() or to the <strong>datatype</strong> option of Pmw.Counter
+ or the <strong>validate</strong> option of Pmw.EntryField.</p>
+
+</li>
+<li><p>The justify() method from Pmw.ScrolledListBox has been removed.
+ Use the xview() or yview() methods instead.</p>
+
+</li>
+<li><p>Replace the getFrame() method of Pmw.ScrolledFrame with the
+ interior() method.</p>
+
+</li>
+<li><p>Replace the <strong>ringpadx</strong> and <strong>ringpady</strong> options of Pmw.Group by
+ padding the megawidget itself or by padding the children of the
+ megawidget. </p>
+
+</li>
+<li><p>Replace the canvasbind() and canvasunbind() methods of
+ Pmw.Balloon with tagbind() and tagunbind().</p>
+
+</li>
+<li><p>The return value of Pmw.EntryField <strong>command</strong> callback is now
+ ignored. Previously, if the callback destroyed the megawidget,
+ it was required to return the string <strong>'break'</strong>, to work around a
+ problem in the event handling mechanism in Tkinter. With python
+ 1.5.2, Tkinter has been fixed. Therefore, user-supplied
+ callback functions should use Pmw.hulldestroyed to check if the
+ megawidget has been destroyed before performing any operations
+ on it.</p>
+
+</li>
+<li><p>If you require the <strong>'pmw1'</strong> fontScheme when running under
+ Microsoft Windows and Macintosh, you will need to set the Tk
+ font options manually.</p>
+
+</li></ul>
+<p> <strong>Porting from 0.6 to 0.7</strong></p>
+
+<ul><li><p>Replace the <strong>maxwidth</strong> option of Pmw.EntryField with the <strong>'max'</strong>
+ field of the <strong>validate</strong> option.</p>
+
+</li>
+<li><p>To specify that there should be no validation performed for a
+ Pmw.EntryField, the <strong>validate</strong> option must be None, not <strong>''</strong> as
+ before.</p>
+
+</li>
+<li><p>The date and time values of the Pmw.EntryField <strong>validate</strong> option
+ (such as <strong>'date_dmy'</strong> and <strong>'time24'</strong>, etc) are no longer supported.
+ Instead use a dictionary as the value of the <strong>validate</strong> option
+ with <strong>'date'</strong> or <strong>'time'</strong> in the <strong>'validator'</strong> field. Include
+ other fields in the dictionary to further specify the
+ validation.</p>
+
+</li>
+<li><p>Pmw.Counter no longer supports the old date and time values for
+ the <strong>datatype</strong> option. Use a dictionary with a <strong>'counter'</strong>
+ field of <strong>'date'</strong> or <strong>'time'</strong> and other fields to further
+ specify the counting.</p>
+
+</li>
+<li><p>Pmw.Counter no longer supports the <strong>min</strong> and <strong>max</strong> options. Use
+ the Pmw.EntryField <strong>validate</strong> option instead.</p>
+
+</li>
+<li><p>The bbox method of Pmw.ScrolledListBox now refers to the bbox
+ method of the <strong>listbox</strong> component, not the <strong>hull</strong> component.</p>
+
+</li>
+<li><p>By default, Pmw.MenuBar now automatically adds hotkeys to menus
+ and menu items for keyboard traversal. To turn this off, use the
+ <code>hotkeys = 0</code> option.</p>
+
+</li>
+<li><p>The createcomponent() method now disallows the creation of
+ component names containing an underscore. If any component
+ names contain an underscore, rename them.</p>
+
+</li></ul>
+<p> <strong>Porting from 0.5 to 0.6</strong></p>
+
+<p> To port applications using Pmw version 0.5 to version 0.6, make
+ sure you are using python1.5. Then, simply change any lines in
+ your application like this:</p>
+
+<dl><dd><pre> from PmwLazy import Pmw</pre></dd></dl>
+
+<p> to this:</p>
+
+<dl><dd><pre> import Pmw</pre></dd></dl>
+
+<p> Also, if you have added the <code>lib</code> directory of a specific version
+ of Pmw to <code>sys.path</code> or <code>PYTHONPATH</code>, this can be removed, as long
+ as Pmw can now be found from the default path, such as in the
+ python <code>site-packages</code> directory.</p>
+
+<p> <strong>Porting from 0.2 to 0.4</strong></p>
+
+<ul><li><p>To get Pmw.0.2 default fonts (helvetica with bold italic menus
+ and italic scales) initialise with:</p>
+<dl><dd><pre> Pmw.initialise(fontScheme = 'pmw1')</pre></dd></dl>
+
+<p> If no <strong>fontScheme</strong> is given, the standard Tk default fonts are used.</p>
+
+
+</li>
+<li><p>Remove all calls to setdefaultresources(), usual(), keep(),
+ renameoptions(), ignore() and defineoptiontypes().</p>
+
+</li>
+<li><p>Move call to defineoptions() to before call to base class
+ constructor, create optiondefs tuple from self.defineoptions
+ arguments, then call defineoptions().</p>
+
+</li>
+<li><p>Remove resource class and name from optiondefs.</p>
+
+</li>
+<li><p>The last element in the optiondefs tuple (callback function)
+ must be given (may be None).</p>
+
+</li>
+<li><p>Add to classes currently without any options:</p>
+<dl><dd><pre> optiondefs = ()
+ self.defineoptions(kw, optiondefs)</pre></dd></dl>
+
+
+</li>
+<li><p>Use createcomponent() to create components - this replaces the
+ calls to the component widget constructor and to
+ registercomponent().</p>
+
+</li>
+<li><p>Do not inherit from Pmw.LabeledWidget. Instead, replace with
+ Pmw.MegaWidget with labelpos and labelmargin options and a call
+ to self.createlabel(). If calling createlabel(), must replace
+ pack() with grid().</p>
+
+</li>
+<li><p>When calling a megawidget constructor, include subcomponent name when
+ setting subcomponent options (eg labeltext -> label_text)</p>
+
+</li>
+<li><p>The items option of ScrolledListBox is an initialisation option
+ only - use setlist() method after initialisation.</p>
+
+</li>
+<li><p>The <strong>autorelief</strong> option for Counter, EntryField, ScrolledText,
+ TextDialog has been removed.</p>
+
+</li>
+<li><p>ScrolledListBox.getcurselection() always returns a tuple of strings,
+ possibly of zero length.</p>
+
+</li>
+<li><p>Counter increment is always initialised to 1.</p>
+
+</li>
+<li><p>The <strong>'time'</strong> Counter <strong>datatype</strong> option has been replaced by
+ <strong>'timeN'</strong> and <strong>'time24'</strong>.</p>
+
+</li>
+<li><p>The <strong>'time'</strong> EntryField <strong>validate</strong> option has been replaced by
+ <strong>'timeN'</strong> and <strong>'time24'</strong>.</p>
+
+</li>
+<li><p>Replace call to initialise() with initialiseoptions(), removing
+ "kw" arg. This should always be the last line in a megawidget
+ constructor.</p>
+
+</li>
+<li><p>Replace hide() with withdraw().</p>
+
+</li>
+<li><p>Now need iconpos option for MessageDialogs with icon_bitmap option set.</p>
+
+</li>
+<li><p>Example megawidget class definition:</p>
+
+</li></ul>
+<dl><dd><pre>class MyBigWidget(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = (
+ ('errorbackground', 'pink', None),
+ ('maxwidth', 0, self._myfunc),
+ ('myinit', 'good', Pmw.INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ self._widget = self.createcomponent('component',
+ (('alias', 'component_alias'),), None,
+ Tkinter.Button, (interior,))
+ self._widget.grid(column=0, row=0, sticky='nsew')
+
+ self.createlabel(interior)
+
+ # Initialise instance variables.
+ self.deriveddummy = None
+
+ # Check keywords and initialise options.
+ self.initialiseoptions(MyBigWidget)
+</pre></dd></dl>
+
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw reference manual index</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw reference manual<br>index</h1>
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+<dl><dt> <strong>Base classes</strong></dt><dd>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="MegaArchetype.html">Pmw.MegaArchetype</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="MegaWidget.html">Pmw.MegaWidget</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="MegaToplevel.html">Pmw.MegaToplevel</a>
+</dd></dl>
+<dl><dt> <strong>Widgets</strong></dt><dd>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ButtonBox.html">Pmw.ButtonBox</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ComboBox.html">Pmw.ComboBox</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="Counter.html">Pmw.Counter</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="EntryField.html">Pmw.EntryField</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="Group.html">Pmw.Group</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="HistoryText.html">Pmw.HistoryText</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="LabeledWidget.html">Pmw.LabeledWidget</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="MainMenuBar.html">Pmw.MainMenuBar</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="MenuBar.html">Pmw.MenuBar</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="MessageBar.html">Pmw.MessageBar</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="NoteBook.html">Pmw.NoteBook</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="OptionMenu.html">Pmw.OptionMenu</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="PanedWidget.html">Pmw.PanedWidget</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="RadioSelect.html">Pmw.RadioSelect</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ScrolledCanvas.html">Pmw.ScrolledCanvas</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ScrolledField.html">Pmw.ScrolledField</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ScrolledFrame.html">Pmw.ScrolledFrame</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ScrolledListBox.html">Pmw.ScrolledListBox</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ScrolledText.html">Pmw.ScrolledText</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="TimeCounter.html">Pmw.TimeCounter</a>
+</dd></dl>
+<dl><dt> <strong>Dialogs</strong></dt><dd>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="AboutDialog.html">Pmw.AboutDialog</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="ComboBoxDialog.html">Pmw.ComboBoxDialog</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="CounterDialog.html">Pmw.CounterDialog</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="Dialog.html">Pmw.Dialog</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="MessageDialog.html">Pmw.MessageDialog</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="PromptDialog.html">Pmw.PromptDialog</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="SelectionDialog.html">Pmw.SelectionDialog</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="TextDialog.html">Pmw.TextDialog</a>
+</dd></dl>
+<dl><dt> <strong>Miscellaneous</strong></dt><dd>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="Balloon.html">Pmw.Balloon</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="Blt.html">Pmw.Blt</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="Color.html">Pmw.Color</a>
+<IMG SRC = blueball.gif ALT = "" WIDTH=14 HEIGHT=14><a href="PmwFunctions.html">Module functions</a>
+</dd></dl>
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Getting started with Pmw</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Getting started with Pmw</h1>
+
+<center><P ALIGN="CENTER">
+<IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+</p></center>
+
+<dl>
+<dt> <h2>Introduction</h2></dt><dd>
+<p>
+This document describes how to fetch and install Pmw, and how to run
+the demonstrations and tests.
+
+</p>
+
+</dd>
+<dt> <h2>Requirements</h2></dt><dd>
+<p>
+Pmw.1.2 requires the _tkinter and Tkinter modules. It works
+with python versions 1.5.2 and greater (tested up to 2.2.1) and Tk
+versions 8.0 and greater (tested up to 8.3.2).
+
+</p>
+
+<p>
+If the BLT extension to Tk is present, Pmw will use the BLT busy
+command during modal dialogs to display a clock cursor. Also, the
+Pmw.Blt interface to the BLT busy, graph, stripchart, tabset and
+vector commands will be available. BLT versions 2.4i and greater are
+supported (tested up to 2.4u). You can find BLT at
+<a href="http://www.tcltk.com/blt/">http://www.tcltk.com/blt/</a>.
+
+</p>
+
+</dd>
+<dt> <h2>Distribution and installation</h2></dt><dd>
+<p>
+Releases of the Pmw distribution are available via http from
+<code>http://download.sourceforge.net/pmw/</code>. This release is available
+as <a href="http://download.sourceforge.net/pmw/Pmw.1.2.tar.gz">
+<code>Pmw.1.2.tar.gz</code></a>, released on 5 August 2003.
+This is a compressed tar file. Under Linux, Unix, etc, you will need to
+unpack it using <code>tar</code> and you may also need to use
+<code>gzip</code> or <code>gunzip</code> to uncompress it.
+Under Microsoft Windows, you will need a program such as WinZip (<a
+href="http://www.winzip.com">http://www.winzip.com</a>) that can
+unpack the gzipped tar files. You may need to change the suffix of
+the file to <strong>.tgz</strong> for WinZip to recognise it.
+
+</p>
+
+<p>
+
+This will unpack into a directory named Pmw. You now need to put this
+directory somewhere python can find it, preferably in one of the
+standard places, such as in the <code>site-packages</code> directory
+(eg: <code>/usr/lib/python2.2/site-packages/Pmw</code>) or the
+<code>sys.prefix</code> directory (eg: <code>C:\Program
+Files\Python\Pmw</code> or <code>/usr/lib/python2.2</code>).
+
+</p>
+
+<p>
+
+For example, under Unix, assuming you have placed the tar file in the
+<code>/tmp</code> directory, you can simply run the following
+commands:
+
+</p>
+
+<dl>
+<dd>
+<pre>
+cd /usr/lib/python2.2/site-packages
+gunzip /tmp/Pmw.1.2.tar.gz (or gzip -d /tmp/Pmw.1.2.tar.gz)
+tar xvf /tmp/Pmw.1.2.tar
+</pre>
+</dd>
+</dl>
+
+<p>
+
+If you do not have write permission for these standard directories,
+place the Pmw
+directory somewhere on your <code>PYTHONPATH</code> or
+<code>sys.path</code>. If this is not possible, place the Pmw
+directory somewhere else and add the parent directory to your
+<code>PYTHONPATH</code> or <code>sys.path</code>.
+
+</p>
+
+<p>
+
+If you have previously installed Pmw version 0.6 or later, then the
+new version can share the same <code>Pmw</code> directory as the
+previous versions. You will need to perform the <code>tar</code>
+extraction in the directory containing (that is, the parent directory
+of) the existing <code>Pmw</code> directory. By default, your
+applications will use the most recent version of Pmw. If required,
+the function <code>Pmw.setversion()</code> can be used to specify a
+version to be used. See the reference manual for details. If you are
+no longer using the older versions, you can safely remove the
+corresponding subdirectories from the <code>Pmw</code> directory.
+
+</p>
+
+<p>
+
+If you need assistance in installing BLT under Unix, please contact me
+(<a href="mailto:gregm@iname.com"><i>gregm@iname.com</i></a>) and I
+will try to help. For other operating systems, such as Microsoft or
+Macintosh, you should try asking the python newsgroup. If anyone can
+give me a description of how to install BLT under other operating
+systems please contribute it and I will place it here.
+
+</p>
+
+</dd>
+<dt> <h2>Documentation</h2></dt><dd>
+<p>
+The <code>doc</code> directory for each Pmw version contains all the
+documentation for that version of Pmw. See the local <a
+href="index.html">home page</a> for a complete list of documents. The
+files in this directory are also available from the <a
+href="http://pmw.sourceforge.net/">official Pmw home page</a>.
+
+</p>
+
+<p>
+An excellent tutorial and reference covering the Pmw interface to the
+powerful Blt graph widget, "<a
+href="http://www.ifi.uio.no/~hpl/Pmw.Blt/doc/">A User's Guide to
+Pmw.Blt</a>" written by Bjørn Ove Thue and Hans Petter Langtangen, is
+available. You can also download <a
+href="http://www.ifi.uio.no/~hpl/Pmw.Blt/Pmw.Blt.doc.tar.gz">the full
+HTML document</a> for local viewing.
+
+</p>
+</dd>
+<dt> <h2>Demonstrations and tests</h2></dt><dd>
+<p>
+ A good way to get an overview of the functionality provided by Pmw
+ is to run the demonstrations and tests and look at the demonstration
+ code. To view a comprehensive demonstration of many of the features
+ of Pmw run the <code>All.py</code> script, which can be found in the
+ <code>demos</code> subdirectory of each version of Pmw.
+</p>
+
+<p>
+
+ You do not have to install Pmw to run the demonstrations and tests,
+ simply change into the appropriate directory and run the file
+ <code>All.py</code>. See <a
+ href="demosandtests.html">Demonstrations and tests</a> for more
+ information about running the demonstrations and tests and how to
+ create your own.
+</p>
+
+<p>
+
+Note that there are some bugs in later versions of BLT (at least 2.4t
+and 2.4u) which cause some tests of Pmw.Blt.Graph to crash with
+python2.0 under Linux. These tests have been commented out (until BLT
+is fixed).
+
+<a name=contributions></a>
+</dd>
+<dt> <h2>Contributions welcome</h2></dt><dd>
+
+<p>
+If you create some whiz-bang megawidgets and would like to contribute
+them to Pmw, they will be most welcome. You should be able to get
+some idea of the coding style used in Pmw code by reading <a
+href="howtobuild.html">How to build Pmw megawidgets</a> and by looking
+at the Pmw library code itself in the <code>lib</code> directory of
+each Pmw version.
+
+</p>
+
+<p>
+If you would like to contribute a megawidget, it would be preferable if it
+also came with a simple demonstration and a test script. See <a
+href="demosandtests.html">Demonstrations and tests</a> for information
+about how to create new demonstrations and tests.
+</p>
+
+<p>
+Each megawidget should also have a reference manual describing its
+options, components and methods.
+
+</p>
+
+<a name=docgen></a>
+</dd>
+<dt> <h2>Generating the documentation</h2></dt><dd>
+
+<p>
+The released reference manuals are
+automatically generated by merging specially marked-up text with the
+output from megawidget query methods, such as
+<code>components()</code>, <code>options()</code> and
+<code>componentaliases()</code>, and various other introspective
+devices. If you are interested to see how the documentation is generated,
+you can fetch the marked-up text and the python script to convert the
+text to html from
+<a href="http://download.sourceforge.net/pmw/Pmw.1.2.docsrc.tar.gz">
+<code>http://download.sourceforge.net/pmw/Pmw.1.2.docsrc.tar.gz</code>
+</a>. Download this
+file into the <code>Pmw/Pmw_1_2</code> directory of the Pmw source
+tree. Unzip and untar the file. This will create a
+<code>docsrc</code> sub-directory of <code>Pmw/Pmw_1_2</code>. If
+you want to keep the documentation which came with the Pmw
+distribution, rename the old <code>doc</code> directory. Then change
+directory to <code>docsrc</code> and run <code>createmanuals.py</code>.
+After printing lots of warnings about documentation that has not been
+written yet, this will create a new <code>doc</code> directory
+containing all the html documentation.
+</p>
+
+<p>
+Here is an example set of commands to unpack the documentation source
+and regenerate the documentation, assuming you have downloaded the
+source in the Pmw/Pmw_1_2 directory:
+</p>
+
+<dl>
+<dd>
+<pre>
+cd Pmw/Pmw_1_2
+gunzip Pmw.1.2.docsrc.tar.gz
+tar xvf Pmw.1.2.docsrc.tar
+mv doc doc.old
+cd docsrc
+./createmanuals.py
+</pre>
+</dd>
+</dl>
+
+<p>
+If running under Unix, you will need to run the
+<code>createmanuals.py</code> script with a valid DISPLAY environment
+variable, since it creates each megawidget and then queries it for its
+options, components, etc. This is because Tk (and hence Tkinter)
+requires a connection to an X server to run.
+
+</p>
+
+</dd>
+<dt> <h2>Future plans and bugs</h2></dt><dd>
+
+<p>
+The <a href="todo.html">todo list</a> contains a long list of of
+suggestions, bugs and enhancements for Pmw. If you are interested in
+doing any of these, please let the maintainer
+(<a href="mailto:gregm@iname.com"><i>gregm@iname.com</i></a>) know.
+Some of the items in the todo list may be considered bugs. There are
+also some other problems due to idiosyncrasies in the implementation
+of Tk.
+
+</p>
+
+</dd>
+<dt> <h2>Licence</h2></dt><dd>
+
+<p>
+The official Pmw licence (see <a href="copyright.html">copyright</a>)
+basically lets you do anything with Pmw as long as you don't hurt anyone.
+There is also another licence, the "Postcard Licence":
+</p>
+<cite>
+"I'd like to get a postcard from you! I'm interested in who is using
+Pmw, where you live and where in the world Pmw is doing it's job"
+</cite>
+<p>
+Please send me an e-mail to
+<a href="mailto:gregm@iname.com"><i>gregm@iname.com</i></a>
+to get my postal address.
+</p>
+
+</dd>
+<dt> <h2>Acknowledgements</h2></dt><dd>
+
+<p>
+The initial ideas for Pmw were blatantly stolen from the itcl
+extensions
+<a href="http://www.tcltk.com/itk">[incr Tk]</a>
+by Michael McLennan and
+<a href="http://www.tcltk.com/iwidgets">[incr Widgets]</a>
+by Mark Ulferts. Several of the megawidgets are direct translations
+from the itcl to python.
+</p>
+
+<p>
+The base classes and most megawidgets were written by Greg McFarlane
+and Peter Munnings. Contributed megawidgets include: Pmw.TimeCounter
+by Joe VanAndel, Pmw.Group and an early version of Pmw.NoteBook by Case Roole,
+Pmw.ScrolledCanvas, Pmw.ScrolledFrame and another early version of
+Pmw.NoteBook by Joe Saltiel
+and Pmw.OptionMenu by Roman Sulzhyk. A big thank you to the following
+people for their bug reports, fixes, enhancements and suggestions:
+
+David Ascher,
+Robin Becker,
+Siggy Brentrup,
+Mark Colclough,
+Jerome Gay,
+Clemens Hintze,
+Rob Hooft
+Jack Jansen,
+Jonathan Kelly,
+Magnus Kessler,
+Matthias Klose,
+Andreas Kostyrka,
+Fredrik Lundh,
+Magnus Lycka,
+Graham Matthews,
+Dieter Maurer,
+Michael McLay,
+Daniel Michelson,
+Georg Mischler,
+Rob Pearson,
+Case Roole,
+Joe Saltiel,
+Roman Sulzhyk,
+Shen Wang,
+Chris Wright,
+ and
+Guido van Rossum.
+
+Special thanks to Case Roole and Michael McLay for help with getting
+Pmw to work with python packages and many other nifty features.
+
+My deepest apologies if I have forgotten anyone. Please let me know.
+
+</p>
+
+<p>
+The Pmw home page and project site is made available courtesy of
+<a href="http://sourceforge.net">SourceForge</a>.
+
+</p>
+<p>
+
+The current maintainer is Greg McFarlane. I monitor the <a
+href="http://lists.sourceforge.net/lists/listinfo/pmw-general">Pmw
+discussion and announcement mailing list</a> so please send any
+problems, comments, suggestions or enhancements to the list. You may
+also contact me directly at <a
+href="mailto:gregm@iname.com"><i>gregm@iname.com</i></a>.
+
+</p>
+</dd>
+</dl>
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+
+ <html>
+ <head>
+ <meta name="description" content="Pmw - a toolkit for building high-level compound widgets in Python">
+ <meta name="content" content="python, megawidget, mega widget, compound widget, gui, tkinter">
+ <title>Pmw todo list</title>
+ </head>
+
+ <body bgcolor="#ffffff" text="#000000" link="#0000ee"
+ vlink="551a8b" alink="ff0000">
+
+ <h1 ALIGN="CENTER">Pmw todo list</h1>
+
+<p>
+This is a long list of suggestions and enhancements for Pmw. If
+you are interested in doing any of these, please let the Pmw maintainer
+(<em>gregm@iname.com</em>) know.</p>
+
+<p><strong>New Pmw megawidgets</strong></p>
+<ul><li><p>Multicolumn listbox.</p>
+<p> Useful features - smooth scrolling, embedded images, different
+ fonts and colours, text correctly masked when it is longer than
+ its column width, interactive resizing of columns.</p>
+
+<p> Probably should be implemented as canvas widget rather than by
+ using multiple frames or multiple listboxes. There would be a
+ lot of work needed to position all the elements - you can't just
+ pack or grid them.</p>
+
+
+</li>
+<li><p>File dialog.</p>
+
+</li>
+<li><p>Main window class (App class), with menu bar, information line
+ with status boxes and an about box. (See iwidgets' mainwindow
+ class for example.) This should handle creation of multiple main
+ windows, recycling of unused main windows and should exit if
+ last open main window is closed.</p>
+
+</li>
+<li><p>Searchable text megawidget.</p>
+
+</li>
+<li><p>Tree browser.</p>
+
+</li>
+<li><p>Check out Doug Hellmann's contributed megawidgets at
+ <http://www.mindspring.com/~doughellmann/Projects/PmwContribD> or
+ <http://members.home.net/doughellmann/PmwContribD/>
+ and integrate into Pmw.</p>
+
+</li></ul>
+
+<p><strong>Changes to current megawidgets</strong></p>
+<p> MegaToplevel</p>
+<ul><li><p>Modify activate() geometry argument to allow window positioning
+ relative to the pointer, another window or the screen and
+ allow the centering of the window relative to the
+ positioning point or by a specified offset. Also add the
+ ability to position the window so that the mouse is over a
+ particular widget in the toplevel.</p>
+<p> Should handle all combinations of</p>
+<dl><dd><pre> when (always/first)
+ where (center/geometry/mouse)
+ parent (screen/window)
+
+ and None (don't position)</pre></dd></dl>
+
+
+<p> Check Tix4.1.0/library/DialogS.tcl center method for how to
+ center over another window</p>
+
+<p> Check iwidget's shell.itk for code to center widget over
+ screen or another widget.</p>
+
+<p> See Pmw.Balloon code for how to position over pointer.</p>
+
+<p> Tcl code to center over another (parent) window:</p>
+<dl><dd><pre> # center client relative to master (default xoff, yoff = -1)
+ set geomaster [split [wm geometry $master] "x+"]
+ set geoclient [split [wm geometry $client] "x+"]
+
+ if {$xoff == -1} {
+ set xoff [expr (
+ ([lindex $geomaster 0] - [lindex $geoclient 0]) / 2)]
+ }
+ set newxpos [expr [lindex $geomaster 2] + $xoff]
+
+ if {$yoff == -1} {
+ set yoff [expr (
+ ([lindex $geomaster 1] - [lindex $geoclient 1]) / 2)]
+ }
+ set newypos [expr [lindex $geomaster 3] + $yoff]
+
+ wm geometry $client +$newxpos+$newypos</pre></dd></dl>
+
+
+<p> More tcl code to center dialog over another (parent) window:</p>
+<dl><dd><pre> (args: parent dlg)
+ # First, display the dialog offscreen to get dimensions.
+ set screenW [winfo screenwidth $parent]
+ set screenH [winfo screenheight $parent]
+ set w [expr $screenW + 1]
+ wm geometry $dlg +$w+0
+ update
+
+ # Get relative center of parent.
+ set w [winfo width $parent]
+ set h [winfo height $parent]
+ set w [expr $w/2]
+ set h [expr $h/2]
+
+ # Get and add screen offset of parent.
+ set w [expr $w + [winfo rootx $parent]]
+ set h [expr $h + [winfo rooty $parent]]
+
+ # Get dimensions of dialog.
+ set dlgW [winfo width $dlg]
+ set dlgH [winfo height $dlg]
+
+ # Make adjustments for actual dimensions of dialog.
+ set w [expr $w - $dlgW / 2]
+ set h [expr $h - $dlgH / 2]
+
+ # Let's keep the entire dialog onscreen at all times.
+ # Center in screen if things are awry.
+ set recenter 0
+ if { $w < 0 } { set recenter 1 }
+ if { $h < 0 } { set recenter 1 }
+ if { [expr $w + $dlgW] > $screenW } { set recenter 1 }
+ if { [expr $h + $dlgH] > $screenH } { set recenter 1 }
+ if { $recenter } {
+ set w [expr ($screenW -$dlgW) / 2]
+ set h [expr ($screenH - $dlgH) / 2]
+ }
+
+ wm geometry $dlg +$w+$h</pre></dd></dl>
+
+
+
+</li>
+<li><p>Add geometry argument to show() (same as activate() above).</p>
+
+</li></ul>
+
+<p> Dialog</p>
+<ul><li><p>Add label (header?) to Dialog class. May not be necessary, or
+ too complicated.</p>
+
+</li></ul>
+
+<p> ButtonBox</p>
+<ul><li><p>When a horizontal ButtonBox is stretched, the left button
+ stays anchored to the left edge and there is too much space
+ between the last button and the right edge.</p>
+
+</li>
+<li><p>Add an option to either evenly space the buttons across the
+ button box, or to keep them together and justify them to the
+ left, right or center. Check that deleting buttons works
+ correctly.</p>
+
+</li></ul>
+
+<p> ComboBox</p>
+<ul><li><p>Remove arrowrelief option from ComboBox and do what counter
+ does: gets value of arrow's relief just before sinking it,
+ then restores it later.</p>
+
+</li>
+<li><p>Change bindings: remove all bindings from arrow key and remove
+ arrow key from <tab> focus sequence; only implement these
+ bindings on the entry widget:</p>
+<dl><dd><pre> Up popup dropdown list, scroll up if already displayed
+ Down popup dropdown list, scroll down if already displayed
+ Esc popdown dropdown list, return entry to previous value
+ Enter popdown dropdown list, execute current selection</pre></dd></dl>
+
+<p> Remove bindings from listbox and scrollbar(s), so that all
+ bindings are via the entry widget?</p>
+
+
+</li>
+<li><p>When entering keys when list is displayed, scroll list to
+ first entry beginning with entered keys. If no match,
+ scroll list to top.</p>
+
+</li>
+<li><p>Remove many of the arrow bindings from Pmw.ComboBox - there
+ are just too many key bindings on the arrow button. There
+ is no need for it to respond to keys such as the up/down
+ keys when the adjacent Entry widget already does so. I
+ propose to remove all Pmw.ComboBox arrow button key bindings
+ except for <space>, which can be used to bring up the
+ dropdown list. The Entry widget behaviour would remain
+ unchanged: when it has focus, you can use the up/down keys
+ to go to the next/previous entries and then use <Return> to
+ invoke the selection command.</p>
+<p> Alternatively, make the bindings the same as the MS-Windows
+ combobox. (Use the url entry field in Navigator or IE as an
+ example of MS-Windows behaviour). These have been reported
+ to be:</p>
+<ul><li><p>All mouse actions are exclusively triggered by the left
+ button.</p>
+
+</li>
+<li><p>Right button displays "Direkthilfe" on my german system
+ ("Direct Help"). This is a floating button, that
+ triggers display of a tool tip like the |?| button that
+ appears next to the |x| at the right end of the title
+ bar of some native windows dialogs.</p>
+
+</li>
+<li><p>The arrow is very slim (acutally flat: width/height is
+ about 2/1)</p>
+
+</li>
+<li><p>Entry and popup have the same color ("window color")</p>
+
+</li>
+<li><p>The popup has a 1 pixel dark border, no spacing between
+ popup and scrollbar.</p>
+
+</li>
+<li><p>If the box has the focus, the full entry is displayed in
+ "selected" style.</p>
+
+</li>
+<li><p>If the box has the focus, up and left keys rotate items
+ up, down and right keys rotate items down, all with
+ immediate effect.</p>
+
+</li>
+<li><p>If the box has the focus, keys a-z (not case sensitive)
+ rotate through the items with same first character, with
+ immediate effect.</p>
+
+</li>
+<li><p>No separate focus for the arrowbutton</p>
+
+</li>
+<li><p>Discussing how the combobox behaves with arrow keys when
+ it has the focus: "The concept is almost identical to
+ what you already have, just gives more visual feedback.
+ In your current implementation you allow to rotate
+ through the values with the up and down arrow keys,
+ showing the strings in the entryfield, and accepting the
+ values when the user presses the spacebar (hmmm, how can
+ I exit this without moving back to the original value
+ manually?). On Windows, the choice is not shown in the
+ entryfield, but the popup opens when you press the up or
+ down arrow keys, as if you clicked on the arrowbutton,
+ and you then navigate the values in the listbox. This
+ avoids the display of not finally selected values in the
+ entryfield and is a lot more obvious and less confusing.
+ The current behaviour certainly confused me, which is
+ why I first proposed the changes to the moveup/down
+ methods." (Georg Mischler)</p>
+
+</li></ul>
+
+<p> Also, check bindings on other megawidgets for consistency.</p>
+
+
+</li>
+<li><p>Modify Pmw.ComboBox so that the width of the entry widget is
+ forced to be the same as the width of the dropdown listbox.
+ If the "width" option to the standard listbox is 0, Tk sets
+ the requested width of the listbox to be just large enough
+ to hold the widest element in the listbox. Using this
+ option, I can see that listbox.winfo_reqwidth() is changing
+ as I insert items into an unmapped listbox. The question
+ is, how do I get notified of these events so that I can set
+ the width of the entry?</p>
+<p> The problem is that the listbox is in another toplevel which
+ has not yet been displayed, so I can't bind to <Configure>
+ to determine its width.</p>
+
+<p> One suggestion is to override the insert and delete methods
+ of the Listbox class. The problem with this is what if the
+ font changed, or the borderwidth, etc? You would need to
+ override and check many more methods.</p>
+
+
+</li>
+<li><p>Add ability to tearoff dropdown list (suggested by Dean N.
+ Williams).</p>
+
+</li>
+<li><p>Should be able to disable/enable arrow button.</p>
+
+</li></ul>
+
+<p> Counter</p>
+<ul><li><p>Add option for different increment/decrement behaviour. For
+ example, assuming increment is 1:</p>
+<ol><li><p>Current behaviour - move to the next multiple of the
+ increment, eg: 1.0 -> 2.0, 1.234 -> 2.0</p>
+
+</li>
+<li><p>Add or subtract the increment to whatever is displayed,
+ eg: 1.0 -> 2.0, 1.234 -> 2.234</p>
+
+</li>
+<li><p>Move to the next multiple of the increment, offset by some value.
+ eg: (if offset is 0.5) 0.5 -> 1.5, 1.234 -> 1.5, 1.678 -> 2.5</p>
+
+</li></ol>
+
+</li>
+<li><p>Add wrap option (to wrap around at limits) (then don't need
+ time24 arg to <strong>'time'</strong> datatype).</p>
+
+</li>
+<li><p>Add a state option to disable Counter.</p>
+
+</li>
+<li><p>Add option to Counter to allow the buttons to be on the same
+ side, one on top of the other, like Tix, Itcl, Motif,
+ Windows 95, etc. There should probably also be an option to
+ lay the current large buttons on the same side of the entry
+ field, next to each other.</p>
+
+</li>
+<li><p>Redo TimeCounter using vertical Counter, add limitcommand
+ option to Counter to allow overflow from seconds to minutes
+ to hours</p>
+
+</li></ul>
+
+<p> Arrowed megawidgets (Counter, ComboBox, TimeCounter)</p>
+<ul><li><p>Potential construction speed up if Canvas arrows are replaced
+ by Label with Bitmap or BitmapImage. The hard part would be
+ to make the bitmap change size depending on size of Label.</p>
+
+</li>
+<li><p>Pmw.drawarrow should draw arrows which look like Tk cascade
+ menu arrows.</p>
+
+</li></ul>
+
+<p> EntryField</p>
+<ul><li><p>Can it be modified to change all entered characters to upper
+ or lower case automatically? Or first-upper or
+ first-of-each-word-upper?</p>
+
+</li>
+<li><p>If the validity of the currently displayed text is ERROR,
+ allow any changes, even those which result in invalid text.
+ This is useful when invalid data has been given to the
+ <strong>value</strong> option and the user is trying to correct it.</p>
+
+</li></ul>
+
+<p> LabeledWidget</p>
+<ul><li><p>Add tix-style border.</p>
+
+</li></ul>
+
+<p> MenuBar</p>
+<ul><li><p>Maybe Pmw.MenuBar should also have (optional) balloon help
+ for menu items as well as menu buttons. I am not sure
+ whether users would find this useful.</p>
+
+</li>
+<li><p>The status help hints do not appear when using F10/arrow
+ keys.</p>
+
+</li>
+<li><p>Look at the Tk8.0 menu demo and check the help bindings for
+ ideas, in particular, how can you get help when using
+ keyboard bindings.</p>
+
+</li>
+<li><p>Check the new menu features in Tk8.0 for creating "native"
+ menu bars and the special ".help" menu.</p>
+
+</li>
+<li><p>Add index() method.</p>
+
+</li>
+<li><p>Add a <strong>'position'</strong> option to addmenu and deletemenu methods.
+ This option should accept an index number, a menuName or
+ <strong>Pmw.END</strong>.</p>
+
+</li>
+<li><p>Look at itcl menubar for ideas.</p>
+
+</li></ul>
+
+<p> Balloon</p>
+<ul><li><p>Positioning of the balloon with respect to the target
+ widget or canvas item: There are a number of ways that
+ Pmw.Balloon could be improved. For example, currently the
+ the top left corner of the balloon is positioned relative to
+ the bottom left corner of the target, offset by the
+ [xy]offset options. These options apply to all targets -
+ they can not be set differently for different targets.</p>
+<p> To make it more configurable, the user should be able to
+ specify, for each target:</p>
+<ul><li><p>the base position in the target relative to which the
+ balloon should be placed (n, s, e, w, nw, sw, ne, se, c)
+ (Currently sw)</p>
+
+</li>
+<li><p>the x and y offsets (Default (20, 1))</p>
+
+</li>
+<li><p>the position in the balloon that should be placed at the
+ offset position (n, s, e, w, nw, sw, ne, se, c)
+ (Currently nw)</p>
+<p> Note, if this is anything other than nw,
+ update_idletasks() will need to be called to get the
+ size of the balloon before it is positioned - there is a
+ possibility that this may cause weird ugly flashing.</p>
+
+
+</li>
+<li><p>whether either the base x or y position should be taken
+ relative to the current mouse position rather than as
+ one of the corners of the target. This would be useful
+ for large targets, such as text widgets, or strange
+ shaped canvas items. This could be specified using
+ special base positions, such as (nm, sm, em, wm). For
+ example, for <strong>'sm'</strong>, the x base position is the mouse x
+ position and y base position is the bottom (south) edge
+ of the target.</p>
+
+</li></ul>
+
+<p> The user should be able to specify global defaults for all
+ of these, as well as be able to override them for each
+ target. The Pmw.Balloon options and their defaults could
+ be:</p>
+<dl><dd><pre> basepoint sw # Position on target.
+ anchor nw # Position on the balloon
+ xoffset 20 # x distance between basepoint and anchor
+ yoffset 1 # y distance between basepoint and anchor</pre></dd></dl>
+
+
+<p> To be able to override these, the bind() and tagbind()
+ methods would have to accept these as additional arguments.
+ Each would default to None, in which case the default values
+ at the time the balloon is deiconified would be used.</p>
+
+<p> I'm not sure about how to handle the case when the balloon
+ is configured to come up under the mouse. When this happens
+ the balloon flashes on and off continuously. This can
+ happen now if you set the yoffset to a negative number.
+ Should the balloon widget detect this and do something about
+ it?</p>
+
+
+</li>
+<li><p>Add showballoon(x, y, text) method to Balloon and use in
+ balloon help for a listbox:</p>
+<p> On 3 Dec, Michael Lackhoff wrote:</p>
+
+<dl><dd><pre> And another question:
+ Is it possible to create a balloon-help for the entries
+ in the listbox? Not all the information is in the
+ listbox and it would be nice if a balloon help could
+ give addtional information.</pre></dd></dl>
+
+<p> Rather than popup a balloon help window as the mouse moves
+ over items in the listbox, I think it would be better if it
+ pops up after you clicked on an item (or a short time
+ afterwards). Pmw.Balloon displays the balloon help a short
+ time after the mouse enters a widget, so is not directly
+ usable in this case. However, a method could be added to
+ Pmw.Balloon to request it to popup the balloon at a
+ particular x,y position. This method could be called from
+ the listbox_focus method above. Something like:</p>
+<dl><dd><pre> def listbox_focus(self, event):
+ self.indexlist.component('listbox').focus_set()</pre></dd></dl>
+
+<dl><dd><pre> text = self.indexlist.getcurselection()
+ # expand text to whatever you want:
+ text = 'This is ' + text
+ self.balloon.showballoon(x, y, text)</pre></dd></dl>
+
+
+<p> The Pmw.Balloon showballoon() method would have to set a
+ timer which sometime later calls another method which
+ displays the text. You would also need to bind
+ <ButtonRelease-1> to a hideballoon() method which withdraws
+ the popup.</p>
+
+
+</li>
+<li><p>The balloon can be displayed off-screen if the window is
+ near the edge of the screen. Add a fix so that the balloon
+ always stays on the screen (but does not popup under the
+ mouse, otherwise it will immediately pop down).</p>
+
+</li>
+<li><p>Add a fix so that the balloon does not disappear if the
+ mouse enters it. Could do this by setting a short timer on
+ the Leave event before withdrawing the balloon and if there
+ is an Enter event on the balloon itself, do not withdraw it.</p>
+
+</li>
+<li><p>For tagged items in text widgets, the balloon is placed
+ relative to the character in the tagged item closest to the
+ mouse. This is not consistent: in the other cases
+ (including canvas), the balloon is placed relative to the
+ bottom left corner of the widget or canvas item. This
+ should also be the case for text items.</p>
+
+</li>
+<li><p>Is the new (in Tk8) "<<MenuSelect>>" event useful for
+ balloon and/or status help.</p>
+
+</li></ul>
+
+<p> MessageBar</p>
+<ul><li><p>Finish logmessage functionality.</p>
+
+</li>
+<li><p>Add colours and fonts to MessageBar message types. For
+ example, systemerror message types could have bold font on a
+ red background.</p>
+
+</li>
+<li><p>Add message logging history view (like the ddd debugger).</p>
+
+</li></ul>
+
+<p> NoteBook</p>
+<ul><li><p>Notebook should recalculate layout if the requested size of a tab
+ changes (eg font size, text, etc).</p>
+
+</li>
+<li><p>The tabpos option should accept <em>s</em>, <em>e</em> and <em>w</em> as well as <em>n</em>.</p>
+
+</li>
+<li><p>Possible new options (borrowed from iwidgets):</p>
+<ul><li><p><strong>equaltabs</strong></p>
+<p> If set to true, causes horizontal tabs to be equal in
+ in width and vertical tabs to equal in height.</p>
+
+<p> Specifies whether to force tabs to be equal sized or
+ not. A value of true means constrain tabs to be equal
+ sized. A value of false allows each tab to size based
+ on the text label size. The value may have any of the
+ forms accepted by the Tcl_GetBoolean, such as true,
+ false, 0, 1, yes, or no.</p>
+
+<p> For horizontally positioned tabs (tabpos is either s or
+ n), true forces all tabs to be equal width (the width
+ being equal to the longest label plus any padX speci-
+ fied). Horizontal tabs are always equal in height.</p>
+
+<p> For vertically positioned tabs (tabpos is either w or
+ e), true forces all tabs to be equal height (the height
+ being equal to the height of the label with the largest
+ font). Vertically oriented tabs are always equal in
+ width.</p>
+
+<p> Could have a special value which sets equal sized and
+ also forces tabs to completely fill notebook width
+ (apparently like
+ Windows).</p>
+
+
+</li>
+<li><p><strong>tabgap</strong></p>
+<p> Specifies the amount of pixel space to place between
+ each tab. Value may be any pixel offset value. In addi-
+ tion, a special keyword overlap can be used as the
+ value to achieve a standard overlap of tabs. This value
+ may have any of the forms acceptable to Tk_GetPixels. </p>
+
+
+</li>
+<li><p><strong>raiseselect</strong></p>
+<p> Sets whether to raise selected tabs slightly (2 pixels).</p>
+
+<p> Specifes whether to slightly raise the selected tab
+ from the rest of the tabs. The selected tab is drawn 2
+ pixels closer to the outside of the tabnotebook than
+ the unselected tabs. A value of true says to raise
+ selected tabs, a value of false turns this feature off.
+ The default is false. The value may have any of the
+ forms accepted by the Tcl_GetBoolean, such as true,
+ false, 0, 1, yes, or no.</p>
+
+
+</li>
+<li><p><strong>bevelamount</strong></p>
+<p> Specifies pixel size of tab corners. 0 means no corners.</p>
+
+
+</li></ul>
+
+</li>
+<li><p>There should be a way to temporarily hide a page, without
+ deleting it (like pack_forget). (Suggested by Michel Sanner)</p>
+
+</li></ul>
+
+<p> OptionMenu</p>
+<ul><li><p>Should accept focus and obey up and down arrow keys.</p>
+
+</li></ul>
+
+<p> PanedWidget</p>
+<ul><li><p>Add index() method</p>
+
+</li>
+<li><p>Modify all methods so that they accept <strong>Pmw.END</strong> as a pane
+ identifier as well as an index or a name.</p>
+
+</li>
+<li><p>Check iwidgets pane and panedwindow classes.</p>
+
+</li></ul>
+
+<p> RadioSelect</p>
+<ul><li><p>Add insert() and delete() methods.</p>
+
+</li>
+<li><p>The index method should have <strong>forInsert</strong> argument.</p>
+
+</li>
+<li><p>Add Pmw.SELECT to index() method. For single selectmode
+ this returns an integer, for multiple selectmode this
+ returns a list of integers.</p>
+
+</li>
+<li><p>Add option to set background color on selected buttons.
+ Maybe should also be able set selected foreground as well.
+ Any others?</p>
+
+</li></ul>
+
+<p> LogicalFont</p>
+<ul><li><p>Add boldFixed fonts,</p>
+
+</li>
+<li><p>Search for closest size font if no exact match.</p>
+
+</li>
+<li><p>Maybe replace with Tk8.0 font mechanism.</p>
+
+</li>
+<li><p>Can the Tk8.0 font measuring functionality be used in Pmw somehow?</p>
+
+</li></ul>
+
+<p> Scrolled widgets</p>
+<ul><li><p>Can some common scrolling methods be factored out, either as
+ a base class, "ScrolledMixin" mixin class or as helper functions?
+ Candidate methods: constructor, destroy, interior, _hscrollMode,
+ _vscrollMode, _configureScrollCommands, _scrollXNow, _scrollYNow,
+ _scrollBothLater, _scrollBothNow, _toggleHorizScrollbar,
+ _toggleVertScrollbar.</p>
+
+</li>
+<li><p>ScrolledField should have optional arrow buttons, so that it
+ can still be scrolled even if the mouse does not have a
+ middle button.</p>
+
+</li></ul>
+
+<p> Miscellaneous</p>
+<ul><li><p>Add a button to the Pmw "Stack trace window" which
+ optionally removes all grabs:</p>
+<p> I normally interact with the "Stack trace window"
+ immediately, and dismiss it afterwards. In many cases
+ where a bug appears like this, the rest of the application
+ is still functional (many of the problems appearing at
+ this stage of development of my application are unforeseen
+ exceptions communicating with a robot on the other end of
+ a socket, not affecting the GUI per se). For that reason
+ I'd prefer if the "stack trace window" would push another
+ grab on the grab stack (if any grabs are active at the
+ moment the exception occurs). Could the window have an
+ extra "Terminate application" option for this case?</p>
+
+
+</li>
+<li><p>need to handle component option queries in configure():</p>
+<dl><dd><pre> foo = Pmw.AboutDialog(applicationname = 'abc XYZ')
+ foo.component('message').configure('text') - works
+ foo.cget('message_text') - works
+ foo.configure('message_text') - doesn't</pre></dd></dl>
+
+
+</li>
+<li><p>Implement bindings (ComboBox, etc) via a dictionary lookup,
+ to allow people to invent new bindings, such as for
+ handicapped users. (Suggested by Michael McLay)</p>
+
+</li>
+<li><p>Modify bundlepmw.py so that it checks Pmw.def to see that no
+ files have been missed.</p>
+
+</li>
+<li><p>Potential cheap speedup by adding this to each module, or
+ inside functions if it has a loop containing calls to
+ builtins:</p>
+<dl><dd><pre> from __builtin__ import *</pre></dd></dl>
+
+
+</li>
+<li><p>Look at how update_idletasks and after_* are used in Pmw -
+ are they consistent? could it be improved? What are the
+ problems of using these on other bits of an application
+ (such as when the size of the toplevel is being determined
+ for the window manager).</p>
+
+</li>
+<li><p>If lots of errors occur (such as in a fast time callback)
+ the error window may not appear, since Tk will wait until it
+ is idle - which may never occur. The solution is to call
+ update_idletask when updating the error window, but only
+ after a short time has passed. This will provide better
+ user response. However, it may not be possible to do this
+ if some python interpretes (omppython, for example) do not
+ handle calls to update_idletasks at certain times.</p>
+
+</li>
+<li><p>In the Pmw FAQ, in the "Why don't Pmw megawidgets have a
+ <strong>'state'</strong> option?" section, it mentions several Pmw
+ megawidgets that can not be disabled. Fix them.</p>
+
+</li>
+<li><p>Add RCSID version string to all files.</p>
+
+</li>
+<li><p>When raising exceptions use the third argument to raise:</p>
+<dl><dd><pre> raise SimulationException, msg, sys.exc_info()[2]</pre></dd></dl>
+
+
+</li>
+<li><p>When update_idletasks is called all pending changes are
+ flushed to the window system server. However, it may take
+ some time for the server to display the changes. If it is
+ required that the display be up-to-date, update_idletasks
+ should be followed by a call that blocks until processed by
+ the server and a reply received. This may be useful in
+ Pmw.busycallback to ensure the busy cursor remains visible
+ until the display is actually modified.</p>
+
+</li>
+<li><p>There is a small bug which appears only with Tk8.0 (the bug
+ is not apparent with Tk4.2). If a dialog is activated and
+ pops up directly over the cursor and the dialog has a
+ default button, then pressing the <strong>Return</strong>
+ key will not invoke the default button. If you move the
+ mouse out of and then back into the dialog, pressing the
+ <strong>Return</strong> key will work. This behaviour has
+ been noticed in Tcl-only programs, so it is probably a bug
+ in Tk. (Tested on Solaris.)</p>
+
+</li>
+<li><p>Modify PmwBlt.py to use blt2.4 instead of blt8.0.unoff.
+ Nick Belshaw <nickb@earth.ox.ac.uk> is looking at wrapping
+ the new BLT StripChart and TabSet into Pmw.</p>
+
+</li>
+<li><p>Perhaps Pmw should have its own exception defined, like
+ TkInters's TclError, perhaps called PmwError.</p>
+
+</li>
+<li><p>This one is caused by a bug in the implementation of Tcl/Tk
+ for Microsoft Windows NT (and maybe other Microsoft
+ products). Mouse release events can get lost if the
+ grab_set and grab_release commands are used and the mouse
+ goes outside of the window while the mouse button is down.
+ This can occur while Pmw modal dialogs are active. Below
+ is some Tkinter-only code which demonstrates the problem.
+ Maybe there is a work around.</p>
+<dl><dd><pre> # Test script to demonstrate bug in Tk
+ #implementation of grab under NT.
+
+ # Click on "Dialog" to bring up the modal
+ # dialog window. Then button down on the scale,
+ # move the mouse outside the window,
+ # then button up. The scale slider will still
+ # be sunken and clicks on the "OK" button
+ # will be ineffective.
+
+ import Tkinter
+
+ def activate():
+ waitVar.set(0)
+ toplevel.deiconify()
+ toplevel.wait_visibility()
+ toplevel.grab_set() # Problem here
+ toplevel.focus_set()
+ toplevel.wait_variable(waitVar)
+
+ def deactivate():
+ toplevel.withdraw()
+ toplevel.grab_release() # and here
+ waitVar.set(1)
+
+ root = Tkinter.Tk()
+ toplevel = Tkinter.Toplevel()
+ waitVar = Tkinter.IntVar()
+ toplevel.withdraw()
+ scale = Tkinter.Scale(toplevel, orient='horizontal', length=200)
+ scale.pack()
+ button = Tkinter.Button(toplevel, text='OK', command=deactivate)
+ button.pack()
+
+ button = Tkinter.Button(text='Dialog', command=activate)
+ button.pack()
+ button = Tkinter.Button(text='Exit', command=root.destroy)
+ button.pack()
+
+ root.mainloop()</pre></dd></dl>
+
+
+</li></ul>
+
+
+<p><strong>Documentation</strong></p>
+<ul><li><p>Document how to get Pmw working on a Mac, for example:</p>
+<ul><li><p>Unzip and untar</p>
+<p> This depends on what you use to unpack the tar file. If
+ you use (macgzip and) SunTar you have to tell it that files
+ with ".py" extensions are text files (in the
+ preferences/file type section). If you use stuffit
+ expander: this can be made to do the conversion
+ correctly, but it could be that this only works if you set
+ the .py extension correctly in Internet Config.</p>
+
+<ul><li><p>Where do you untar Pmw?</p>
+
+</li>
+<li><p>How do you get line terminators correct (carriage
+ return/line feed)?</p>
+
+</li>
+<li><p>Is there any problem with file name case? (mixed
+ upper/lower case)</p>
+
+</li>
+<li><p>Is there any problem with file name length?</p>
+
+</li></ul>
+<p> (Joseph Saltiel says: It was the same type of operation
+ as in Windows/Unix. Run a program that unzips it and
+ untars it. It seems to get case and length right on its
+ own.)</p>
+
+
+</li>
+<li><p>Let python know where Pmw is</p>
+<ul><li><p>If Pmw is in its own folder you will have to add the
+ parent of that folder to the sys paths in Edit
+ PythonPaths. If it is in the Python home folder, you
+ do not need to do this.</p>
+
+</li>
+<li><p>Make sure that the Pmw folder is called "Pmw" and not
+ something else. Since Pmw is a package, python expects
+ to find a "Pmw" folder somewhere in sys.path.</p>
+
+</li></ul>
+<p> (Joseph Saltiel says: With the Python distribution on the
+ Mac there is an application called editPythonPrefs, when
+ you run it it gives you a list of a paths. These paths
+ are similiar to the PYTHONPATH variable. I just added the
+ path to Pmw at the bottom of this list.)</p>
+
+
+</li></ul>
+
+</li>
+<li><p>Document general ideas about building guis, eg:</p>
+<p> When I write gui applications, I usually defer creation of windows
+ as much as possible - this means that the application starts up
+ quickly because it usually only has to create the main window.
+ Whenever another window is required for the first time, it is
+ created then. When the user has finished with the window, the
+ window is withdrawn, not deleted, so that next time it is required
+ it much faster to come up.</p>
+
+<p> In summary - don't create a window until you need and
+ don't destroy a window if you may want it again.</p>
+
+<p> The amount of memory required to keep the windows should not be
+ very much - except for very long running programs where the user
+ may create thousands of different windows.</p>
+
+
+</li>
+<li><p>Add class hierarchy diagram to documentation:</p>
+<dl><dd><pre> MegaArchetype
+ MegaToplevel
+ etc
+ MegaWidget
+ etc</pre></dd></dl>
+
+
+</li>
+<li><p>Add to doco something like: "Another way to extend a Pmw
+ megawidget is to specify a non-default type for one of the
+ components. For example <code>text_pytype = FontText</code>."</p>
+
+</li>
+<li><p>Document pyclass and pyclass = None (options for null components
+ are ignored; the only time this can be used is with the
+ Group's tag component - all
+ other's use the component widget in some way)</p>
+
+</li>
+<li><p>Create index of all Pmw methods, functions, options, components.</p>
+
+</li>
+<li><p>Add description of how to run the Pmw demos without installing.</p>
+
+</li>
+<li><p>Add description of how to install Pmw.</p>
+
+</li>
+<li><p>Describe grid structure of megawidgets, so that it is possible
+ to extend megawidgets by adding new widgets into the interior
+ (hence avoiding a childsite in most megawidgets)</p>
+
+</li>
+<li><p>Document error display and difference between callback and
+ binding error reports.</p>
+
+</li>
+<li><p>Document difference between <strong>'Helvetica 12'</strong> and <strong>'Helvetica size: 12'</strong>
+ in logicalfont.</p>
+
+</li>
+<li><p>Add to howtouse, to describe using the option database to set
+ options for a specific megawidget:</p>
+<dl><dd><pre> import Pmw
+ root = Pmw.initialise(useTkOptionDb = 1)
+ root.option_add('*entryfield24*Label.text', 'German')
+ e = Pmw.EntryField(hull_name = 'entryfield24', labelpos = 'w')
+ e.pack()
+ root.update()</pre></dd></dl>
+
+
+</li>
+<li><p>Also document hull_name and hull_class.</p>
+
+</li>
+<li><p>Finish FAQ, ReleaseProcedure and StructuredText test.</p>
+
+</li>
+<li><p>Put html through gifwizard and html lint.</p>
+<dl><dd><pre> http://www.cen.uiuc.edu/cgi-bin/weblint
+ (eg: http://www.cre.canon.co.uk/~neilb/weblint/manpage.html)</pre></dd></dl>
+
+
+</li>
+<li><p>Delete comments from source if they have been added to docs
+ (should not have two copies of anything).</p>
+
+</li>
+<li><p>Need to document non-standard initial values for component
+ options, such as border in ButtonBox and Dialog's childsite.</p>
+
+</li>
+<li><p>Docs should have DEFAULT BINDINGS section (like iwidget combobox).</p>
+
+</li>
+<li><p>Promote home page:</p>
+<dl><dd><pre> http://www.geocities.com/homestead/promote.html
+ http://www.submit-it.com/subopt.htm, etc</pre></dd></dl>
+
+
+</li>
+<li><p>Create man pages as well as html (modify createmanuals to produce both).</p>
+
+</li>
+<li><p>Maybe something with html frames like: itcl2.2/html/index.html</p>
+
+</li>
+<li><p>Add to starting.html a note that Pmw is a python "package" and add
+ a pointer to python documentation on packages.</p>
+
+</li>
+<li><p>Document scrolled widget implementations, explaining why they
+ are all slightly different (because the underlying widgets which
+ are being scrolled have different behaviors).</p>
+
+</li>
+<li><p>Make copyright clearer. Maybe borrow python's?</p>
+
+</li></ul>
+
+<p><strong>Demos</strong></p>
+<ul><li><p>Check for missing demos.</p>
+
+</li>
+<li><p>In all demos can move the three lines beginning with "Import Pmw
+ from the sibling directory", to inside "if __name__" clause.
+ Also, "sibling directory" is now incorrect. Also, add note that
+ this is only necessary when running demos without installing Pmw.</p>
+
+</li>
+<li><p>Change demo/All.py so that it displays the traceback if it
+ cannot load or run a demo (easier for users to report errors).</p>
+
+</li>
+<li><p>Add option to demo/All.py: "Display demos in separate window"
+ to allow resizing of sub-demos</p>
+
+</li>
+<li><p>TimeCounter and Spectrum demos beep when they come up, using:</p>
+<dl><dd><pre> root.option_add('*EntryField*value', 'VALUE')</pre></dd></dl>
+
+
+</li>
+<li><p>In demos, add <code>title = 'blah'</code> to top of file and replace
+ <code>root.title(..)</code> with <code>root.title(title)</code> at bottom.</p>
+
+</li>
+<li><p>Add comprehensive speed test demo which creates one or more of
+ each (common) megawidget. Remove old SpeedTest demo.</p>
+
+</li>
+<li><p>Check demos work when called from ptui. (Changes have to do
+ with calling compile/exec where __name__ is not the name of the
+ All.py script, but is <strong>'__builtin__'</strong>)</p>
+
+</li>
+<li><p>PromptDialog demo should not remember password.</p>
+
+</li>
+<li><p>Finish Counter, Radioselect demos.</p>
+
+</li>
+<li><p>Modify the All demo so that you can reload a demo module.</p>
+
+</li>
+<li><p>The syntax-coloured code viewer looks strange on Microsoft NT,
+ because the size of the fonts differ. Check out Guido's
+ idle-0.1 text colouring for Pmw code viewer.</p>
+
+</li>
+<li><p>Document restrictions on adding bindings to a megawidget: you
+ probably need to bind to components of the megawidget and also
+ check that you are not destroying bindings set up by the
+ megawidget itself.</p>
+
+</li>
+<li><p>Add a demo that demonstrates setting the color scheme at run time.</p>
+
+</li></ul>
+
+<p><strong>Tests</strong></p>
+<ul><li><p>Check for missing tests, such as TimeCounter, RadioSelect,
+ SelectionDialog, MessageBar, MenuBar, ComboBoxDialog, Balloon.</p>
+
+</li>
+<li><p>Create test for useTkOptionDb option to Pmw.initialise().</p>
+
+</li>
+<li><p>Check that destroyed widgets' python classes are garbage
+ collected (add test and/or demo).</p>
+
+</li>
+<li><p>Add tests for changecolor, setscheme, etc.</p>
+
+</li>
+<li><p>Need Resources test.</p>
+
+</li>
+<li><p>Create tests for deriving from Pmw classes (eg ComboBox).</p>
+
+</li></ul>
+
+<p><strong>Ideas</strong></p>
+<ul><li><p>Add more Tix (www.xpi.com/tix/screenshot.html) and iwidgets widgets.</p>
+
+</li>
+<li><p>Look at spinner.itk for how to do vertical orientation on
+ same side for Counter.</p>
+
+</li>
+<li><p>Investigate these new features in Tk8.0 and see if they could be
+ used in Pmw:</p>
+<dl><dd><pre> embedded images in text widgets
+ destroy command ignores windows that don't exist
+</pre></dd></dl>
+
+
+</li></ul>
+
+
+
+ <center><P ALIGN="CENTER">
+ <IMG SRC = blue_line.gif ALT = "" WIDTH=320 HEIGHT=5>
+ </p></center>
+
+
+ <font size=-1>
+ <center><P ALIGN="CENTER">
+ Pmw 1.2 -
+ 5 Aug 2003
+ - <a href="index.html">Home</a>
+
+ </p></center>
+ </font>
+
+ </body>
+ </html>
+
--- /dev/null
+# [Emacs: -*- python -*-]
+# --- This is the Pmw definition file ---
+#
+# It is invoked by the Pmw dynamic loader in Pmw.__init__.
+#
+# widgets : tuple with the names of those widget classes that are
+# stacked in a module of the same name.
+# widgetclasses : dictionary from names of widget classes to module names.
+# functions : dictionary from function names to modules names.
+# modules : tuple of module names that don't contain widget classes
+# of the same name.
+#
+
+# Widgets whose name is the same as its module.
+_widgets = (
+ 'AboutDialog', 'Balloon', 'ButtonBox', 'ComboBox',
+ 'ComboBoxDialog', 'Counter', 'CounterDialog', 'Dialog',
+ 'EntryField', 'Group', 'HistoryText', 'LabeledWidget',
+ 'MainMenuBar', 'MenuBar', 'MessageBar',
+ 'MessageDialog', 'NoteBook', 'OptionMenu', 'PanedWidget',
+ 'PromptDialog', 'RadioSelect', 'ScrolledCanvas', 'ScrolledField',
+ 'ScrolledFrame', 'ScrolledListBox', 'ScrolledText', 'SelectionDialog',
+ 'TextDialog', 'TimeCounter',
+)
+
+# Widgets whose name is not the same as its module.
+_extraWidgets = {
+}
+
+_functions = {
+ 'logicalfont' : 'LogicalFont',
+ 'logicalfontnames' : 'LogicalFont',
+ 'aboutversion' : 'AboutDialog',
+ 'aboutcopyright' : 'AboutDialog',
+ 'aboutcontact' : 'AboutDialog',
+ 'datestringtojdn' : 'TimeFuncs',
+ 'timestringtoseconds' : 'TimeFuncs',
+ 'setyearpivot' : 'TimeFuncs',
+ 'ymdtojdn' : 'TimeFuncs',
+ 'jdntoymd' : 'TimeFuncs',
+ 'stringtoreal' : 'TimeFuncs',
+ 'aligngrouptags' : 'Group',
+ 'OK' : 'EntryField',
+ 'ERROR' : 'EntryField',
+ 'PARTIAL' : 'EntryField',
+ 'numericvalidator' : 'EntryField',
+ 'integervalidator' : 'EntryField',
+ 'hexadecimalvalidator' : 'EntryField',
+ 'realvalidator' : 'EntryField',
+ 'alphabeticvalidator' : 'EntryField',
+ 'alphanumericvalidator' : 'EntryField',
+ 'timevalidator' : 'EntryField',
+ 'datevalidator' : 'EntryField',
+}
+
+_modules = (
+ 'Color', 'Blt',
+)
--- /dev/null
+import Pmw
+
+class AboutDialog(Pmw.MessageDialog):
+ # Window to display version and contact information.
+
+ # Class members containing resettable 'default' values:
+ _version = ''
+ _copyright = ''
+ _contact = ''
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('applicationname', '', INITOPT),
+ ('iconpos', 'w', None),
+ ('icon_bitmap', 'info', None),
+ ('buttons', ('Close',), None),
+ ('defaultbutton', 0, None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MessageDialog.__init__(self, parent)
+
+ applicationname = self['applicationname']
+ if not kw.has_key('title'):
+ self.configure(title = 'About ' + applicationname)
+
+ if not kw.has_key('message_text'):
+ text = applicationname + '\n\n'
+ if AboutDialog._version != '':
+ text = text + 'Version ' + AboutDialog._version + '\n'
+ if AboutDialog._copyright != '':
+ text = text + AboutDialog._copyright + '\n\n'
+ if AboutDialog._contact != '':
+ text = text + AboutDialog._contact
+
+ self.configure(message_text=text)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+def aboutversion(value):
+ AboutDialog._version = value
+
+def aboutcopyright(value):
+ AboutDialog._copyright = value
+
+def aboutcontact(value):
+ AboutDialog._contact = value
--- /dev/null
+import os
+import string
+import Tkinter
+import Pmw
+
+class Balloon(Pmw.MegaToplevel):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = (
+ ('initwait', 500, None), # milliseconds
+ ('label_background', 'lightyellow', None),
+ ('label_foreground', 'black', None),
+ ('label_justify', 'left', None),
+ ('master', 'parent', None),
+ ('relmouse', 'none', self._relmouse),
+ ('state', 'both', self._state),
+ ('statuscommand', None, None),
+ ('xoffset', 20, None), # pixels
+ ('yoffset', 1, None), # pixels
+ ('hull_highlightthickness', 1, None),
+ ('hull_highlightbackground', 'black', None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaToplevel.__init__(self, parent)
+
+ self.withdraw()
+ self.overrideredirect(1)
+
+ # Create the components.
+ interior = self.interior()
+ self._label = self.createcomponent('label',
+ (), None,
+ Tkinter.Label, (interior,))
+ self._label.pack()
+
+ # The default hull configuration options give a black border
+ # around the balloon, but avoids a black 'flash' when the
+ # balloon is deiconified, before the text appears.
+ if not kw.has_key('hull_background'):
+ self.configure(hull_background = \
+ str(self._label.cget('background')))
+
+ # Initialise instance variables.
+ self._timer = None
+
+ # The widget or item that is currently triggering the balloon.
+ # It is None if the balloon is not being displayed. It is a
+ # one-tuple if the balloon is being displayed in response to a
+ # widget binding (value is the widget). It is a two-tuple if
+ # the balloon is being displayed in response to a canvas or
+ # text item binding (value is the widget and the item).
+ self._currentTrigger = None
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+ Pmw.MegaToplevel.destroy(self)
+
+ def bind(self, widget, balloonHelp, statusHelp = None):
+
+ # If a previous bind for this widget exists, remove it.
+ self.unbind(widget)
+
+ if balloonHelp is None and statusHelp is None:
+ return
+
+ if statusHelp is None:
+ statusHelp = balloonHelp
+ enterId = widget.bind('<Enter>',
+ lambda event, self = self, w = widget,
+ sHelp = statusHelp, bHelp = balloonHelp:
+ self._enter(event, w, sHelp, bHelp, 0))
+
+ # Set Motion binding so that if the pointer remains at rest
+ # within the widget until the status line removes the help and
+ # then the pointer moves again, then redisplay the help in the
+ # status line.
+ # Note: The Motion binding only works for basic widgets, and
+ # the hull of megawidgets but not for other megawidget components.
+ motionId = widget.bind('<Motion>',
+ lambda event = None, self = self, statusHelp = statusHelp:
+ self.showstatus(statusHelp))
+
+ leaveId = widget.bind('<Leave>', self._leave)
+ buttonId = widget.bind('<ButtonPress>', self._buttonpress)
+
+ # Set Destroy binding so that the balloon can be withdrawn and
+ # the timer can be cancelled if the widget is destroyed.
+ destroyId = widget.bind('<Destroy>', self._destroy)
+
+ # Use the None item in the widget's private Pmw dictionary to
+ # store the widget's bind callbacks, for later clean up.
+ if not hasattr(widget, '_Pmw_BalloonBindIds'):
+ widget._Pmw_BalloonBindIds = {}
+ widget._Pmw_BalloonBindIds[None] = \
+ (enterId, motionId, leaveId, buttonId, destroyId)
+
+ def unbind(self, widget):
+ if hasattr(widget, '_Pmw_BalloonBindIds'):
+ if widget._Pmw_BalloonBindIds.has_key(None):
+ (enterId, motionId, leaveId, buttonId, destroyId) = \
+ widget._Pmw_BalloonBindIds[None]
+ # Need to pass in old bindings, so that Tkinter can
+ # delete the commands. Otherwise, memory is leaked.
+ widget.unbind('<Enter>', enterId)
+ widget.unbind('<Motion>', motionId)
+ widget.unbind('<Leave>', leaveId)
+ widget.unbind('<ButtonPress>', buttonId)
+ widget.unbind('<Destroy>', destroyId)
+ del widget._Pmw_BalloonBindIds[None]
+
+ if self._currentTrigger is not None and len(self._currentTrigger) == 1:
+ # The balloon is currently being displayed and the current
+ # trigger is a widget.
+ triggerWidget = self._currentTrigger[0]
+ if triggerWidget == widget:
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+ self.withdraw()
+ self.clearstatus()
+ self._currentTrigger = None
+
+ def tagbind(self, widget, tagOrItem, balloonHelp, statusHelp = None):
+
+ # If a previous bind for this widget's tagOrItem exists, remove it.
+ self.tagunbind(widget, tagOrItem)
+
+ if balloonHelp is None and statusHelp is None:
+ return
+
+ if statusHelp is None:
+ statusHelp = balloonHelp
+ enterId = widget.tag_bind(tagOrItem, '<Enter>',
+ lambda event, self = self, w = widget,
+ sHelp = statusHelp, bHelp = balloonHelp:
+ self._enter(event, w, sHelp, bHelp, 1))
+ motionId = widget.tag_bind(tagOrItem, '<Motion>',
+ lambda event = None, self = self, statusHelp = statusHelp:
+ self.showstatus(statusHelp))
+ leaveId = widget.tag_bind(tagOrItem, '<Leave>', self._leave)
+ buttonId = widget.tag_bind(tagOrItem, '<ButtonPress>', self._buttonpress)
+
+ # Use the tagOrItem item in the widget's private Pmw dictionary to
+ # store the tagOrItem's bind callbacks, for later clean up.
+ if not hasattr(widget, '_Pmw_BalloonBindIds'):
+ widget._Pmw_BalloonBindIds = {}
+ widget._Pmw_BalloonBindIds[tagOrItem] = \
+ (enterId, motionId, leaveId, buttonId)
+
+ def tagunbind(self, widget, tagOrItem):
+ if hasattr(widget, '_Pmw_BalloonBindIds'):
+ if widget._Pmw_BalloonBindIds.has_key(tagOrItem):
+ (enterId, motionId, leaveId, buttonId) = \
+ widget._Pmw_BalloonBindIds[tagOrItem]
+ widget.tag_unbind(tagOrItem, '<Enter>', enterId)
+ widget.tag_unbind(tagOrItem, '<Motion>', motionId)
+ widget.tag_unbind(tagOrItem, '<Leave>', leaveId)
+ widget.tag_unbind(tagOrItem, '<ButtonPress>', buttonId)
+ del widget._Pmw_BalloonBindIds[tagOrItem]
+
+ if self._currentTrigger is None:
+ # The balloon is not currently being displayed.
+ return
+
+ if len(self._currentTrigger) == 1:
+ # The current trigger is a widget.
+ return
+
+ if len(self._currentTrigger) == 2:
+ # The current trigger is a canvas item.
+ (triggerWidget, triggerItem) = self._currentTrigger
+ if triggerWidget == widget and triggerItem == tagOrItem:
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+ self.withdraw()
+ self.clearstatus()
+ self._currentTrigger = None
+ else: # The current trigger is a text item.
+ (triggerWidget, x, y) = self._currentTrigger
+ if triggerWidget == widget:
+ currentPos = widget.index('@%d,%d' % (x, y))
+ currentTags = widget.tag_names(currentPos)
+ if tagOrItem in currentTags:
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+ self.withdraw()
+ self.clearstatus()
+ self._currentTrigger = None
+
+ def showstatus(self, statusHelp):
+ if self['state'] in ('status', 'both'):
+ cmd = self['statuscommand']
+ if callable(cmd):
+ cmd(statusHelp)
+
+ def clearstatus(self):
+ self.showstatus(None)
+
+ def _state(self):
+ if self['state'] not in ('both', 'balloon', 'status', 'none'):
+ raise ValueError, 'bad state option ' + repr(self['state']) + \
+ ': should be one of \'both\', \'balloon\', ' + \
+ '\'status\' or \'none\''
+
+ def _relmouse(self):
+ if self['relmouse'] not in ('both', 'x', 'y', 'none'):
+ raise ValueError, 'bad relmouse option ' + repr(self['relmouse'])+ \
+ ': should be one of \'both\', \'x\', ' + '\'y\' or \'none\''
+
+ def _enter(self, event, widget, statusHelp, balloonHelp, isItem):
+
+ # Do not display balloon if mouse button is pressed. This
+ # will only occur if the button was pressed inside a widget,
+ # then the mouse moved out of and then back into the widget,
+ # with the button still held down. The number 0x1f00 is the
+ # button mask for the 5 possible buttons in X.
+ buttonPressed = (event.state & 0x1f00) != 0
+
+ if not buttonPressed and balloonHelp is not None and \
+ self['state'] in ('balloon', 'both'):
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+
+ self._timer = self.after(self['initwait'],
+ lambda self = self, widget = widget, help = balloonHelp,
+ isItem = isItem:
+ self._showBalloon(widget, help, isItem))
+
+ if isItem:
+ if hasattr(widget, 'canvasx'):
+ # The widget is a canvas.
+ item = widget.find_withtag('current')
+ if len(item) > 0:
+ item = item[0]
+ else:
+ item = None
+ self._currentTrigger = (widget, item)
+ else:
+ # The widget is a text widget.
+ self._currentTrigger = (widget, event.x, event.y)
+ else:
+ self._currentTrigger = (widget,)
+
+ self.showstatus(statusHelp)
+
+ def _leave(self, event):
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+ self.withdraw()
+ self.clearstatus()
+ self._currentTrigger = None
+
+ def _destroy(self, event):
+
+ # Only withdraw the balloon and cancel the timer if the widget
+ # being destroyed is the widget that triggered the balloon.
+ # Note that in a Tkinter Destroy event, the widget field is a
+ # string and not a widget as usual.
+
+ if self._currentTrigger is None:
+ # The balloon is not currently being displayed
+ return
+
+ if len(self._currentTrigger) == 1:
+ # The current trigger is a widget (not an item)
+ triggerWidget = self._currentTrigger[0]
+ if str(triggerWidget) == event.widget:
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+ self.withdraw()
+ self.clearstatus()
+ self._currentTrigger = None
+
+ def _buttonpress(self, event):
+ if self._timer is not None:
+ self.after_cancel(self._timer)
+ self._timer = None
+ self.withdraw()
+ self._currentTrigger = None
+
+ def _showBalloon(self, widget, balloonHelp, isItem):
+
+ self._label.configure(text = balloonHelp)
+
+ # First, display the balloon offscreen to get dimensions.
+ screenWidth = self.winfo_screenwidth()
+ screenHeight = self.winfo_screenheight()
+ self.geometry('+%d+0' % (screenWidth + 1))
+ self.update_idletasks()
+
+ if isItem:
+ # Get the bounding box of the current item.
+ bbox = widget.bbox('current')
+ if bbox is None:
+ # The item that triggered the balloon has disappeared,
+ # perhaps by a user's timer event that occured between
+ # the <Enter> event and the 'initwait' timer calling
+ # this method.
+ return
+
+ # The widget is either a text or canvas. The meaning of
+ # the values returned by the bbox method is different for
+ # each, so use the existence of the 'canvasx' method to
+ # distinguish between them.
+ if hasattr(widget, 'canvasx'):
+ # The widget is a canvas. Place balloon under canvas
+ # item. The positions returned by bbox are relative
+ # to the entire canvas, not just the visible part, so
+ # need to convert to window coordinates.
+ leftrel = bbox[0] - widget.canvasx(0)
+ toprel = bbox[1] - widget.canvasy(0)
+ bottomrel = bbox[3] - widget.canvasy(0)
+ else:
+ # The widget is a text widget. Place balloon under
+ # the character closest to the mouse. The positions
+ # returned by bbox are relative to the text widget
+ # window (ie the visible part of the text only).
+ leftrel = bbox[0]
+ toprel = bbox[1]
+ bottomrel = bbox[1] + bbox[3]
+ else:
+ leftrel = 0
+ toprel = 0
+ bottomrel = widget.winfo_height()
+
+ xpointer, ypointer = widget.winfo_pointerxy() # -1 if off screen
+
+ if xpointer >= 0 and self['relmouse'] in ('both', 'x'):
+ x = xpointer
+ else:
+ x = leftrel + widget.winfo_rootx()
+ x = x + self['xoffset']
+
+ if ypointer >= 0 and self['relmouse'] in ('both', 'y'):
+ y = ypointer
+ else:
+ y = bottomrel + widget.winfo_rooty()
+ y = y + self['yoffset']
+
+ edges = (string.atoi(str(self.cget('hull_highlightthickness'))) +
+ string.atoi(str(self.cget('hull_borderwidth')))) * 2
+ if x + self._label.winfo_reqwidth() + edges > screenWidth:
+ x = screenWidth - self._label.winfo_reqwidth() - edges
+
+ if y + self._label.winfo_reqheight() + edges > screenHeight:
+ if ypointer >= 0 and self['relmouse'] in ('both', 'y'):
+ y = ypointer
+ else:
+ y = toprel + widget.winfo_rooty()
+ y = y - self._label.winfo_reqheight() - self['yoffset'] - edges
+
+ Pmw.setgeometryanddeiconify(self, '+%d+%d' % (x, y))
--- /dev/null
+# Pmw megawidget base classes.
+
+# This module provides a foundation for building megawidgets. It
+# contains the MegaArchetype class which manages component widgets and
+# configuration options. Also provided are the MegaToplevel and
+# MegaWidget classes, derived from the MegaArchetype class. The
+# MegaToplevel class contains a Tkinter Toplevel widget to act as the
+# container of the megawidget. This is used as the base class of all
+# megawidgets that are contained in their own top level window, such
+# as a Dialog window. The MegaWidget class contains a Tkinter Frame
+# to act as the container of the megawidget. This is used as the base
+# class of all other megawidgets, such as a ComboBox or ButtonBox.
+#
+# Megawidgets are built by creating a class that inherits from either
+# the MegaToplevel or MegaWidget class.
+
+import os
+import string
+import sys
+import traceback
+import types
+import Tkinter
+
+# Special values used in index() methods of several megawidgets.
+END = ['end']
+SELECT = ['select']
+DEFAULT = ['default']
+
+# Constant used to indicate that an option can only be set by a call
+# to the constructor.
+INITOPT = ['initopt']
+_DEFAULT_OPTION_VALUE = ['default_option_value']
+_useTkOptionDb = 0
+
+# Symbolic constants for the indexes into an optionInfo list.
+_OPT_DEFAULT = 0
+_OPT_VALUE = 1
+_OPT_FUNCTION = 2
+
+# Stacks
+
+_busyStack = []
+ # Stack which tracks nested calls to show/hidebusycursor (called
+ # either directly or from activate()/deactivate()). Each element
+ # is a dictionary containing:
+ # 'newBusyWindows' : List of windows which had busy_hold called
+ # on them during a call to showbusycursor().
+ # The corresponding call to hidebusycursor()
+ # will call busy_release on these windows.
+ # 'busyFocus' : The blt _Busy window which showbusycursor()
+ # set the focus to.
+ # 'previousFocus' : The focus as it was when showbusycursor()
+ # was called. The corresponding call to
+ # hidebusycursor() will restore this focus if
+ # the focus has not been changed from busyFocus.
+
+_grabStack = []
+ # Stack of grabbed windows. It tracks calls to push/popgrab()
+ # (called either directly or from activate()/deactivate()). The
+ # window on the top of the stack is the window currently with the
+ # grab. Each element is a dictionary containing:
+ # 'grabWindow' : The window grabbed by pushgrab(). The
+ # corresponding call to popgrab() will release
+ # the grab on this window and restore the grab
+ # on the next window in the stack (if there is one).
+ # 'globalMode' : True if the grabWindow was grabbed with a
+ # global grab, false if the grab was local
+ # and 'nograb' if no grab was performed.
+ # 'previousFocus' : The focus as it was when pushgrab()
+ # was called. The corresponding call to
+ # popgrab() will restore this focus.
+ # 'deactivateFunction' :
+ # The function to call (usually grabWindow.deactivate) if
+ # popgrab() is called (usually from a deactivate() method)
+ # on a window which is not at the top of the stack (that is,
+ # does not have the grab or focus). For example, if a modal
+ # dialog is deleted by the window manager or deactivated by
+ # a timer. In this case, all dialogs above and including
+ # this one are deactivated, starting at the top of the
+ # stack.
+
+ # Note that when dealing with focus windows, the name of the Tk
+ # widget is used, since it may be the '_Busy' window, which has no
+ # python instance associated with it.
+
+#=============================================================================
+
+# Functions used to forward methods from a class to a component.
+
+# Fill in a flattened method resolution dictionary for a class (attributes are
+# filtered out). Flattening honours the MI method resolution rules
+# (depth-first search of bases in order). The dictionary has method names
+# for keys and functions for values.
+def __methodDict(cls, dict):
+
+ # the strategy is to traverse the class in the _reverse_ of the normal
+ # order, and overwrite any duplicates.
+ baseList = list(cls.__bases__)
+ baseList.reverse()
+
+ # do bases in reverse order, so first base overrides last base
+ for super in baseList:
+ __methodDict(super, dict)
+
+ # do my methods last to override base classes
+ for key, value in cls.__dict__.items():
+ # ignore class attributes
+ if type(value) == types.FunctionType:
+ dict[key] = value
+
+def __methods(cls):
+ # Return all method names for a class.
+
+ # Return all method names for a class (attributes are filtered
+ # out). Base classes are searched recursively.
+
+ dict = {}
+ __methodDict(cls, dict)
+ return dict.keys()
+
+# Function body to resolve a forwarding given the target method name and the
+# attribute name. The resulting lambda requires only self, but will forward
+# any other parameters.
+__stringBody = (
+ 'def %(method)s(this, *args, **kw): return ' +
+ 'apply(this.%(attribute)s.%(method)s, args, kw)')
+
+# Get a unique id
+__counter = 0
+def __unique():
+ global __counter
+ __counter = __counter + 1
+ return str(__counter)
+
+# Function body to resolve a forwarding given the target method name and the
+# index of the resolution function. The resulting lambda requires only self,
+# but will forward any other parameters. The target instance is identified
+# by invoking the resolution function.
+__funcBody = (
+ 'def %(method)s(this, *args, **kw): return ' +
+ 'apply(this.%(forwardFunc)s().%(method)s, args, kw)')
+
+def forwardmethods(fromClass, toClass, toPart, exclude = ()):
+ # Forward all methods from one class to another.
+
+ # Forwarders will be created in fromClass to forward method
+ # invocations to toClass. The methods to be forwarded are
+ # identified by flattening the interface of toClass, and excluding
+ # methods identified in the exclude list. Methods already defined
+ # in fromClass, or special methods with one or more leading or
+ # trailing underscores will not be forwarded.
+
+ # For a given object of class fromClass, the corresponding toClass
+ # object is identified using toPart. This can either be a String
+ # denoting an attribute of fromClass objects, or a function taking
+ # a fromClass object and returning a toClass object.
+
+ # Example:
+ # class MyClass:
+ # ...
+ # def __init__(self):
+ # ...
+ # self.__target = TargetClass()
+ # ...
+ # def findtarget(self):
+ # return self.__target
+ # forwardmethods(MyClass, TargetClass, '__target', ['dangerous1', 'dangerous2'])
+ # # ...or...
+ # forwardmethods(MyClass, TargetClass, MyClass.findtarget,
+ # ['dangerous1', 'dangerous2'])
+
+ # In both cases, all TargetClass methods will be forwarded from
+ # MyClass except for dangerous1, dangerous2, special methods like
+ # __str__, and pre-existing methods like findtarget.
+
+
+ # Allow an attribute name (String) or a function to determine the instance
+ if type(toPart) != types.StringType:
+
+ # check that it is something like a function
+ if callable(toPart):
+
+ # If a method is passed, use the function within it
+ if hasattr(toPart, 'im_func'):
+ toPart = toPart.im_func
+
+ # After this is set up, forwarders in this class will use
+ # the forwarding function. The forwarding function name is
+ # guaranteed to be unique, so that it can't be hidden by subclasses
+ forwardName = '__fwdfunc__' + __unique()
+ fromClass.__dict__[forwardName] = toPart
+
+ # It's not a valid type
+ else:
+ raise TypeError, 'toPart must be attribute name, function or method'
+
+ # get the full set of candidate methods
+ dict = {}
+ __methodDict(toClass, dict)
+
+ # discard special methods
+ for ex in dict.keys():
+ if ex[:1] == '_' or ex[-1:] == '_':
+ del dict[ex]
+ # discard dangerous methods supplied by the caller
+ for ex in exclude:
+ if dict.has_key(ex):
+ del dict[ex]
+ # discard methods already defined in fromClass
+ for ex in __methods(fromClass):
+ if dict.has_key(ex):
+ del dict[ex]
+
+ for method, func in dict.items():
+ d = {'method': method, 'func': func}
+ if type(toPart) == types.StringType:
+ execString = \
+ __stringBody % {'method' : method, 'attribute' : toPart}
+ else:
+ execString = \
+ __funcBody % {'forwardFunc' : forwardName, 'method' : method}
+
+ exec execString in d
+
+ # this creates a method
+ fromClass.__dict__[method] = d[method]
+
+#=============================================================================
+
+def setgeometryanddeiconify(window, geom):
+ # To avoid flashes on X and to position the window correctly on NT
+ # (caused by Tk bugs).
+
+ if os.name == 'nt' or \
+ (os.name == 'posix' and sys.platform[:6] == 'cygwin'):
+ # Require overrideredirect trick to stop window frame
+ # appearing momentarily.
+ redirect = window.overrideredirect()
+ if not redirect:
+ window.overrideredirect(1)
+ window.deiconify()
+ if geom is not None:
+ window.geometry(geom)
+ # Call update_idletasks to ensure NT moves the window to the
+ # correct position it is raised.
+ window.update_idletasks()
+ window.tkraise()
+ if not redirect:
+ window.overrideredirect(0)
+ else:
+ if geom is not None:
+ window.geometry(geom)
+
+ # Problem!? Which way around should the following two calls
+ # go? If deiconify() is called first then I get complaints
+ # from people using the enlightenment or sawfish window
+ # managers that when a dialog is activated it takes about 2
+ # seconds for the contents of the window to appear. But if
+ # tkraise() is called first then I get complaints from people
+ # using the twm window manager that when a dialog is activated
+ # it appears in the top right corner of the screen and also
+ # takes about 2 seconds to appear.
+
+ #window.tkraise()
+ # Call update_idletasks to ensure certain window managers (eg:
+ # enlightenment and sawfish) do not cause Tk to delay for
+ # about two seconds before displaying window.
+ #window.update_idletasks()
+ #window.deiconify()
+
+ window.deiconify()
+ if window.overrideredirect():
+ # The window is not under the control of the window manager
+ # and so we need to raise it ourselves.
+ window.tkraise()
+
+#=============================================================================
+
+class MegaArchetype:
+ # Megawidget abstract root class.
+
+ # This class provides methods which are inherited by classes
+ # implementing useful bases (this class doesn't provide a
+ # container widget inside which the megawidget can be built).
+
+ def __init__(self, parent = None, hullClass = None):
+
+ # Mapping from each megawidget option to a list of information
+ # about the option
+ # - default value
+ # - current value
+ # - function to call when the option is initialised in the
+ # call to initialiseoptions() in the constructor or
+ # modified via configure(). If this is INITOPT, the
+ # option is an initialisation option (an option that can
+ # be set by the call to the constructor but can not be
+ # used with configure).
+ # This mapping is not initialised here, but in the call to
+ # defineoptions() which precedes construction of this base class.
+ #
+ # self._optionInfo = {}
+
+ # Mapping from each component name to a tuple of information
+ # about the component.
+ # - component widget instance
+ # - configure function of widget instance
+ # - the class of the widget (Frame, EntryField, etc)
+ # - cget function of widget instance
+ # - the name of the component group of this component, if any
+ self.__componentInfo = {}
+
+ # Mapping from alias names to the names of components or
+ # sub-components.
+ self.__componentAliases = {}
+
+ # Contains information about the keywords provided to the
+ # constructor. It is a mapping from the keyword to a tuple
+ # containing:
+ # - value of keyword
+ # - a boolean indicating if the keyword has been used.
+ # A keyword is used if, during the construction of a megawidget,
+ # - it is defined in a call to defineoptions() or addoptions(), or
+ # - it references, by name, a component of the megawidget, or
+ # - it references, by group, at least one component
+ # At the end of megawidget construction, a call is made to
+ # initialiseoptions() which reports an error if there are
+ # unused options given to the constructor.
+ #
+ # After megawidget construction, the dictionary contains
+ # keywords which refer to a dynamic component group, so that
+ # these components can be created after megawidget
+ # construction and still use the group options given to the
+ # constructor.
+ #
+ # self._constructorKeywords = {}
+
+ # List of dynamic component groups. If a group is included in
+ # this list, then it not an error if a keyword argument for
+ # the group is given to the constructor or to configure(), but
+ # no components with this group have been created.
+ # self._dynamicGroups = ()
+
+ if hullClass is None:
+ self._hull = None
+ else:
+ if parent is None:
+ parent = Tkinter._default_root
+
+ # Create the hull.
+ self._hull = self.createcomponent('hull',
+ (), None,
+ hullClass, (parent,))
+ _hullToMegaWidget[self._hull] = self
+
+ if _useTkOptionDb:
+ # Now that a widget has been created, query the Tk
+ # option database to get the default values for the
+ # options which have not been set in the call to the
+ # constructor. This assumes that defineoptions() is
+ # called before the __init__().
+ option_get = self.option_get
+ _VALUE = _OPT_VALUE
+ _DEFAULT = _OPT_DEFAULT
+ for name, info in self._optionInfo.items():
+ value = info[_VALUE]
+ if value is _DEFAULT_OPTION_VALUE:
+ resourceClass = string.upper(name[0]) + name[1:]
+ value = option_get(name, resourceClass)
+ if value != '':
+ try:
+ # Convert the string to int/float/tuple, etc
+ value = eval(value, {'__builtins__': {}})
+ except:
+ pass
+ info[_VALUE] = value
+ else:
+ info[_VALUE] = info[_DEFAULT]
+
+ def destroy(self):
+ # Clean up optionInfo in case it contains circular references
+ # in the function field, such as self._settitle in class
+ # MegaToplevel.
+
+ self._optionInfo = {}
+ if self._hull is not None:
+ del _hullToMegaWidget[self._hull]
+ self._hull.destroy()
+
+ #======================================================================
+ # Methods used (mainly) during the construction of the megawidget.
+
+ def defineoptions(self, keywords, optionDefs, dynamicGroups = ()):
+ # Create options, providing the default value and the method
+ # to call when the value is changed. If any option created by
+ # base classes has the same name as one in <optionDefs>, the
+ # base class's value and function will be overriden.
+
+ # This should be called before the constructor of the base
+ # class, so that default values defined in the derived class
+ # override those in the base class.
+
+ if not hasattr(self, '_constructorKeywords'):
+ # First time defineoptions has been called.
+ tmp = {}
+ for option, value in keywords.items():
+ tmp[option] = [value, 0]
+ self._constructorKeywords = tmp
+ self._optionInfo = {}
+ self._initialiseoptions_counter = 0
+ self._initialiseoptions_counter = self._initialiseoptions_counter + 1
+
+ if not hasattr(self, '_dynamicGroups'):
+ self._dynamicGroups = ()
+ self._dynamicGroups = self._dynamicGroups + tuple(dynamicGroups)
+ self.addoptions(optionDefs)
+
+ def addoptions(self, optionDefs):
+ # Add additional options, providing the default value and the
+ # method to call when the value is changed. See
+ # "defineoptions" for more details
+
+ # optimisations:
+ optionInfo = self._optionInfo
+ optionInfo_has_key = optionInfo.has_key
+ keywords = self._constructorKeywords
+ keywords_has_key = keywords.has_key
+ FUNCTION = _OPT_FUNCTION
+
+ for name, default, function in optionDefs:
+ if '_' not in name:
+ # The option will already exist if it has been defined
+ # in a derived class. In this case, do not override the
+ # default value of the option or the callback function
+ # if it is not None.
+ if not optionInfo_has_key(name):
+ if keywords_has_key(name):
+ value = keywords[name][0]
+ optionInfo[name] = [default, value, function]
+ del keywords[name]
+ else:
+ if _useTkOptionDb:
+ optionInfo[name] = \
+ [default, _DEFAULT_OPTION_VALUE, function]
+ else:
+ optionInfo[name] = [default, default, function]
+ elif optionInfo[name][FUNCTION] is None:
+ optionInfo[name][FUNCTION] = function
+ else:
+ # This option is of the form "component_option". If this is
+ # not already defined in self._constructorKeywords add it.
+ # This allows a derived class to override the default value
+ # of an option of a component of a base class.
+ if not keywords_has_key(name):
+ keywords[name] = [default, 0]
+
+ def createcomponent(self, componentName, componentAliases,
+ componentGroup, widgetClass, *widgetArgs, **kw):
+ # Create a component (during construction or later).
+
+ if self.__componentInfo.has_key(componentName):
+ raise ValueError, 'Component "%s" already exists' % componentName
+
+ if '_' in componentName:
+ raise ValueError, \
+ 'Component name "%s" must not contain "_"' % componentName
+
+ if hasattr(self, '_constructorKeywords'):
+ keywords = self._constructorKeywords
+ else:
+ keywords = {}
+ for alias, component in componentAliases:
+ # Create aliases to the component and its sub-components.
+ index = string.find(component, '_')
+ if index < 0:
+ self.__componentAliases[alias] = (component, None)
+ else:
+ mainComponent = component[:index]
+ subComponent = component[(index + 1):]
+ self.__componentAliases[alias] = (mainComponent, subComponent)
+
+ # Remove aliases from the constructor keyword arguments by
+ # replacing any keyword arguments that begin with *alias*
+ # with corresponding keys beginning with *component*.
+
+ alias = alias + '_'
+ aliasLen = len(alias)
+ for option in keywords.keys():
+ if len(option) > aliasLen and option[:aliasLen] == alias:
+ newkey = component + '_' + option[aliasLen:]
+ keywords[newkey] = keywords[option]
+ del keywords[option]
+
+ componentPrefix = componentName + '_'
+ nameLen = len(componentPrefix)
+ for option in keywords.keys():
+ if len(option) > nameLen and option[:nameLen] == componentPrefix:
+ # The keyword argument refers to this component, so add
+ # this to the options to use when constructing the widget.
+ kw[option[nameLen:]] = keywords[option][0]
+ del keywords[option]
+ else:
+ # Check if this keyword argument refers to the group
+ # of this component. If so, add this to the options
+ # to use when constructing the widget. Mark the
+ # keyword argument as being used, but do not remove it
+ # since it may be required when creating another
+ # component.
+ index = string.find(option, '_')
+ if index >= 0 and componentGroup == option[:index]:
+ rest = option[(index + 1):]
+ kw[rest] = keywords[option][0]
+ keywords[option][1] = 1
+
+ if kw.has_key('pyclass'):
+ widgetClass = kw['pyclass']
+ del kw['pyclass']
+ if widgetClass is None:
+ return None
+ if len(widgetArgs) == 1 and type(widgetArgs[0]) == types.TupleType:
+ # Arguments to the constructor can be specified as either
+ # multiple trailing arguments to createcomponent() or as a
+ # single tuple argument.
+ widgetArgs = widgetArgs[0]
+ widget = apply(widgetClass, widgetArgs, kw)
+ componentClass = widget.__class__.__name__
+ self.__componentInfo[componentName] = (widget, widget.configure,
+ componentClass, widget.cget, componentGroup)
+
+ return widget
+
+ def destroycomponent(self, name):
+ # Remove a megawidget component.
+
+ # This command is for use by megawidget designers to destroy a
+ # megawidget component.
+
+ self.__componentInfo[name][0].destroy()
+ del self.__componentInfo[name]
+
+ def createlabel(self, parent, childCols = 1, childRows = 1):
+
+ labelpos = self['labelpos']
+ labelmargin = self['labelmargin']
+ if labelpos is None:
+ return
+
+ label = self.createcomponent('label',
+ (), None,
+ Tkinter.Label, (parent,))
+
+ if labelpos[0] in 'ns':
+ # vertical layout
+ if labelpos[0] == 'n':
+ row = 0
+ margin = 1
+ else:
+ row = childRows + 3
+ margin = row - 1
+ label.grid(column=2, row=row, columnspan=childCols, sticky=labelpos)
+ parent.grid_rowconfigure(margin, minsize=labelmargin)
+ else:
+ # horizontal layout
+ if labelpos[0] == 'w':
+ col = 0
+ margin = 1
+ else:
+ col = childCols + 3
+ margin = col - 1
+ label.grid(column=col, row=2, rowspan=childRows, sticky=labelpos)
+ parent.grid_columnconfigure(margin, minsize=labelmargin)
+
+ def initialiseoptions(self, dummy = None):
+ self._initialiseoptions_counter = self._initialiseoptions_counter - 1
+ if self._initialiseoptions_counter == 0:
+ unusedOptions = []
+ keywords = self._constructorKeywords
+ for name in keywords.keys():
+ used = keywords[name][1]
+ if not used:
+ # This keyword argument has not been used. If it
+ # does not refer to a dynamic group, mark it as
+ # unused.
+ index = string.find(name, '_')
+ if index < 0 or name[:index] not in self._dynamicGroups:
+ unusedOptions.append(name)
+ if len(unusedOptions) > 0:
+ if len(unusedOptions) == 1:
+ text = 'Unknown option "'
+ else:
+ text = 'Unknown options "'
+ raise KeyError, text + string.join(unusedOptions, ', ') + \
+ '" for ' + self.__class__.__name__
+
+ # Call the configuration callback function for every option.
+ FUNCTION = _OPT_FUNCTION
+ for info in self._optionInfo.values():
+ func = info[FUNCTION]
+ if func is not None and func is not INITOPT:
+ func()
+
+ #======================================================================
+ # Method used to configure the megawidget.
+
+ def configure(self, option=None, **kw):
+ # Query or configure the megawidget options.
+ #
+ # If not empty, *kw* is a dictionary giving new
+ # values for some of the options of this megawidget or its
+ # components. For options defined for this megawidget, set
+ # the value of the option to the new value and call the
+ # configuration callback function, if any. For options of the
+ # form <component>_<option>, where <component> is a component
+ # of this megawidget, call the configure method of the
+ # component giving it the new value of the option. The
+ # <component> part may be an alias or a component group name.
+ #
+ # If *option* is None, return all megawidget configuration
+ # options and settings. Options are returned as standard 5
+ # element tuples
+ #
+ # If *option* is a string, return the 5 element tuple for the
+ # given configuration option.
+
+ # First, deal with the option queries.
+ if len(kw) == 0:
+ # This configure call is querying the values of one or all options.
+ # Return 5-tuples:
+ # (optionName, resourceName, resourceClass, default, value)
+ if option is None:
+ rtn = {}
+ for option, config in self._optionInfo.items():
+ resourceClass = string.upper(option[0]) + option[1:]
+ rtn[option] = (option, option, resourceClass,
+ config[_OPT_DEFAULT], config[_OPT_VALUE])
+ return rtn
+ else:
+ config = self._optionInfo[option]
+ resourceClass = string.upper(option[0]) + option[1:]
+ return (option, option, resourceClass, config[_OPT_DEFAULT],
+ config[_OPT_VALUE])
+
+ # optimisations:
+ optionInfo = self._optionInfo
+ optionInfo_has_key = optionInfo.has_key
+ componentInfo = self.__componentInfo
+ componentInfo_has_key = componentInfo.has_key
+ componentAliases = self.__componentAliases
+ componentAliases_has_key = componentAliases.has_key
+ VALUE = _OPT_VALUE
+ FUNCTION = _OPT_FUNCTION
+
+ # This will contain a list of options in *kw* which
+ # are known to this megawidget.
+ directOptions = []
+
+ # This will contain information about the options in
+ # *kw* of the form <component>_<option>, where
+ # <component> is a component of this megawidget. It is a
+ # dictionary whose keys are the configure method of each
+ # component and whose values are a dictionary of options and
+ # values for the component.
+ indirectOptions = {}
+ indirectOptions_has_key = indirectOptions.has_key
+
+ for option, value in kw.items():
+ if optionInfo_has_key(option):
+ # This is one of the options of this megawidget.
+ # Make sure it is not an initialisation option.
+ if optionInfo[option][FUNCTION] is INITOPT:
+ raise KeyError, \
+ 'Cannot configure initialisation option "' \
+ + option + '" for ' + self.__class__.__name__
+ optionInfo[option][VALUE] = value
+ directOptions.append(option)
+ else:
+ index = string.find(option, '_')
+ if index >= 0:
+ # This option may be of the form <component>_<option>.
+ component = option[:index]
+ componentOption = option[(index + 1):]
+
+ # Expand component alias
+ if componentAliases_has_key(component):
+ component, subComponent = componentAliases[component]
+ if subComponent is not None:
+ componentOption = subComponent + '_' \
+ + componentOption
+
+ # Expand option string to write on error
+ option = component + '_' + componentOption
+
+ if componentInfo_has_key(component):
+ # Configure the named component
+ componentConfigFuncs = [componentInfo[component][1]]
+ else:
+ # Check if this is a group name and configure all
+ # components in the group.
+ componentConfigFuncs = []
+ for info in componentInfo.values():
+ if info[4] == component:
+ componentConfigFuncs.append(info[1])
+
+ if len(componentConfigFuncs) == 0 and \
+ component not in self._dynamicGroups:
+ raise KeyError, 'Unknown option "' + option + \
+ '" for ' + self.__class__.__name__
+
+ # Add the configure method(s) (may be more than
+ # one if this is configuring a component group)
+ # and option/value to dictionary.
+ for componentConfigFunc in componentConfigFuncs:
+ if not indirectOptions_has_key(componentConfigFunc):
+ indirectOptions[componentConfigFunc] = {}
+ indirectOptions[componentConfigFunc][componentOption] \
+ = value
+ else:
+ raise KeyError, 'Unknown option "' + option + \
+ '" for ' + self.__class__.__name__
+
+ # Call the configure methods for any components.
+ map(apply, indirectOptions.keys(),
+ ((),) * len(indirectOptions), indirectOptions.values())
+
+ # Call the configuration callback function for each option.
+ for option in directOptions:
+ info = optionInfo[option]
+ func = info[_OPT_FUNCTION]
+ if func is not None:
+ func()
+
+ def __setitem__(self, key, value):
+ apply(self.configure, (), {key: value})
+
+ #======================================================================
+ # Methods used to query the megawidget.
+
+ def component(self, name):
+ # Return a component widget of the megawidget given the
+ # component's name
+ # This allows the user of a megawidget to access and configure
+ # widget components directly.
+
+ # Find the main component and any subcomponents
+ index = string.find(name, '_')
+ if index < 0:
+ component = name
+ remainingComponents = None
+ else:
+ component = name[:index]
+ remainingComponents = name[(index + 1):]
+
+ # Expand component alias
+ if self.__componentAliases.has_key(component):
+ component, subComponent = self.__componentAliases[component]
+ if subComponent is not None:
+ if remainingComponents is None:
+ remainingComponents = subComponent
+ else:
+ remainingComponents = subComponent + '_' \
+ + remainingComponents
+
+ widget = self.__componentInfo[component][0]
+ if remainingComponents is None:
+ return widget
+ else:
+ return widget.component(remainingComponents)
+
+ def interior(self):
+ return self._hull
+
+ def hulldestroyed(self):
+ return not _hullToMegaWidget.has_key(self._hull)
+
+ def __str__(self):
+ return str(self._hull)
+
+ def cget(self, option):
+ # Get current configuration setting.
+
+ # Return the value of an option, for example myWidget['font'].
+
+ if self._optionInfo.has_key(option):
+ return self._optionInfo[option][_OPT_VALUE]
+ else:
+ index = string.find(option, '_')
+ if index >= 0:
+ component = option[:index]
+ componentOption = option[(index + 1):]
+
+ # Expand component alias
+ if self.__componentAliases.has_key(component):
+ component, subComponent = self.__componentAliases[component]
+ if subComponent is not None:
+ componentOption = subComponent + '_' + componentOption
+
+ # Expand option string to write on error
+ option = component + '_' + componentOption
+
+ if self.__componentInfo.has_key(component):
+ # Call cget on the component.
+ componentCget = self.__componentInfo[component][3]
+ return componentCget(componentOption)
+ else:
+ # If this is a group name, call cget for one of
+ # the components in the group.
+ for info in self.__componentInfo.values():
+ if info[4] == component:
+ componentCget = info[3]
+ return componentCget(componentOption)
+
+ raise KeyError, 'Unknown option "' + option + \
+ '" for ' + self.__class__.__name__
+
+ __getitem__ = cget
+
+ def isinitoption(self, option):
+ return self._optionInfo[option][_OPT_FUNCTION] is INITOPT
+
+ def options(self):
+ options = []
+ if hasattr(self, '_optionInfo'):
+ for option, info in self._optionInfo.items():
+ isinit = info[_OPT_FUNCTION] is INITOPT
+ default = info[_OPT_DEFAULT]
+ options.append((option, default, isinit))
+ options.sort()
+ return options
+
+ def components(self):
+ # Return a list of all components.
+
+ # This list includes the 'hull' component and all widget subcomponents
+
+ names = self.__componentInfo.keys()
+ names.sort()
+ return names
+
+ def componentaliases(self):
+ # Return a list of all component aliases.
+
+ componentAliases = self.__componentAliases
+
+ names = componentAliases.keys()
+ names.sort()
+ rtn = []
+ for alias in names:
+ (mainComponent, subComponent) = componentAliases[alias]
+ if subComponent is None:
+ rtn.append((alias, mainComponent))
+ else:
+ rtn.append((alias, mainComponent + '_' + subComponent))
+
+ return rtn
+
+ def componentgroup(self, name):
+ return self.__componentInfo[name][4]
+
+#=============================================================================
+
+# The grab functions are mainly called by the activate() and
+# deactivate() methods.
+#
+# Use pushgrab() to add a new window to the grab stack. This
+# releases the grab by the window currently on top of the stack (if
+# there is one) and gives the grab and focus to the new widget.
+#
+# To remove the grab from the window on top of the grab stack, call
+# popgrab().
+#
+# Use releasegrabs() to release the grab and clear the grab stack.
+
+def pushgrab(grabWindow, globalMode, deactivateFunction):
+ prevFocus = grabWindow.tk.call('focus')
+ grabInfo = {
+ 'grabWindow' : grabWindow,
+ 'globalMode' : globalMode,
+ 'previousFocus' : prevFocus,
+ 'deactivateFunction' : deactivateFunction,
+ }
+ _grabStack.append(grabInfo)
+ _grabtop()
+ grabWindow.focus_set()
+
+def popgrab(window):
+ # Return the grab to the next window in the grab stack, if any.
+
+ # If this window is not at the top of the grab stack, then it has
+ # just been deleted by the window manager or deactivated by a
+ # timer. Call the deactivate method for the modal dialog above
+ # this one on the stack.
+ if _grabStack[-1]['grabWindow'] != window:
+ for index in range(len(_grabStack)):
+ if _grabStack[index]['grabWindow'] == window:
+ _grabStack[index + 1]['deactivateFunction']()
+ break
+
+ grabInfo = _grabStack[-1]
+ del _grabStack[-1]
+
+ topWidget = grabInfo['grabWindow']
+ prevFocus = grabInfo['previousFocus']
+ globalMode = grabInfo['globalMode']
+
+ if globalMode != 'nograb':
+ topWidget.grab_release()
+
+ if len(_grabStack) > 0:
+ _grabtop()
+ if prevFocus != '':
+ try:
+ topWidget.tk.call('focus', prevFocus)
+ except Tkinter.TclError:
+ # Previous focus widget has been deleted. Set focus
+ # to root window.
+ Tkinter._default_root.focus_set()
+ else:
+ # Make sure that focus does not remain on the released widget.
+ if len(_grabStack) > 0:
+ topWidget = _grabStack[-1]['grabWindow']
+ topWidget.focus_set()
+ else:
+ Tkinter._default_root.focus_set()
+
+def grabstacktopwindow():
+ if len(_grabStack) == 0:
+ return None
+ else:
+ return _grabStack[-1]['grabWindow']
+
+def releasegrabs():
+ # Release grab and clear the grab stack.
+
+ current = Tkinter._default_root.grab_current()
+ if current is not None:
+ current.grab_release()
+ _grabStack[:] = []
+
+def _grabtop():
+ grabInfo = _grabStack[-1]
+ topWidget = grabInfo['grabWindow']
+ globalMode = grabInfo['globalMode']
+
+ if globalMode == 'nograb':
+ return
+
+ while 1:
+ try:
+ if globalMode:
+ topWidget.grab_set_global()
+ else:
+ topWidget.grab_set()
+ break
+ except Tkinter.TclError:
+ # Another application has grab. Keep trying until
+ # grab can succeed.
+ topWidget.after(100)
+
+#=============================================================================
+
+class MegaToplevel(MegaArchetype):
+
+ def __init__(self, parent = None, **kw):
+ # Define the options for this megawidget.
+ optiondefs = (
+ ('activatecommand', None, None),
+ ('deactivatecommand', None, None),
+ ('master', None, None),
+ ('title', None, self._settitle),
+ ('hull_class', self.__class__.__name__, None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ MegaArchetype.__init__(self, parent, Tkinter.Toplevel)
+
+ # Initialise instance.
+
+ # Set WM_DELETE_WINDOW protocol, deleting any old callback, so
+ # memory does not leak.
+ if hasattr(self._hull, '_Pmw_WM_DELETE_name'):
+ self._hull.tk.deletecommand(self._hull._Pmw_WM_DELETE_name)
+ self._hull._Pmw_WM_DELETE_name = \
+ self.register(self._userDeleteWindow, needcleanup = 0)
+ self.protocol('WM_DELETE_WINDOW', self._hull._Pmw_WM_DELETE_name)
+
+ # Initialise instance variables.
+
+ self._firstShowing = 1
+ # Used by show() to ensure window retains previous position on screen.
+
+ # The IntVar() variable to wait on during a modal dialog.
+ self._wait = None
+
+ self._active = 0
+ self._userDeleteFunc = self.destroy
+ self._userModalDeleteFunc = self.deactivate
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _settitle(self):
+ title = self['title']
+ if title is not None:
+ self.title(title)
+
+ def userdeletefunc(self, func=None):
+ if func:
+ self._userDeleteFunc = func
+ else:
+ return self._userDeleteFunc
+
+ def usermodaldeletefunc(self, func=None):
+ if func:
+ self._userModalDeleteFunc = func
+ else:
+ return self._userModalDeleteFunc
+
+ def _userDeleteWindow(self):
+ if self.active():
+ self._userModalDeleteFunc()
+ else:
+ self._userDeleteFunc()
+
+ def destroy(self):
+ # Allow this to be called more than once.
+ if _hullToMegaWidget.has_key(self._hull):
+ self.deactivate()
+
+ # Remove circular references, so that object can get cleaned up.
+ del self._userDeleteFunc
+ del self._userModalDeleteFunc
+
+ MegaArchetype.destroy(self)
+
+ def show(self, master = None):
+ if self.state() != 'normal':
+ if self._firstShowing:
+ # Just let the window manager determine the window
+ # position for the first time.
+ geom = None
+ else:
+ # Position the window at the same place it was last time.
+ geom = self._sameposition()
+ setgeometryanddeiconify(self, geom)
+
+ if self._firstShowing:
+ self._firstShowing = 0
+ else:
+ if self.transient() == '':
+ self.tkraise()
+
+ # Do this last, otherwise get flashing on NT:
+ if master is not None:
+ if master == 'parent':
+ parent = self.winfo_parent()
+ # winfo_parent() should return the parent widget, but the
+ # the current version of Tkinter returns a string.
+ if type(parent) == types.StringType:
+ parent = self._hull._nametowidget(parent)
+ master = parent.winfo_toplevel()
+ self.transient(master)
+
+ self.focus()
+
+ def _centreonscreen(self):
+ # Centre the window on the screen. (Actually halfway across
+ # and one third down.)
+
+ parent = self.winfo_parent()
+ if type(parent) == types.StringType:
+ parent = self._hull._nametowidget(parent)
+
+ # Find size of window.
+ self.update_idletasks()
+ width = self.winfo_width()
+ height = self.winfo_height()
+ if width == 1 and height == 1:
+ # If the window has not yet been displayed, its size is
+ # reported as 1x1, so use requested size.
+ width = self.winfo_reqwidth()
+ height = self.winfo_reqheight()
+
+ # Place in centre of screen:
+ x = (self.winfo_screenwidth() - width) / 2 - parent.winfo_vrootx()
+ y = (self.winfo_screenheight() - height) / 3 - parent.winfo_vrooty()
+ if x < 0:
+ x = 0
+ if y < 0:
+ y = 0
+ return '+%d+%d' % (x, y)
+
+ def _sameposition(self):
+ # Position the window at the same place it was last time.
+
+ geometry = self.geometry()
+ index = string.find(geometry, '+')
+ if index >= 0:
+ return geometry[index:]
+ else:
+ return None
+
+ def activate(self, globalMode = 0, geometry = 'centerscreenfirst'):
+ if self._active:
+ raise ValueError, 'Window is already active'
+ if self.state() == 'normal':
+ self.withdraw()
+
+ self._active = 1
+
+ showbusycursor()
+
+ if self._wait is None:
+ self._wait = Tkinter.IntVar()
+ self._wait.set(0)
+
+ if geometry == 'centerscreenalways':
+ geom = self._centreonscreen()
+ elif geometry == 'centerscreenfirst':
+ if self._firstShowing:
+ # Centre the window the first time it is displayed.
+ geom = self._centreonscreen()
+ else:
+ # Position the window at the same place it was last time.
+ geom = self._sameposition()
+ elif geometry[:5] == 'first':
+ if self._firstShowing:
+ geom = geometry[5:]
+ else:
+ # Position the window at the same place it was last time.
+ geom = self._sameposition()
+ else:
+ geom = geometry
+
+ self._firstShowing = 0
+
+ setgeometryanddeiconify(self, geom)
+
+ # Do this last, otherwise get flashing on NT:
+ master = self['master']
+ if master is not None:
+ if master == 'parent':
+ parent = self.winfo_parent()
+ # winfo_parent() should return the parent widget, but the
+ # the current version of Tkinter returns a string.
+ if type(parent) == types.StringType:
+ parent = self._hull._nametowidget(parent)
+ master = parent.winfo_toplevel()
+ self.transient(master)
+
+ pushgrab(self._hull, globalMode, self.deactivate)
+ command = self['activatecommand']
+ if callable(command):
+ command()
+ self.wait_variable(self._wait)
+
+ return self._result
+
+ def deactivate(self, result=None):
+ if not self._active:
+ return
+ self._active = 0
+
+ # Restore the focus before withdrawing the window, since
+ # otherwise the window manager may take the focus away so we
+ # can't redirect it. Also, return the grab to the next active
+ # window in the stack, if any.
+ popgrab(self._hull)
+
+ command = self['deactivatecommand']
+ if callable(command):
+ command()
+
+ self.withdraw()
+ hidebusycursor(forceFocusRestore = 1)
+
+ self._result = result
+ self._wait.set(1)
+
+ def active(self):
+ return self._active
+
+forwardmethods(MegaToplevel, Tkinter.Toplevel, '_hull')
+
+#=============================================================================
+
+class MegaWidget(MegaArchetype):
+ def __init__(self, parent = None, **kw):
+ # Define the options for this megawidget.
+ optiondefs = (
+ ('hull_class', self.__class__.__name__, None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ MegaArchetype.__init__(self, parent, Tkinter.Frame)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+forwardmethods(MegaWidget, Tkinter.Frame, '_hull')
+
+#=============================================================================
+
+# Public functions
+#-----------------
+
+_traceTk = 0
+def tracetk(root = None, on = 1, withStackTrace = 0, file=None):
+ global _withStackTrace
+ global _traceTkFile
+ global _traceTk
+
+ if root is None:
+ root = Tkinter._default_root
+
+ _withStackTrace = withStackTrace
+ _traceTk = on
+ if on:
+ if hasattr(root.tk, '__class__'):
+ # Tracing already on
+ return
+ if file is None:
+ _traceTkFile = sys.stderr
+ else:
+ _traceTkFile = file
+ tk = _TraceTk(root.tk)
+ else:
+ if not hasattr(root.tk, '__class__'):
+ # Tracing already off
+ return
+ tk = root.tk.getTclInterp()
+ _setTkInterps(root, tk)
+
+def showbusycursor():
+
+ _addRootToToplevelBusyInfo()
+ root = Tkinter._default_root
+
+ busyInfo = {
+ 'newBusyWindows' : [],
+ 'previousFocus' : None,
+ 'busyFocus' : None,
+ }
+ _busyStack.append(busyInfo)
+
+ if _disableKeyboardWhileBusy:
+ # Remember the focus as it is now, before it is changed.
+ busyInfo['previousFocus'] = root.tk.call('focus')
+
+ if not _havebltbusy(root):
+ # No busy command, so don't call busy hold on any windows.
+ return
+
+ for (window, winInfo) in _toplevelBusyInfo.items():
+ if (window.state() != 'withdrawn' and not winInfo['isBusy']
+ and not winInfo['excludeFromBusy']):
+ busyInfo['newBusyWindows'].append(window)
+ winInfo['isBusy'] = 1
+ _busy_hold(window, winInfo['busyCursorName'])
+
+ # Make sure that no events for the busy window get
+ # through to Tkinter, otherwise it will crash in
+ # _nametowidget with a 'KeyError: _Busy' if there is
+ # a binding on the toplevel window.
+ window.tk.call('bindtags', winInfo['busyWindow'], 'Pmw_Dummy_Tag')
+
+ if _disableKeyboardWhileBusy:
+ # Remember previous focus widget for this toplevel window
+ # and set focus to the busy window, which will ignore all
+ # keyboard events.
+ winInfo['windowFocus'] = \
+ window.tk.call('focus', '-lastfor', window._w)
+ window.tk.call('focus', winInfo['busyWindow'])
+ busyInfo['busyFocus'] = winInfo['busyWindow']
+
+ if len(busyInfo['newBusyWindows']) > 0:
+ if os.name == 'nt':
+ # NT needs an "update" before it will change the cursor.
+ window.update()
+ else:
+ window.update_idletasks()
+
+def hidebusycursor(forceFocusRestore = 0):
+
+ # Remember the focus as it is now, before it is changed.
+ root = Tkinter._default_root
+ if _disableKeyboardWhileBusy:
+ currentFocus = root.tk.call('focus')
+
+ # Pop the busy info off the stack.
+ busyInfo = _busyStack[-1]
+ del _busyStack[-1]
+
+ for window in busyInfo['newBusyWindows']:
+ # If this window has not been deleted, release the busy cursor.
+ if _toplevelBusyInfo.has_key(window):
+ winInfo = _toplevelBusyInfo[window]
+ winInfo['isBusy'] = 0
+ _busy_release(window)
+
+ if _disableKeyboardWhileBusy:
+ # Restore previous focus window for this toplevel window,
+ # but only if is still set to the busy window (it may have
+ # been changed).
+ windowFocusNow = window.tk.call('focus', '-lastfor', window._w)
+ if windowFocusNow == winInfo['busyWindow']:
+ try:
+ window.tk.call('focus', winInfo['windowFocus'])
+ except Tkinter.TclError:
+ # Previous focus widget has been deleted. Set focus
+ # to toplevel window instead (can't leave focus on
+ # busy window).
+ window.focus_set()
+
+ if _disableKeyboardWhileBusy:
+ # Restore the focus, depending on whether the focus had changed
+ # between the calls to showbusycursor and hidebusycursor.
+ if forceFocusRestore or busyInfo['busyFocus'] == currentFocus:
+ # The focus had not changed, so restore it to as it was before
+ # the call to showbusycursor,
+ previousFocus = busyInfo['previousFocus']
+ if previousFocus is not None:
+ try:
+ root.tk.call('focus', previousFocus)
+ except Tkinter.TclError:
+ # Previous focus widget has been deleted; forget it.
+ pass
+ else:
+ # The focus had changed, so restore it to what it had been
+ # changed to before the call to hidebusycursor.
+ root.tk.call('focus', currentFocus)
+
+def clearbusycursor():
+ while len(_busyStack) > 0:
+ hidebusycursor()
+
+def setbusycursorattributes(window, **kw):
+ _addRootToToplevelBusyInfo()
+ for name, value in kw.items():
+ if name == 'exclude':
+ _toplevelBusyInfo[window]['excludeFromBusy'] = value
+ elif name == 'cursorName':
+ _toplevelBusyInfo[window]['busyCursorName'] = value
+ else:
+ raise KeyError, 'Unknown busycursor attribute "' + name + '"'
+
+def _addRootToToplevelBusyInfo():
+ # Include the Tk root window in the list of toplevels. This must
+ # not be called before Tkinter has had a chance to be initialised by
+ # the application.
+
+ root = Tkinter._default_root
+ if root == None:
+ root = Tkinter.Tk()
+ if not _toplevelBusyInfo.has_key(root):
+ _addToplevelBusyInfo(root)
+
+def busycallback(command, updateFunction = None):
+ if not callable(command):
+ raise ValueError, \
+ 'cannot register non-command busy callback %s %s' % \
+ (repr(command), type(command))
+ wrapper = _BusyWrapper(command, updateFunction)
+ return wrapper.callback
+
+_errorReportFile = None
+_errorWindow = None
+
+def reporterrorstofile(file = None):
+ global _errorReportFile
+ _errorReportFile = file
+
+def displayerror(text):
+ global _errorWindow
+
+ if _errorReportFile is not None:
+ _errorReportFile.write(text + '\n')
+ else:
+ # Print error on standard error as well as to error window.
+ # Useful if error window fails to be displayed, for example
+ # when exception is triggered in a <Destroy> binding for root
+ # window.
+ sys.stderr.write(text + '\n')
+
+ if _errorWindow is None:
+ # The error window has not yet been created.
+ _errorWindow = _ErrorWindow()
+
+ _errorWindow.showerror(text)
+
+_root = None
+_disableKeyboardWhileBusy = 1
+
+def initialise(
+ root = None,
+ size = None,
+ fontScheme = None,
+ useTkOptionDb = 0,
+ noBltBusy = 0,
+ disableKeyboardWhileBusy = None,
+):
+ # Remember if show/hidebusycursor should ignore keyboard events.
+ global _disableKeyboardWhileBusy
+ if disableKeyboardWhileBusy is not None:
+ _disableKeyboardWhileBusy = disableKeyboardWhileBusy
+
+ # Do not use blt busy command if noBltBusy is set. Otherwise,
+ # use blt busy if it is available.
+ global _haveBltBusy
+ if noBltBusy:
+ _haveBltBusy = 0
+
+ # Save flag specifying whether the Tk option database should be
+ # queried when setting megawidget option default values.
+ global _useTkOptionDb
+ _useTkOptionDb = useTkOptionDb
+
+ # If we haven't been given a root window, use the default or
+ # create one.
+ if root is None:
+ if Tkinter._default_root is None:
+ root = Tkinter.Tk()
+ else:
+ root = Tkinter._default_root
+
+ # If this call is initialising a different Tk interpreter than the
+ # last call, then re-initialise all global variables. Assume the
+ # last interpreter has been destroyed - ie: Pmw does not (yet)
+ # support multiple simultaneous interpreters.
+ global _root
+ if _root is not None and _root != root:
+ global _busyStack
+ global _errorWindow
+ global _grabStack
+ global _hullToMegaWidget
+ global _toplevelBusyInfo
+ _busyStack = []
+ _errorWindow = None
+ _grabStack = []
+ _hullToMegaWidget = {}
+ _toplevelBusyInfo = {}
+ _root = root
+
+ # Trap Tkinter Toplevel constructors so that a list of Toplevels
+ # can be maintained.
+ Tkinter.Toplevel.title = __TkinterToplevelTitle
+
+ # Trap Tkinter widget destruction so that megawidgets can be
+ # destroyed when their hull widget is destoyed and the list of
+ # Toplevels can be pruned.
+ Tkinter.Toplevel.destroy = __TkinterToplevelDestroy
+ Tkinter.Widget.destroy = __TkinterWidgetDestroy
+
+ # Modify Tkinter's CallWrapper class to improve the display of
+ # errors which occur in callbacks.
+ Tkinter.CallWrapper = __TkinterCallWrapper
+
+ # Make sure we get to know when the window manager deletes the
+ # root window. Only do this if the protocol has not yet been set.
+ # This is required if there is a modal dialog displayed and the
+ # window manager deletes the root window. Otherwise the
+ # application will not exit, even though there are no windows.
+ if root.protocol('WM_DELETE_WINDOW') == '':
+ root.protocol('WM_DELETE_WINDOW', root.destroy)
+
+ # Set the base font size for the application and set the
+ # Tk option database font resources.
+ import PmwLogicalFont
+ PmwLogicalFont._font_initialise(root, size, fontScheme)
+
+ return root
+
+def alignlabels(widgets, sticky = None):
+ if len(widgets) == 0:
+ return
+
+ widgets[0].update_idletasks()
+
+ # Determine the size of the maximum length label string.
+ maxLabelWidth = 0
+ for iwid in widgets:
+ labelWidth = iwid.grid_bbox(0, 1)[2]
+ if labelWidth > maxLabelWidth:
+ maxLabelWidth = labelWidth
+
+ # Adjust the margins for the labels such that the child sites and
+ # labels line up.
+ for iwid in widgets:
+ if sticky is not None:
+ iwid.component('label').grid(sticky=sticky)
+ iwid.grid_columnconfigure(0, minsize = maxLabelWidth)
+#=============================================================================
+
+# Private routines
+#-----------------
+_callToTkReturned = 1
+_recursionCounter = 1
+
+class _TraceTk:
+ def __init__(self, tclInterp):
+ self.tclInterp = tclInterp
+
+ def getTclInterp(self):
+ return self.tclInterp
+
+ # Calling from python into Tk.
+ def call(self, *args, **kw):
+ global _callToTkReturned
+ global _recursionCounter
+
+ _callToTkReturned = 0
+ if len(args) == 1 and type(args[0]) == types.TupleType:
+ argStr = str(args[0])
+ else:
+ argStr = str(args)
+ _traceTkFile.write('CALL TK> %d:%s%s' %
+ (_recursionCounter, ' ' * _recursionCounter, argStr))
+ _recursionCounter = _recursionCounter + 1
+ try:
+ result = apply(self.tclInterp.call, args, kw)
+ except Tkinter.TclError, errorString:
+ _callToTkReturned = 1
+ _recursionCounter = _recursionCounter - 1
+ _traceTkFile.write('\nTK ERROR> %d:%s-> %s\n' %
+ (_recursionCounter, ' ' * _recursionCounter,
+ repr(errorString)))
+ if _withStackTrace:
+ _traceTkFile.write('CALL TK> stack:\n')
+ traceback.print_stack()
+ raise Tkinter.TclError, errorString
+
+ _recursionCounter = _recursionCounter - 1
+ if _callToTkReturned:
+ _traceTkFile.write('CALL RTN> %d:%s-> %s' %
+ (_recursionCounter, ' ' * _recursionCounter, repr(result)))
+ else:
+ _callToTkReturned = 1
+ if result:
+ _traceTkFile.write(' -> %s' % repr(result))
+ _traceTkFile.write('\n')
+ if _withStackTrace:
+ _traceTkFile.write('CALL TK> stack:\n')
+ traceback.print_stack()
+
+ _traceTkFile.flush()
+ return result
+
+ def __getattr__(self, key):
+ return getattr(self.tclInterp, key)
+
+def _setTkInterps(window, tk):
+ window.tk = tk
+ for child in window.children.values():
+ _setTkInterps(child, tk)
+
+#=============================================================================
+
+# Functions to display a busy cursor. Keep a list of all toplevels
+# and display the busy cursor over them. The list will contain the Tk
+# root toplevel window as well as all other toplevel windows.
+# Also keep a list of the widget which last had focus for each
+# toplevel.
+
+# Map from toplevel windows to
+# {'isBusy', 'windowFocus', 'busyWindow',
+# 'excludeFromBusy', 'busyCursorName'}
+_toplevelBusyInfo = {}
+
+# Pmw needs to know all toplevel windows, so that it can call blt busy
+# on them. This is a hack so we get notified when a Tk topevel is
+# created. Ideally, the __init__ 'method' should be overridden, but
+# it is a 'read-only special attribute'. Luckily, title() is always
+# called from the Tkinter Toplevel constructor.
+
+def _addToplevelBusyInfo(window):
+ if window._w == '.':
+ busyWindow = '._Busy'
+ else:
+ busyWindow = window._w + '._Busy'
+
+ _toplevelBusyInfo[window] = {
+ 'isBusy' : 0,
+ 'windowFocus' : None,
+ 'busyWindow' : busyWindow,
+ 'excludeFromBusy' : 0,
+ 'busyCursorName' : None,
+ }
+
+def __TkinterToplevelTitle(self, *args):
+ # If this is being called from the constructor, include this
+ # Toplevel in the list of toplevels and set the initial
+ # WM_DELETE_WINDOW protocol to destroy() so that we get to know
+ # about it.
+ if not _toplevelBusyInfo.has_key(self):
+ _addToplevelBusyInfo(self)
+ self._Pmw_WM_DELETE_name = self.register(self.destroy, None, 0)
+ self.protocol('WM_DELETE_WINDOW', self._Pmw_WM_DELETE_name)
+
+ return apply(Tkinter.Wm.title, (self,) + args)
+
+_haveBltBusy = None
+def _havebltbusy(window):
+ global _busy_hold, _busy_release, _haveBltBusy
+ if _haveBltBusy is None:
+ import PmwBlt
+ _haveBltBusy = PmwBlt.havebltbusy(window)
+ _busy_hold = PmwBlt.busy_hold
+ if os.name == 'nt':
+ # There is a bug in Blt 2.4i on NT where the busy window
+ # does not follow changes in the children of a window.
+ # Using forget works around the problem.
+ _busy_release = PmwBlt.busy_forget
+ else:
+ _busy_release = PmwBlt.busy_release
+ return _haveBltBusy
+
+class _BusyWrapper:
+ def __init__(self, command, updateFunction):
+ self._command = command
+ self._updateFunction = updateFunction
+
+ def callback(self, *args):
+ showbusycursor()
+ rtn = apply(self._command, args)
+
+ # Call update before hiding the busy windows to clear any
+ # events that may have occurred over the busy windows.
+ if callable(self._updateFunction):
+ self._updateFunction()
+
+ hidebusycursor()
+ return rtn
+
+#=============================================================================
+
+def drawarrow(canvas, color, direction, tag, baseOffset = 0.25, edgeOffset = 0.15):
+ canvas.delete(tag)
+
+ bw = (string.atoi(canvas['borderwidth']) +
+ string.atoi(canvas['highlightthickness']))
+ width = string.atoi(canvas['width'])
+ height = string.atoi(canvas['height'])
+
+ if direction in ('up', 'down'):
+ majorDimension = height
+ minorDimension = width
+ else:
+ majorDimension = width
+ minorDimension = height
+
+ offset = round(baseOffset * majorDimension)
+ if direction in ('down', 'right'):
+ base = bw + offset
+ apex = bw + majorDimension - offset
+ else:
+ base = bw + majorDimension - offset
+ apex = bw + offset
+
+ if minorDimension > 3 and minorDimension % 2 == 0:
+ minorDimension = minorDimension - 1
+ half = int(minorDimension * (1 - 2 * edgeOffset)) / 2
+ low = round(bw + edgeOffset * minorDimension)
+ middle = low + half
+ high = low + 2 * half
+
+ if direction in ('up', 'down'):
+ coords = (low, base, high, base, middle, apex)
+ else:
+ coords = (base, low, base, high, apex, middle)
+ kw = {'fill' : color, 'outline' : color, 'tag' : tag}
+ apply(canvas.create_polygon, coords, kw)
+
+#=============================================================================
+
+# Modify the Tkinter destroy methods so that it notifies us when a Tk
+# toplevel or frame is destroyed.
+
+# A map from the 'hull' component of a megawidget to the megawidget.
+# This is used to clean up a megawidget when its hull is destroyed.
+_hullToMegaWidget = {}
+
+def __TkinterToplevelDestroy(tkWidget):
+ if _hullToMegaWidget.has_key(tkWidget):
+ mega = _hullToMegaWidget[tkWidget]
+ try:
+ mega.destroy()
+ except:
+ _reporterror(mega.destroy, ())
+ else:
+ # Delete the busy info structure for this toplevel (if the
+ # window was created before Pmw.initialise() was called, it
+ # will not have any.
+ if _toplevelBusyInfo.has_key(tkWidget):
+ del _toplevelBusyInfo[tkWidget]
+ if hasattr(tkWidget, '_Pmw_WM_DELETE_name'):
+ tkWidget.tk.deletecommand(tkWidget._Pmw_WM_DELETE_name)
+ del tkWidget._Pmw_WM_DELETE_name
+ Tkinter.BaseWidget.destroy(tkWidget)
+
+def __TkinterWidgetDestroy(tkWidget):
+ if _hullToMegaWidget.has_key(tkWidget):
+ mega = _hullToMegaWidget[tkWidget]
+ try:
+ mega.destroy()
+ except:
+ _reporterror(mega.destroy, ())
+ else:
+ Tkinter.BaseWidget.destroy(tkWidget)
+
+#=============================================================================
+
+# Add code to Tkinter to improve the display of errors which occur in
+# callbacks.
+
+class __TkinterCallWrapper:
+ def __init__(self, func, subst, widget):
+ self.func = func
+ self.subst = subst
+ self.widget = widget
+
+ # Calling back from Tk into python.
+ def __call__(self, *args):
+ try:
+ if self.subst:
+ args = apply(self.subst, args)
+ if _traceTk:
+ if not _callToTkReturned:
+ _traceTkFile.write('\n')
+ if hasattr(self.func, 'im_class'):
+ name = self.func.im_class.__name__ + '.' + \
+ self.func.__name__
+ else:
+ name = self.func.__name__
+ if len(args) == 1 and hasattr(args[0], 'type'):
+ # The argument to the callback is an event.
+ eventName = _eventTypeToName[string.atoi(args[0].type)]
+ if eventName in ('KeyPress', 'KeyRelease',):
+ argStr = '(%s %s Event: %s)' % \
+ (eventName, args[0].keysym, args[0].widget)
+ else:
+ argStr = '(%s Event, %s)' % (eventName, args[0].widget)
+ else:
+ argStr = str(args)
+ _traceTkFile.write('CALLBACK> %d:%s%s%s\n' %
+ (_recursionCounter, ' ' * _recursionCounter, name, argStr))
+ _traceTkFile.flush()
+ return apply(self.func, args)
+ except SystemExit, msg:
+ raise SystemExit, msg
+ except:
+ _reporterror(self.func, args)
+
+_eventTypeToName = {
+ 2 : 'KeyPress', 15 : 'VisibilityNotify', 28 : 'PropertyNotify',
+ 3 : 'KeyRelease', 16 : 'CreateNotify', 29 : 'SelectionClear',
+ 4 : 'ButtonPress', 17 : 'DestroyNotify', 30 : 'SelectionRequest',
+ 5 : 'ButtonRelease', 18 : 'UnmapNotify', 31 : 'SelectionNotify',
+ 6 : 'MotionNotify', 19 : 'MapNotify', 32 : 'ColormapNotify',
+ 7 : 'EnterNotify', 20 : 'MapRequest', 33 : 'ClientMessage',
+ 8 : 'LeaveNotify', 21 : 'ReparentNotify', 34 : 'MappingNotify',
+ 9 : 'FocusIn', 22 : 'ConfigureNotify', 35 : 'VirtualEvents',
+ 10 : 'FocusOut', 23 : 'ConfigureRequest', 36 : 'ActivateNotify',
+ 11 : 'KeymapNotify', 24 : 'GravityNotify', 37 : 'DeactivateNotify',
+ 12 : 'Expose', 25 : 'ResizeRequest', 38 : 'MouseWheelEvent',
+ 13 : 'GraphicsExpose', 26 : 'CirculateNotify',
+ 14 : 'NoExpose', 27 : 'CirculateRequest',
+}
+
+def _reporterror(func, args):
+ # Fetch current exception values.
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+
+ # Give basic information about the callback exception.
+ if type(exc_type) == types.ClassType:
+ # Handle python 1.5 class exceptions.
+ exc_type = exc_type.__name__
+ msg = exc_type + ' Exception in Tk callback\n'
+ msg = msg + ' Function: %s (type: %s)\n' % (repr(func), type(func))
+ msg = msg + ' Args: %s\n' % str(args)
+
+ if type(args) == types.TupleType and len(args) > 0 and \
+ hasattr(args[0], 'type'):
+ eventArg = 1
+ else:
+ eventArg = 0
+
+ # If the argument to the callback is an event, add the event type.
+ if eventArg:
+ eventNum = string.atoi(args[0].type)
+ if eventNum in _eventTypeToName.keys():
+ msg = msg + ' Event type: %s (type num: %d)\n' % \
+ (_eventTypeToName[eventNum], eventNum)
+ else:
+ msg = msg + ' Unknown event type (type num: %d)\n' % eventNum
+
+ # Add the traceback.
+ msg = msg + 'Traceback (innermost last):\n'
+ for tr in traceback.extract_tb(exc_traceback):
+ msg = msg + ' File "%s", line %s, in %s\n' % (tr[0], tr[1], tr[2])
+ msg = msg + ' %s\n' % tr[3]
+ msg = msg + '%s: %s\n' % (exc_type, exc_value)
+
+ # If the argument to the callback is an event, add the event contents.
+ if eventArg:
+ msg = msg + '\n================================================\n'
+ msg = msg + ' Event contents:\n'
+ keys = args[0].__dict__.keys()
+ keys.sort()
+ for key in keys:
+ msg = msg + ' %s: %s\n' % (key, args[0].__dict__[key])
+
+ clearbusycursor()
+ try:
+ displayerror(msg)
+ except:
+ pass
+
+class _ErrorWindow:
+ def __init__(self):
+
+ self._errorQueue = []
+ self._errorCount = 0
+ self._open = 0
+ self._firstShowing = 1
+
+ # Create the toplevel window
+ self._top = Tkinter.Toplevel()
+ self._top.protocol('WM_DELETE_WINDOW', self._hide)
+ self._top.title('Error in background function')
+ self._top.iconname('Background error')
+
+ # Create the text widget and scrollbar in a frame
+ upperframe = Tkinter.Frame(self._top)
+
+ scrollbar = Tkinter.Scrollbar(upperframe, orient='vertical')
+ scrollbar.pack(side = 'right', fill = 'y')
+
+ self._text = Tkinter.Text(upperframe, yscrollcommand=scrollbar.set)
+ self._text.pack(fill = 'both', expand = 1)
+ scrollbar.configure(command=self._text.yview)
+
+ # Create the buttons and label in a frame
+ lowerframe = Tkinter.Frame(self._top)
+
+ ignore = Tkinter.Button(lowerframe,
+ text = 'Ignore remaining errors', command = self._hide)
+ ignore.pack(side='left')
+
+ self._nextError = Tkinter.Button(lowerframe,
+ text = 'Show next error', command = self._next)
+ self._nextError.pack(side='left')
+
+ self._label = Tkinter.Label(lowerframe, relief='ridge')
+ self._label.pack(side='left', fill='x', expand=1)
+
+ # Pack the lower frame first so that it does not disappear
+ # when the window is resized.
+ lowerframe.pack(side = 'bottom', fill = 'x')
+ upperframe.pack(side = 'bottom', fill = 'both', expand = 1)
+
+ def showerror(self, text):
+ if self._open:
+ self._errorQueue.append(text)
+ else:
+ self._display(text)
+ self._open = 1
+
+ # Display the error window in the same place it was before.
+ if self._top.state() == 'normal':
+ # If update_idletasks is not called here, the window may
+ # be placed partially off the screen. Also, if it is not
+ # called and many errors are generated quickly in
+ # succession, the error window may not display errors
+ # until the last one is generated and the interpreter
+ # becomes idle.
+ # XXX: remove this, since it causes omppython to go into an
+ # infinite loop if an error occurs in an omp callback.
+ # self._top.update_idletasks()
+
+ pass
+ else:
+ if self._firstShowing:
+ geom = None
+ else:
+ geometry = self._top.geometry()
+ index = string.find(geometry, '+')
+ if index >= 0:
+ geom = geometry[index:]
+ else:
+ geom = None
+ setgeometryanddeiconify(self._top, geom)
+
+ if self._firstShowing:
+ self._firstShowing = 0
+ else:
+ self._top.tkraise()
+
+ self._top.focus()
+
+ self._updateButtons()
+
+ # Release any grab, so that buttons in the error window work.
+ releasegrabs()
+
+ def _hide(self):
+ self._errorCount = self._errorCount + len(self._errorQueue)
+ self._errorQueue = []
+ self._top.withdraw()
+ self._open = 0
+
+ def _next(self):
+ # Display the next error in the queue.
+
+ text = self._errorQueue[0]
+ del self._errorQueue[0]
+
+ self._display(text)
+ self._updateButtons()
+
+ def _display(self, text):
+ self._errorCount = self._errorCount + 1
+ text = 'Error: %d\n%s' % (self._errorCount, text)
+ self._text.delete('1.0', 'end')
+ self._text.insert('end', text)
+
+ def _updateButtons(self):
+ numQueued = len(self._errorQueue)
+ if numQueued > 0:
+ self._label.configure(text='%d more errors' % numQueued)
+ self._nextError.configure(state='normal')
+ else:
+ self._label.configure(text='No more errors')
+ self._nextError.configure(state='disabled')
--- /dev/null
+# Python interface to some of the commands of the 2.4 version of the
+# BLT extension to tcl.
+
+import string
+import types
+import Tkinter
+
+# Supported commands:
+_busyCommand = '::blt::busy'
+_vectorCommand = '::blt::vector'
+_graphCommand = '::blt::graph'
+_testCommand = '::blt::*'
+_chartCommand = '::blt::stripchart'
+_tabsetCommand = '::blt::tabset'
+
+_haveBlt = None
+_haveBltBusy = None
+
+def _checkForBlt(window):
+ global _haveBlt
+ global _haveBltBusy
+
+ # Blt may be a package which has not yet been loaded. Try to load it.
+ try:
+ window.tk.call('package', 'require', 'BLT')
+ except Tkinter.TclError:
+ # Another way to try to dynamically load blt:
+ try:
+ window.tk.call('load', '', 'Blt')
+ except Tkinter.TclError:
+ pass
+
+ _haveBlt= (window.tk.call('info', 'commands', _testCommand) != '')
+ _haveBltBusy = (window.tk.call('info', 'commands', _busyCommand) != '')
+
+def haveblt(window):
+ if _haveBlt is None:
+ _checkForBlt(window)
+ return _haveBlt
+
+def havebltbusy(window):
+ if _haveBlt is None:
+ _checkForBlt(window)
+ return _haveBltBusy
+
+def _loadBlt(window):
+ if _haveBlt is None:
+ if window is None:
+ window = Tkinter._default_root
+ if window is None:
+ window = Tkinter.Tk()
+ _checkForBlt(window)
+
+def busy_hold(window, cursor = None):
+ _loadBlt(window)
+ if cursor is None:
+ window.tk.call(_busyCommand, 'hold', window._w)
+ else:
+ window.tk.call(_busyCommand, 'hold', window._w, '-cursor', cursor)
+
+def busy_release(window):
+ _loadBlt(window)
+ window.tk.call(_busyCommand, 'release', window._w)
+
+def busy_forget(window):
+ _loadBlt(window)
+ window.tk.call(_busyCommand, 'forget', window._w)
+
+#=============================================================================
+# Interface to the blt vector command which makes it look like the
+# builtin python list type.
+# The -variable, -command, -watchunset creation options are not supported.
+# The dup, merge, notify, offset, populate, seq and variable methods
+# and the +, -, * and / operations are not supported.
+
+# Blt vector functions:
+def vector_expr(expression):
+ tk = Tkinter._default_root.tk
+ strList = tk.splitlist(tk.call(_vectorCommand, 'expr', expression))
+ return tuple(map(string.atof, strList))
+
+def vector_names(pattern = None):
+ tk = Tkinter._default_root.tk
+ return tk.splitlist(tk.call(_vectorCommand, 'names', pattern))
+
+class Vector:
+ _varnum = 0
+ def __init__(self, size=None, master=None):
+ # <size> can be either an integer size, or a string "first:last".
+ _loadBlt(master)
+ if master:
+ self._master = master
+ else:
+ self._master = Tkinter._default_root
+ self.tk = self._master.tk
+ self._name = 'PY_VEC' + str(Vector._varnum)
+ Vector._varnum = Vector._varnum + 1
+ if size is None:
+ self.tk.call(_vectorCommand, 'create', self._name)
+ else:
+ self.tk.call(_vectorCommand, 'create', '%s(%s)' % (self._name, size))
+ def __del__(self):
+ self.tk.call(_vectorCommand, 'destroy', self._name)
+ def __str__(self):
+ return self._name
+
+ def __repr__(self):
+ return '[' + string.join(map(str, self), ', ') + ']'
+ def __cmp__(self, list):
+ return cmp(self[:], list)
+
+ def __len__(self):
+ return self.tk.getint(self.tk.call(self._name, 'length'))
+ def __getitem__(self, key):
+ oldkey = key
+ if key < 0:
+ key = key + len(self)
+ try:
+ return self.tk.getdouble(self.tk.globalgetvar(self._name, str(key)))
+ except Tkinter.TclError:
+ raise IndexError, oldkey
+ def __setitem__(self, key, value):
+ if key < 0:
+ key = key + len(self)
+ return self.tk.globalsetvar(self._name, str(key), float(value))
+
+ def __delitem__(self, key):
+ if key < 0:
+ key = key + len(self)
+ return self.tk.globalunsetvar(self._name, str(key))
+
+ def __getslice__(self, start, end):
+ length = len(self)
+ if start < 0:
+ start = 0
+ if end > length:
+ end = length
+ if start >= end:
+ return []
+ end = end - 1 # Blt vector slices include end point.
+ text = self.tk.globalgetvar(self._name, str(start) + ':' + str(end))
+ return map(self.tk.getdouble, self.tk.splitlist(text))
+
+ def __setslice__(self, start, end, list):
+ if start > end:
+ end = start
+ self.set(self[:start] + list + self[end:])
+
+ def __delslice__(self, start, end):
+ if start < end:
+ self.set(self[:start] + self[end:])
+
+ def __add__(self, list):
+ return self[:] + list
+ def __radd__(self, list):
+ return list + self[:]
+ def __mul__(self, n):
+ return self[:] * n
+ __rmul__ = __mul__
+
+ # Python builtin list methods:
+ def append(self, *args):
+ self.tk.call(self._name, 'append', args)
+ def count(self, obj):
+ return self[:].count(obj)
+ def index(self, value):
+ return self[:].index(value)
+ def insert(self, index, value):
+ self[index:index] = [value]
+ def remove(self, value):
+ del self[self.index(value)]
+ def reverse(self):
+ s = self[:]
+ s.reverse()
+ self.set(s)
+ def sort(self, *args):
+ s = self[:]
+ s.sort()
+ self.set(s)
+
+ # Blt vector instance methods:
+ # append - same as list method above
+ def clear(self):
+ self.tk.call(self._name, 'clear')
+ def delete(self, *args):
+ self.tk.call((self._name, 'delete') + args)
+ def expr(self, expression):
+ self.tk.call(self._name, 'expr', expression)
+ def length(self, newSize=None):
+ return self.tk.getint(self.tk.call(self._name, 'length', newSize))
+ def range(self, first, last=None):
+ # Note that, unlike self[first:last], this includes the last
+ # item in the returned range.
+ text = self.tk.call(self._name, 'range', first, last)
+ return map(self.tk.getdouble, self.tk.splitlist(text))
+ def search(self, start, end=None):
+ return self._master._getints(self.tk.call(
+ self._name, 'search', start, end))
+ def set(self, list):
+ if type(list) != types.TupleType:
+ list = tuple(list)
+ self.tk.call(self._name, 'set', list)
+
+ # The blt vector sort method has different semantics to the python
+ # list sort method. Call these blt_sort:
+ def blt_sort(self, *args):
+ self.tk.call((self._name, 'sort') + args)
+ def blt_sort_reverse(self, *args):
+ self.tk.call((self._name, 'sort', '-reverse') + args)
+
+ # Special blt vector indexes:
+ def min(self):
+ return self.tk.getdouble(self.tk.globalgetvar(self._name, 'min'))
+ def max(self):
+ return self.tk.getdouble(self.tk.globalgetvar(self._name, 'max'))
+
+ # Method borrowed from Tkinter.Var class:
+ def get(self):
+ return self[:]
+
+#=============================================================================
+
+# This is a general purpose configure routine which can handle the
+# configuration of widgets, items within widgets, etc. Supports the
+# forms configure() and configure('font') for querying and
+# configure(font = 'fixed', text = 'hello') for setting.
+
+def _doConfigure(widget, subcommand, option, kw):
+
+ if not option and not kw:
+ # Return a description of all options.
+ ret = {}
+ options = widget.tk.splitlist(widget.tk.call(subcommand))
+ for optionString in options:
+ optionInfo = widget.tk.splitlist(optionString)
+ option = optionInfo[0][1:]
+ ret[option] = (option,) + optionInfo[1:]
+ return ret
+
+ if option:
+ # Return a description of the option given by <option>.
+ if kw:
+ # Having keywords implies setting configuration options.
+ # Can't set and get in one command!
+ raise ValueError, 'cannot have option argument with keywords'
+ option = '-' + option
+ optionInfo = widget.tk.splitlist(widget.tk.call(subcommand + (option,)))
+ return (optionInfo[0][1:],) + optionInfo[1:]
+
+ # Otherwise, set the given configuration options.
+ widget.tk.call(subcommand + widget._options(kw))
+
+#=============================================================================
+
+class Graph(Tkinter.Widget):
+ # Wrapper for the blt graph widget, version 2.4.
+
+ def __init__(self, master=None, cnf={}, **kw):
+ _loadBlt(master)
+ Tkinter.Widget.__init__(self, master, _graphCommand, cnf, kw)
+
+ def bar_create(self, name, **kw):
+ self.tk.call((self._w, 'bar', 'create', name) + self._options(kw))
+
+ def line_create(self, name, **kw):
+ self.tk.call((self._w, 'line', 'create', name) + self._options(kw))
+
+ def extents(self, item):
+ return self.tk.getint(self.tk.call(self._w, 'extents', item))
+
+ def invtransform(self, winX, winY):
+ return self._getdoubles(
+ self.tk.call(self._w, 'invtransform', winX, winY))
+
+ def inside(self, x, y):
+ return self.tk.getint(self.tk.call(self._w, 'inside', x, y))
+
+ def snap(self, photoName):
+ self.tk.call(self._w, 'snap', photoName)
+
+ def transform(self, x, y):
+ return self._getdoubles(self.tk.call(self._w, 'transform', x, y))
+
+ def axis_cget(self, axisName, key):
+ return self.tk.call(self._w, 'axis', 'cget', axisName, '-' + key)
+ def axis_configure(self, axes, option=None, **kw):
+ # <axes> may be a list of axisNames.
+ if type(axes) == types.StringType:
+ axes = [axes]
+ subcommand = (self._w, 'axis', 'configure') + tuple(axes)
+ return _doConfigure(self, subcommand, option, kw)
+ def axis_create(self, axisName, **kw):
+ self.tk.call((self._w, 'axis', 'create', axisName) + self._options(kw))
+ def axis_delete(self, *args):
+ self.tk.call((self._w, 'axis', 'delete') + args)
+ def axis_invtransform(self, axisName, value):
+ return self.tk.getdouble(self.tk.call(
+ self._w, 'axis', 'invtransform', axisName, value))
+ def axis_limits(self, axisName):
+ return self._getdoubles(self.tk.call(
+ self._w, 'axis', 'limits', axisName))
+ def axis_names(self, *args):
+ return self.tk.splitlist(
+ self.tk.call((self._w, 'axis', 'names') + args))
+ def axis_transform(self, axisName, value):
+ return self.tk.getint(self.tk.call(
+ self._w, 'axis', 'transform', axisName, value))
+
+ def xaxis_cget(self, key):
+ return self.tk.call(self._w, 'xaxis', 'cget', '-' + key)
+ def xaxis_configure(self, option=None, **kw):
+ subcommand = (self._w, 'xaxis', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+ def xaxis_invtransform(self, value):
+ return self.tk.getdouble(self.tk.call(
+ self._w, 'xaxis', 'invtransform', value))
+ def xaxis_limits(self):
+ return self._getdoubles(self.tk.call(self._w, 'xaxis', 'limits'))
+ def xaxis_transform(self, value):
+ return self.tk.getint(self.tk.call(
+ self._w, 'xaxis', 'transform', value))
+ def xaxis_use(self, axisName = None):
+ return self.tk.call(self._w, 'xaxis', 'use', axisName)
+
+ def x2axis_cget(self, key):
+ return self.tk.call(self._w, 'x2axis', 'cget', '-' + key)
+ def x2axis_configure(self, option=None, **kw):
+ subcommand = (self._w, 'x2axis', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+ def x2axis_invtransform(self, value):
+ return self.tk.getdouble(self.tk.call(
+ self._w, 'x2axis', 'invtransform', value))
+ def x2axis_limits(self):
+ return self._getdoubles(self.tk.call(self._w, 'x2axis', 'limits'))
+ def x2axis_transform(self, value):
+ return self.tk.getint(self.tk.call(
+ self._w, 'x2axis', 'transform', value))
+ def x2axis_use(self, axisName = None):
+ return self.tk.call(self._w, 'x2axis', 'use', axisName)
+
+ def yaxis_cget(self, key):
+ return self.tk.call(self._w, 'yaxis', 'cget', '-' + key)
+ def yaxis_configure(self, option=None, **kw):
+ subcommand = (self._w, 'yaxis', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+ def yaxis_invtransform(self, value):
+ return self.tk.getdouble(self.tk.call(
+ self._w, 'yaxis', 'invtransform', value))
+ def yaxis_limits(self):
+ return self._getdoubles(self.tk.call(self._w, 'yaxis', 'limits'))
+ def yaxis_transform(self, value):
+ return self.tk.getint(self.tk.call(
+ self._w, 'yaxis', 'transform', value))
+ def yaxis_use(self, axisName = None):
+ return self.tk.call(self._w, 'yaxis', 'use', axisName)
+
+ def y2axis_cget(self, key):
+ return self.tk.call(self._w, 'y2axis', 'cget', '-' + key)
+ def y2axis_configure(self, option=None, **kw):
+ subcommand = (self._w, 'y2axis', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+ def y2axis_invtransform(self, value):
+ return self.tk.getdouble(self.tk.call(
+ self._w, 'y2axis', 'invtransform', value))
+ def y2axis_limits(self):
+ return self._getdoubles(self.tk.call(self._w, 'y2axis', 'limits'))
+ def y2axis_transform(self, value):
+ return self.tk.getint(self.tk.call(
+ self._w, 'y2axis', 'transform', value))
+ def y2axis_use(self, axisName = None):
+ return self.tk.call(self._w, 'y2axis', 'use', axisName)
+
+ def crosshairs_cget(self, key):
+ return self.tk.call(self._w, 'crosshairs', 'cget', '-' + key)
+ def crosshairs_configure(self, option=None, **kw):
+ subcommand = (self._w, 'crosshairs', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+ def crosshairs_off(self):
+ self.tk.call(self._w, 'crosshairs', 'off')
+ def crosshairs_on(self):
+ self.tk.call(self._w, 'crosshairs', 'on')
+ def crosshairs_toggle(self):
+ self.tk.call(self._w, 'crosshairs', 'toggle')
+
+ def element_activate(self, name, *args):
+ self.tk.call((self._w, 'element', 'activate', name) + args)
+ def element_bind(self, tagName, sequence=None, func=None, add=None):
+ return self._bind((self._w, 'element', 'bind', tagName),
+ sequence, func, add)
+ def element_unbind(self, tagName, sequence, funcid=None):
+ self.tk.call(self._w, 'element', 'bind', tagName, sequence, '')
+ if funcid:
+ self.deletecommand(funcid)
+
+ def element_cget(self, name, key):
+ return self.tk.call(self._w, 'element', 'cget', name, '-' + key)
+
+ def element_closest(self, x, y, *args, **kw):
+ var = 'python_private_1'
+ success = self.tk.getint(self.tk.call(
+ (self._w, 'element', 'closest', x, y, var) +
+ self._options(kw) + args))
+ if success:
+ rtn = {}
+ rtn['dist'] = self.tk.getdouble(self.tk.globalgetvar(var, 'dist'))
+ rtn['x'] = self.tk.getdouble(self.tk.globalgetvar(var, 'x'))
+ rtn['y'] = self.tk.getdouble(self.tk.globalgetvar(var, 'y'))
+ rtn['index'] = self.tk.getint(self.tk.globalgetvar(var, 'index'))
+ rtn['name'] = self.tk.globalgetvar(var, 'name')
+ return rtn
+ else:
+ return None
+
+ def element_configure(self, names, option=None, **kw):
+ # <names> may be a list of elemNames.
+ if type(names) == types.StringType:
+ names = [names]
+ subcommand = (self._w, 'element', 'configure') + tuple(names)
+ return _doConfigure(self, subcommand, option, kw)
+
+ def element_deactivate(self, *args):
+ self.tk.call((self._w, 'element', 'deactivate') + args)
+
+ def element_delete(self, *args):
+ self.tk.call((self._w, 'element', 'delete') + args)
+ def element_exists(self, name):
+ return self.tk.getboolean(
+ self.tk.call(self._w, 'element', 'exists', name))
+
+ def element_names(self, *args):
+ return self.tk.splitlist(
+ self.tk.call((self._w, 'element', 'names') + args))
+ def element_show(self, nameList=None):
+ if nameList is not None:
+ nameList = tuple(nameList)
+ return self.tk.splitlist(
+ self.tk.call(self._w, 'element', 'show', nameList))
+ def element_type(self, name):
+ return self.tk.call(self._w, 'element', 'type', name)
+
+ def grid_cget(self, key):
+ return self.tk.call(self._w, 'grid', 'cget', '-' + key)
+ def grid_configure(self, option=None, **kw):
+ subcommand = (self._w, 'grid', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+
+ def grid_off(self):
+ self.tk.call(self._w, 'grid', 'off')
+ def grid_on(self):
+ self.tk.call(self._w, 'grid', 'on')
+ def grid_toggle(self):
+ self.tk.call(self._w, 'grid', 'toggle')
+
+ def legend_activate(self, *args):
+ self.tk.call((self._w, 'legend', 'activate') + args)
+ def legend_bind(self, tagName, sequence=None, func=None, add=None):
+ return self._bind((self._w, 'legend', 'bind', tagName),
+ sequence, func, add)
+ def legend_unbind(self, tagName, sequence, funcid=None):
+ self.tk.call(self._w, 'legend', 'bind', tagName, sequence, '')
+ if funcid:
+ self.deletecommand(funcid)
+
+ def legend_cget(self, key):
+ return self.tk.call(self._w, 'legend', 'cget', '-' + key)
+ def legend_configure(self, option=None, **kw):
+ subcommand = (self._w, 'legend', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+ def legend_deactivate(self, *args):
+ self.tk.call((self._w, 'legend', 'deactivate') + args)
+ def legend_get(self, pos):
+ return self.tk.call(self._w, 'legend', 'get', pos)
+
+ def pen_cget(self, name, key):
+ return self.tk.call(self._w, 'pen', 'cget', name, '-' + key)
+ def pen_configure(self, names, option=None, **kw):
+ # <names> may be a list of penNames.
+ if type(names) == types.StringType:
+ names = [names]
+ subcommand = (self._w, 'pen', 'configure') + tuple(names)
+ return _doConfigure(self, subcommand, option, kw)
+ def pen_create(self, name, **kw):
+ self.tk.call((self._w, 'pen', 'create', name) + self._options(kw))
+ def pen_delete(self, *args):
+ self.tk.call((self._w, 'pen', 'delete') + args)
+ def pen_names(self, *args):
+ return self.tk.splitlist(self.tk.call((self._w, 'pen', 'names') + args))
+
+ def postscript_cget(self, key):
+ return self.tk.call(self._w, 'postscript', 'cget', '-' + key)
+ def postscript_configure(self, option=None, **kw):
+ subcommand = (self._w, 'postscript', 'configure')
+ return _doConfigure(self, subcommand, option, kw)
+ def postscript_output(self, fileName=None, **kw):
+ prefix = (self._w, 'postscript', 'output')
+ if fileName is None:
+ return self.tk.call(prefix + self._options(kw))
+ else:
+ self.tk.call(prefix + (fileName,) + self._options(kw))
+
+ def marker_after(self, first, second=None):
+ self.tk.call(self._w, 'marker', 'after', first, second)
+ def marker_before(self, first, second=None):
+ self.tk.call(self._w, 'marker', 'before', first, second)
+ def marker_bind(self, tagName, sequence=None, func=None, add=None):
+ return self._bind((self._w, 'marker', 'bind', tagName),
+ sequence, func, add)
+ def marker_unbind(self, tagName, sequence, funcid=None):
+ self.tk.call(self._w, 'marker', 'bind', tagName, sequence, '')
+ if funcid:
+ self.deletecommand(funcid)
+
+ def marker_cget(self, name, key):
+ return self.tk.call(self._w, 'marker', 'cget', name, '-' + key)
+ def marker_configure(self, names, option=None, **kw):
+ # <names> may be a list of markerIds.
+ if type(names) == types.StringType:
+ names = [names]
+ subcommand = (self._w, 'marker', 'configure') + tuple(names)
+ return _doConfigure(self, subcommand, option, kw)
+ def marker_create(self, type, **kw):
+ return self.tk.call(
+ (self._w, 'marker', 'create', type) + self._options(kw))
+
+ def marker_delete(self, *args):
+ self.tk.call((self._w, 'marker', 'delete') + args)
+ def marker_exists(self, name):
+ return self.tk.getboolean(
+ self.tk.call(self._w, 'marker', 'exists', name))
+ def marker_names(self, *args):
+ return self.tk.splitlist(
+ self.tk.call((self._w, 'marker', 'names') + args))
+ def marker_type(self, name):
+ type = self.tk.call(self._w, 'marker', 'type', name)
+ if type == '':
+ type = None
+ return type
+
+#=============================================================================
+class Stripchart(Graph):
+ # Wrapper for the blt stripchart widget, version 2.4.
+
+ def __init__(self, master=None, cnf={}, **kw):
+ _loadBlt(master)
+ Tkinter.Widget.__init__(self, master, _chartCommand, cnf, kw)
+
+#=============================================================================
+class Tabset(Tkinter.Widget):
+
+ # Wrapper for the blt TabSet widget, version 2.4.
+
+ def __init__(self, master=None, cnf={}, **kw):
+ _loadBlt(master)
+ Tkinter.Widget.__init__(self, master, _tabsetCommand, cnf, kw)
+
+ def activate(self, tabIndex):
+ self.tk.call(self._w, 'activate', tabIndex)
+
+ # This is the 'bind' sub-command:
+ def tag_bind(self, tagName, sequence=None, func=None, add=None):
+ return self._bind((self._w, 'bind', tagName), sequence, func, add)
+
+ def tag_unbind(self, tagName, sequence, funcid=None):
+ self.tk.call(self._w, 'bind', tagName, sequence, '')
+ if funcid:
+ self.deletecommand(funcid)
+
+ def delete(self, first, last = None):
+ self.tk.call(self._w, 'delete', first, last)
+
+ # This is the 'focus' sub-command:
+ def tab_focus(self, tabIndex):
+ self.tk.call(self._w, 'focus', tabIndex)
+
+ def get(self, tabIndex):
+ return self.tk.call(self._w, 'get', tabIndex)
+
+ def index(self, tabIndex):
+ index = self.tk.call(self._w, 'index', tabIndex)
+ if index == '':
+ return None
+ else:
+ return self.tk.getint(self.tk.call(self._w, 'index', tabIndex))
+
+ def insert(self, position, name1, *names, **kw):
+ self.tk.call(
+ (self._w, 'insert', position, name1) + names + self._options(kw))
+
+ def invoke(self, tabIndex):
+ return self.tk.call(self._w, 'invoke', tabIndex)
+
+ def move(self, tabIndex1, beforeOrAfter, tabIndex2):
+ self.tk.call(self._w, 'move', tabIndex1, beforeOrAfter, tabIndex2)
+
+ def nearest(self, x, y):
+ return self.tk.call(self._w, 'nearest', x, y)
+
+ def scan_mark(self, x, y):
+ self.tk.call(self._w, 'scan', 'mark', x, y)
+
+ def scan_dragto(self, x, y):
+ self.tk.call(self._w, 'scan', 'dragto', x, y)
+
+ def see(self, index):
+ self.tk.call(self._w, 'see', index)
+
+ def see(self, tabIndex):
+ self.tk.call(self._w,'see',tabIndex)
+
+ def size(self):
+ return self.tk.getint(self.tk.call(self._w, 'size'))
+
+ def tab_cget(self, tabIndex, option):
+ if option[:1] != '-':
+ option = '-' + option
+ if option[-1:] == '_':
+ option = option[:-1]
+ return self.tk.call(self._w, 'tab', 'cget', tabIndex, option)
+
+ def tab_configure(self, tabIndexes, option=None, **kw):
+ # <tabIndexes> may be a list of tabs.
+ if type(tabIndexes) in (types.StringType, types.IntType):
+ tabIndexes = [tabIndexes]
+ subcommand = (self._w, 'tab', 'configure') + tuple(tabIndexes)
+ return _doConfigure(self, subcommand, option, kw)
+
+ def tab_names(self, *args):
+ return self.tk.splitlist(self.tk.call((self._w, 'tab', 'names') + args))
+
+ def tab_tearoff(self, tabIndex, newName = None):
+ if newName is None:
+ name = self.tk.call(self._w, 'tab', 'tearoff', tabIndex)
+ return self.nametowidget(name)
+ else:
+ self.tk.call(self._w, 'tab', 'tearoff', tabIndex, newName)
+
+ def view(self):
+ s = self.tk.call(self._w, 'view')
+ return tuple(map(self.tk.getint, self.tk.splitlist(s)))
+ def view_moveto(self, fraction):
+ self.tk.call(self._w, 'view', 'moveto', fraction)
+ def view_scroll(self, number, what):
+ self.tk.call(self._w, 'view', 'scroll', number, what)
--- /dev/null
+# Based on iwidgets2.2.0/buttonbox.itk code.
+
+import types
+import Tkinter
+import Pmw
+
+class ButtonBox(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('orient', 'horizontal', INITOPT),
+ ('padx', 3, INITOPT),
+ ('pady', 3, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs, dynamicGroups = ('Button',))
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ if self['labelpos'] is None:
+ self._buttonBoxFrame = self._hull
+ columnOrRow = 0
+ else:
+ self._buttonBoxFrame = self.createcomponent('frame',
+ (), None,
+ Tkinter.Frame, (interior,))
+ self._buttonBoxFrame.grid(column=2, row=2, sticky='nsew')
+ columnOrRow = 2
+
+ self.createlabel(interior)
+
+ orient = self['orient']
+ if orient == 'horizontal':
+ interior.grid_columnconfigure(columnOrRow, weight = 1)
+ elif orient == 'vertical':
+ interior.grid_rowconfigure(columnOrRow, weight = 1)
+ else:
+ raise ValueError, 'bad orient option ' + repr(orient) + \
+ ': must be either \'horizontal\' or \'vertical\''
+
+ # Initialise instance variables.
+
+ # List of tuples describing the buttons:
+ # - name
+ # - button widget
+ self._buttonList = []
+
+ # The index of the default button.
+ self._defaultButton = None
+
+ self._timerId = None
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self._timerId:
+ self.after_cancel(self._timerId)
+ self._timerId = None
+ Pmw.MegaWidget.destroy(self)
+
+ def numbuttons(self):
+ return len(self._buttonList)
+
+ def index(self, index, forInsert = 0):
+ listLength = len(self._buttonList)
+ if type(index) == types.IntType:
+ if forInsert and index <= listLength:
+ return index
+ elif not forInsert and index < listLength:
+ return index
+ else:
+ raise ValueError, 'index "%s" is out of range' % index
+ elif index is Pmw.END:
+ if forInsert:
+ return listLength
+ elif listLength > 0:
+ return listLength - 1
+ else:
+ raise ValueError, 'ButtonBox has no buttons'
+ elif index is Pmw.DEFAULT:
+ if self._defaultButton is not None:
+ return self._defaultButton
+ raise ValueError, 'ButtonBox has no default'
+ else:
+ names = map(lambda t: t[0], self._buttonList)
+ if index in names:
+ return names.index(index)
+ validValues = 'a name, a number, Pmw.END or Pmw.DEFAULT'
+ raise ValueError, \
+ 'bad index "%s": must be %s' % (index, validValues)
+
+ def insert(self, componentName, beforeComponent = 0, **kw):
+ if componentName in self.components():
+ raise ValueError, 'button "%s" already exists' % componentName
+ if not kw.has_key('text'):
+ kw['text'] = componentName
+ kw['default'] = 'normal'
+ button = apply(self.createcomponent, (componentName,
+ (), 'Button',
+ Tkinter.Button, (self._buttonBoxFrame,)), kw)
+
+ index = self.index(beforeComponent, 1)
+ horizontal = self['orient'] == 'horizontal'
+ numButtons = len(self._buttonList)
+
+ # Shift buttons up one position.
+ for i in range(numButtons - 1, index - 1, -1):
+ widget = self._buttonList[i][1]
+ pos = i * 2 + 3
+ if horizontal:
+ widget.grid(column = pos, row = 0)
+ else:
+ widget.grid(column = 0, row = pos)
+
+ # Display the new button.
+ if horizontal:
+ button.grid(column = index * 2 + 1, row = 0, sticky = 'ew',
+ padx = self['padx'], pady = self['pady'])
+ self._buttonBoxFrame.grid_columnconfigure(
+ numButtons * 2 + 2, weight = 1)
+ else:
+ button.grid(column = 0, row = index * 2 + 1, sticky = 'ew',
+ padx = self['padx'], pady = self['pady'])
+ self._buttonBoxFrame.grid_rowconfigure(
+ numButtons * 2 + 2, weight = 1)
+ self._buttonList.insert(index, (componentName, button))
+
+ return button
+
+ def add(self, componentName, **kw):
+ return apply(self.insert, (componentName, len(self._buttonList)), kw)
+
+ def delete(self, index):
+ index = self.index(index)
+ (name, widget) = self._buttonList[index]
+ widget.grid_forget()
+ self.destroycomponent(name)
+
+ numButtons = len(self._buttonList)
+
+ # Shift buttons down one position.
+ horizontal = self['orient'] == 'horizontal'
+ for i in range(index + 1, numButtons):
+ widget = self._buttonList[i][1]
+ pos = i * 2 - 1
+ if horizontal:
+ widget.grid(column = pos, row = 0)
+ else:
+ widget.grid(column = 0, row = pos)
+
+ if horizontal:
+ self._buttonBoxFrame.grid_columnconfigure(numButtons * 2 - 1,
+ minsize = 0)
+ self._buttonBoxFrame.grid_columnconfigure(numButtons * 2, weight = 0)
+ else:
+ self._buttonBoxFrame.grid_rowconfigure(numButtons * 2, weight = 0)
+ del self._buttonList[index]
+
+ def setdefault(self, index):
+ # Turn off the default ring around the current default button.
+ if self._defaultButton is not None:
+ button = self._buttonList[self._defaultButton][1]
+ button.configure(default = 'normal')
+ self._defaultButton = None
+
+ # Turn on the default ring around the new default button.
+ if index is not None:
+ index = self.index(index)
+ self._defaultButton = index
+ button = self._buttonList[index][1]
+ button.configure(default = 'active')
+
+ def invoke(self, index = Pmw.DEFAULT, noFlash = 0):
+ # Invoke the callback associated with the *index* button. If
+ # *noFlash* is not set, flash the button to indicate to the
+ # user that something happened.
+
+ button = self._buttonList[self.index(index)][1]
+ if not noFlash:
+ state = button.cget('state')
+ relief = button.cget('relief')
+ button.configure(state = 'active', relief = 'sunken')
+ self.update_idletasks()
+ self.after(100)
+ button.configure(state = state, relief = relief)
+ return button.invoke()
+
+ def button(self, buttonIndex):
+ return self._buttonList[self.index(buttonIndex)][1]
+
+ def alignbuttons(self, when = 'later'):
+ if when == 'later':
+ if not self._timerId:
+ self._timerId = self.after_idle(self.alignbuttons, 'now')
+ return
+ self.update_idletasks()
+ self._timerId = None
+
+ # Determine the width of the maximum length button.
+ max = 0
+ horizontal = (self['orient'] == 'horizontal')
+ for index in range(len(self._buttonList)):
+ gridIndex = index * 2 + 1
+ if horizontal:
+ width = self._buttonBoxFrame.grid_bbox(gridIndex, 0)[2]
+ else:
+ width = self._buttonBoxFrame.grid_bbox(0, gridIndex)[2]
+ if width > max:
+ max = width
+
+ # Set the width of all the buttons to be the same.
+ if horizontal:
+ for index in range(len(self._buttonList)):
+ self._buttonBoxFrame.grid_columnconfigure(index * 2 + 1,
+ minsize = max)
+ else:
+ self._buttonBoxFrame.grid_columnconfigure(0, minsize = max)
--- /dev/null
+# Functions for converting colors and modifying the color scheme of
+# an application.
+
+import math
+import string
+import sys
+import Tkinter
+
+_PI = math.pi
+_TWO_PI = _PI * 2
+_THIRD_PI = _PI / 3
+_SIXTH_PI = _PI / 6
+_MAX_RGB = float(256 * 256 - 1) # max size of rgb values returned from Tk
+
+def setscheme(root, background=None, **kw):
+ root = root._root()
+ palette = apply(_calcPalette, (root, background,), kw)
+ for option, value in palette.items():
+ root.option_add('*' + option, value, 'widgetDefault')
+
+def getdefaultpalette(root):
+ # Return the default values of all options, using the defaults
+ # from a few widgets.
+
+ ckbtn = Tkinter.Checkbutton(root)
+ entry = Tkinter.Entry(root)
+ scbar = Tkinter.Scrollbar(root)
+
+ orig = {}
+ orig['activeBackground'] = str(ckbtn.configure('activebackground')[4])
+ orig['activeForeground'] = str(ckbtn.configure('activeforeground')[4])
+ orig['background'] = str(ckbtn.configure('background')[4])
+ orig['disabledForeground'] = str(ckbtn.configure('disabledforeground')[4])
+ orig['foreground'] = str(ckbtn.configure('foreground')[4])
+ orig['highlightBackground'] = str(ckbtn.configure('highlightbackground')[4])
+ orig['highlightColor'] = str(ckbtn.configure('highlightcolor')[4])
+ orig['insertBackground'] = str(entry.configure('insertbackground')[4])
+ orig['selectColor'] = str(ckbtn.configure('selectcolor')[4])
+ orig['selectBackground'] = str(entry.configure('selectbackground')[4])
+ orig['selectForeground'] = str(entry.configure('selectforeground')[4])
+ orig['troughColor'] = str(scbar.configure('troughcolor')[4])
+
+ ckbtn.destroy()
+ entry.destroy()
+ scbar.destroy()
+
+ return orig
+
+#======================================================================
+
+# Functions dealing with brightness, hue, saturation and intensity of colors.
+
+def changebrightness(root, colorName, brightness):
+ # Convert the color name into its hue and back into a color of the
+ # required brightness.
+
+ rgb = name2rgb(root, colorName)
+ hue, saturation, intensity = rgb2hsi(rgb)
+ if saturation == 0.0:
+ hue = None
+ return hue2name(hue, brightness)
+
+def hue2name(hue, brightness = None):
+ # Convert the requested hue and brightness into a color name. If
+ # hue is None, return a grey of the requested brightness.
+
+ if hue is None:
+ rgb = hsi2rgb(0.0, 0.0, brightness)
+ else:
+ while hue < 0:
+ hue = hue + _TWO_PI
+ while hue >= _TWO_PI:
+ hue = hue - _TWO_PI
+
+ rgb = hsi2rgb(hue, 1.0, 1.0)
+ if brightness is not None:
+ b = rgb2brightness(rgb)
+ i = 1.0 - (1.0 - brightness) * b
+ s = bhi2saturation(brightness, hue, i)
+ rgb = hsi2rgb(hue, s, i)
+
+ return rgb2name(rgb)
+
+def bhi2saturation(brightness, hue, intensity):
+ while hue < 0:
+ hue = hue + _TWO_PI
+ while hue >= _TWO_PI:
+ hue = hue - _TWO_PI
+ hue = hue / _THIRD_PI
+ f = hue - math.floor(hue)
+
+ pp = intensity
+ pq = intensity * f
+ pt = intensity - intensity * f
+ pv = 0
+
+ hue = int(hue)
+ if hue == 0: rgb = (pv, pt, pp)
+ elif hue == 1: rgb = (pq, pv, pp)
+ elif hue == 2: rgb = (pp, pv, pt)
+ elif hue == 3: rgb = (pp, pq, pv)
+ elif hue == 4: rgb = (pt, pp, pv)
+ elif hue == 5: rgb = (pv, pp, pq)
+
+ return (intensity - brightness) / rgb2brightness(rgb)
+
+def hsi2rgb(hue, saturation, intensity):
+ i = intensity
+ if saturation == 0:
+ rgb = [i, i, i]
+ else:
+ while hue < 0:
+ hue = hue + _TWO_PI
+ while hue >= _TWO_PI:
+ hue = hue - _TWO_PI
+ hue = hue / _THIRD_PI
+ f = hue - math.floor(hue)
+ p = i * (1.0 - saturation)
+ q = i * (1.0 - saturation * f)
+ t = i * (1.0 - saturation * (1.0 - f))
+
+ hue = int(hue)
+ if hue == 0: rgb = [i, t, p]
+ elif hue == 1: rgb = [q, i, p]
+ elif hue == 2: rgb = [p, i, t]
+ elif hue == 3: rgb = [p, q, i]
+ elif hue == 4: rgb = [t, p, i]
+ elif hue == 5: rgb = [i, p, q]
+
+ for index in range(3):
+ val = rgb[index]
+ if val < 0.0:
+ val = 0.0
+ if val > 1.0:
+ val = 1.0
+ rgb[index] = val
+
+ return rgb
+
+def average(rgb1, rgb2, fraction):
+ return (
+ rgb2[0] * fraction + rgb1[0] * (1.0 - fraction),
+ rgb2[1] * fraction + rgb1[1] * (1.0 - fraction),
+ rgb2[2] * fraction + rgb1[2] * (1.0 - fraction)
+ )
+
+def rgb2name(rgb):
+ return '#%02x%02x%02x' % \
+ (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))
+
+def rgb2brightness(rgb):
+ # Return the perceived grey level of the color
+ # (0.0 == black, 1.0 == white).
+
+ rf = 0.299
+ gf = 0.587
+ bf = 0.114
+ return rf * rgb[0] + gf * rgb[1] + bf * rgb[2]
+
+def rgb2hsi(rgb):
+ maxc = max(rgb[0], rgb[1], rgb[2])
+ minc = min(rgb[0], rgb[1], rgb[2])
+
+ intensity = maxc
+ if maxc != 0:
+ saturation = (maxc - minc) / maxc
+ else:
+ saturation = 0.0
+
+ hue = 0.0
+ if saturation != 0.0:
+ c = []
+ for index in range(3):
+ c.append((maxc - rgb[index]) / (maxc - minc))
+
+ if rgb[0] == maxc:
+ hue = c[2] - c[1]
+ elif rgb[1] == maxc:
+ hue = 2 + c[0] - c[2]
+ elif rgb[2] == maxc:
+ hue = 4 + c[1] - c[0]
+
+ hue = hue * _THIRD_PI
+ if hue < 0.0:
+ hue = hue + _TWO_PI
+
+ return (hue, saturation, intensity)
+
+def name2rgb(root, colorName, asInt = 0):
+ if colorName[0] == '#':
+ # Extract rgb information from the color name itself, assuming
+ # it is either #rgb, #rrggbb, #rrrgggbbb, or #rrrrggggbbbb
+ # This is useful, since tk may return incorrect rgb values if
+ # the colormap is full - it will return the rbg values of the
+ # closest color available.
+ colorName = colorName[1:]
+ digits = len(colorName) / 3
+ factor = 16 ** (4 - digits)
+ rgb = (
+ string.atoi(colorName[0:digits], 16) * factor,
+ string.atoi(colorName[digits:digits * 2], 16) * factor,
+ string.atoi(colorName[digits * 2:digits * 3], 16) * factor,
+ )
+ else:
+ # We have no choice but to ask Tk what the rgb values are.
+ rgb = root.winfo_rgb(colorName)
+
+ if not asInt:
+ rgb = (rgb[0] / _MAX_RGB, rgb[1] / _MAX_RGB, rgb[2] / _MAX_RGB)
+ return rgb
+
+def _calcPalette(root, background=None, **kw):
+ # Create a map that has the complete new palette. If some colors
+ # aren't specified, compute them from other colors that are specified.
+ new = {}
+ for key, value in kw.items():
+ new[key] = value
+ if background is not None:
+ new['background'] = background
+ if not new.has_key('background'):
+ raise ValueError, 'must specify a background color'
+
+ if not new.has_key('foreground'):
+ new['foreground'] = 'black'
+
+ bg = name2rgb(root, new['background'])
+ fg = name2rgb(root, new['foreground'])
+
+ for i in ('activeForeground', 'insertBackground', 'selectForeground',
+ 'highlightColor'):
+ if not new.has_key(i):
+ new[i] = new['foreground']
+
+ if not new.has_key('disabledForeground'):
+ newCol = average(bg, fg, 0.3)
+ new['disabledForeground'] = rgb2name(newCol)
+
+ if not new.has_key('highlightBackground'):
+ new['highlightBackground'] = new['background']
+
+ # Set <lighterBg> to a color that is a little lighter that the
+ # normal background. To do this, round each color component up by
+ # 9% or 1/3 of the way to full white, whichever is greater.
+ lighterBg = []
+ for i in range(3):
+ lighterBg.append(bg[i])
+ inc1 = lighterBg[i] * 0.09
+ inc2 = (1.0 - lighterBg[i]) / 3
+ if inc1 > inc2:
+ lighterBg[i] = lighterBg[i] + inc1
+ else:
+ lighterBg[i] = lighterBg[i] + inc2
+ if lighterBg[i] > 1.0:
+ lighterBg[i] = 1.0
+
+ # Set <darkerBg> to a color that is a little darker that the
+ # normal background.
+ darkerBg = (bg[0] * 0.9, bg[1] * 0.9, bg[2] * 0.9)
+
+ if not new.has_key('activeBackground'):
+ # If the foreground is dark, pick a light active background.
+ # If the foreground is light, pick a dark active background.
+ # XXX This has been disabled, since it does not look very
+ # good with dark backgrounds. If this is ever fixed, the
+ # selectBackground and troughColor options should also be fixed.
+
+ if rgb2brightness(fg) < 0.5:
+ new['activeBackground'] = rgb2name(lighterBg)
+ else:
+ new['activeBackground'] = rgb2name(lighterBg)
+
+ if not new.has_key('selectBackground'):
+ new['selectBackground'] = rgb2name(darkerBg)
+ if not new.has_key('troughColor'):
+ new['troughColor'] = rgb2name(darkerBg)
+ if not new.has_key('selectColor'):
+ new['selectColor'] = 'yellow'
+
+ return new
+
+def spectrum(numColors, correction = 1.0, saturation = 1.0, intensity = 1.0,
+ extraOrange = 1, returnHues = 0):
+ colorList = []
+ division = numColors / 7.0
+ for index in range(numColors):
+ if extraOrange:
+ if index < 2 * division:
+ hue = index / division
+ else:
+ hue = 2 + 2 * (index - 2 * division) / division
+ hue = hue * _SIXTH_PI
+ else:
+ hue = index * _TWO_PI / numColors
+ if returnHues:
+ colorList.append(hue)
+ else:
+ rgb = hsi2rgb(hue, saturation, intensity)
+ if correction != 1.0:
+ rgb = correct(rgb, correction)
+ name = rgb2name(rgb)
+ colorList.append(name)
+ return colorList
+
+def correct(rgb, correction):
+ correction = float(correction)
+ rtn = []
+ for index in range(3):
+ rtn.append((1 - (1 - rgb[index]) ** correction) ** (1 / correction))
+ return rtn
+
+#==============================================================================
+
+def _recolorTree(widget, oldpalette, newcolors):
+ # Change the colors in a widget and its descendants.
+
+ # Change the colors in <widget> and all of its descendants,
+ # according to the <newcolors> dictionary. It only modifies
+ # colors that have their default values as specified by the
+ # <oldpalette> variable. The keys of the <newcolors> dictionary
+ # are named after widget configuration options and the values are
+ # the new value for that option.
+
+ for dbOption in newcolors.keys():
+ option = string.lower(dbOption)
+ try:
+ value = str(widget.cget(option))
+ except:
+ continue
+ if oldpalette is None or value == oldpalette[dbOption]:
+ apply(widget.configure, (), {option : newcolors[dbOption]})
+
+ for child in widget.winfo_children():
+ _recolorTree(child, oldpalette, newcolors)
+
+def changecolor(widget, background=None, **kw):
+ root = widget._root()
+ if not hasattr(widget, '_Pmw_oldpalette'):
+ widget._Pmw_oldpalette = getdefaultpalette(root)
+ newpalette = apply(_calcPalette, (root, background,), kw)
+ _recolorTree(widget, widget._Pmw_oldpalette, newpalette)
+ widget._Pmw_oldpalette = newpalette
+
+def bordercolors(root, colorName):
+ # This is the same method that Tk uses for shadows, in TkpGetShadows.
+
+ lightRGB = []
+ darkRGB = []
+ for value in name2rgb(root, colorName, 1):
+ value40pc = (14 * value) / 10
+ if value40pc > _MAX_RGB:
+ value40pc = _MAX_RGB
+ valueHalfWhite = (_MAX_RGB + value) / 2;
+ lightRGB.append(max(value40pc, valueHalfWhite))
+
+ darkValue = (60 * value) / 100
+ darkRGB.append(darkValue)
+
+ return (
+ '#%04x%04x%04x' % (lightRGB[0], lightRGB[1], lightRGB[2]),
+ '#%04x%04x%04x' % (darkRGB[0], darkRGB[1], darkRGB[2])
+ )
--- /dev/null
+# Based on iwidgets2.2.0/combobox.itk code.
+
+import os
+import string
+import types
+import Tkinter
+import Pmw
+
+class ComboBox(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('autoclear', 0, INITOPT),
+ ('buttonaspect', 1.0, INITOPT),
+ ('dropdown', 1, INITOPT),
+ ('fliparrow', 0, INITOPT),
+ ('history', 1, INITOPT),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('listheight', 200, INITOPT),
+ ('selectioncommand', None, None),
+ ('sticky', 'ew', INITOPT),
+ ('unique', 1, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ self._entryfield = self.createcomponent('entryfield',
+ (('entry', 'entryfield_entry'),), None,
+ Pmw.EntryField, (interior,))
+ self._entryfield.grid(column=2, row=2, sticky=self['sticky'])
+ interior.grid_columnconfigure(2, weight = 1)
+ self._entryWidget = self._entryfield.component('entry')
+
+ if self['dropdown']:
+ self._isPosted = 0
+ interior.grid_rowconfigure(2, weight = 1)
+
+ # Create the arrow button.
+ self._arrowBtn = self.createcomponent('arrowbutton',
+ (), None,
+ Tkinter.Canvas, (interior,), borderwidth = 2,
+ relief = 'raised',
+ width = 16, height = 16)
+ if 'n' in self['sticky']:
+ sticky = 'n'
+ else:
+ sticky = ''
+ if 's' in self['sticky']:
+ sticky = sticky + 's'
+ self._arrowBtn.grid(column=3, row=2, sticky = sticky)
+ self._arrowRelief = self._arrowBtn.cget('relief')
+
+ # Create the label.
+ self.createlabel(interior, childCols=2)
+
+ # Create the dropdown window.
+ self._popup = self.createcomponent('popup',
+ (), None,
+ Tkinter.Toplevel, (interior,))
+ self._popup.withdraw()
+ self._popup.overrideredirect(1)
+
+ # Create the scrolled listbox inside the dropdown window.
+ self._list = self.createcomponent('scrolledlist',
+ (('listbox', 'scrolledlist_listbox'),), None,
+ Pmw.ScrolledListBox, (self._popup,),
+ hull_borderwidth = 2,
+ hull_relief = 'raised',
+ hull_height = self['listheight'],
+ usehullsize = 1,
+ listbox_exportselection = 0)
+ self._list.pack(expand=1, fill='both')
+ self.__listbox = self._list.component('listbox')
+
+ # Bind events to the arrow button.
+ self._arrowBtn.bind('<1>', self._postList)
+ self._arrowBtn.bind('<Configure>', self._drawArrow)
+ self._arrowBtn.bind('<3>', self._next)
+ self._arrowBtn.bind('<Shift-3>', self._previous)
+ self._arrowBtn.bind('<Down>', self._next)
+ self._arrowBtn.bind('<Up>', self._previous)
+ self._arrowBtn.bind('<Control-n>', self._next)
+ self._arrowBtn.bind('<Control-p>', self._previous)
+ self._arrowBtn.bind('<Shift-Down>', self._postList)
+ self._arrowBtn.bind('<Shift-Up>', self._postList)
+ self._arrowBtn.bind('<F34>', self._postList)
+ self._arrowBtn.bind('<F28>', self._postList)
+ self._arrowBtn.bind('<space>', self._postList)
+
+ # Bind events to the dropdown window.
+ self._popup.bind('<Escape>', self._unpostList)
+ self._popup.bind('<space>', self._selectUnpost)
+ self._popup.bind('<Return>', self._selectUnpost)
+ self._popup.bind('<ButtonRelease-1>', self._dropdownBtnRelease)
+ self._popup.bind('<ButtonPress-1>', self._unpostOnNextRelease)
+
+ # Bind events to the Tk listbox.
+ self.__listbox.bind('<Enter>', self._unpostOnNextRelease)
+
+ # Bind events to the Tk entry widget.
+ self._entryWidget.bind('<Configure>', self._resizeArrow)
+ self._entryWidget.bind('<Shift-Down>', self._postList)
+ self._entryWidget.bind('<Shift-Up>', self._postList)
+ self._entryWidget.bind('<F34>', self._postList)
+ self._entryWidget.bind('<F28>', self._postList)
+
+ # Need to unpost the popup if the entryfield is unmapped (eg:
+ # its toplevel window is withdrawn) while the popup list is
+ # displayed.
+ self._entryWidget.bind('<Unmap>', self._unpostList)
+
+ else:
+ # Create the scrolled listbox below the entry field.
+ self._list = self.createcomponent('scrolledlist',
+ (('listbox', 'scrolledlist_listbox'),), None,
+ Pmw.ScrolledListBox, (interior,),
+ selectioncommand = self._selectCmd)
+ self._list.grid(column=2, row=3, sticky='nsew')
+ self.__listbox = self._list.component('listbox')
+
+ # The scrolled listbox should expand vertically.
+ interior.grid_rowconfigure(3, weight = 1)
+
+ # Create the label.
+ self.createlabel(interior, childRows=2)
+
+ self._entryWidget.bind('<Down>', self._next)
+ self._entryWidget.bind('<Up>', self._previous)
+ self._entryWidget.bind('<Control-n>', self._next)
+ self._entryWidget.bind('<Control-p>', self._previous)
+ self.__listbox.bind('<Control-n>', self._next)
+ self.__listbox.bind('<Control-p>', self._previous)
+
+ if self['history']:
+ self._entryfield.configure(command=self._addHistory)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self['dropdown'] and self._isPosted:
+ Pmw.popgrab(self._popup)
+ Pmw.MegaWidget.destroy(self)
+
+ #======================================================================
+
+ # Public methods
+
+ def get(self, first = None, last=None):
+ if first is None:
+ return self._entryWidget.get()
+ else:
+ return self._list.get(first, last)
+
+ def invoke(self):
+ if self['dropdown']:
+ self._postList()
+ else:
+ return self._selectCmd()
+
+ def selectitem(self, index, setentry=1):
+ if type(index) == types.StringType:
+ text = index
+ items = self._list.get(0, 'end')
+ if text in items:
+ index = list(items).index(text)
+ else:
+ raise IndexError, 'index "%s" not found' % text
+ elif setentry:
+ text = self._list.get(0, 'end')[index]
+
+ self._list.select_clear(0, 'end')
+ self._list.select_set(index, index)
+ self._list.activate(index)
+ self.see(index)
+ if setentry:
+ self._entryfield.setentry(text)
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)size method inherited from Tkinter.Frame.Grid.
+ def size(self):
+ return self._list.size()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+ def bbox(self, index):
+ return self._list.bbox(index)
+
+ def clear(self):
+ self._entryfield.clear()
+ self._list.clear()
+
+ #======================================================================
+
+ # Private methods for both dropdown and simple comboboxes.
+
+ def _addHistory(self):
+ input = self._entryWidget.get()
+
+ if input != '':
+ index = None
+ if self['unique']:
+ # If item is already in list, select it and return.
+ items = self._list.get(0, 'end')
+ if input in items:
+ index = list(items).index(input)
+
+ if index is None:
+ index = self._list.index('end')
+ self._list.insert('end', input)
+
+ self.selectitem(index)
+ if self['autoclear']:
+ self._entryWidget.delete(0, 'end')
+
+ # Execute the selectioncommand on the new entry.
+ self._selectCmd()
+
+ def _next(self, event):
+ size = self.size()
+ if size <= 1:
+ return
+
+ cursels = self.curselection()
+
+ if len(cursels) == 0:
+ index = 0
+ else:
+ index = string.atoi(cursels[0])
+ if index == size - 1:
+ index = 0
+ else:
+ index = index + 1
+
+ self.selectitem(index)
+
+ def _previous(self, event):
+ size = self.size()
+ if size <= 1:
+ return
+
+ cursels = self.curselection()
+
+ if len(cursels) == 0:
+ index = size - 1
+ else:
+ index = string.atoi(cursels[0])
+ if index == 0:
+ index = size - 1
+ else:
+ index = index - 1
+
+ self.selectitem(index)
+
+ def _selectCmd(self, event=None):
+
+ sels = self.getcurselection()
+ if len(sels) == 0:
+ item = None
+ else:
+ item = sels[0]
+ self._entryfield.setentry(item)
+
+ cmd = self['selectioncommand']
+ if callable(cmd):
+ if event is None:
+ # Return result of selectioncommand for invoke() method.
+ return cmd(item)
+ else:
+ cmd(item)
+
+ #======================================================================
+
+ # Private methods for dropdown combobox.
+
+ def _drawArrow(self, event=None, sunken=0):
+ arrow = self._arrowBtn
+ if sunken:
+ self._arrowRelief = arrow.cget('relief')
+ arrow.configure(relief = 'sunken')
+ else:
+ arrow.configure(relief = self._arrowRelief)
+
+ if self._isPosted and self['fliparrow']:
+ direction = 'up'
+ else:
+ direction = 'down'
+ Pmw.drawarrow(arrow, self['entry_foreground'], direction, 'arrow')
+
+ def _postList(self, event = None):
+ self._isPosted = 1
+ self._drawArrow(sunken=1)
+
+ # Make sure that the arrow is displayed sunken.
+ self.update_idletasks()
+
+ x = self._entryfield.winfo_rootx()
+ y = self._entryfield.winfo_rooty() + \
+ self._entryfield.winfo_height()
+ w = self._entryfield.winfo_width() + self._arrowBtn.winfo_width()
+ h = self.__listbox.winfo_height()
+ sh = self.winfo_screenheight()
+
+ if y + h > sh and y > sh / 2:
+ y = self._entryfield.winfo_rooty() - h
+
+ self._list.configure(hull_width=w)
+
+ Pmw.setgeometryanddeiconify(self._popup, '+%d+%d' % (x, y))
+
+ # Grab the popup, so that all events are delivered to it, and
+ # set focus to the listbox, to make keyboard navigation
+ # easier.
+ Pmw.pushgrab(self._popup, 1, self._unpostList)
+ self.__listbox.focus_set()
+
+ self._drawArrow()
+
+ # Ignore the first release of the mouse button after posting the
+ # dropdown list, unless the mouse enters the dropdown list.
+ self._ignoreRelease = 1
+
+ def _dropdownBtnRelease(self, event):
+ if (event.widget == self._list.component('vertscrollbar') or
+ event.widget == self._list.component('horizscrollbar')):
+ return
+
+ if self._ignoreRelease:
+ self._unpostOnNextRelease()
+ return
+
+ self._unpostList()
+
+ if (event.x >= 0 and event.x < self.__listbox.winfo_width() and
+ event.y >= 0 and event.y < self.__listbox.winfo_height()):
+ self._selectCmd()
+
+ def _unpostOnNextRelease(self, event = None):
+ self._ignoreRelease = 0
+
+ def _resizeArrow(self, event):
+ bw = (string.atoi(self._arrowBtn['borderwidth']) +
+ string.atoi(self._arrowBtn['highlightthickness']))
+ newHeight = self._entryfield.winfo_reqheight() - 2 * bw
+ newWidth = int(newHeight * self['buttonaspect'])
+ self._arrowBtn.configure(width=newWidth, height=newHeight)
+ self._drawArrow()
+
+ def _unpostList(self, event=None):
+ if not self._isPosted:
+ # It is possible to get events on an unposted popup. For
+ # example, by repeatedly pressing the space key to post
+ # and unpost the popup. The <space> event may be
+ # delivered to the popup window even though
+ # Pmw.popgrab() has set the focus away from the
+ # popup window. (Bug in Tk?)
+ return
+
+ # Restore the focus before withdrawing the window, since
+ # otherwise the window manager may take the focus away so we
+ # can't redirect it. Also, return the grab to the next active
+ # window in the stack, if any.
+ Pmw.popgrab(self._popup)
+ self._popup.withdraw()
+
+ self._isPosted = 0
+ self._drawArrow()
+
+ def _selectUnpost(self, event):
+ self._unpostList()
+ self._selectCmd()
+
+Pmw.forwardmethods(ComboBox, Pmw.ScrolledListBox, '_list')
+Pmw.forwardmethods(ComboBox, Pmw.EntryField, '_entryfield')
--- /dev/null
+# Not Based on iwidgets version.
+
+import Pmw
+
+class ComboBoxDialog(Pmw.Dialog):
+ # Dialog window with simple combobox.
+
+ # Dialog window displaying a list and entry field and requesting
+ # the user to make a selection or enter a value
+
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderx', 10, INITOPT),
+ ('bordery', 10, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.Dialog.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ aliases = (
+ ('listbox', 'combobox_listbox'),
+ ('scrolledlist', 'combobox_scrolledlist'),
+ ('entry', 'combobox_entry'),
+ ('label', 'combobox_label'),
+ )
+ self._combobox = self.createcomponent('combobox',
+ aliases, None,
+ Pmw.ComboBox, (interior,),
+ scrolledlist_dblclickcommand = self.invoke,
+ dropdown = 0,
+ )
+ self._combobox.pack(side='top', expand='true', fill='both',
+ padx = self['borderx'], pady = self['bordery'])
+
+ if not kw.has_key('activatecommand'):
+ # Whenever this dialog is activated, set the focus to the
+ # ComboBox's listbox widget.
+ listbox = self.component('listbox')
+ self.configure(activatecommand = listbox.focus_set)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)size method inherited from Tkinter.Toplevel.Grid.
+ def size(self):
+ return self._combobox.size()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)bbox method inherited from Tkinter.Toplevel.Grid.
+ def bbox(self, index):
+ return self._combobox.bbox(index)
+
+Pmw.forwardmethods(ComboBoxDialog, Pmw.ComboBox, '_combobox')
--- /dev/null
+import string
+import sys
+import types
+import Tkinter
+import Pmw
+
+class Counter(Pmw.MegaWidget):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('autorepeat', 1, None),
+ ('buttonaspect', 1.0, INITOPT),
+ ('datatype', 'numeric', self._datatype),
+ ('increment', 1, None),
+ ('initwait', 300, None),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('orient', 'horizontal', INITOPT),
+ ('padx', 0, INITOPT),
+ ('pady', 0, INITOPT),
+ ('repeatrate', 50, None),
+ ('sticky', 'ew', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Initialise instance variables.
+ self._timerId = None
+ self._normalRelief = None
+
+ # Create the components.
+ interior = self.interior()
+
+ # If there is no label, put the arrows and the entry directly
+ # into the interior, otherwise create a frame for them. In
+ # either case the border around the arrows and the entry will
+ # be raised (but not around the label).
+ if self['labelpos'] is None:
+ frame = interior
+ if not kw.has_key('hull_relief'):
+ frame.configure(relief = 'raised')
+ if not kw.has_key('hull_borderwidth'):
+ frame.configure(borderwidth = 1)
+ else:
+ frame = self.createcomponent('frame',
+ (), None,
+ Tkinter.Frame, (interior,),
+ relief = 'raised', borderwidth = 1)
+ frame.grid(column=2, row=2, sticky=self['sticky'])
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ # Create the down arrow.
+ self._downArrowBtn = self.createcomponent('downarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+
+ # Create the entry field.
+ self._counterEntry = self.createcomponent('entryfield',
+ (('entry', 'entryfield_entry'),), None,
+ Pmw.EntryField, (frame,))
+
+ # Create the up arrow.
+ self._upArrowBtn = self.createcomponent('uparrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+
+ padx = self['padx']
+ pady = self['pady']
+ orient = self['orient']
+ if orient == 'horizontal':
+ self._downArrowBtn.grid(column = 0, row = 0)
+ self._counterEntry.grid(column = 1, row = 0,
+ sticky = self['sticky'])
+ self._upArrowBtn.grid(column = 2, row = 0)
+ frame.grid_columnconfigure(1, weight = 1)
+ frame.grid_rowconfigure(0, weight = 1)
+ if Tkinter.TkVersion >= 4.2:
+ frame.grid_columnconfigure(0, pad = padx)
+ frame.grid_columnconfigure(2, pad = padx)
+ frame.grid_rowconfigure(0, pad = pady)
+ elif orient == 'vertical':
+ self._upArrowBtn.grid(column = 0, row = 0, sticky = 's')
+ self._counterEntry.grid(column = 0, row = 1,
+ sticky = self['sticky'])
+ self._downArrowBtn.grid(column = 0, row = 2, sticky = 'n')
+ frame.grid_columnconfigure(0, weight = 1)
+ frame.grid_rowconfigure(0, weight = 1)
+ frame.grid_rowconfigure(2, weight = 1)
+ if Tkinter.TkVersion >= 4.2:
+ frame.grid_rowconfigure(0, pad = pady)
+ frame.grid_rowconfigure(2, pad = pady)
+ frame.grid_columnconfigure(0, pad = padx)
+ else:
+ raise ValueError, 'bad orient option ' + repr(orient) + \
+ ': must be either \'horizontal\' or \'vertical\''
+
+ self.createlabel(interior)
+
+ self._upArrowBtn.bind('<Configure>', self._drawUpArrow)
+ self._upArrowBtn.bind('<1>', self._countUp)
+ self._upArrowBtn.bind('<Any-ButtonRelease-1>', self._stopCounting)
+ self._downArrowBtn.bind('<Configure>', self._drawDownArrow)
+ self._downArrowBtn.bind('<1>', self._countDown)
+ self._downArrowBtn.bind('<Any-ButtonRelease-1>', self._stopCounting)
+ self._counterEntry.bind('<Configure>', self._resizeArrow)
+ entry = self._counterEntry.component('entry')
+ entry.bind('<Down>', lambda event, s = self: s._key_decrement(event))
+ entry.bind('<Up>', lambda event, s = self: s._key_increment(event))
+
+ # Need to cancel the timer if an arrow button is unmapped (eg:
+ # its toplevel window is withdrawn) while the mouse button is
+ # held down. The canvas will not get the ButtonRelease event
+ # if it is not mapped, since the implicit grab is cancelled.
+ self._upArrowBtn.bind('<Unmap>', self._stopCounting)
+ self._downArrowBtn.bind('<Unmap>', self._stopCounting)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _resizeArrow(self, event):
+ for btn in (self._upArrowBtn, self._downArrowBtn):
+ bw = (string.atoi(btn['borderwidth']) +
+ string.atoi(btn['highlightthickness']))
+ newHeight = self._counterEntry.winfo_reqheight() - 2 * bw
+ newWidth = int(newHeight * self['buttonaspect'])
+ btn.configure(width=newWidth, height=newHeight)
+ self._drawArrow(btn)
+
+ def _drawUpArrow(self, event):
+ self._drawArrow(self._upArrowBtn)
+
+ def _drawDownArrow(self, event):
+ self._drawArrow(self._downArrowBtn)
+
+ def _drawArrow(self, arrow):
+ if self['orient'] == 'vertical':
+ if arrow == self._upArrowBtn:
+ direction = 'up'
+ else:
+ direction = 'down'
+ else:
+ if arrow == self._upArrowBtn:
+ direction = 'right'
+ else:
+ direction = 'left'
+ Pmw.drawarrow(arrow, self['entry_foreground'], direction, 'arrow')
+
+ def _stopCounting(self, event = None):
+ if self._timerId is not None:
+ self.after_cancel(self._timerId)
+ self._timerId = None
+ if self._normalRelief is not None:
+ button, relief = self._normalRelief
+ button.configure(relief=relief)
+ self._normalRelief = None
+
+ def _countUp(self, event):
+ self._normalRelief = (self._upArrowBtn, self._upArrowBtn.cget('relief'))
+ self._upArrowBtn.configure(relief='sunken')
+ # Force arrow down (it may come up immediately, if increment fails).
+ self._upArrowBtn.update_idletasks()
+ self._count(1, 1)
+
+ def _countDown(self, event):
+ self._normalRelief = (self._downArrowBtn, self._downArrowBtn.cget('relief'))
+ self._downArrowBtn.configure(relief='sunken')
+ # Force arrow down (it may come up immediately, if increment fails).
+ self._downArrowBtn.update_idletasks()
+ self._count(-1, 1)
+
+ def increment(self):
+ self._forceCount(1)
+
+ def decrement(self):
+ self._forceCount(-1)
+
+ def _key_increment(self, event):
+ self._forceCount(1)
+ self.update_idletasks()
+
+ def _key_decrement(self, event):
+ self._forceCount(-1)
+ self.update_idletasks()
+
+ def _datatype(self):
+ datatype = self['datatype']
+
+ if type(datatype) is types.DictionaryType:
+ self._counterArgs = datatype.copy()
+ if self._counterArgs.has_key('counter'):
+ datatype = self._counterArgs['counter']
+ del self._counterArgs['counter']
+ else:
+ datatype = 'numeric'
+ else:
+ self._counterArgs = {}
+
+ if _counterCommands.has_key(datatype):
+ self._counterCommand = _counterCommands[datatype]
+ elif callable(datatype):
+ self._counterCommand = datatype
+ else:
+ validValues = _counterCommands.keys()
+ validValues.sort()
+ raise ValueError, ('bad datatype value "%s": must be a' +
+ ' function or one of %s') % (datatype, validValues)
+
+ def _forceCount(self, factor):
+ if not self.valid():
+ self.bell()
+ return
+
+ text = self._counterEntry.get()
+ try:
+ value = apply(self._counterCommand,
+ (text, factor, self['increment']), self._counterArgs)
+ except ValueError:
+ self.bell()
+ return
+
+ previousICursor = self._counterEntry.index('insert')
+ if self._counterEntry.setentry(value) == Pmw.OK:
+ self._counterEntry.xview('end')
+ self._counterEntry.icursor(previousICursor)
+
+ def _count(self, factor, first):
+ if not self.valid():
+ self.bell()
+ return
+
+ self._timerId = None
+ origtext = self._counterEntry.get()
+ try:
+ value = apply(self._counterCommand,
+ (origtext, factor, self['increment']), self._counterArgs)
+ except ValueError:
+ # If text is invalid, stop counting.
+ self._stopCounting()
+ self.bell()
+ return
+
+ # If incrementing produces an invalid value, restore previous
+ # text and stop counting.
+ previousICursor = self._counterEntry.index('insert')
+ valid = self._counterEntry.setentry(value)
+ if valid != Pmw.OK:
+ self._stopCounting()
+ self._counterEntry.setentry(origtext)
+ if valid == Pmw.PARTIAL:
+ self.bell()
+ return
+ self._counterEntry.xview('end')
+ self._counterEntry.icursor(previousICursor)
+
+ if self['autorepeat']:
+ if first:
+ delay = self['initwait']
+ else:
+ delay = self['repeatrate']
+ self._timerId = self.after(delay,
+ lambda self=self, factor=factor: self._count(factor, 0))
+
+ def destroy(self):
+ self._stopCounting()
+ Pmw.MegaWidget.destroy(self)
+
+Pmw.forwardmethods(Counter, Pmw.EntryField, '_counterEntry')
+
+def _changeNumber(text, factor, increment):
+ value = string.atol(text)
+ if factor > 0:
+ value = (value / increment) * increment + increment
+ else:
+ value = ((value - 1) / increment) * increment
+
+ # Get rid of the 'L' at the end of longs (in python up to 1.5.2).
+ rtn = str(value)
+ if rtn[-1] == 'L':
+ return rtn[:-1]
+ else:
+ return rtn
+
+def _changeReal(text, factor, increment, separator = '.'):
+ value = Pmw.stringtoreal(text, separator)
+ div = value / increment
+
+ # Compare reals using str() to avoid problems caused by binary
+ # numbers being only approximations to decimal numbers.
+ # For example, if value is -0.3 and increment is 0.1, then
+ # int(value/increment) = -2, not -3 as one would expect.
+ if str(div)[-2:] == '.0':
+ # value is an even multiple of increment.
+ div = round(div) + factor
+ else:
+ # value is not an even multiple of increment.
+ div = int(div) * 1.0
+ if value < 0:
+ div = div - 1
+ if factor > 0:
+ div = (div + 1)
+
+ value = div * increment
+
+ text = str(value)
+ if separator != '.':
+ index = string.find(text, '.')
+ if index >= 0:
+ text = text[:index] + separator + text[index + 1:]
+ return text
+
+def _changeDate(value, factor, increment, format = 'ymd',
+ separator = '/', yyyy = 0):
+
+ jdn = Pmw.datestringtojdn(value, format, separator) + factor * increment
+
+ y, m, d = Pmw.jdntoymd(jdn)
+ result = ''
+ for index in range(3):
+ if index > 0:
+ result = result + separator
+ f = format[index]
+ if f == 'y':
+ if yyyy:
+ result = result + '%02d' % y
+ else:
+ result = result + '%02d' % (y % 100)
+ elif f == 'm':
+ result = result + '%02d' % m
+ elif f == 'd':
+ result = result + '%02d' % d
+
+ return result
+
+_SECSPERDAY = 24 * 60 * 60
+def _changeTime(value, factor, increment, separator = ':', time24 = 0):
+ unixTime = Pmw.timestringtoseconds(value, separator)
+ if factor > 0:
+ chunks = unixTime / increment + 1
+ else:
+ chunks = (unixTime - 1) / increment
+ unixTime = chunks * increment
+ if time24:
+ while unixTime < 0:
+ unixTime = unixTime + _SECSPERDAY
+ while unixTime >= _SECSPERDAY:
+ unixTime = unixTime - _SECSPERDAY
+ if unixTime < 0:
+ unixTime = -unixTime
+ sign = '-'
+ else:
+ sign = ''
+ secs = unixTime % 60
+ unixTime = unixTime / 60
+ mins = unixTime % 60
+ hours = unixTime / 60
+ return '%s%02d%s%02d%s%02d' % (sign, hours, separator, mins, separator, secs)
+
+# hexadecimal, alphabetic, alphanumeric not implemented
+_counterCommands = {
+ 'numeric' : _changeNumber, # } integer
+ 'integer' : _changeNumber, # } these two use the same function
+ 'real' : _changeReal, # real number
+ 'time' : _changeTime,
+ 'date' : _changeDate,
+}
--- /dev/null
+import Pmw
+
+# A Dialog with a counter
+
+class CounterDialog(Pmw.Dialog):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderx', 20, INITOPT),
+ ('bordery', 20, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.Dialog.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ # Create the counter.
+ aliases = (
+ ('entryfield', 'counter_entryfield'),
+ ('entry', 'counter_entryfield_entry'),
+ ('label', 'counter_label')
+ )
+ self._cdCounter = self.createcomponent('counter',
+ aliases, None,
+ Pmw.Counter, (interior,))
+ self._cdCounter.pack(fill='x', expand=1,
+ padx = self['borderx'], pady = self['bordery'])
+
+ if not kw.has_key('activatecommand'):
+ # Whenever this dialog is activated, set the focus to the
+ # Counter's entry widget.
+ tkentry = self.component('entry')
+ self.configure(activatecommand = tkentry.focus_set)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ # Supply aliases to some of the entry component methods.
+ def insertentry(self, index, text):
+ self._cdCounter.insert(index, text)
+
+ def deleteentry(self, first, last=None):
+ self._cdCounter.delete(first, last)
+
+ def indexentry(self, index):
+ return self._cdCounter.index(index)
+
+Pmw.forwardmethods(CounterDialog, Pmw.Counter, '_cdCounter')
--- /dev/null
+# Based on iwidgets2.2.0/dialog.itk and iwidgets2.2.0/dialogshell.itk code.
+
+# Convention:
+# Each dialog window should have one of these as the rightmost button:
+# Close Close a window which only displays information.
+# Cancel Close a window which may be used to change the state of
+# the application.
+
+import sys
+import types
+import Tkinter
+import Pmw
+
+# A Toplevel with a ButtonBox and child site.
+
+class Dialog(Pmw.MegaToplevel):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('buttonbox_hull_borderwidth', 1, None),
+ ('buttonbox_hull_relief', 'raised', None),
+ ('buttonboxpos', 's', INITOPT),
+ ('buttons', ('OK',), self._buttons),
+ ('command', None, None),
+ ('dialogchildsite_borderwidth', 1, None),
+ ('dialogchildsite_relief', 'raised', None),
+ ('defaultbutton', None, self._defaultButton),
+ ('master', 'parent', None),
+ ('separatorwidth', 0, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaToplevel.__init__(self, parent)
+
+ # Create the components.
+
+ oldInterior = Pmw.MegaToplevel.interior(self)
+
+ # Set up pack options according to the position of the button box.
+ pos = self['buttonboxpos']
+ if pos not in 'nsew':
+ raise ValueError, \
+ 'bad buttonboxpos option "%s": should be n, s, e, or w' \
+ % pos
+
+ if pos in 'ns':
+ orient = 'horizontal'
+ fill = 'x'
+ if pos == 'n':
+ side = 'top'
+ else:
+ side = 'bottom'
+ else:
+ orient = 'vertical'
+ fill = 'y'
+ if pos == 'w':
+ side = 'left'
+ else:
+ side = 'right'
+
+ # Create the button box.
+ self._buttonBox = self.createcomponent('buttonbox',
+ (), None,
+ Pmw.ButtonBox, (oldInterior,), orient = orient)
+ self._buttonBox.pack(side = side, fill = fill)
+
+ # Create the separating line.
+ width = self['separatorwidth']
+ if width > 0:
+ self._separator = self.createcomponent('separator',
+ (), None,
+ Tkinter.Frame, (oldInterior,), relief = 'sunken',
+ height = width, width = width, borderwidth = width / 2)
+ self._separator.pack(side = side, fill = fill)
+
+ # Create the child site.
+ self.__dialogChildSite = self.createcomponent('dialogchildsite',
+ (), None,
+ Tkinter.Frame, (oldInterior,))
+ self.__dialogChildSite.pack(side=side, fill='both', expand=1)
+
+ self.oldButtons = ()
+ self.oldDefault = None
+
+ self.bind('<Return>', self._invokeDefault)
+ self.userdeletefunc(self._doCommand)
+ self.usermodaldeletefunc(self._doCommand)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def interior(self):
+ return self.__dialogChildSite
+
+ def invoke(self, index = Pmw.DEFAULT):
+ return self._buttonBox.invoke(index)
+
+ def _invokeDefault(self, event):
+ try:
+ self._buttonBox.index(Pmw.DEFAULT)
+ except ValueError:
+ return
+ self._buttonBox.invoke()
+
+ def _doCommand(self, name = None):
+ if name is not None and self.active() and \
+ Pmw.grabstacktopwindow() != self.component('hull'):
+ # This is a modal dialog but is not on the top of the grab
+ # stack (ie: should not have the grab), so ignore this
+ # event. This seems to be a bug in Tk and may occur in
+ # nested modal dialogs.
+ #
+ # An example is the PromptDialog demonstration. To
+ # trigger the problem, start the demo, then move the mouse
+ # to the main window, hit <TAB> and then <TAB> again. The
+ # highlight border of the "Show prompt dialog" button
+ # should now be displayed. Now hit <SPACE>, <RETURN>,
+ # <RETURN> rapidly several times. Eventually, hitting the
+ # return key invokes the password dialog "OK" button even
+ # though the confirm dialog is active (and therefore
+ # should have the keyboard focus). Observed under Solaris
+ # 2.5.1, python 1.5.2 and Tk8.0.
+
+ # TODO: Give focus to the window on top of the grabstack.
+ return
+
+ command = self['command']
+ if callable(command):
+ return command(name)
+ else:
+ if self.active():
+ self.deactivate(name)
+ else:
+ self.withdraw()
+
+ def _buttons(self):
+ buttons = self['buttons']
+ if type(buttons) != types.TupleType and type(buttons) != types.ListType:
+ raise ValueError, \
+ 'bad buttons option "%s": should be a tuple' % str(buttons)
+ if self.oldButtons == buttons:
+ return
+
+ self.oldButtons = buttons
+
+ for index in range(self._buttonBox.numbuttons()):
+ self._buttonBox.delete(0)
+ for name in buttons:
+ self._buttonBox.add(name,
+ command=lambda self=self, name=name: self._doCommand(name))
+
+ if len(buttons) > 0:
+ defaultbutton = self['defaultbutton']
+ if defaultbutton is None:
+ self._buttonBox.setdefault(None)
+ else:
+ try:
+ self._buttonBox.index(defaultbutton)
+ except ValueError:
+ pass
+ else:
+ self._buttonBox.setdefault(defaultbutton)
+ self._buttonBox.alignbuttons()
+
+ def _defaultButton(self):
+ defaultbutton = self['defaultbutton']
+ if self.oldDefault == defaultbutton:
+ return
+
+ self.oldDefault = defaultbutton
+
+ if len(self['buttons']) > 0:
+ if defaultbutton is None:
+ self._buttonBox.setdefault(None)
+ else:
+ try:
+ self._buttonBox.index(defaultbutton)
+ except ValueError:
+ pass
+ else:
+ self._buttonBox.setdefault(defaultbutton)
--- /dev/null
+# Based on iwidgets2.2.0/entryfield.itk code.
+
+import re
+import string
+import types
+import Tkinter
+import Pmw
+
+# Possible return values of validation functions.
+OK = 1
+ERROR = 0
+PARTIAL = -1
+
+class EntryField(Pmw.MegaWidget):
+ _classBindingsDefinedFor = 0
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('command', None, None),
+ ('errorbackground', 'pink', None),
+ ('invalidcommand', self.bell, None),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('modifiedcommand', None, None),
+ ('sticky', 'ew', INITOPT),
+ ('validate', None, self._validate),
+ ('extravalidators', {}, None),
+ ('value', '', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ self._entryFieldEntry = self.createcomponent('entry',
+ (), None,
+ Tkinter.Entry, (interior,))
+ self._entryFieldEntry.grid(column=2, row=2, sticky=self['sticky'])
+ if self['value'] != '':
+ self.__setEntry(self['value'])
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ self.createlabel(interior)
+
+ # Initialise instance variables.
+
+ self.normalBackground = None
+ self._previousText = None
+
+ # Initialise instance.
+
+ _registerEntryField(self._entryFieldEntry, self)
+
+ # Establish the special class bindings if not already done.
+ # Also create bindings if the Tkinter default interpreter has
+ # changed. Use Tkinter._default_root to create class
+ # bindings, so that a reference to root is created by
+ # bind_class rather than a reference to self, which would
+ # prevent object cleanup.
+ if EntryField._classBindingsDefinedFor != Tkinter._default_root:
+ tagList = self._entryFieldEntry.bindtags()
+ root = Tkinter._default_root
+
+ allSequences = {}
+ for tag in tagList:
+
+ sequences = root.bind_class(tag)
+ if type(sequences) is types.StringType:
+ # In old versions of Tkinter, bind_class returns a string
+ sequences = root.tk.splitlist(sequences)
+
+ for sequence in sequences:
+ allSequences[sequence] = None
+ for sequence in allSequences.keys():
+ root.bind_class('EntryFieldPre', sequence, _preProcess)
+ root.bind_class('EntryFieldPost', sequence, _postProcess)
+
+ EntryField._classBindingsDefinedFor = root
+
+ self._entryFieldEntry.bindtags(('EntryFieldPre',) +
+ self._entryFieldEntry.bindtags() + ('EntryFieldPost',))
+ self._entryFieldEntry.bind('<Return>', self._executeCommand)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ _deregisterEntryField(self._entryFieldEntry)
+ Pmw.MegaWidget.destroy(self)
+
+ def _getValidatorFunc(self, validator, index):
+ # Search the extra and standard validator lists for the
+ # given 'validator'. If 'validator' is an alias, then
+ # continue the search using the alias. Make sure that
+ # self-referencial aliases do not cause infinite loops.
+
+ extraValidators = self['extravalidators']
+ traversedValidators = []
+
+ while 1:
+ traversedValidators.append(validator)
+ if extraValidators.has_key(validator):
+ validator = extraValidators[validator][index]
+ elif _standardValidators.has_key(validator):
+ validator = _standardValidators[validator][index]
+ else:
+ return validator
+ if validator in traversedValidators:
+ return validator
+
+ def _validate(self):
+ dict = {
+ 'validator' : None,
+ 'min' : None,
+ 'max' : None,
+ 'minstrict' : 1,
+ 'maxstrict' : 1,
+ }
+ opt = self['validate']
+ if type(opt) is types.DictionaryType:
+ dict.update(opt)
+ else:
+ dict['validator'] = opt
+
+ # Look up validator maps and replace 'validator' field with
+ # the corresponding function.
+ validator = dict['validator']
+ valFunction = self._getValidatorFunc(validator, 0)
+ self._checkValidateFunction(valFunction, 'validate', validator)
+ dict['validator'] = valFunction
+
+ # Look up validator maps and replace 'stringtovalue' field
+ # with the corresponding function.
+ if dict.has_key('stringtovalue'):
+ stringtovalue = dict['stringtovalue']
+ strFunction = self._getValidatorFunc(stringtovalue, 1)
+ self._checkValidateFunction(
+ strFunction, 'stringtovalue', stringtovalue)
+ else:
+ strFunction = self._getValidatorFunc(validator, 1)
+ if strFunction == validator:
+ strFunction = len
+ dict['stringtovalue'] = strFunction
+
+ self._validationInfo = dict
+ args = dict.copy()
+ del args['validator']
+ del args['min']
+ del args['max']
+ del args['minstrict']
+ del args['maxstrict']
+ del args['stringtovalue']
+ self._validationArgs = args
+ self._previousText = None
+
+ if type(dict['min']) == types.StringType and strFunction is not None:
+ dict['min'] = apply(strFunction, (dict['min'],), args)
+ if type(dict['max']) == types.StringType and strFunction is not None:
+ dict['max'] = apply(strFunction, (dict['max'],), args)
+
+ self._checkValidity()
+
+ def _checkValidateFunction(self, function, option, validator):
+ # Raise an error if 'function' is not a function or None.
+
+ if function is not None and not callable(function):
+ extraValidators = self['extravalidators']
+ extra = extraValidators.keys()
+ extra.sort()
+ extra = tuple(extra)
+ standard = _standardValidators.keys()
+ standard.sort()
+ standard = tuple(standard)
+ msg = 'bad %s value "%s": must be a function or one of ' \
+ 'the standard validators %s or extra validators %s'
+ raise ValueError, msg % (option, validator, standard, extra)
+
+ def _executeCommand(self, event = None):
+ cmd = self['command']
+ if callable(cmd):
+ if event is None:
+ # Return result of command for invoke() method.
+ return cmd()
+ else:
+ cmd()
+
+ def _preProcess(self):
+
+ self._previousText = self._entryFieldEntry.get()
+ self._previousICursor = self._entryFieldEntry.index('insert')
+ self._previousXview = self._entryFieldEntry.index('@0')
+ if self._entryFieldEntry.selection_present():
+ self._previousSel= (self._entryFieldEntry.index('sel.first'),
+ self._entryFieldEntry.index('sel.last'))
+ else:
+ self._previousSel = None
+
+ def _postProcess(self):
+
+ # No need to check if text has not changed.
+ previousText = self._previousText
+ if previousText == self._entryFieldEntry.get():
+ return self.valid()
+
+ valid = self._checkValidity()
+ if self.hulldestroyed():
+ # The invalidcommand called by _checkValidity() destroyed us.
+ return valid
+
+ cmd = self['modifiedcommand']
+ if callable(cmd) and previousText != self._entryFieldEntry.get():
+ cmd()
+ return valid
+
+ def checkentry(self):
+ # If there is a variable specified by the entry_textvariable
+ # option, checkentry() should be called after the set() method
+ # of the variable is called.
+
+ self._previousText = None
+ return self._postProcess()
+
+ def _getValidity(self):
+ text = self._entryFieldEntry.get()
+ dict = self._validationInfo
+ args = self._validationArgs
+
+ if dict['validator'] is not None:
+ status = apply(dict['validator'], (text,), args)
+ if status != OK:
+ return status
+
+ # Check for out of (min, max) range.
+ if dict['stringtovalue'] is not None:
+ min = dict['min']
+ max = dict['max']
+ if min is None and max is None:
+ return OK
+ val = apply(dict['stringtovalue'], (text,), args)
+ if min is not None and val < min:
+ if dict['minstrict']:
+ return ERROR
+ else:
+ return PARTIAL
+ if max is not None and val > max:
+ if dict['maxstrict']:
+ return ERROR
+ else:
+ return PARTIAL
+ return OK
+
+ def _checkValidity(self):
+ valid = self._getValidity()
+ oldValidity = valid
+
+ if valid == ERROR:
+ # The entry is invalid.
+ cmd = self['invalidcommand']
+ if callable(cmd):
+ cmd()
+ if self.hulldestroyed():
+ # The invalidcommand destroyed us.
+ return oldValidity
+
+ # Restore the entry to its previous value.
+ if self._previousText is not None:
+ self.__setEntry(self._previousText)
+ self._entryFieldEntry.icursor(self._previousICursor)
+ self._entryFieldEntry.xview(self._previousXview)
+ if self._previousSel is not None:
+ self._entryFieldEntry.selection_range(self._previousSel[0],
+ self._previousSel[1])
+
+ # Check if the saved text is valid as well.
+ valid = self._getValidity()
+
+ self._valid = valid
+
+ if self.hulldestroyed():
+ # The validator or stringtovalue commands called by
+ # _checkValidity() destroyed us.
+ return oldValidity
+
+ if valid == OK:
+ if self.normalBackground is not None:
+ self._entryFieldEntry.configure(
+ background = self.normalBackground)
+ self.normalBackground = None
+ else:
+ if self.normalBackground is None:
+ self.normalBackground = self._entryFieldEntry.cget('background')
+ self._entryFieldEntry.configure(
+ background = self['errorbackground'])
+
+ return oldValidity
+
+ def invoke(self):
+ return self._executeCommand()
+
+ def valid(self):
+ return self._valid == OK
+
+ def clear(self):
+ self.setentry('')
+
+ def __setEntry(self, text):
+ oldState = str(self._entryFieldEntry.cget('state'))
+ if oldState != 'normal':
+ self._entryFieldEntry.configure(state='normal')
+ self._entryFieldEntry.delete(0, 'end')
+ self._entryFieldEntry.insert(0, text)
+ if oldState != 'normal':
+ self._entryFieldEntry.configure(state=oldState)
+
+ def setentry(self, text):
+ self._preProcess()
+ self.__setEntry(text)
+ return self._postProcess()
+
+ def getvalue(self):
+ return self._entryFieldEntry.get()
+
+ def setvalue(self, text):
+ return self.setentry(text)
+
+Pmw.forwardmethods(EntryField, Tkinter.Entry, '_entryFieldEntry')
+
+# ======================================================================
+
+
+# Entry field validation functions
+
+_numericregex = re.compile('^[0-9]*$')
+_alphabeticregex = re.compile('^[a-z]*$', re.IGNORECASE)
+_alphanumericregex = re.compile('^[0-9a-z]*$', re.IGNORECASE)
+
+def numericvalidator(text):
+ if text == '':
+ return PARTIAL
+ else:
+ if _numericregex.match(text) is None:
+ return ERROR
+ else:
+ return OK
+
+def integervalidator(text):
+ if text in ('', '-', '+'):
+ return PARTIAL
+ try:
+ string.atol(text)
+ return OK
+ except ValueError:
+ return ERROR
+
+def alphabeticvalidator(text):
+ if _alphabeticregex.match(text) is None:
+ return ERROR
+ else:
+ return OK
+
+def alphanumericvalidator(text):
+ if _alphanumericregex.match(text) is None:
+ return ERROR
+ else:
+ return OK
+
+def hexadecimalvalidator(text):
+ if text in ('', '0x', '0X', '+', '+0x', '+0X', '-', '-0x', '-0X'):
+ return PARTIAL
+ try:
+ string.atol(text, 16)
+ return OK
+ except ValueError:
+ return ERROR
+
+def realvalidator(text, separator = '.'):
+ if separator != '.':
+ if string.find(text, '.') >= 0:
+ return ERROR
+ index = string.find(text, separator)
+ if index >= 0:
+ text = text[:index] + '.' + text[index + 1:]
+ try:
+ string.atof(text)
+ return OK
+ except ValueError:
+ # Check if the string could be made valid by appending a digit
+ # eg ('-', '+', '.', '-.', '+.', '1.23e', '1E-').
+ if len(text) == 0:
+ return PARTIAL
+ if text[-1] in string.digits:
+ return ERROR
+ try:
+ string.atof(text + '0')
+ return PARTIAL
+ except ValueError:
+ return ERROR
+
+def timevalidator(text, separator = ':'):
+ try:
+ Pmw.timestringtoseconds(text, separator)
+ return OK
+ except ValueError:
+ if len(text) > 0 and text[0] in ('+', '-'):
+ text = text[1:]
+ if re.search('[^0-9' + separator + ']', text) is not None:
+ return ERROR
+ return PARTIAL
+
+def datevalidator(text, format = 'ymd', separator = '/'):
+ try:
+ Pmw.datestringtojdn(text, format, separator)
+ return OK
+ except ValueError:
+ if re.search('[^0-9' + separator + ']', text) is not None:
+ return ERROR
+ return PARTIAL
+
+_standardValidators = {
+ 'numeric' : (numericvalidator, string.atol),
+ 'integer' : (integervalidator, string.atol),
+ 'hexadecimal' : (hexadecimalvalidator, lambda s: string.atol(s, 16)),
+ 'real' : (realvalidator, Pmw.stringtoreal),
+ 'alphabetic' : (alphabeticvalidator, len),
+ 'alphanumeric' : (alphanumericvalidator, len),
+ 'time' : (timevalidator, Pmw.timestringtoseconds),
+ 'date' : (datevalidator, Pmw.datestringtojdn),
+}
+
+_entryCache = {}
+
+def _registerEntryField(entry, entryField):
+ # Register an EntryField widget for an Entry widget
+
+ _entryCache[entry] = entryField
+
+def _deregisterEntryField(entry):
+ # Deregister an Entry widget
+ del _entryCache[entry]
+
+def _preProcess(event):
+ # Forward preprocess events for an Entry to it's EntryField
+
+ _entryCache[event.widget]._preProcess()
+
+def _postProcess(event):
+ # Forward postprocess events for an Entry to it's EntryField
+
+ # The function specified by the 'command' option may have destroyed
+ # the megawidget in a binding earlier in bindtags, so need to check.
+ if _entryCache.has_key(event.widget):
+ _entryCache[event.widget]._postProcess()
--- /dev/null
+import string
+import Tkinter
+import Pmw
+
+def aligngrouptags(groups):
+ # Adjust the y position of the tags in /groups/ so that they all
+ # have the height of the highest tag.
+
+ maxTagHeight = 0
+ for group in groups:
+ if group._tag is None:
+ height = (string.atoi(str(group._ring.cget('borderwidth'))) +
+ string.atoi(str(group._ring.cget('highlightthickness'))))
+ else:
+ height = group._tag.winfo_reqheight()
+ if maxTagHeight < height:
+ maxTagHeight = height
+
+ for group in groups:
+ ringBorder = (string.atoi(str(group._ring.cget('borderwidth'))) +
+ string.atoi(str(group._ring.cget('highlightthickness'))))
+ topBorder = maxTagHeight / 2 - ringBorder / 2
+ group._hull.grid_rowconfigure(0, minsize = topBorder)
+ group._ring.grid_rowconfigure(0,
+ minsize = maxTagHeight - topBorder - ringBorder)
+ if group._tag is not None:
+ group._tag.place(y = maxTagHeight / 2)
+
+class Group( Pmw.MegaWidget ):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('collapsedsize', 6, INITOPT),
+ ('ring_borderwidth', 2, None),
+ ('ring_relief', 'groove', None),
+ ('tagindent', 10, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = Pmw.MegaWidget.interior(self)
+
+ self._ring = self.createcomponent(
+ 'ring',
+ (), None,
+ Tkinter.Frame, (interior,),
+ )
+
+ self._groupChildSite = self.createcomponent(
+ 'groupchildsite',
+ (), None,
+ Tkinter.Frame, (self._ring,)
+ )
+
+ self._tag = self.createcomponent(
+ 'tag',
+ (), None,
+ Tkinter.Label, (interior,),
+ )
+
+ ringBorder = (string.atoi(str(self._ring.cget('borderwidth'))) +
+ string.atoi(str(self._ring.cget('highlightthickness'))))
+ if self._tag is None:
+ tagHeight = ringBorder
+ else:
+ tagHeight = self._tag.winfo_reqheight()
+ self._tag.place(
+ x = ringBorder + self['tagindent'],
+ y = tagHeight / 2,
+ anchor = 'w')
+
+ topBorder = tagHeight / 2 - ringBorder / 2
+ self._ring.grid(column = 0, row = 1, sticky = 'nsew')
+ interior.grid_columnconfigure(0, weight = 1)
+ interior.grid_rowconfigure(1, weight = 1)
+ interior.grid_rowconfigure(0, minsize = topBorder)
+
+ self._groupChildSite.grid(column = 0, row = 1, sticky = 'nsew')
+ self._ring.grid_columnconfigure(0, weight = 1)
+ self._ring.grid_rowconfigure(1, weight = 1)
+ self._ring.grid_rowconfigure(0,
+ minsize = tagHeight - topBorder - ringBorder)
+
+ self.showing = 1
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def toggle(self):
+ if self.showing:
+ self.collapse()
+ else:
+ self.expand()
+ self.showing = not self.showing
+
+ def expand(self):
+ self._groupChildSite.grid(column = 0, row = 1, sticky = 'nsew')
+
+ def collapse(self):
+ self._groupChildSite.grid_forget()
+ if self._tag is None:
+ tagHeight = 0
+ else:
+ tagHeight = self._tag.winfo_reqheight()
+ self._ring.configure(height=(tagHeight / 2) + self['collapsedsize'])
+
+ def interior(self):
+ return self._groupChildSite
--- /dev/null
+import Pmw
+
+_ORIGINAL = 0
+_MODIFIED = 1
+_DISPLAY = 2
+
+class HistoryText(Pmw.ScrolledText):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = (
+ ('compressany', 1, None),
+ ('compresstail', 1, None),
+ ('historycommand', None, None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.ScrolledText.__init__(self, parent)
+
+ # Initialise instance variables.
+ self._list = []
+ self._currIndex = 0
+ self._pastIndex = None
+ self._lastIndex = 0 # pointer to end of history list
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def addhistory(self):
+ text = self.get()
+ if text[-1] == '\n':
+ text = text[:-1]
+
+ if len(self._list) == 0:
+ # This is the first history entry. Add it.
+ self._list.append([text, text, _MODIFIED])
+ return
+
+ currentEntry = self._list[self._currIndex]
+ if text == currentEntry[_ORIGINAL]:
+ # The current history entry has not been modified. Check if
+ # we need to add it again.
+
+ if self['compresstail'] and self._currIndex == self._lastIndex:
+ return
+
+ if self['compressany']:
+ return
+
+ # Undo any changes for the current history entry, since they
+ # will now be available in the new entry.
+ currentEntry[_MODIFIED] = currentEntry[_ORIGINAL]
+
+ historycommand = self['historycommand']
+ if self._currIndex == self._lastIndex:
+ # The last history entry is currently being displayed,
+ # so disable the special meaning of the 'Next' button.
+ self._pastIndex = None
+ nextState = 'disabled'
+ else:
+ # A previous history entry is currently being displayed,
+ # so allow the 'Next' button to go to the entry after this one.
+ self._pastIndex = self._currIndex
+ nextState = 'normal'
+ if callable(historycommand):
+ historycommand('normal', nextState)
+
+ # Create the new history entry.
+ self._list.append([text, text, _MODIFIED])
+
+ # Move the pointer into the history entry list to the end.
+ self._lastIndex = self._lastIndex + 1
+ self._currIndex = self._lastIndex
+
+ def next(self):
+ if self._currIndex == self._lastIndex and self._pastIndex is None:
+ self.bell()
+ else:
+ self._modifyDisplay('next')
+
+ def prev(self):
+ self._pastIndex = None
+ if self._currIndex == 0:
+ self.bell()
+ else:
+ self._modifyDisplay('prev')
+
+ def undo(self):
+ if len(self._list) != 0:
+ self._modifyDisplay('undo')
+
+ def redo(self):
+ if len(self._list) != 0:
+ self._modifyDisplay('redo')
+
+ def gethistory(self):
+ return self._list
+
+ def _modifyDisplay(self, command):
+ # Modify the display to show either the next or previous
+ # history entry (next, prev) or the original or modified
+ # version of the current history entry (undo, redo).
+
+ # Save the currently displayed text.
+ currentText = self.get()
+ if currentText[-1] == '\n':
+ currentText = currentText[:-1]
+
+ currentEntry = self._list[self._currIndex]
+ if currentEntry[_DISPLAY] == _MODIFIED:
+ currentEntry[_MODIFIED] = currentText
+ elif currentEntry[_ORIGINAL] != currentText:
+ currentEntry[_MODIFIED] = currentText
+ if command in ('next', 'prev'):
+ currentEntry[_DISPLAY] = _MODIFIED
+
+ if command in ('next', 'prev'):
+ prevstate = 'normal'
+ nextstate = 'normal'
+ if command == 'next':
+ if self._pastIndex is not None:
+ self._currIndex = self._pastIndex
+ self._pastIndex = None
+ self._currIndex = self._currIndex + 1
+ if self._currIndex == self._lastIndex:
+ nextstate = 'disabled'
+ elif command == 'prev':
+ self._currIndex = self._currIndex - 1
+ if self._currIndex == 0:
+ prevstate = 'disabled'
+ historycommand = self['historycommand']
+ if callable(historycommand):
+ historycommand(prevstate, nextstate)
+ currentEntry = self._list[self._currIndex]
+ else:
+ if command == 'undo':
+ currentEntry[_DISPLAY] = _ORIGINAL
+ elif command == 'redo':
+ currentEntry[_DISPLAY] = _MODIFIED
+
+ # Display the new text.
+ self.delete('1.0', 'end')
+ self.insert('end', currentEntry[currentEntry[_DISPLAY]])
--- /dev/null
+import Tkinter
+import Pmw
+
+class LabeledWidget(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('sticky', 'nsew', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = Pmw.MegaWidget.interior(self)
+ self._labelChildSite = self.createcomponent('labelchildsite',
+ (), None,
+ Tkinter.Frame, (interior,))
+ self._labelChildSite.grid(column=2, row=2, sticky=self['sticky'])
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ self.createlabel(interior)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def interior(self):
+ return self._labelChildSite
--- /dev/null
+# This module is used by the Pmw package system.
+# The PmwLoader class can be used to simulate a python module,
+# but also supports importing of submodules on demand. This technique
+# reduces startup time because Pmw submodules which are not used are
+# not loaded.
+#
+# The PmwLoader class also supports runtime selection of the Pmw
+# version(s) to use.
+
+import sys
+import os
+import string
+import types
+
+_PMW_DEF = 'Pmw.def' # Pmw definition file
+_BASEMODULE = 'Base' # Name of Base module
+
+class PmwLoader:
+
+ def __init__(self, dirpath, instdirs, dirs):
+ self._dirpath = dirpath
+ self._instdirs = instdirs
+ self._dirs = dirs
+ self._initialised = 0
+ self._version = string.replace(instdirs[0][4:], '_', '.')
+ self._alpha_versions = ()
+
+ #======================================================================
+
+ # Public methods. These methods will be seen as "module methods".
+
+ def setversion(self, version):
+ if self._version == version:
+ return
+ if self._initialised:
+ raise ValueError, 'Cannot change Pmw version after initialisation'
+ self._version = version
+
+ def setalphaversions(self, *alpha_versions):
+ if self._alpha_versions == alpha_versions:
+ return
+ if self._initialised:
+ raise ValueError, \
+ 'Cannot change Pmw alpha versions after initialisation'
+ self._alpha_versions = alpha_versions
+
+ def version(self, alpha = 0):
+ if alpha:
+ return self._alpha_versions
+ else:
+ return self._version
+
+ def installedversions(self, alpha = 0):
+ rtn = []
+ if alpha:
+ dirs = filter(lambda x: x[:5] == 'Alpha', self._dirs)
+ dirs.sort()
+ dirs.reverse()
+ for dir in dirs:
+ rtn.append(string.replace(dir[6:], '_', '.'))
+ else:
+ for dir in self._instdirs:
+ rtn.append(string.replace(dir[4:], '_', '.'))
+ return rtn
+
+ #======================================================================
+
+ # Private methods
+
+ def _getmodule(self,modpath):
+ __import__(modpath)
+ mod = sys.modules[modpath]
+ return mod
+
+ def _initialise(self):
+ searchpath = []
+
+ for version in self._alpha_versions:
+ alphadir = '_Pmw.Alpha_%s.lib' % string.replace(version, '.', '_')
+ searchpath.append(alphadir)
+
+ libdir = '_Pmw.Pmw_%s.lib' % string.replace(self._version, '.', '_')
+ searchpath.append(libdir)
+
+ # Create attributes for the PmwBase classes and functions.
+ for path in searchpath:
+ try:
+ basemodule = self._getmodule(path + '.Pmw' + _BASEMODULE)
+ break
+ except ImportError, msg:
+ if path == searchpath[-1]:
+ # No PmwBase module found.
+ raise ImportError, msg
+
+ for k,v in basemodule.__dict__.items():
+ if k[0] is not '_' and type(v) != types.ModuleType:
+ self.__dict__[k] = v
+
+ # Set the Pmw definitions from the Pmw.def file.
+ dict = {
+ '_widgets' : {},
+ '_extraWidgets' : {},
+ '_functions' : {},
+ '_modules' : {},
+ }
+ for name in dict.keys():
+ self.__dict__[name] = {}
+ searchpath.reverse()
+ for path in searchpath:
+ pathbit = apply(os.path.join, tuple(string.split(path[5:], '.')))
+ lpath = os.path.join(self._dirpath, pathbit)
+ d = {}
+ execfile(os.path.join(lpath,_PMW_DEF), d)
+ for k,v in d.items():
+ if dict.has_key(k):
+ if type(v) == types.TupleType:
+ for item in v:
+ modpath = path + '.Pmw' + item
+ dict[k][item] = modpath
+ elif type(v) == types.DictionaryType:
+ for k1, v1 in v.items():
+ modpath = path + '.Pmw' + v1
+ dict[k][k1] = modpath
+ self.__dict__.update(dict)
+ self._widgets_keys = self._widgets.keys()
+ self._extraWidgets_keys = self._extraWidgets.keys()
+ self._functions_keys = self._functions.keys()
+ self._modules_keys = self._modules.keys()
+
+ self._initialised = 1
+
+ def __getattr__(self, name):
+ if not self._initialised:
+ self._initialise()
+ # Beware: _initialise may have defined 'name'
+ if name in self.__dict__.keys():
+ return self.__dict__[name]
+
+ # The requested attribute is not yet set. Look it up in the
+ # tables set by Pmw.def, import the appropriate module and
+ # set the attribute so that it will be found next time.
+
+ if name in self._widgets_keys:
+ # The attribute is a widget name.
+ mod = self._getmodule(self._widgets[name])
+ cls = getattr(mod,name)
+ self.__dict__[name] = cls
+ return cls
+
+ if name in self._functions_keys:
+ # The attribute is a function from one of the modules.
+ modname = self._functions[name]
+ mod = self._getmodule(modname)
+ func = getattr(mod, name)
+ self.__dict__[name] = func
+ return func
+
+ if name in self._modules_keys:
+ # The attribute is a module
+ mod = self._getmodule(self._modules[name])
+ self.__dict__[name] = mod
+ return mod
+
+ if name in self._extraWidgets_keys:
+ # XXX I should import them all, once I've started.
+ # The attribute is a widget name in a module of another name
+ modname = self._extraWidgets[name]
+ mod = self._getmodule(modname)
+ cls = getattr(mod, name)
+ self.__dict__[name] = cls
+ return cls
+
+ # The attribute is not known by Pmw, report an error.
+ raise AttributeError, name
--- /dev/null
+import os
+import string
+
+def _font_initialise(root, size=None, fontScheme = None):
+ global _fontSize
+ if size is not None:
+ _fontSize = size
+
+ if fontScheme in ('pmw1', 'pmw2'):
+ if os.name == 'posix':
+ defaultFont = logicalfont('Helvetica')
+ menuFont = logicalfont('Helvetica', weight='bold', slant='italic')
+ scaleFont = logicalfont('Helvetica', slant='italic')
+ root.option_add('*Font', defaultFont, 'userDefault')
+ root.option_add('*Menu*Font', menuFont, 'userDefault')
+ root.option_add('*Menubutton*Font', menuFont, 'userDefault')
+ root.option_add('*Scale.*Font', scaleFont, 'userDefault')
+
+ if fontScheme == 'pmw1':
+ balloonFont = logicalfont('Helvetica', -6, pixel = '12')
+ else: # fontScheme == 'pmw2'
+ balloonFont = logicalfont('Helvetica', -2)
+ root.option_add('*Balloon.*Font', balloonFont, 'userDefault')
+ else:
+ defaultFont = logicalfont('Helvetica')
+ root.option_add('*Font', defaultFont, 'userDefault')
+ elif fontScheme == 'default':
+ defaultFont = ('Helvetica', '-%d' % (_fontSize,), 'bold')
+ entryFont = ('Helvetica', '-%d' % (_fontSize,))
+ textFont = ('Courier', '-%d' % (_fontSize,))
+ root.option_add('*Font', defaultFont, 'userDefault')
+ root.option_add('*Entry*Font', entryFont, 'userDefault')
+ root.option_add('*Text*Font', textFont, 'userDefault')
+
+def logicalfont(name='Helvetica', sizeIncr = 0, **kw):
+ if not _fontInfo.has_key(name):
+ raise ValueError, 'font %s does not exist' % name
+
+ rtn = []
+ for field in _fontFields:
+ if kw.has_key(field):
+ logicalValue = kw[field]
+ elif _fontInfo[name].has_key(field):
+ logicalValue = _fontInfo[name][field]
+ else:
+ logicalValue = '*'
+
+ if _propertyAliases[name].has_key((field, logicalValue)):
+ realValue = _propertyAliases[name][(field, logicalValue)]
+ elif _propertyAliases[name].has_key((field, None)):
+ realValue = _propertyAliases[name][(field, None)]
+ elif _propertyAliases[None].has_key((field, logicalValue)):
+ realValue = _propertyAliases[None][(field, logicalValue)]
+ elif _propertyAliases[None].has_key((field, None)):
+ realValue = _propertyAliases[None][(field, None)]
+ else:
+ realValue = logicalValue
+
+ if field == 'size':
+ if realValue == '*':
+ realValue = _fontSize
+ realValue = str((realValue + sizeIncr) * 10)
+
+ rtn.append(realValue)
+
+ return string.join(rtn, '-')
+
+def logicalfontnames():
+ return _fontInfo.keys()
+
+if os.name == 'nt':
+ _fontSize = 16
+else:
+ _fontSize = 14
+
+_fontFields = (
+ 'registry', 'foundry', 'family', 'weight', 'slant', 'width', 'style',
+ 'pixel', 'size', 'xres', 'yres', 'spacing', 'avgwidth', 'charset', 'encoding')
+
+# <_propertyAliases> defines other names for which property values may
+# be known by. This is required because italics in adobe-helvetica
+# are specified by 'o', while other fonts use 'i'.
+
+_propertyAliases = {}
+
+_propertyAliases[None] = {
+ ('slant', 'italic') : 'i',
+ ('slant', 'normal') : 'r',
+ ('weight', 'light') : 'normal',
+ ('width', 'wide') : 'normal',
+ ('width', 'condensed') : 'normal',
+}
+
+# <_fontInfo> describes a 'logical' font, giving the default values of
+# some of its properties.
+
+_fontInfo = {}
+
+_fontInfo['Helvetica'] = {
+ 'foundry' : 'adobe',
+ 'family' : 'helvetica',
+ 'registry' : '',
+ 'charset' : 'iso8859',
+ 'encoding' : '1',
+ 'spacing' : 'p',
+ 'slant' : 'normal',
+ 'width' : 'normal',
+ 'weight' : 'normal',
+}
+
+_propertyAliases['Helvetica'] = {
+ ('slant', 'italic') : 'o',
+ ('weight', 'normal') : 'medium',
+ ('weight', 'light') : 'medium',
+}
+
+_fontInfo['Times'] = {
+ 'foundry' : 'adobe',
+ 'family' : 'times',
+ 'registry' : '',
+ 'charset' : 'iso8859',
+ 'encoding' : '1',
+ 'spacing' : 'p',
+ 'slant' : 'normal',
+ 'width' : 'normal',
+ 'weight' : 'normal',
+}
+
+_propertyAliases['Times'] = {
+ ('weight', 'normal') : 'medium',
+ ('weight', 'light') : 'medium',
+}
+
+_fontInfo['Fixed'] = {
+ 'foundry' : 'misc',
+ 'family' : 'fixed',
+ 'registry' : '',
+ 'charset' : 'iso8859',
+ 'encoding' : '1',
+ 'spacing' : 'c',
+ 'slant' : 'normal',
+ 'width' : 'normal',
+ 'weight' : 'normal',
+}
+
+_propertyAliases['Fixed'] = {
+ ('weight', 'normal') : 'medium',
+ ('weight', 'light') : 'medium',
+ ('style', None) : '',
+ ('width', 'condensed') : 'semicondensed',
+}
+
+_fontInfo['Courier'] = {
+ 'foundry' : 'adobe',
+ 'family' : 'courier',
+ 'registry' : '',
+ 'charset' : 'iso8859',
+ 'encoding' : '1',
+ 'spacing' : 'm',
+ 'slant' : 'normal',
+ 'width' : 'normal',
+ 'weight' : 'normal',
+}
+
+_propertyAliases['Courier'] = {
+ ('weight', 'normal') : 'medium',
+ ('weight', 'light') : 'medium',
+ ('style', None) : '',
+}
+
+_fontInfo['Typewriter'] = {
+ 'foundry' : 'b&h',
+ 'family' : 'lucidatypewriter',
+ 'registry' : '',
+ 'charset' : 'iso8859',
+ 'encoding' : '1',
+ 'spacing' : 'm',
+ 'slant' : 'normal',
+ 'width' : 'normal',
+ 'weight' : 'normal',
+}
+
+_propertyAliases['Typewriter'] = {
+ ('weight', 'normal') : 'medium',
+ ('weight', 'light') : 'medium',
+}
+
+if os.name == 'nt':
+ # For some reason 'fixed' fonts on NT aren't.
+ _fontInfo['Fixed'] = _fontInfo['Courier']
+ _propertyAliases['Fixed'] = _propertyAliases['Courier']
--- /dev/null
+# Main menubar
+
+import string
+import types
+import Tkinter
+import Pmw
+
+class MainMenuBar(Pmw.MegaArchetype):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('balloon', None, None),
+ ('hotkeys', 1, INITOPT),
+ ('hull_tearoff', 0, None),
+ )
+ self.defineoptions(kw, optiondefs, dynamicGroups = ('Menu',))
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaArchetype.__init__(self, parent, Tkinter.Menu)
+
+ self._menuInfo = {}
+ self._menuInfo[None] = (None, [])
+ # Map from a menu name to a tuple of information about the menu.
+ # The first item in the tuple is the name of the parent menu (for
+ # toplevel menus this is None). The second item in the tuple is
+ # a list of status help messages for each item in the menu.
+ # The key for the information for the main menubar is None.
+
+ self._menu = self.interior()
+ self._menu.bind('<Leave>', self._resetHelpmessage)
+ self._menu.bind('<Motion>',
+ lambda event=None, self=self: self._menuHelp(event, None))
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def deletemenuitems(self, menuName, start, end = None):
+ self.component(menuName).delete(start, end)
+ if end is None:
+ del self._menuInfo[menuName][1][start]
+ else:
+ self._menuInfo[menuName][1][start:end+1] = []
+
+ def deletemenu(self, menuName):
+ """Delete should be called for cascaded menus before main menus.
+ """
+
+ parentName = self._menuInfo[menuName][0]
+ del self._menuInfo[menuName]
+ if parentName is None:
+ parentMenu = self._menu
+ else:
+ parentMenu = self.component(parentName)
+
+ menu = self.component(menuName)
+ menuId = str(menu)
+ for item in range(parentMenu.index('end') + 1):
+ if parentMenu.type(item) == 'cascade':
+ itemMenu = str(parentMenu.entrycget(item, 'menu'))
+ if itemMenu == menuId:
+ parentMenu.delete(item)
+ del self._menuInfo[parentName][1][item]
+ break
+
+ self.destroycomponent(menuName)
+
+ def disableall(self):
+ for index in range(len(self._menuInfo[None][1])):
+ self.entryconfigure(index, state = 'disabled')
+
+ def enableall(self):
+ for index in range(len(self._menuInfo[None][1])):
+ self.entryconfigure(index, state = 'normal')
+
+ def addmenu(self, menuName, balloonHelp, statusHelp = None,
+ traverseSpec = None, **kw):
+ if statusHelp is None:
+ statusHelp = balloonHelp
+ self._addmenu(None, menuName, balloonHelp, statusHelp,
+ traverseSpec, kw)
+
+ def addcascademenu(self, parentMenuName, menuName, statusHelp='',
+ traverseSpec = None, **kw):
+ self._addmenu(parentMenuName, menuName, None, statusHelp,
+ traverseSpec, kw)
+
+ def _addmenu(self, parentMenuName, menuName, balloonHelp, statusHelp,
+ traverseSpec, kw):
+
+ if (menuName) in self.components():
+ raise ValueError, 'menu "%s" already exists' % menuName
+
+ menukw = {}
+ if kw.has_key('tearoff'):
+ menukw['tearoff'] = kw['tearoff']
+ del kw['tearoff']
+ else:
+ menukw['tearoff'] = 0
+ if kw.has_key('name'):
+ menukw['name'] = kw['name']
+ del kw['name']
+
+ if not kw.has_key('label'):
+ kw['label'] = menuName
+
+ self._addHotkeyToOptions(parentMenuName, kw, traverseSpec)
+
+ if parentMenuName is None:
+ parentMenu = self._menu
+ balloon = self['balloon']
+ # Bug in Tk: balloon help not implemented
+ # if balloon is not None:
+ # balloon.mainmenubind(parentMenu, balloonHelp, statusHelp)
+ else:
+ parentMenu = self.component(parentMenuName)
+
+ apply(parentMenu.add_cascade, (), kw)
+
+ menu = apply(self.createcomponent, (menuName,
+ (), 'Menu',
+ Tkinter.Menu, (parentMenu,)), menukw)
+ parentMenu.entryconfigure('end', menu = menu)
+
+ self._menuInfo[parentMenuName][1].append(statusHelp)
+ self._menuInfo[menuName] = (parentMenuName, [])
+
+ menu.bind('<Leave>', self._resetHelpmessage)
+ menu.bind('<Motion>',
+ lambda event=None, self=self, menuName=menuName:
+ self._menuHelp(event, menuName))
+
+ def addmenuitem(self, menuName, itemType, statusHelp = '',
+ traverseSpec = None, **kw):
+
+ menu = self.component(menuName)
+ if itemType != 'separator':
+ self._addHotkeyToOptions(menuName, kw, traverseSpec)
+
+ if itemType == 'command':
+ command = menu.add_command
+ elif itemType == 'separator':
+ command = menu.add_separator
+ elif itemType == 'checkbutton':
+ command = menu.add_checkbutton
+ elif itemType == 'radiobutton':
+ command = menu.add_radiobutton
+ elif itemType == 'cascade':
+ command = menu.add_cascade
+ else:
+ raise ValueError, 'unknown menuitem type "%s"' % itemType
+
+ self._menuInfo[menuName][1].append(statusHelp)
+ apply(command, (), kw)
+
+ def _addHotkeyToOptions(self, menuName, kw, traverseSpec):
+
+ if (not self['hotkeys'] or kw.has_key('underline') or
+ not kw.has_key('label')):
+ return
+
+ if type(traverseSpec) == types.IntType:
+ kw['underline'] = traverseSpec
+ return
+
+ if menuName is None:
+ menu = self._menu
+ else:
+ menu = self.component(menuName)
+ hotkeyList = []
+ end = menu.index('end')
+ if end is not None:
+ for item in range(end + 1):
+ if menu.type(item) not in ('separator', 'tearoff'):
+ underline = \
+ string.atoi(str(menu.entrycget(item, 'underline')))
+ if underline != -1:
+ label = str(menu.entrycget(item, 'label'))
+ if underline < len(label):
+ hotkey = string.lower(label[underline])
+ if hotkey not in hotkeyList:
+ hotkeyList.append(hotkey)
+
+ name = kw['label']
+
+ if type(traverseSpec) == types.StringType:
+ lowerLetter = string.lower(traverseSpec)
+ if traverseSpec in name and lowerLetter not in hotkeyList:
+ kw['underline'] = string.index(name, traverseSpec)
+ else:
+ targets = string.digits + string.letters
+ lowerName = string.lower(name)
+ for letter_index in range(len(name)):
+ letter = lowerName[letter_index]
+ if letter in targets and letter not in hotkeyList:
+ kw['underline'] = letter_index
+ break
+
+ def _menuHelp(self, event, menuName):
+ if menuName is None:
+ menu = self._menu
+ index = menu.index('@%d'% event.x)
+ else:
+ menu = self.component(menuName)
+ index = menu.index('@%d'% event.y)
+
+ balloon = self['balloon']
+ if balloon is not None:
+ if index is None:
+ balloon.showstatus('')
+ else:
+ if str(menu.cget('tearoff')) == '1':
+ index = index - 1
+ if index >= 0:
+ help = self._menuInfo[menuName][1][index]
+ balloon.showstatus(help)
+
+ def _resetHelpmessage(self, event=None):
+ balloon = self['balloon']
+ if balloon is not None:
+ balloon.clearstatus()
+
+Pmw.forwardmethods(MainMenuBar, Tkinter.Menu, '_hull')
--- /dev/null
+# Manager widget for menus.
+
+import string
+import types
+import Tkinter
+import Pmw
+
+class MenuBar(Pmw.MegaWidget):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('balloon', None, None),
+ ('hotkeys', 1, INITOPT),
+ ('padx', 0, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs, dynamicGroups = ('Menu', 'Button'))
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ self._menuInfo = {}
+ # Map from a menu name to a tuple of information about the menu.
+ # The first item in the tuple is the name of the parent menu (for
+ # toplevel menus this is None). The second item in the tuple is
+ # a list of status help messages for each item in the menu.
+ # The third item in the tuple is the id of the binding used
+ # to detect mouse motion to display status help.
+ # Information for the toplevel menubuttons is not stored here.
+
+ self._mydeletecommand = self.component('hull').tk.deletecommand
+ # Cache this method for use later.
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def deletemenuitems(self, menuName, start, end = None):
+ self.component(menuName + '-menu').delete(start, end)
+ if end is None:
+ del self._menuInfo[menuName][1][start]
+ else:
+ self._menuInfo[menuName][1][start:end+1] = []
+
+ def deletemenu(self, menuName):
+ """Delete should be called for cascaded menus before main menus.
+ """
+
+ # Clean up binding for this menu.
+ parentName = self._menuInfo[menuName][0]
+ bindId = self._menuInfo[menuName][2]
+ _bindtag = 'PmwMenuBar' + str(self) + menuName
+ self.unbind_class(_bindtag, '<Motion>')
+ self._mydeletecommand(bindId) # unbind_class does not clean up
+ del self._menuInfo[menuName]
+
+ if parentName is None:
+ self.destroycomponent(menuName + '-button')
+ else:
+ parentMenu = self.component(parentName + '-menu')
+
+ menu = self.component(menuName + '-menu')
+ menuId = str(menu)
+ for item in range(parentMenu.index('end') + 1):
+ if parentMenu.type(item) == 'cascade':
+ itemMenu = str(parentMenu.entrycget(item, 'menu'))
+ if itemMenu == menuId:
+ parentMenu.delete(item)
+ del self._menuInfo[parentName][1][item]
+ break
+
+ self.destroycomponent(menuName + '-menu')
+
+ def disableall(self):
+ for menuName in self._menuInfo.keys():
+ if self._menuInfo[menuName][0] is None:
+ menubutton = self.component(menuName + '-button')
+ menubutton.configure(state = 'disabled')
+
+ def enableall(self):
+ for menuName in self._menuInfo.keys():
+ if self._menuInfo[menuName][0] is None:
+ menubutton = self.component(menuName + '-button')
+ menubutton.configure(state = 'normal')
+
+ def addmenu(self, menuName, balloonHelp, statusHelp = None,
+ side = 'left', traverseSpec = None, **kw):
+
+ self._addmenu(None, menuName, balloonHelp, statusHelp,
+ traverseSpec, side, 'text', kw)
+
+ def addcascademenu(self, parentMenuName, menuName, statusHelp = '',
+ traverseSpec = None, **kw):
+
+ self._addmenu(parentMenuName, menuName, None, statusHelp,
+ traverseSpec, None, 'label', kw)
+
+ def _addmenu(self, parentMenuName, menuName, balloonHelp, statusHelp,
+ traverseSpec, side, textKey, kw):
+
+ if (menuName + '-menu') in self.components():
+ raise ValueError, 'menu "%s" already exists' % menuName
+
+ menukw = {}
+ if kw.has_key('tearoff'):
+ menukw['tearoff'] = kw['tearoff']
+ del kw['tearoff']
+ else:
+ menukw['tearoff'] = 0
+
+ if not kw.has_key(textKey):
+ kw[textKey] = menuName
+
+ self._addHotkeyToOptions(parentMenuName, kw, textKey, traverseSpec)
+
+ if parentMenuName is None:
+ button = apply(self.createcomponent, (menuName + '-button',
+ (), 'Button',
+ Tkinter.Menubutton, (self.interior(),)), kw)
+ button.pack(side=side, padx = self['padx'])
+ balloon = self['balloon']
+ if balloon is not None:
+ balloon.bind(button, balloonHelp, statusHelp)
+ parentMenu = button
+ else:
+ parentMenu = self.component(parentMenuName + '-menu')
+ apply(parentMenu.add_cascade, (), kw)
+ self._menuInfo[parentMenuName][1].append(statusHelp)
+
+ menu = apply(self.createcomponent, (menuName + '-menu',
+ (), 'Menu',
+ Tkinter.Menu, (parentMenu,)), menukw)
+ if parentMenuName is None:
+ button.configure(menu = menu)
+ else:
+ parentMenu.entryconfigure('end', menu = menu)
+
+ # Need to put this binding after the class bindings so that
+ # menu.index() does not lag behind.
+ _bindtag = 'PmwMenuBar' + str(self) + menuName
+ bindId = self.bind_class(_bindtag, '<Motion>',
+ lambda event=None, self=self, menuName=menuName:
+ self._menuHelp(menuName))
+ menu.bindtags(menu.bindtags() + (_bindtag,))
+ menu.bind('<Leave>', self._resetHelpmessage)
+
+ self._menuInfo[menuName] = (parentMenuName, [], bindId)
+
+ def addmenuitem(self, menuName, itemType, statusHelp = '',
+ traverseSpec = None, **kw):
+
+ menu = self.component(menuName + '-menu')
+ if itemType != 'separator':
+ self._addHotkeyToOptions(menuName, kw, 'label', traverseSpec)
+
+ if itemType == 'command':
+ command = menu.add_command
+ elif itemType == 'separator':
+ command = menu.add_separator
+ elif itemType == 'checkbutton':
+ command = menu.add_checkbutton
+ elif itemType == 'radiobutton':
+ command = menu.add_radiobutton
+ elif itemType == 'cascade':
+ command = menu.add_cascade
+ else:
+ raise ValueError, 'unknown menuitem type "%s"' % itemType
+
+ self._menuInfo[menuName][1].append(statusHelp)
+ apply(command, (), kw)
+
+ def _addHotkeyToOptions(self, menuName, kw, textKey, traverseSpec):
+
+ if (not self['hotkeys'] or kw.has_key('underline') or
+ not kw.has_key(textKey)):
+ return
+
+ if type(traverseSpec) == types.IntType:
+ kw['underline'] = traverseSpec
+ return
+
+ hotkeyList = []
+ if menuName is None:
+ for menuName in self._menuInfo.keys():
+ if self._menuInfo[menuName][0] is None:
+ menubutton = self.component(menuName + '-button')
+ underline = string.atoi(str(menubutton.cget('underline')))
+ if underline != -1:
+ label = str(menubutton.cget(textKey))
+ if underline < len(label):
+ hotkey = string.lower(label[underline])
+ if hotkey not in hotkeyList:
+ hotkeyList.append(hotkey)
+ else:
+ menu = self.component(menuName + '-menu')
+ end = menu.index('end')
+ if end is not None:
+ for item in range(end + 1):
+ if menu.type(item) not in ('separator', 'tearoff'):
+ underline = string.atoi(
+ str(menu.entrycget(item, 'underline')))
+ if underline != -1:
+ label = str(menu.entrycget(item, textKey))
+ if underline < len(label):
+ hotkey = string.lower(label[underline])
+ if hotkey not in hotkeyList:
+ hotkeyList.append(hotkey)
+
+ name = kw[textKey]
+
+ if type(traverseSpec) == types.StringType:
+ lowerLetter = string.lower(traverseSpec)
+ if traverseSpec in name and lowerLetter not in hotkeyList:
+ kw['underline'] = string.index(name, traverseSpec)
+ else:
+ targets = string.digits + string.letters
+ lowerName = string.lower(name)
+ for letter_index in range(len(name)):
+ letter = lowerName[letter_index]
+ if letter in targets and letter not in hotkeyList:
+ kw['underline'] = letter_index
+ break
+
+ def _menuHelp(self, menuName):
+ menu = self.component(menuName + '-menu')
+ index = menu.index('active')
+
+ balloon = self['balloon']
+ if balloon is not None:
+ if index is None:
+ balloon.showstatus('')
+ else:
+ if str(menu.cget('tearoff')) == '1':
+ index = index - 1
+ if index >= 0:
+ help = self._menuInfo[menuName][1][index]
+ balloon.showstatus(help)
+
+ def _resetHelpmessage(self, event=None):
+ balloon = self['balloon']
+ if balloon is not None:
+ balloon.clearstatus()
--- /dev/null
+# Class to display messages in an information line.
+
+import string
+import Tkinter
+import Pmw
+
+class MessageBar(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ defaultMessageTypes = {
+ # (priority, showtime, bells, logmessage)
+ 'systemerror' : (5, 10, 2, 1),
+ 'usererror' : (4, 5, 1, 0),
+ 'busy' : (3, 0, 0, 0),
+ 'systemevent' : (2, 5, 0, 0),
+ 'userevent' : (2, 5, 0, 0),
+ 'help' : (1, 5, 0, 0),
+ 'state' : (0, 0, 0, 0),
+ }
+ optiondefs = (
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('messagetypes', defaultMessageTypes, INITOPT),
+ ('silent', 0, None),
+ ('sticky', 'ew', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ self._messageBarEntry = self.createcomponent('entry',
+ (), None,
+ Tkinter.Entry, (interior,))
+
+ # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+ try:
+ self._messageBarEntry.configure(state = 'readonly')
+ except Tkinter.TclError:
+ self._messageBarEntry.configure(state = 'disabled')
+
+ self._messageBarEntry.grid(column=2, row=2, sticky=self['sticky'])
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ self.createlabel(interior)
+
+ # Initialise instance variables.
+ self._numPriorities = 0
+ for info in self['messagetypes'].values():
+ if self._numPriorities < info[0]:
+ self._numPriorities = info[0]
+
+ self._numPriorities = self._numPriorities + 1
+ self._timer = [None] * self._numPriorities
+ self._messagetext = [''] * self._numPriorities
+ self._activemessage = [0] * self._numPriorities
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ for timerId in self._timer:
+ if timerId is not None:
+ self.after_cancel(timerId)
+ self._timer = [None] * self._numPriorities
+ Pmw.MegaWidget.destroy(self)
+
+ def message(self, type, text):
+ # Display a message in the message bar.
+
+ (priority, showtime, bells, logmessage) = self['messagetypes'][type]
+
+ if not self['silent']:
+ for i in range(bells):
+ if i != 0:
+ self.after(100)
+ self.bell()
+
+ self._activemessage[priority] = 1
+ if text is None:
+ text = ''
+ self._messagetext[priority] = string.replace(text, '\n', ' ')
+ self._redisplayInfoMessage()
+
+ if logmessage:
+ # Should log this text to a text widget.
+ pass
+
+ if showtime > 0:
+ if self._timer[priority] is not None:
+ self.after_cancel(self._timer[priority])
+
+ # Define a callback to clear this message after a time.
+ def _clearmessage(self=self, priority=priority):
+ self._clearActivemessage(priority)
+
+ mseconds = int(showtime * 1000)
+ self._timer[priority] = self.after(mseconds, _clearmessage)
+
+ def helpmessage(self, text):
+ if text is None:
+ self.resetmessages('help')
+ else:
+ self.message('help', text)
+
+ def resetmessages(self, type):
+ priority = self['messagetypes'][type][0]
+ self._clearActivemessage(priority)
+ for messagetype, info in self['messagetypes'].items():
+ thisPriority = info[0]
+ showtime = info[1]
+ if thisPriority < priority and showtime != 0:
+ self._clearActivemessage(thisPriority)
+
+ def _clearActivemessage(self, priority):
+ self._activemessage[priority] = 0
+ if self._timer[priority] is not None:
+ self.after_cancel(self._timer[priority])
+ self._timer[priority] = None
+ self._redisplayInfoMessage()
+
+ def _redisplayInfoMessage(self):
+ text = ''
+ for priority in range(self._numPriorities - 1, -1, -1):
+ if self._activemessage[priority]:
+ text = self._messagetext[priority]
+ break
+ self._messageBarEntry.configure(state = 'normal')
+ self._messageBarEntry.delete(0, 'end')
+ self._messageBarEntry.insert('end', text)
+
+ # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+ try:
+ self._messageBarEntry.configure(state = 'readonly')
+ except Tkinter.TclError:
+ self._messageBarEntry.configure(state = 'disabled')
+
+Pmw.forwardmethods(MessageBar, Tkinter.Entry, '_messageBarEntry')
--- /dev/null
+# Based on iwidgets2.2.0/messagedialog.itk code.
+
+import Tkinter
+import Pmw
+
+class MessageDialog(Pmw.Dialog):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderx', 20, INITOPT),
+ ('bordery', 20, INITOPT),
+ ('iconmargin', 20, INITOPT),
+ ('iconpos', None, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.Dialog.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ self._message = self.createcomponent('message',
+ (), None,
+ Tkinter.Label, (interior,))
+
+ iconpos = self['iconpos']
+ iconmargin = self['iconmargin']
+ borderx = self['borderx']
+ bordery = self['bordery']
+ border_right = 2
+ border_bottom = 2
+ if iconpos is None:
+ self._message.grid(column = 1, row = 1)
+ else:
+ self._icon = self.createcomponent('icon',
+ (), None,
+ Tkinter.Label, (interior,))
+ if iconpos not in 'nsew':
+ raise ValueError, \
+ 'bad iconpos option "%s": should be n, s, e, or w' \
+ % iconpos
+
+ if iconpos in 'nw':
+ icon = 1
+ message = 3
+ else:
+ icon = 3
+ message = 1
+
+ if iconpos in 'ns':
+ # vertical layout
+ self._icon.grid(column = 1, row = icon)
+ self._message.grid(column = 1, row = message)
+ interior.grid_rowconfigure(2, minsize = iconmargin)
+ border_bottom = 4
+ else:
+ # horizontal layout
+ self._icon.grid(column = icon, row = 1)
+ self._message.grid(column = message, row = 1)
+ interior.grid_columnconfigure(2, minsize = iconmargin)
+ border_right = 4
+
+ interior.grid_columnconfigure(0, minsize = borderx)
+ interior.grid_rowconfigure(0, minsize = bordery)
+ interior.grid_columnconfigure(border_right, minsize = borderx)
+ interior.grid_rowconfigure(border_bottom, minsize = bordery)
+
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
--- /dev/null
+import string
+import types
+import Tkinter
+import Pmw
+
+class NoteBook(Pmw.MegaArchetype):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('hull_highlightthickness', 0, None),
+ ('hull_borderwidth', 0, None),
+ ('arrownavigation', 1, INITOPT),
+ ('borderwidth', 2, INITOPT),
+ ('createcommand', None, None),
+ ('lowercommand', None, None),
+ ('pagemargin', 4, INITOPT),
+ ('raisecommand', None, None),
+ ('tabpos', 'n', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs, dynamicGroups = ('Page', 'Tab'))
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaArchetype.__init__(self, parent, Tkinter.Canvas)
+
+ self.bind('<Map>', self._handleMap)
+ self.bind('<Configure>', self._handleConfigure)
+
+ tabpos = self['tabpos']
+ if tabpos is not None and tabpos != 'n':
+ raise ValueError, \
+ 'bad tabpos option %s: should be n or None' % repr(tabpos)
+ self._withTabs = (tabpos is not None)
+ self._pageMargin = self['pagemargin']
+ self._borderWidth = self['borderwidth']
+
+ # Use a dictionary as a set of bits indicating what needs to
+ # be redisplayed the next time _layout() is called. If
+ # dictionary contains 'topPage' key, the value is the new top
+ # page to be displayed. None indicates that all pages have
+ # been deleted and that _layout() should draw a border under where
+ # the tabs should be.
+ self._pending = {}
+ self._pending['size'] = 1
+ self._pending['borderColor'] = 1
+ self._pending['topPage'] = None
+ if self._withTabs:
+ self._pending['tabs'] = 1
+
+ self._canvasSize = None # This gets set by <Configure> events
+
+ # Set initial height of space for tabs
+ if self._withTabs:
+ self.tabBottom = 35
+ else:
+ self.tabBottom = 0
+
+ self._lightBorderColor, self._darkBorderColor = \
+ Pmw.Color.bordercolors(self, self['hull_background'])
+
+ self._pageNames = [] # List of page names
+
+ # Map from page name to page info. Each item is itself a
+ # dictionary containing the following items:
+ # page the Tkinter.Frame widget for the page
+ # created set to true the first time the page is raised
+ # tabbutton the Tkinter.Button widget for the button (if any)
+ # tabreqwidth requested width of the tab
+ # tabreqheight requested height of the tab
+ # tabitems the canvas items for the button: the button
+ # window item, the lightshadow and the darkshadow
+ # left the left and right canvas coordinates of the tab
+ # right
+ self._pageAttrs = {}
+
+ # Name of page currently on top (actually displayed, using
+ # create_window, not pending). Ignored if current top page
+ # has been deleted or new top page is pending. None indicates
+ # no pages in notebook.
+ self._topPageName = None
+
+ # Canvas items used:
+ # Per tab:
+ # top and left shadow
+ # right shadow
+ # button
+ # Per notebook:
+ # page
+ # top page
+ # left shadow
+ # bottom and right shadow
+ # top (one or two items)
+
+ # Canvas tags used:
+ # lighttag - top and left shadows of tabs and page
+ # darktag - bottom and right shadows of tabs and page
+ # (if no tabs then these are reversed)
+ # (used to color the borders by recolorborders)
+
+ # Create page border shadows.
+ if self._withTabs:
+ self._pageLeftBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+ fill = self._lightBorderColor, tags = 'lighttag')
+ self._pageBottomRightBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+ fill = self._darkBorderColor, tags = 'darktag')
+ self._pageTop1Border = self.create_polygon(0, 0, 0, 0, 0, 0,
+ fill = self._darkBorderColor, tags = 'lighttag')
+ self._pageTop2Border = self.create_polygon(0, 0, 0, 0, 0, 0,
+ fill = self._darkBorderColor, tags = 'lighttag')
+ else:
+ self._pageLeftBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+ fill = self._darkBorderColor, tags = 'darktag')
+ self._pageBottomRightBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+ fill = self._lightBorderColor, tags = 'lighttag')
+ self._pageTopBorder = self.create_polygon(0, 0, 0, 0, 0, 0,
+ fill = self._darkBorderColor, tags = 'darktag')
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def insert(self, pageName, before = 0, **kw):
+ if self._pageAttrs.has_key(pageName):
+ msg = 'Page "%s" already exists.' % pageName
+ raise ValueError, msg
+
+ # Do this early to catch bad <before> spec before creating any items.
+ beforeIndex = self.index(before, 1)
+
+ pageOptions = {}
+ if self._withTabs:
+ # Default tab button options.
+ tabOptions = {
+ 'text' : pageName,
+ 'borderwidth' : 0,
+ }
+
+ # Divide the keyword options into the 'page_' and 'tab_' options.
+ for key in kw.keys():
+ if key[:5] == 'page_':
+ pageOptions[key[5:]] = kw[key]
+ del kw[key]
+ elif self._withTabs and key[:4] == 'tab_':
+ tabOptions[key[4:]] = kw[key]
+ del kw[key]
+ else:
+ raise KeyError, 'Unknown option "' + key + '"'
+
+ # Create the frame to contain the page.
+ page = apply(self.createcomponent, (pageName,
+ (), 'Page',
+ Tkinter.Frame, self._hull), pageOptions)
+
+ attributes = {}
+ attributes['page'] = page
+ attributes['created'] = 0
+
+ if self._withTabs:
+ # Create the button for the tab.
+ def raiseThisPage(self = self, pageName = pageName):
+ self.selectpage(pageName)
+ tabOptions['command'] = raiseThisPage
+ tab = apply(self.createcomponent, (pageName + '-tab',
+ (), 'Tab',
+ Tkinter.Button, self._hull), tabOptions)
+
+ if self['arrownavigation']:
+ # Allow the use of the arrow keys for Tab navigation:
+ def next(event, self = self, pageName = pageName):
+ self.nextpage(pageName)
+ def prev(event, self = self, pageName = pageName):
+ self.previouspage(pageName)
+ tab.bind('<Left>', prev)
+ tab.bind('<Right>', next)
+
+ attributes['tabbutton'] = tab
+ attributes['tabreqwidth'] = tab.winfo_reqwidth()
+ attributes['tabreqheight'] = tab.winfo_reqheight()
+
+ # Create the canvas item to manage the tab's button and the items
+ # for the tab's shadow.
+ windowitem = self.create_window(0, 0, window = tab, anchor = 'nw')
+ lightshadow = self.create_polygon(0, 0, 0, 0, 0, 0,
+ tags = 'lighttag', fill = self._lightBorderColor)
+ darkshadow = self.create_polygon(0, 0, 0, 0, 0, 0,
+ tags = 'darktag', fill = self._darkBorderColor)
+ attributes['tabitems'] = (windowitem, lightshadow, darkshadow)
+ self._pending['tabs'] = 1
+
+ self._pageAttrs[pageName] = attributes
+ self._pageNames.insert(beforeIndex, pageName)
+
+ # If this is the first page added, make it the new top page
+ # and call the create and raise callbacks.
+ if self.getcurselection() is None:
+ self._pending['topPage'] = pageName
+ self._raiseNewTop(pageName)
+
+ self._layout()
+ return page
+
+ def add(self, pageName, **kw):
+ return apply(self.insert, (pageName, len(self._pageNames)), kw)
+
+ def delete(self, *pageNames):
+ newTopPage = 0
+ for page in pageNames:
+ pageIndex = self.index(page)
+ pageName = self._pageNames[pageIndex]
+ pageInfo = self._pageAttrs[pageName]
+
+ if self.getcurselection() == pageName:
+ if len(self._pageNames) == 1:
+ newTopPage = 0
+ self._pending['topPage'] = None
+ elif pageIndex == len(self._pageNames) - 1:
+ newTopPage = 1
+ self._pending['topPage'] = self._pageNames[pageIndex - 1]
+ else:
+ newTopPage = 1
+ self._pending['topPage'] = self._pageNames[pageIndex + 1]
+
+ if self._topPageName == pageName:
+ self._hull.delete(self._topPageItem)
+ self._topPageName = None
+
+ if self._withTabs:
+ self.destroycomponent(pageName + '-tab')
+ apply(self._hull.delete, pageInfo['tabitems'])
+ self.destroycomponent(pageName)
+ del self._pageAttrs[pageName]
+ del self._pageNames[pageIndex]
+
+ # If the old top page was deleted and there are still pages
+ # left in the notebook, call the create and raise callbacks.
+ if newTopPage:
+ pageName = self._pending['topPage']
+ self._raiseNewTop(pageName)
+
+ if self._withTabs:
+ self._pending['tabs'] = 1
+ self._layout()
+
+ def page(self, pageIndex):
+ pageName = self._pageNames[self.index(pageIndex)]
+ return self._pageAttrs[pageName]['page']
+
+ def pagenames(self):
+ return list(self._pageNames)
+
+ def getcurselection(self):
+ if self._pending.has_key('topPage'):
+ return self._pending['topPage']
+ else:
+ return self._topPageName
+
+ def tab(self, pageIndex):
+ if self._withTabs:
+ pageName = self._pageNames[self.index(pageIndex)]
+ return self._pageAttrs[pageName]['tabbutton']
+ else:
+ return None
+
+ def index(self, index, forInsert = 0):
+ listLength = len(self._pageNames)
+ if type(index) == types.IntType:
+ if forInsert and index <= listLength:
+ return index
+ elif not forInsert and index < listLength:
+ return index
+ else:
+ raise ValueError, 'index "%s" is out of range' % index
+ elif index is Pmw.END:
+ if forInsert:
+ return listLength
+ elif listLength > 0:
+ return listLength - 1
+ else:
+ raise ValueError, 'NoteBook has no pages'
+ elif index is Pmw.SELECT:
+ if listLength == 0:
+ raise ValueError, 'NoteBook has no pages'
+ return self._pageNames.index(self.getcurselection())
+ else:
+ if index in self._pageNames:
+ return self._pageNames.index(index)
+ validValues = 'a name, a number, Pmw.END or Pmw.SELECT'
+ raise ValueError, \
+ 'bad index "%s": must be %s' % (index, validValues)
+
+ def selectpage(self, page):
+ pageName = self._pageNames[self.index(page)]
+ oldTopPage = self.getcurselection()
+ if pageName != oldTopPage:
+ self._pending['topPage'] = pageName
+ if oldTopPage == self._topPageName:
+ self._hull.delete(self._topPageItem)
+ cmd = self['lowercommand']
+ if cmd is not None:
+ cmd(oldTopPage)
+ self._raiseNewTop(pageName)
+
+ self._layout()
+
+ # Set focus to the tab of new top page:
+ if self._withTabs and self['arrownavigation']:
+ self._pageAttrs[pageName]['tabbutton'].focus_set()
+
+ def previouspage(self, pageIndex = None):
+ if pageIndex is None:
+ curpage = self.index(Pmw.SELECT)
+ else:
+ curpage = self.index(pageIndex)
+ if curpage > 0:
+ self.selectpage(curpage - 1)
+
+ def nextpage(self, pageIndex = None):
+ if pageIndex is None:
+ curpage = self.index(Pmw.SELECT)
+ else:
+ curpage = self.index(pageIndex)
+ if curpage < len(self._pageNames) - 1:
+ self.selectpage(curpage + 1)
+
+ def setnaturalsize(self, pageNames = None):
+ self.update_idletasks()
+ maxPageWidth = 1
+ maxPageHeight = 1
+ if pageNames is None:
+ pageNames = self.pagenames()
+ for pageName in pageNames:
+ pageInfo = self._pageAttrs[pageName]
+ page = pageInfo['page']
+ w = page.winfo_reqwidth()
+ h = page.winfo_reqheight()
+ if maxPageWidth < w:
+ maxPageWidth = w
+ if maxPageHeight < h:
+ maxPageHeight = h
+ pageBorder = self._borderWidth + self._pageMargin
+ width = maxPageWidth + pageBorder * 2
+ height = maxPageHeight + pageBorder * 2
+
+ if self._withTabs:
+ maxTabHeight = 0
+ for pageInfo in self._pageAttrs.values():
+ if maxTabHeight < pageInfo['tabreqheight']:
+ maxTabHeight = pageInfo['tabreqheight']
+ height = height + maxTabHeight + self._borderWidth * 1.5
+
+ # Note that, since the hull is a canvas, the width and height
+ # options specify the geometry *inside* the borderwidth and
+ # highlightthickness.
+ self.configure(hull_width = width, hull_height = height)
+
+ def recolorborders(self):
+ self._pending['borderColor'] = 1
+ self._layout()
+
+ def _handleMap(self, event):
+ self._layout()
+
+ def _handleConfigure(self, event):
+ self._canvasSize = (event.width, event.height)
+ self._pending['size'] = 1
+ self._layout()
+
+ def _raiseNewTop(self, pageName):
+ if not self._pageAttrs[pageName]['created']:
+ self._pageAttrs[pageName]['created'] = 1
+ cmd = self['createcommand']
+ if cmd is not None:
+ cmd(pageName)
+ cmd = self['raisecommand']
+ if cmd is not None:
+ cmd(pageName)
+
+ # This is the vertical layout of the notebook, from top (assuming
+ # tabpos is 'n'):
+ # hull highlightthickness (top)
+ # hull borderwidth (top)
+ # borderwidth (top border of tabs)
+ # borderwidth * 0.5 (space for bevel)
+ # tab button (maximum of requested height of all tab buttons)
+ # borderwidth (border between tabs and page)
+ # pagemargin (top)
+ # the page itself
+ # pagemargin (bottom)
+ # borderwidth (border below page)
+ # hull borderwidth (bottom)
+ # hull highlightthickness (bottom)
+ #
+ # canvasBorder is sum of top two elements.
+ # tabBottom is sum of top five elements.
+ #
+ # Horizontal layout (and also vertical layout when tabpos is None):
+ # hull highlightthickness
+ # hull borderwidth
+ # borderwidth
+ # pagemargin
+ # the page itself
+ # pagemargin
+ # borderwidth
+ # hull borderwidth
+ # hull highlightthickness
+ #
+ def _layout(self):
+ if not self.winfo_ismapped() or self._canvasSize is None:
+ # Don't layout if the window is not displayed, or we
+ # haven't yet received a <Configure> event.
+ return
+
+ hullWidth, hullHeight = self._canvasSize
+ borderWidth = self._borderWidth
+ canvasBorder = string.atoi(self._hull['borderwidth']) + \
+ string.atoi(self._hull['highlightthickness'])
+ if not self._withTabs:
+ self.tabBottom = canvasBorder
+ oldTabBottom = self.tabBottom
+
+ if self._pending.has_key('borderColor'):
+ self._lightBorderColor, self._darkBorderColor = \
+ Pmw.Color.bordercolors(self, self['hull_background'])
+
+ # Draw all the tabs.
+ if self._withTabs and (self._pending.has_key('tabs') or
+ self._pending.has_key('size')):
+ # Find total requested width and maximum requested height
+ # of tabs.
+ sumTabReqWidth = 0
+ maxTabHeight = 0
+ for pageInfo in self._pageAttrs.values():
+ sumTabReqWidth = sumTabReqWidth + pageInfo['tabreqwidth']
+ if maxTabHeight < pageInfo['tabreqheight']:
+ maxTabHeight = pageInfo['tabreqheight']
+ if maxTabHeight != 0:
+ # Add the top tab border plus a bit for the angled corners
+ self.tabBottom = canvasBorder + maxTabHeight + borderWidth * 1.5
+
+ # Prepare for drawing the border around each tab button.
+ tabTop = canvasBorder
+ tabTop2 = tabTop + borderWidth
+ tabTop3 = tabTop + borderWidth * 1.5
+ tabBottom2 = self.tabBottom
+ tabBottom = self.tabBottom + borderWidth
+
+ numTabs = len(self._pageNames)
+ availableWidth = hullWidth - 2 * canvasBorder - \
+ numTabs * 2 * borderWidth
+ x = canvasBorder
+ cumTabReqWidth = 0
+ cumTabWidth = 0
+
+ # Position all the tabs.
+ for pageName in self._pageNames:
+ pageInfo = self._pageAttrs[pageName]
+ (windowitem, lightshadow, darkshadow) = pageInfo['tabitems']
+ if sumTabReqWidth <= availableWidth:
+ tabwidth = pageInfo['tabreqwidth']
+ else:
+ # This ugly calculation ensures that, when the
+ # notebook is not wide enough for the requested
+ # widths of the tabs, the total width given to
+ # the tabs exactly equals the available width,
+ # without rounding errors.
+ cumTabReqWidth = cumTabReqWidth + pageInfo['tabreqwidth']
+ tmp = (2*cumTabReqWidth*availableWidth + sumTabReqWidth) \
+ / (2 * sumTabReqWidth)
+ tabwidth = tmp - cumTabWidth
+ cumTabWidth = tmp
+
+ # Position the tab's button canvas item.
+ self.coords(windowitem, x + borderWidth, tabTop3)
+ self.itemconfigure(windowitem,
+ width = tabwidth, height = maxTabHeight)
+
+ # Make a beautiful border around the tab.
+ left = x
+ left2 = left + borderWidth
+ left3 = left + borderWidth * 1.5
+ right = left + tabwidth + 2 * borderWidth
+ right2 = left + tabwidth + borderWidth
+ right3 = left + tabwidth + borderWidth * 0.5
+
+ self.coords(lightshadow,
+ left, tabBottom2, left, tabTop2, left2, tabTop,
+ right2, tabTop, right3, tabTop2, left3, tabTop2,
+ left2, tabTop3, left2, tabBottom,
+ )
+ self.coords(darkshadow,
+ right2, tabTop, right, tabTop2, right, tabBottom2,
+ right2, tabBottom, right2, tabTop3, right3, tabTop2,
+ )
+ pageInfo['left'] = left
+ pageInfo['right'] = right
+
+ x = x + tabwidth + 2 * borderWidth
+
+ # Redraw shadow under tabs so that it appears that tab for old
+ # top page is lowered and that tab for new top page is raised.
+ if self._withTabs and (self._pending.has_key('topPage') or
+ self._pending.has_key('tabs') or self._pending.has_key('size')):
+
+ if self.getcurselection() is None:
+ # No pages, so draw line across top of page area.
+ self.coords(self._pageTop1Border,
+ canvasBorder, self.tabBottom,
+ hullWidth - canvasBorder, self.tabBottom,
+ hullWidth - canvasBorder - borderWidth,
+ self.tabBottom + borderWidth,
+ borderWidth + canvasBorder, self.tabBottom + borderWidth,
+ )
+
+ # Ignore second top border.
+ self.coords(self._pageTop2Border, 0, 0, 0, 0, 0, 0)
+ else:
+ # Draw two lines, one on each side of the tab for the
+ # top page, so that the tab appears to be raised.
+ pageInfo = self._pageAttrs[self.getcurselection()]
+ left = pageInfo['left']
+ right = pageInfo['right']
+ self.coords(self._pageTop1Border,
+ canvasBorder, self.tabBottom,
+ left, self.tabBottom,
+ left + borderWidth, self.tabBottom + borderWidth,
+ canvasBorder + borderWidth, self.tabBottom + borderWidth,
+ )
+
+ self.coords(self._pageTop2Border,
+ right, self.tabBottom,
+ hullWidth - canvasBorder, self.tabBottom,
+ hullWidth - canvasBorder - borderWidth,
+ self.tabBottom + borderWidth,
+ right - borderWidth, self.tabBottom + borderWidth,
+ )
+
+ # Prevent bottom of dark border of tabs appearing over
+ # page top border.
+ self.tag_raise(self._pageTop1Border)
+ self.tag_raise(self._pageTop2Border)
+
+ # Position the page border shadows.
+ if self._pending.has_key('size') or oldTabBottom != self.tabBottom:
+
+ self.coords(self._pageLeftBorder,
+ canvasBorder, self.tabBottom,
+ borderWidth + canvasBorder,
+ self.tabBottom + borderWidth,
+ borderWidth + canvasBorder,
+ hullHeight - canvasBorder - borderWidth,
+ canvasBorder, hullHeight - canvasBorder,
+ )
+
+ self.coords(self._pageBottomRightBorder,
+ hullWidth - canvasBorder, self.tabBottom,
+ hullWidth - canvasBorder, hullHeight - canvasBorder,
+ canvasBorder, hullHeight - canvasBorder,
+ borderWidth + canvasBorder,
+ hullHeight - canvasBorder - borderWidth,
+ hullWidth - canvasBorder - borderWidth,
+ hullHeight - canvasBorder - borderWidth,
+ hullWidth - canvasBorder - borderWidth,
+ self.tabBottom + borderWidth,
+ )
+
+ if not self._withTabs:
+ self.coords(self._pageTopBorder,
+ canvasBorder, self.tabBottom,
+ hullWidth - canvasBorder, self.tabBottom,
+ hullWidth - canvasBorder - borderWidth,
+ self.tabBottom + borderWidth,
+ borderWidth + canvasBorder, self.tabBottom + borderWidth,
+ )
+
+ # Color borders.
+ if self._pending.has_key('borderColor'):
+ self.itemconfigure('lighttag', fill = self._lightBorderColor)
+ self.itemconfigure('darktag', fill = self._darkBorderColor)
+
+ newTopPage = self._pending.get('topPage')
+ pageBorder = borderWidth + self._pageMargin
+
+ # Raise new top page.
+ if newTopPage is not None:
+ self._topPageName = newTopPage
+ self._topPageItem = self.create_window(
+ pageBorder + canvasBorder, self.tabBottom + pageBorder,
+ window = self._pageAttrs[newTopPage]['page'],
+ anchor = 'nw',
+ )
+
+ # Change position of top page if tab height has changed.
+ if self._topPageName is not None and oldTabBottom != self.tabBottom:
+ self.coords(self._topPageItem,
+ pageBorder + canvasBorder, self.tabBottom + pageBorder)
+
+ # Change size of top page if,
+ # 1) there is a new top page.
+ # 2) canvas size has changed, but not if there is no top
+ # page (eg: initially or when all pages deleted).
+ # 3) tab height has changed, due to difference in the height of a tab
+ if (newTopPage is not None or \
+ self._pending.has_key('size') and self._topPageName is not None
+ or oldTabBottom != self.tabBottom):
+ self.itemconfigure(self._topPageItem,
+ width = hullWidth - 2 * canvasBorder - pageBorder * 2,
+ height = hullHeight - 2 * canvasBorder - pageBorder * 2 -
+ (self.tabBottom - canvasBorder),
+ )
+
+ self._pending = {}
+
+# Need to do forwarding to get the pack, grid, etc methods.
+# Unfortunately this means that all the other canvas methods are also
+# forwarded.
+Pmw.forwardmethods(NoteBook, Tkinter.Canvas, '_hull')
--- /dev/null
+import types
+import Tkinter
+import Pmw
+
+class OptionMenu(Pmw.MegaWidget):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('command', None, None),
+ ('items', (), INITOPT),
+ ('initialitem', None, INITOPT),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('sticky', 'ew', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ self._menubutton = self.createcomponent('menubutton',
+ (), None,
+ Tkinter.Menubutton, (interior,),
+ borderwidth = 2,
+ indicatoron = 1,
+ relief = 'raised',
+ anchor = 'c',
+ highlightthickness = 2,
+ direction = 'flush',
+ takefocus = 1,
+ )
+ self._menubutton.grid(column = 2, row = 2, sticky = self['sticky'])
+
+ self._menu = self.createcomponent('menu',
+ (), None,
+ Tkinter.Menu, (self._menubutton,),
+ tearoff=0
+ )
+ self._menubutton.configure(menu = self._menu)
+
+ interior.grid_columnconfigure(2, weight = 1)
+ interior.grid_rowconfigure(2, weight = 1)
+
+ # Create the label.
+ self.createlabel(interior)
+
+ # Add the items specified by the initialisation option.
+ self._itemList = []
+ self.setitems(self['items'], self['initialitem'])
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def setitems(self, items, index = None):
+
+ # Clean up old items and callback commands.
+ for oldIndex in range(len(self._itemList)):
+ tclCommandName = str(self._menu.entrycget(oldIndex, 'command'))
+ if tclCommandName != '':
+ self._menu.deletecommand(tclCommandName)
+ self._menu.delete(0, 'end')
+ self._itemList = list(items)
+
+ # Set the items in the menu component.
+ for item in items:
+ self._menu.add_command(label = item,
+ command = lambda self = self, item = item: self._invoke(item))
+
+ # Set the currently selected value.
+ if index is None:
+ var = str(self._menubutton.cget('textvariable'))
+ if var != '':
+ # None means do not change text variable.
+ return
+ if len(items) == 0:
+ text = ''
+ elif str(self._menubutton.cget('text')) in items:
+ # Do not change selection if it is still valid
+ return
+ else:
+ text = items[0]
+ else:
+ index = self.index(index)
+ text = self._itemList[index]
+
+ self.setvalue(text)
+
+ def getcurselection(self):
+ var = str(self._menubutton.cget('textvariable'))
+ if var == '':
+ return str(self._menubutton.cget('text'))
+ else:
+ return self._menu.tk.globalgetvar(var)
+
+ def getvalue(self):
+ return self.getcurselection()
+
+ def setvalue(self, text):
+ var = str(self._menubutton.cget('textvariable'))
+ if var == '':
+ self._menubutton.configure(text = text)
+ else:
+ self._menu.tk.globalsetvar(var, text)
+
+ def index(self, index):
+ listLength = len(self._itemList)
+ if type(index) == types.IntType:
+ if index < listLength:
+ return index
+ else:
+ raise ValueError, 'index "%s" is out of range' % index
+ elif index is Pmw.END:
+ if listLength > 0:
+ return listLength - 1
+ else:
+ raise ValueError, 'OptionMenu has no items'
+ else:
+ if index is Pmw.SELECT:
+ if listLength > 0:
+ index = self.getcurselection()
+ else:
+ raise ValueError, 'OptionMenu has no items'
+ if index in self._itemList:
+ return self._itemList.index(index)
+ raise ValueError, \
+ 'bad index "%s": must be a ' \
+ 'name, a number, Pmw.END or Pmw.SELECT' % (index,)
+
+ def invoke(self, index = Pmw.SELECT):
+ index = self.index(index)
+ text = self._itemList[index]
+
+ return self._invoke(text)
+
+ def _invoke(self, text):
+ self.setvalue(text)
+
+ command = self['command']
+ if callable(command):
+ return command(text)
--- /dev/null
+# PanedWidget
+# a frame which may contain several resizable sub-frames
+
+import string
+import sys
+import types
+import Tkinter
+import Pmw
+
+class PanedWidget(Pmw.MegaWidget):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('command', None, None),
+ ('orient', 'vertical', INITOPT),
+ ('separatorrelief', 'sunken', INITOPT),
+ ('separatorthickness', 2, INITOPT),
+ ('handlesize', 8, INITOPT),
+ ('hull_width', 400, None),
+ ('hull_height', 400, None),
+ )
+ self.defineoptions(kw, optiondefs,
+ dynamicGroups = ('Frame', 'Separator', 'Handle'))
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ self.bind('<Configure>', self._handleConfigure)
+
+ if self['orient'] not in ('horizontal', 'vertical'):
+ raise ValueError, 'bad orient option ' + repr(self['orient']) + \
+ ': must be either \'horizontal\' or \'vertical\''
+
+ self._separatorThickness = self['separatorthickness']
+ self._handleSize = self['handlesize']
+ self._paneNames = [] # List of pane names
+ self._paneAttrs = {} # Map from pane name to pane info
+
+ self._timerId = None
+ self._frame = {}
+ self._separator = []
+ self._button = []
+ self._totalSize = 0
+ self._movePending = 0
+ self._relsize = {}
+ self._relmin = {}
+ self._relmax = {}
+ self._size = {}
+ self._min = {}
+ self._max = {}
+ self._rootp = None
+ self._curSize = None
+ self._beforeLimit = None
+ self._afterLimit = None
+ self._buttonIsDown = 0
+ self._majorSize = 100
+ self._minorSize = 100
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def insert(self, name, before = 0, **kw):
+ # Parse <kw> for options.
+ self._initPaneOptions(name)
+ self._parsePaneOptions(name, kw)
+
+ insertPos = self._nameToIndex(before)
+ atEnd = (insertPos == len(self._paneNames))
+
+ # Add the frame.
+ self._paneNames[insertPos:insertPos] = [name]
+ self._frame[name] = self.createcomponent(name,
+ (), 'Frame',
+ Tkinter.Frame, (self.interior(),))
+
+ # Add separator, if necessary.
+ if len(self._paneNames) > 1:
+ self._addSeparator()
+ else:
+ self._separator.append(None)
+ self._button.append(None)
+
+ # Add the new frame and adjust the PanedWidget
+ if atEnd:
+ size = self._size[name]
+ if size > 0 or self._relsize[name] is not None:
+ if self['orient'] == 'vertical':
+ self._frame[name].place(x=0, relwidth=1,
+ height=size, y=self._totalSize)
+ else:
+ self._frame[name].place(y=0, relheight=1,
+ width=size, x=self._totalSize)
+ else:
+ if self['orient'] == 'vertical':
+ self._frame[name].place(x=0, relwidth=1,
+ y=self._totalSize)
+ else:
+ self._frame[name].place(y=0, relheight=1,
+ x=self._totalSize)
+ else:
+ self._updateSizes()
+
+ self._totalSize = self._totalSize + self._size[name]
+ return self._frame[name]
+
+ def add(self, name, **kw):
+ return apply(self.insert, (name, len(self._paneNames)), kw)
+
+ def delete(self, name):
+ deletePos = self._nameToIndex(name)
+ name = self._paneNames[deletePos]
+ self.destroycomponent(name)
+ del self._paneNames[deletePos]
+ del self._frame[name]
+ del self._size[name]
+ del self._min[name]
+ del self._max[name]
+ del self._relsize[name]
+ del self._relmin[name]
+ del self._relmax[name]
+
+ last = len(self._paneNames)
+ del self._separator[last]
+ del self._button[last]
+ if last > 0:
+ self.destroycomponent(self._sepName(last))
+ self.destroycomponent(self._buttonName(last))
+
+ self._plotHandles()
+
+ def setnaturalsize(self):
+ self.update_idletasks()
+ totalWidth = 0
+ totalHeight = 0
+ maxWidth = 0
+ maxHeight = 0
+ for name in self._paneNames:
+ frame = self._frame[name]
+ w = frame.winfo_reqwidth()
+ h = frame.winfo_reqheight()
+ totalWidth = totalWidth + w
+ totalHeight = totalHeight + h
+ if maxWidth < w:
+ maxWidth = w
+ if maxHeight < h:
+ maxHeight = h
+
+ # Note that, since the hull is a frame, the width and height
+ # options specify the geometry *outside* the borderwidth and
+ # highlightthickness.
+ bw = string.atoi(str(self.cget('hull_borderwidth')))
+ hl = string.atoi(str(self.cget('hull_highlightthickness')))
+ extra = (bw + hl) * 2
+ if str(self.cget('orient')) == 'horizontal':
+ totalWidth = totalWidth + extra
+ maxHeight = maxHeight + extra
+ self.configure(hull_width = totalWidth, hull_height = maxHeight)
+ else:
+ totalHeight = (totalHeight + extra +
+ (len(self._paneNames) - 1) * self._separatorThickness)
+ maxWidth = maxWidth + extra
+ self.configure(hull_width = maxWidth, hull_height = totalHeight)
+
+ def move(self, name, newPos, newPosOffset = 0):
+
+ # see if we can spare ourselves some work
+ numPanes = len(self._paneNames)
+ if numPanes < 2:
+ return
+
+ newPos = self._nameToIndex(newPos) + newPosOffset
+ if newPos < 0 or newPos >=numPanes:
+ return
+
+ deletePos = self._nameToIndex(name)
+
+ if deletePos == newPos:
+ # inserting over ourself is a no-op
+ return
+
+ # delete name from old position in list
+ name = self._paneNames[deletePos]
+ del self._paneNames[deletePos]
+
+ # place in new position
+ self._paneNames[newPos:newPos] = [name]
+
+ # force everything to redraw
+ self._plotHandles()
+ self._updateSizes()
+
+ def _nameToIndex(self, nameOrIndex):
+ try:
+ pos = self._paneNames.index(nameOrIndex)
+ except ValueError:
+ pos = nameOrIndex
+
+ return pos
+
+ def _initPaneOptions(self, name):
+ # Set defaults.
+ self._size[name] = 0
+ self._relsize[name] = None
+ self._min[name] = 0
+ self._relmin[name] = None
+ self._max[name] = 100000
+ self._relmax[name] = None
+
+ def _parsePaneOptions(self, name, args):
+ # Parse <args> for options.
+ for arg, value in args.items():
+ if type(value) == types.FloatType:
+ relvalue = value
+ value = self._absSize(relvalue)
+ else:
+ relvalue = None
+
+ if arg == 'size':
+ self._size[name], self._relsize[name] = value, relvalue
+ elif arg == 'min':
+ self._min[name], self._relmin[name] = value, relvalue
+ elif arg == 'max':
+ self._max[name], self._relmax[name] = value, relvalue
+ else:
+ raise ValueError, 'keyword must be "size", "min", or "max"'
+
+ def _absSize(self, relvalue):
+ return int(round(relvalue * self._majorSize))
+
+ def _sepName(self, n):
+ return 'separator-%d' % n
+
+ def _buttonName(self, n):
+ return 'handle-%d' % n
+
+ def _addSeparator(self):
+ n = len(self._paneNames) - 1
+
+ downFunc = lambda event, s = self, num=n: s._btnDown(event, num)
+ upFunc = lambda event, s = self, num=n: s._btnUp(event, num)
+ moveFunc = lambda event, s = self, num=n: s._btnMove(event, num)
+
+ # Create the line dividing the panes.
+ sep = self.createcomponent(self._sepName(n),
+ (), 'Separator',
+ Tkinter.Frame, (self.interior(),),
+ borderwidth = 1,
+ relief = self['separatorrelief'])
+ self._separator.append(sep)
+
+ sep.bind('<ButtonPress-1>', downFunc)
+ sep.bind('<Any-ButtonRelease-1>', upFunc)
+ sep.bind('<B1-Motion>', moveFunc)
+
+ if self['orient'] == 'vertical':
+ cursor = 'sb_v_double_arrow'
+ sep.configure(height = self._separatorThickness,
+ width = 10000, cursor = cursor)
+ else:
+ cursor = 'sb_h_double_arrow'
+ sep.configure(width = self._separatorThickness,
+ height = 10000, cursor = cursor)
+
+ self._totalSize = self._totalSize + self._separatorThickness
+
+ # Create the handle on the dividing line.
+ handle = self.createcomponent(self._buttonName(n),
+ (), 'Handle',
+ Tkinter.Frame, (self.interior(),),
+ relief = 'raised',
+ borderwidth = 1,
+ width = self._handleSize,
+ height = self._handleSize,
+ cursor = cursor,
+ )
+ self._button.append(handle)
+
+ handle.bind('<ButtonPress-1>', downFunc)
+ handle.bind('<Any-ButtonRelease-1>', upFunc)
+ handle.bind('<B1-Motion>', moveFunc)
+
+ self._plotHandles()
+
+ for i in range(1, len(self._paneNames)):
+ self._separator[i].tkraise()
+ for i in range(1, len(self._paneNames)):
+ self._button[i].tkraise()
+
+ def _btnUp(self, event, item):
+ self._buttonIsDown = 0
+ self._updateSizes()
+ try:
+ self._button[item].configure(relief='raised')
+ except:
+ pass
+
+ def _btnDown(self, event, item):
+ self._button[item].configure(relief='sunken')
+ self._getMotionLimit(item)
+ self._buttonIsDown = 1
+ self._movePending = 0
+
+ def _handleConfigure(self, event = None):
+ self._getNaturalSizes()
+ if self._totalSize == 0:
+ return
+
+ iterRange = list(self._paneNames)
+ iterRange.reverse()
+ if self._majorSize > self._totalSize:
+ n = self._majorSize - self._totalSize
+ self._iterate(iterRange, self._grow, n)
+ elif self._majorSize < self._totalSize:
+ n = self._totalSize - self._majorSize
+ self._iterate(iterRange, self._shrink, n)
+
+ self._plotHandles()
+ self._updateSizes()
+
+ def _getNaturalSizes(self):
+ # Must call this in order to get correct winfo_width, winfo_height
+ self.update_idletasks()
+
+ self._totalSize = 0
+
+ if self['orient'] == 'vertical':
+ self._majorSize = self.winfo_height()
+ self._minorSize = self.winfo_width()
+ majorspec = Tkinter.Frame.winfo_reqheight
+ else:
+ self._majorSize = self.winfo_width()
+ self._minorSize = self.winfo_height()
+ majorspec = Tkinter.Frame.winfo_reqwidth
+
+ bw = string.atoi(str(self.cget('hull_borderwidth')))
+ hl = string.atoi(str(self.cget('hull_highlightthickness')))
+ extra = (bw + hl) * 2
+ self._majorSize = self._majorSize - extra
+ self._minorSize = self._minorSize - extra
+
+ if self._majorSize < 0:
+ self._majorSize = 0
+ if self._minorSize < 0:
+ self._minorSize = 0
+
+ for name in self._paneNames:
+ # adjust the absolute sizes first...
+ if self._relsize[name] is None:
+ #special case
+ if self._size[name] == 0:
+ self._size[name] = apply(majorspec, (self._frame[name],))
+ self._setrel(name)
+ else:
+ self._size[name] = self._absSize(self._relsize[name])
+
+ if self._relmin[name] is not None:
+ self._min[name] = self._absSize(self._relmin[name])
+ if self._relmax[name] is not None:
+ self._max[name] = self._absSize(self._relmax[name])
+
+ # now adjust sizes
+ if self._size[name] < self._min[name]:
+ self._size[name] = self._min[name]
+ self._setrel(name)
+
+ if self._size[name] > self._max[name]:
+ self._size[name] = self._max[name]
+ self._setrel(name)
+
+ self._totalSize = self._totalSize + self._size[name]
+
+ # adjust for separators
+ self._totalSize = (self._totalSize +
+ (len(self._paneNames) - 1) * self._separatorThickness)
+
+ def _setrel(self, name):
+ if self._relsize[name] is not None:
+ if self._majorSize != 0:
+ self._relsize[name] = round(self._size[name]) / self._majorSize
+
+ def _iterate(self, names, proc, n):
+ for i in names:
+ n = apply(proc, (i, n))
+ if n == 0:
+ break
+
+ def _grow(self, name, n):
+ canGrow = self._max[name] - self._size[name]
+
+ if canGrow > n:
+ self._size[name] = self._size[name] + n
+ self._setrel(name)
+ return 0
+ elif canGrow > 0:
+ self._size[name] = self._max[name]
+ self._setrel(name)
+ n = n - canGrow
+
+ return n
+
+ def _shrink(self, name, n):
+ canShrink = self._size[name] - self._min[name]
+
+ if canShrink > n:
+ self._size[name] = self._size[name] - n
+ self._setrel(name)
+ return 0
+ elif canShrink > 0:
+ self._size[name] = self._min[name]
+ self._setrel(name)
+ n = n - canShrink
+
+ return n
+
+ def _updateSizes(self):
+ totalSize = 0
+
+ for name in self._paneNames:
+ size = self._size[name]
+ if self['orient'] == 'vertical':
+ self._frame[name].place(x = 0, relwidth = 1,
+ y = totalSize,
+ height = size)
+ else:
+ self._frame[name].place(y = 0, relheight = 1,
+ x = totalSize,
+ width = size)
+
+ totalSize = totalSize + size + self._separatorThickness
+
+ # Invoke the callback command
+ cmd = self['command']
+ if callable(cmd):
+ cmd(map(lambda x, s = self: s._size[x], self._paneNames))
+
+ def _plotHandles(self):
+ if len(self._paneNames) == 0:
+ return
+
+ if self['orient'] == 'vertical':
+ btnp = self._minorSize - 13
+ else:
+ h = self._minorSize
+
+ if h > 18:
+ btnp = 9
+ else:
+ btnp = h - 9
+
+ firstPane = self._paneNames[0]
+ totalSize = self._size[firstPane]
+
+ first = 1
+ last = len(self._paneNames) - 1
+
+ # loop from first to last, inclusive
+ for i in range(1, last + 1):
+
+ handlepos = totalSize - 3
+ prevSize = self._size[self._paneNames[i - 1]]
+ nextSize = self._size[self._paneNames[i]]
+
+ offset1 = 0
+
+ if i == first:
+ if prevSize < 4:
+ offset1 = 4 - prevSize
+ else:
+ if prevSize < 8:
+ offset1 = (8 - prevSize) / 2
+
+ offset2 = 0
+
+ if i == last:
+ if nextSize < 4:
+ offset2 = nextSize - 4
+ else:
+ if nextSize < 8:
+ offset2 = (nextSize - 8) / 2
+
+ handlepos = handlepos + offset1
+
+ if self['orient'] == 'vertical':
+ height = 8 - offset1 + offset2
+
+ if height > 1:
+ self._button[i].configure(height = height)
+ self._button[i].place(x = btnp, y = handlepos)
+ else:
+ self._button[i].place_forget()
+
+ self._separator[i].place(x = 0, y = totalSize,
+ relwidth = 1)
+ else:
+ width = 8 - offset1 + offset2
+
+ if width > 1:
+ self._button[i].configure(width = width)
+ self._button[i].place(y = btnp, x = handlepos)
+ else:
+ self._button[i].place_forget()
+
+ self._separator[i].place(y = 0, x = totalSize,
+ relheight = 1)
+
+ totalSize = totalSize + nextSize + self._separatorThickness
+
+ def pane(self, name):
+ return self._frame[self._paneNames[self._nameToIndex(name)]]
+
+ # Return the name of all panes
+ def panes(self):
+ return list(self._paneNames)
+
+ def configurepane(self, name, **kw):
+ name = self._paneNames[self._nameToIndex(name)]
+ self._parsePaneOptions(name, kw)
+ self._handleConfigure()
+
+ def updatelayout(self):
+ self._handleConfigure()
+
+ def _getMotionLimit(self, item):
+ curBefore = (item - 1) * self._separatorThickness
+ minBefore, maxBefore = curBefore, curBefore
+
+ for name in self._paneNames[:item]:
+ curBefore = curBefore + self._size[name]
+ minBefore = minBefore + self._min[name]
+ maxBefore = maxBefore + self._max[name]
+
+ curAfter = (len(self._paneNames) - item) * self._separatorThickness
+ minAfter, maxAfter = curAfter, curAfter
+ for name in self._paneNames[item:]:
+ curAfter = curAfter + self._size[name]
+ minAfter = minAfter + self._min[name]
+ maxAfter = maxAfter + self._max[name]
+
+ beforeToGo = min(curBefore - minBefore, maxAfter - curAfter)
+ afterToGo = min(curAfter - minAfter, maxBefore - curBefore)
+
+ self._beforeLimit = curBefore - beforeToGo
+ self._afterLimit = curBefore + afterToGo
+ self._curSize = curBefore
+
+ self._plotHandles()
+
+ # Compress the motion so that update is quick even on slow machines
+ #
+ # theRootp = root position (either rootx or rooty)
+ def _btnMove(self, event, item):
+ self._rootp = event
+
+ if self._movePending == 0:
+ self._timerId = self.after_idle(
+ lambda s = self, i = item: s._btnMoveCompressed(i))
+ self._movePending = 1
+
+ def destroy(self):
+ if self._timerId is not None:
+ self.after_cancel(self._timerId)
+ self._timerId = None
+ Pmw.MegaWidget.destroy(self)
+
+ def _btnMoveCompressed(self, item):
+ if not self._buttonIsDown:
+ return
+
+ if self['orient'] == 'vertical':
+ p = self._rootp.y_root - self.winfo_rooty()
+ else:
+ p = self._rootp.x_root - self.winfo_rootx()
+
+ if p == self._curSize:
+ self._movePending = 0
+ return
+
+ if p < self._beforeLimit:
+ p = self._beforeLimit
+
+ if p >= self._afterLimit:
+ p = self._afterLimit
+
+ self._calculateChange(item, p)
+ self.update_idletasks()
+ self._movePending = 0
+
+ # Calculate the change in response to mouse motions
+ def _calculateChange(self, item, p):
+
+ if p < self._curSize:
+ self._moveBefore(item, p)
+ elif p > self._curSize:
+ self._moveAfter(item, p)
+
+ self._plotHandles()
+
+ def _moveBefore(self, item, p):
+ n = self._curSize - p
+
+ # Shrink the frames before
+ iterRange = list(self._paneNames[:item])
+ iterRange.reverse()
+ self._iterate(iterRange, self._shrink, n)
+
+ # Adjust the frames after
+ iterRange = self._paneNames[item:]
+ self._iterate(iterRange, self._grow, n)
+
+ self._curSize = p
+
+ def _moveAfter(self, item, p):
+ n = p - self._curSize
+
+ # Shrink the frames after
+ iterRange = self._paneNames[item:]
+ self._iterate(iterRange, self._shrink, n)
+
+ # Adjust the frames before
+ iterRange = list(self._paneNames[:item])
+ iterRange.reverse()
+ self._iterate(iterRange, self._grow, n)
+
+ self._curSize = p
--- /dev/null
+# Based on iwidgets2.2.0/promptdialog.itk code.
+
+import Pmw
+
+# A Dialog with an entryfield
+
+class PromptDialog(Pmw.Dialog):
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderx', 20, INITOPT),
+ ('bordery', 20, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.Dialog.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ aliases = (
+ ('entry', 'entryfield_entry'),
+ ('label', 'entryfield_label'),
+ )
+ self._promptDialogEntry = self.createcomponent('entryfield',
+ aliases, None,
+ Pmw.EntryField, (interior,))
+ self._promptDialogEntry.pack(fill='x', expand=1,
+ padx = self['borderx'], pady = self['bordery'])
+
+ if not kw.has_key('activatecommand'):
+ # Whenever this dialog is activated, set the focus to the
+ # EntryField's entry widget.
+ tkentry = self.component('entry')
+ self.configure(activatecommand = tkentry.focus_set)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ # Supply aliases to some of the entry component methods.
+ def insertentry(self, index, text):
+ self._promptDialogEntry.insert(index, text)
+
+ def deleteentry(self, first, last=None):
+ self._promptDialogEntry.delete(first, last)
+
+ def indexentry(self, index):
+ return self._promptDialogEntry.index(index)
+
+Pmw.forwardmethods(PromptDialog, Pmw.EntryField, '_promptDialogEntry')
--- /dev/null
+import types
+import Tkinter
+import Pmw
+
+class RadioSelect(Pmw.MegaWidget):
+ # A collection of several buttons. In single mode, only one
+ # button may be selected. In multiple mode, any number of buttons
+ # may be selected.
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('buttontype', 'button', INITOPT),
+ ('command', None, None),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('orient', 'horizontal', INITOPT),
+ ('padx', 5, INITOPT),
+ ('pady', 5, INITOPT),
+ ('selectmode', 'single', INITOPT),
+ )
+ self.defineoptions(kw, optiondefs, dynamicGroups = ('Button',))
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ if self['labelpos'] is None:
+ self._radioSelectFrame = self._hull
+ else:
+ self._radioSelectFrame = self.createcomponent('frame',
+ (), None,
+ Tkinter.Frame, (interior,))
+ self._radioSelectFrame.grid(column=2, row=2, sticky='nsew')
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ self.createlabel(interior)
+
+ # Initialise instance variables.
+ self._buttonList = []
+ if self['selectmode'] == 'single':
+ self._singleSelect = 1
+ elif self['selectmode'] == 'multiple':
+ self._singleSelect = 0
+ else:
+ raise ValueError, 'bad selectmode option "' + \
+ self['selectmode'] + '": should be single or multiple'
+
+ if self['buttontype'] == 'button':
+ self.buttonClass = Tkinter.Button
+ elif self['buttontype'] == 'radiobutton':
+ self._singleSelect = 1
+ self.var = Tkinter.StringVar()
+ self.buttonClass = Tkinter.Radiobutton
+ elif self['buttontype'] == 'checkbutton':
+ self._singleSelect = 0
+ self.buttonClass = Tkinter.Checkbutton
+ else:
+ raise ValueError, 'bad buttontype option "' + \
+ self['buttontype'] + \
+ '": should be button, radiobutton or checkbutton'
+
+ if self._singleSelect:
+ self.selection = None
+ else:
+ self.selection = []
+
+ if self['orient'] not in ('horizontal', 'vertical'):
+ raise ValueError, 'bad orient option ' + repr(self['orient']) + \
+ ': must be either \'horizontal\' or \'vertical\''
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def getcurselection(self):
+ if self._singleSelect:
+ return self.selection
+ else:
+ return tuple(self.selection)
+
+ def getvalue(self):
+ return self.getcurselection()
+
+ def setvalue(self, textOrList):
+ if self._singleSelect:
+ self.__setSingleValue(textOrList)
+ else:
+ # Multiple selections
+ oldselection = self.selection
+ self.selection = textOrList
+ for button in self._buttonList:
+ if button in oldselection:
+ if button not in self.selection:
+ # button is currently selected but should not be
+ widget = self.component(button)
+ if self['buttontype'] == 'checkbutton':
+ widget.deselect()
+ else: # Button
+ widget.configure(relief='raised')
+ else:
+ if button in self.selection:
+ # button is not currently selected but should be
+ widget = self.component(button)
+ if self['buttontype'] == 'checkbutton':
+ widget.select()
+ else: # Button
+ widget.configure(relief='sunken')
+
+ def numbuttons(self):
+ return len(self._buttonList)
+
+ def index(self, index):
+ # Return the integer index of the button with the given index.
+
+ listLength = len(self._buttonList)
+ if type(index) == types.IntType:
+ if index < listLength:
+ return index
+ else:
+ raise ValueError, 'index "%s" is out of range' % index
+ elif index is Pmw.END:
+ if listLength > 0:
+ return listLength - 1
+ else:
+ raise ValueError, 'RadioSelect has no buttons'
+ else:
+ for count in range(listLength):
+ name = self._buttonList[count]
+ if index == name:
+ return count
+ validValues = 'a name, a number or Pmw.END'
+ raise ValueError, \
+ 'bad index "%s": must be %s' % (index, validValues)
+
+ def button(self, buttonIndex):
+ name = self._buttonList[self.index(buttonIndex)]
+ return self.component(name)
+
+ def add(self, componentName, **kw):
+ if componentName in self._buttonList:
+ raise ValueError, 'button "%s" already exists' % componentName
+
+ kw['command'] = \
+ lambda self=self, name=componentName: self.invoke(name)
+ if not kw.has_key('text'):
+ kw['text'] = componentName
+
+ if self['buttontype'] == 'radiobutton':
+ if not kw.has_key('anchor'):
+ kw['anchor'] = 'w'
+ if not kw.has_key('variable'):
+ kw['variable'] = self.var
+ if not kw.has_key('value'):
+ kw['value'] = kw['text']
+ elif self['buttontype'] == 'checkbutton':
+ if not kw.has_key('anchor'):
+ kw['anchor'] = 'w'
+
+ button = apply(self.createcomponent, (componentName,
+ (), 'Button',
+ self.buttonClass, (self._radioSelectFrame,)), kw)
+
+ if self['orient'] == 'horizontal':
+ self._radioSelectFrame.grid_rowconfigure(0, weight=1)
+ col = len(self._buttonList)
+ button.grid(column=col, row=0, padx = self['padx'],
+ pady = self['pady'], sticky='nsew')
+ self._radioSelectFrame.grid_columnconfigure(col, weight=1)
+ else:
+ self._radioSelectFrame.grid_columnconfigure(0, weight=1)
+ row = len(self._buttonList)
+ button.grid(column=0, row=row, padx = self['padx'],
+ pady = self['pady'], sticky='ew')
+ self._radioSelectFrame.grid_rowconfigure(row, weight=1)
+
+ self._buttonList.append(componentName)
+ return button
+
+ def deleteall(self):
+ for name in self._buttonList:
+ self.destroycomponent(name)
+ self._buttonList = []
+ if self._singleSelect:
+ self.selection = None
+ else:
+ self.selection = []
+
+ def __setSingleValue(self, value):
+ self.selection = value
+ if self['buttontype'] == 'radiobutton':
+ widget = self.component(value)
+ widget.select()
+ else: # Button
+ for button in self._buttonList:
+ widget = self.component(button)
+ if button == value:
+ widget.configure(relief='sunken')
+ else:
+ widget.configure(relief='raised')
+
+ def invoke(self, index):
+ index = self.index(index)
+ name = self._buttonList[index]
+
+ if self._singleSelect:
+ self.__setSingleValue(name)
+ command = self['command']
+ if callable(command):
+ return command(name)
+ else:
+ # Multiple selections
+ widget = self.component(name)
+ if name in self.selection:
+ if self['buttontype'] == 'checkbutton':
+ widget.deselect()
+ else:
+ widget.configure(relief='raised')
+ self.selection.remove(name)
+ state = 0
+ else:
+ if self['buttontype'] == 'checkbutton':
+ widget.select()
+ else:
+ widget.configure(relief='sunken')
+ self.selection.append(name)
+ state = 1
+
+ command = self['command']
+ if callable(command):
+ return command(name, state)
--- /dev/null
+import Tkinter
+import Pmw
+
+class ScrolledCanvas(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderframe', 0, INITOPT),
+ ('canvasmargin', 0, INITOPT),
+ ('hscrollmode', 'dynamic', self._hscrollMode),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('scrollmargin', 2, INITOPT),
+ ('usehullsize', 0, INITOPT),
+ ('vscrollmode', 'dynamic', self._vscrollMode),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ self.origInterior = Pmw.MegaWidget.interior(self)
+
+ if self['usehullsize']:
+ self.origInterior.grid_propagate(0)
+
+ if self['borderframe']:
+ # Create a frame widget to act as the border of the canvas.
+ self._borderframe = self.createcomponent('borderframe',
+ (), None,
+ Tkinter.Frame, (self.origInterior,),
+ relief = 'sunken',
+ borderwidth = 2,
+ )
+ self._borderframe.grid(row = 2, column = 2, sticky = 'news')
+
+ # Create the canvas widget.
+ self._canvas = self.createcomponent('canvas',
+ (), None,
+ Tkinter.Canvas, (self._borderframe,),
+ highlightthickness = 0,
+ borderwidth = 0,
+ )
+ self._canvas.pack(fill = 'both', expand = 1)
+ else:
+ # Create the canvas widget.
+ self._canvas = self.createcomponent('canvas',
+ (), None,
+ Tkinter.Canvas, (self.origInterior,),
+ relief = 'sunken',
+ borderwidth = 2,
+ )
+ self._canvas.grid(row = 2, column = 2, sticky = 'news')
+
+ self.origInterior.grid_rowconfigure(2, weight = 1, minsize = 0)
+ self.origInterior.grid_columnconfigure(2, weight = 1, minsize = 0)
+
+ # Create the horizontal scrollbar
+ self._horizScrollbar = self.createcomponent('horizscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (self.origInterior,),
+ orient='horizontal',
+ command=self._canvas.xview
+ )
+
+ # Create the vertical scrollbar
+ self._vertScrollbar = self.createcomponent('vertscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (self.origInterior,),
+ orient='vertical',
+ command=self._canvas.yview
+ )
+
+ self.createlabel(self.origInterior, childCols = 3, childRows = 3)
+
+ # Initialise instance variables.
+ self._horizScrollbarOn = 0
+ self._vertScrollbarOn = 0
+ self.scrollTimer = None
+ self._scrollRecurse = 0
+ self._horizScrollbarNeeded = 0
+ self._vertScrollbarNeeded = 0
+ self.setregionTimer = None
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self.scrollTimer is not None:
+ self.after_cancel(self.scrollTimer)
+ self.scrollTimer = None
+ if self.setregionTimer is not None:
+ self.after_cancel(self.setregionTimer)
+ self.setregionTimer = None
+ Pmw.MegaWidget.destroy(self)
+
+ # ======================================================================
+
+ # Public methods.
+
+ def interior(self):
+ return self._canvas
+
+ def resizescrollregion(self):
+ if self.setregionTimer is None:
+ self.setregionTimer = self.after_idle(self._setRegion)
+
+ # ======================================================================
+
+ # Configuration methods.
+
+ def _hscrollMode(self):
+ # The horizontal scroll mode has been configured.
+
+ mode = self['hscrollmode']
+
+ if mode == 'static':
+ if not self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'none':
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ self._configureScrollCommands()
+
+ def _vscrollMode(self):
+ # The vertical scroll mode has been configured.
+
+ mode = self['vscrollmode']
+
+ if mode == 'static':
+ if not self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'none':
+ if self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ else:
+ message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ self._configureScrollCommands()
+
+ # ======================================================================
+
+ # Private methods.
+
+ def _configureScrollCommands(self):
+ # If both scrollmodes are not dynamic we can save a lot of
+ # time by not having to create an idle job to handle the
+ # scroll commands.
+
+ # Clean up previous scroll commands to prevent memory leak.
+ tclCommandName = str(self._canvas.cget('xscrollcommand'))
+ if tclCommandName != '':
+ self._canvas.deletecommand(tclCommandName)
+ tclCommandName = str(self._canvas.cget('yscrollcommand'))
+ if tclCommandName != '':
+ self._canvas.deletecommand(tclCommandName)
+
+ if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
+ self._canvas.configure(
+ xscrollcommand=self._scrollBothLater,
+ yscrollcommand=self._scrollBothLater
+ )
+ else:
+ self._canvas.configure(
+ xscrollcommand=self._scrollXNow,
+ yscrollcommand=self._scrollYNow
+ )
+
+ def _scrollXNow(self, first, last):
+ self._horizScrollbar.set(first, last)
+ self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
+
+ if self['hscrollmode'] == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+
+ def _scrollYNow(self, first, last):
+ self._vertScrollbar.set(first, last)
+ self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ def _scrollBothLater(self, first, last):
+ # Called by the canvas to set the horizontal or vertical
+ # scrollbar when it has scrolled or changed scrollregion.
+
+ if self.scrollTimer is None:
+ self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+ def _scrollBothNow(self):
+ # This performs the function of _scrollXNow and _scrollYNow.
+ # If one is changed, the other should be updated to match.
+ self.scrollTimer = None
+
+ # Call update_idletasks to make sure that the containing frame
+ # has been resized before we attempt to set the scrollbars.
+ # Otherwise the scrollbars may be mapped/unmapped continuously.
+ self._scrollRecurse = self._scrollRecurse + 1
+ self.update_idletasks()
+ self._scrollRecurse = self._scrollRecurse - 1
+ if self._scrollRecurse != 0:
+ return
+
+ xview = self._canvas.xview()
+ yview = self._canvas.yview()
+ self._horizScrollbar.set(xview[0], xview[1])
+ self._vertScrollbar.set(yview[0], yview[1])
+
+ self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+ self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+ # If both horizontal and vertical scrollmodes are dynamic and
+ # currently only one scrollbar is mapped and both should be
+ # toggled, then unmap the mapped scrollbar. This prevents a
+ # continuous mapping and unmapping of the scrollbars.
+ if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+ self._horizScrollbarNeeded != self._horizScrollbarOn and
+ self._vertScrollbarNeeded != self._vertScrollbarOn and
+ self._vertScrollbarOn != self._horizScrollbarOn):
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ self._toggleVertScrollbar()
+ return
+
+ if self['hscrollmode'] == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ def _toggleHorizScrollbar(self):
+
+ self._horizScrollbarOn = not self._horizScrollbarOn
+
+ interior = self.origInterior
+ if self._horizScrollbarOn:
+ self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
+ interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
+ else:
+ self._horizScrollbar.grid_forget()
+ interior.grid_rowconfigure(3, minsize = 0)
+
+ def _toggleVertScrollbar(self):
+
+ self._vertScrollbarOn = not self._vertScrollbarOn
+
+ interior = self.origInterior
+ if self._vertScrollbarOn:
+ self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
+ interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
+ else:
+ self._vertScrollbar.grid_forget()
+ interior.grid_columnconfigure(3, minsize = 0)
+
+ def _setRegion(self):
+ self.setregionTimer = None
+
+ region = self._canvas.bbox('all')
+ if region is not None:
+ canvasmargin = self['canvasmargin']
+ region = (region[0] - canvasmargin, region[1] - canvasmargin,
+ region[2] + canvasmargin, region[3] + canvasmargin)
+ self._canvas.configure(scrollregion = region)
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+ def bbox(self, *args):
+ return apply(self._canvas.bbox, args)
+
+Pmw.forwardmethods(ScrolledCanvas, Tkinter.Canvas, '_canvas')
--- /dev/null
+import Tkinter
+import Pmw
+
+class ScrolledField(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('sticky', 'ew', INITOPT),
+ ('text', '', self._text),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ self._scrolledFieldEntry = self.createcomponent('entry',
+ (), None,
+ Tkinter.Entry, (interior,))
+
+ # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+ try:
+ self._scrolledFieldEntry.configure(state = 'readonly')
+ except Tkinter.TclError:
+ self._scrolledFieldEntry.configure(state = 'disabled')
+
+ self._scrolledFieldEntry.grid(column=2, row=2, sticky=self['sticky'])
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ self.createlabel(interior)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _text(self):
+ text = self['text']
+ self._scrolledFieldEntry.configure(state = 'normal')
+ self._scrolledFieldEntry.delete(0, 'end')
+ self._scrolledFieldEntry.insert('end', text)
+
+ # Can't always use 'disabled', since this greys out text in Tk 8.4.2
+ try:
+ self._scrolledFieldEntry.configure(state = 'readonly')
+ except Tkinter.TclError:
+ self._scrolledFieldEntry.configure(state = 'disabled')
+
+Pmw.forwardmethods(ScrolledField, Tkinter.Entry, '_scrolledFieldEntry')
--- /dev/null
+import string
+import types
+import Tkinter
+import Pmw
+
+class ScrolledFrame(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderframe', 1, INITOPT),
+ ('horizflex', 'fixed', self._horizflex),
+ ('horizfraction', 0.05, INITOPT),
+ ('hscrollmode', 'dynamic', self._hscrollMode),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('scrollmargin', 2, INITOPT),
+ ('usehullsize', 0, INITOPT),
+ ('vertflex', 'fixed', self._vertflex),
+ ('vertfraction', 0.05, INITOPT),
+ ('vscrollmode', 'dynamic', self._vscrollMode),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ self.origInterior = Pmw.MegaWidget.interior(self)
+
+ if self['usehullsize']:
+ self.origInterior.grid_propagate(0)
+
+ if self['borderframe']:
+ # Create a frame widget to act as the border of the clipper.
+ self._borderframe = self.createcomponent('borderframe',
+ (), None,
+ Tkinter.Frame, (self.origInterior,),
+ relief = 'sunken',
+ borderwidth = 2,
+ )
+ self._borderframe.grid(row = 2, column = 2, sticky = 'news')
+
+ # Create the clipping window.
+ self._clipper = self.createcomponent('clipper',
+ (), None,
+ Tkinter.Frame, (self._borderframe,),
+ width = 400,
+ height = 300,
+ highlightthickness = 0,
+ borderwidth = 0,
+ )
+ self._clipper.pack(fill = 'both', expand = 1)
+ else:
+ # Create the clipping window.
+ self._clipper = self.createcomponent('clipper',
+ (), None,
+ Tkinter.Frame, (self.origInterior,),
+ width = 400,
+ height = 300,
+ relief = 'sunken',
+ borderwidth = 2,
+ )
+ self._clipper.grid(row = 2, column = 2, sticky = 'news')
+
+ self.origInterior.grid_rowconfigure(2, weight = 1, minsize = 0)
+ self.origInterior.grid_columnconfigure(2, weight = 1, minsize = 0)
+
+ # Create the horizontal scrollbar
+ self._horizScrollbar = self.createcomponent('horizscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (self.origInterior,),
+ orient='horizontal',
+ command=self.xview
+ )
+
+ # Create the vertical scrollbar
+ self._vertScrollbar = self.createcomponent('vertscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (self.origInterior,),
+ orient='vertical',
+ command=self.yview
+ )
+
+ self.createlabel(self.origInterior, childCols = 3, childRows = 3)
+
+ # Initialise instance variables.
+ self._horizScrollbarOn = 0
+ self._vertScrollbarOn = 0
+ self.scrollTimer = None
+ self._scrollRecurse = 0
+ self._horizScrollbarNeeded = 0
+ self._vertScrollbarNeeded = 0
+ self.startX = 0
+ self.startY = 0
+ self._flexoptions = ('fixed', 'expand', 'shrink', 'elastic')
+
+ # Create a frame in the clipper to contain the widgets to be
+ # scrolled.
+ self._frame = self.createcomponent('frame',
+ (), None,
+ Tkinter.Frame, (self._clipper,)
+ )
+
+ # Whenever the clipping window or scrolled frame change size,
+ # update the scrollbars.
+ self._frame.bind('<Configure>', self._reposition)
+ self._clipper.bind('<Configure>', self._reposition)
+
+ # Work around a bug in Tk where the value returned by the
+ # scrollbar get() method is (0.0, 0.0, 0.0, 0.0) rather than
+ # the expected 2-tuple. This occurs if xview() is called soon
+ # after the Pmw.ScrolledFrame has been created.
+ self._horizScrollbar.set(0.0, 1.0)
+ self._vertScrollbar.set(0.0, 1.0)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self.scrollTimer is not None:
+ self.after_cancel(self.scrollTimer)
+ self.scrollTimer = None
+ Pmw.MegaWidget.destroy(self)
+
+ # ======================================================================
+
+ # Public methods.
+
+ def interior(self):
+ return self._frame
+
+ # Set timer to call real reposition method, so that it is not
+ # called multiple times when many things are reconfigured at the
+ # same time.
+ def reposition(self):
+ if self.scrollTimer is None:
+ self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+ # Called when the user clicks in the horizontal scrollbar.
+ # Calculates new position of frame then calls reposition() to
+ # update the frame and the scrollbar.
+ def xview(self, mode = None, value = None, units = None):
+
+ if type(value) == types.StringType:
+ value = string.atof(value)
+ if mode is None:
+ return self._horizScrollbar.get()
+ elif mode == 'moveto':
+ frameWidth = self._frame.winfo_reqwidth()
+ self.startX = value * float(frameWidth)
+ else: # mode == 'scroll'
+ clipperWidth = self._clipper.winfo_width()
+ if units == 'units':
+ jump = int(clipperWidth * self['horizfraction'])
+ else:
+ jump = clipperWidth
+ self.startX = self.startX + value * jump
+
+ self.reposition()
+
+ # Called when the user clicks in the vertical scrollbar.
+ # Calculates new position of frame then calls reposition() to
+ # update the frame and the scrollbar.
+ def yview(self, mode = None, value = None, units = None):
+
+ if type(value) == types.StringType:
+ value = string.atof(value)
+ if mode is None:
+ return self._vertScrollbar.get()
+ elif mode == 'moveto':
+ frameHeight = self._frame.winfo_reqheight()
+ self.startY = value * float(frameHeight)
+ else: # mode == 'scroll'
+ clipperHeight = self._clipper.winfo_height()
+ if units == 'units':
+ jump = int(clipperHeight * self['vertfraction'])
+ else:
+ jump = clipperHeight
+ self.startY = self.startY + value * jump
+
+ self.reposition()
+
+ # ======================================================================
+
+ # Configuration methods.
+
+ def _hscrollMode(self):
+ # The horizontal scroll mode has been configured.
+
+ mode = self['hscrollmode']
+
+ if mode == 'static':
+ if not self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'none':
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ def _vscrollMode(self):
+ # The vertical scroll mode has been configured.
+
+ mode = self['vscrollmode']
+
+ if mode == 'static':
+ if not self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'none':
+ if self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ else:
+ message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ def _horizflex(self):
+ # The horizontal flex mode has been configured.
+
+ flex = self['horizflex']
+
+ if flex not in self._flexoptions:
+ message = 'bad horizflex option "%s": should be one of %s' % \
+ (flex, str(self._flexoptions))
+ raise ValueError, message
+
+ self.reposition()
+
+ def _vertflex(self):
+ # The vertical flex mode has been configured.
+
+ flex = self['vertflex']
+
+ if flex not in self._flexoptions:
+ message = 'bad vertflex option "%s": should be one of %s' % \
+ (flex, str(self._flexoptions))
+ raise ValueError, message
+
+ self.reposition()
+
+ # ======================================================================
+
+ # Private methods.
+
+ def _reposition(self, event):
+ self.reposition()
+
+ def _getxview(self):
+
+ # Horizontal dimension.
+ clipperWidth = self._clipper.winfo_width()
+ frameWidth = self._frame.winfo_reqwidth()
+ if frameWidth <= clipperWidth:
+ # The scrolled frame is smaller than the clipping window.
+
+ self.startX = 0
+ endScrollX = 1.0
+
+ if self['horizflex'] in ('expand', 'elastic'):
+ relwidth = 1
+ else:
+ relwidth = ''
+ else:
+ # The scrolled frame is larger than the clipping window.
+
+ if self['horizflex'] in ('shrink', 'elastic'):
+ self.startX = 0
+ endScrollX = 1.0
+ relwidth = 1
+ else:
+ if self.startX + clipperWidth > frameWidth:
+ self.startX = frameWidth - clipperWidth
+ endScrollX = 1.0
+ else:
+ if self.startX < 0:
+ self.startX = 0
+ endScrollX = (self.startX + clipperWidth) / float(frameWidth)
+ relwidth = ''
+
+ # Position frame relative to clipper.
+ self._frame.place(x = -self.startX, relwidth = relwidth)
+ return (self.startX / float(frameWidth), endScrollX)
+
+ def _getyview(self):
+
+ # Vertical dimension.
+ clipperHeight = self._clipper.winfo_height()
+ frameHeight = self._frame.winfo_reqheight()
+ if frameHeight <= clipperHeight:
+ # The scrolled frame is smaller than the clipping window.
+
+ self.startY = 0
+ endScrollY = 1.0
+
+ if self['vertflex'] in ('expand', 'elastic'):
+ relheight = 1
+ else:
+ relheight = ''
+ else:
+ # The scrolled frame is larger than the clipping window.
+
+ if self['vertflex'] in ('shrink', 'elastic'):
+ self.startY = 0
+ endScrollY = 1.0
+ relheight = 1
+ else:
+ if self.startY + clipperHeight > frameHeight:
+ self.startY = frameHeight - clipperHeight
+ endScrollY = 1.0
+ else:
+ if self.startY < 0:
+ self.startY = 0
+ endScrollY = (self.startY + clipperHeight) / float(frameHeight)
+ relheight = ''
+
+ # Position frame relative to clipper.
+ self._frame.place(y = -self.startY, relheight = relheight)
+ return (self.startY / float(frameHeight), endScrollY)
+
+ # According to the relative geometries of the frame and the
+ # clipper, reposition the frame within the clipper and reset the
+ # scrollbars.
+ def _scrollBothNow(self):
+ self.scrollTimer = None
+
+ # Call update_idletasks to make sure that the containing frame
+ # has been resized before we attempt to set the scrollbars.
+ # Otherwise the scrollbars may be mapped/unmapped continuously.
+ self._scrollRecurse = self._scrollRecurse + 1
+ self.update_idletasks()
+ self._scrollRecurse = self._scrollRecurse - 1
+ if self._scrollRecurse != 0:
+ return
+
+ xview = self._getxview()
+ yview = self._getyview()
+ self._horizScrollbar.set(xview[0], xview[1])
+ self._vertScrollbar.set(yview[0], yview[1])
+
+ self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+ self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+ # If both horizontal and vertical scrollmodes are dynamic and
+ # currently only one scrollbar is mapped and both should be
+ # toggled, then unmap the mapped scrollbar. This prevents a
+ # continuous mapping and unmapping of the scrollbars.
+ if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+ self._horizScrollbarNeeded != self._horizScrollbarOn and
+ self._vertScrollbarNeeded != self._vertScrollbarOn and
+ self._vertScrollbarOn != self._horizScrollbarOn):
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ self._toggleVertScrollbar()
+ return
+
+ if self['hscrollmode'] == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ def _toggleHorizScrollbar(self):
+
+ self._horizScrollbarOn = not self._horizScrollbarOn
+
+ interior = self.origInterior
+ if self._horizScrollbarOn:
+ self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
+ interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
+ else:
+ self._horizScrollbar.grid_forget()
+ interior.grid_rowconfigure(3, minsize = 0)
+
+ def _toggleVertScrollbar(self):
+
+ self._vertScrollbarOn = not self._vertScrollbarOn
+
+ interior = self.origInterior
+ if self._vertScrollbarOn:
+ self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
+ interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
+ else:
+ self._vertScrollbar.grid_forget()
+ interior.grid_columnconfigure(3, minsize = 0)
--- /dev/null
+# Based on iwidgets2.2.0/scrolledlistbox.itk code.
+
+import types
+import Tkinter
+import Pmw
+
+class ScrolledListBox(Pmw.MegaWidget):
+ _classBindingsDefinedFor = 0
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('dblclickcommand', None, None),
+ ('hscrollmode', 'dynamic', self._hscrollMode),
+ ('items', (), INITOPT),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('scrollmargin', 2, INITOPT),
+ ('selectioncommand', None, None),
+ ('usehullsize', 0, INITOPT),
+ ('vscrollmode', 'dynamic', self._vscrollMode),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ if self['usehullsize']:
+ interior.grid_propagate(0)
+
+ # Create the listbox widget.
+ self._listbox = self.createcomponent('listbox',
+ (), None,
+ Tkinter.Listbox, (interior,))
+ self._listbox.grid(row = 2, column = 2, sticky = 'news')
+ interior.grid_rowconfigure(2, weight = 1, minsize = 0)
+ interior.grid_columnconfigure(2, weight = 1, minsize = 0)
+
+ # Create the horizontal scrollbar
+ self._horizScrollbar = self.createcomponent('horizscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (interior,),
+ orient='horizontal',
+ command=self._listbox.xview
+ )
+
+ # Create the vertical scrollbar
+ self._vertScrollbar = self.createcomponent('vertscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (interior,),
+ orient='vertical',
+ command=self._listbox.yview
+ )
+
+ self.createlabel(interior, childCols = 3, childRows = 3)
+
+ # Add the items specified by the initialisation option.
+ items = self['items']
+ if type(items) != types.TupleType:
+ items = tuple(items)
+ if len(items) > 0:
+ apply(self._listbox.insert, ('end',) + items)
+
+ _registerScrolledList(self._listbox, self)
+
+ # Establish the special class bindings if not already done.
+ # Also create bindings if the Tkinter default interpreter has
+ # changed. Use Tkinter._default_root to create class
+ # bindings, so that a reference to root is created by
+ # bind_class rather than a reference to self, which would
+ # prevent object cleanup.
+ theTag = 'ScrolledListBoxTag'
+ if ScrolledListBox._classBindingsDefinedFor != Tkinter._default_root:
+ root = Tkinter._default_root
+
+ def doubleEvent(event):
+ _handleEvent(event, 'double')
+ def keyEvent(event):
+ _handleEvent(event, 'key')
+ def releaseEvent(event):
+ _handleEvent(event, 'release')
+
+ # Bind space and return keys and button 1 to the selectioncommand.
+ root.bind_class(theTag, '<Key-space>', keyEvent)
+ root.bind_class(theTag, '<Key-Return>', keyEvent)
+ root.bind_class(theTag, '<ButtonRelease-1>', releaseEvent)
+
+ # Bind double button 1 click to the dblclickcommand.
+ root.bind_class(theTag, '<Double-ButtonRelease-1>', doubleEvent)
+
+ ScrolledListBox._classBindingsDefinedFor = root
+
+ bindtags = self._listbox.bindtags()
+ self._listbox.bindtags(bindtags + (theTag,))
+
+ # Initialise instance variables.
+ self._horizScrollbarOn = 0
+ self._vertScrollbarOn = 0
+ self.scrollTimer = None
+ self._scrollRecurse = 0
+ self._horizScrollbarNeeded = 0
+ self._vertScrollbarNeeded = 0
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self.scrollTimer is not None:
+ self.after_cancel(self.scrollTimer)
+ self.scrollTimer = None
+ _deregisterScrolledList(self._listbox)
+ Pmw.MegaWidget.destroy(self)
+
+ # ======================================================================
+
+ # Public methods.
+
+ def clear(self):
+ self.setlist(())
+
+ def getcurselection(self):
+ rtn = []
+ for sel in self.curselection():
+ rtn.append(self._listbox.get(sel))
+ return tuple(rtn)
+
+ def getvalue(self):
+ return self.getcurselection()
+
+ def setvalue(self, textOrList):
+ self._listbox.selection_clear(0, 'end')
+ listitems = list(self._listbox.get(0, 'end'))
+ if type(textOrList) == types.StringType:
+ if textOrList in listitems:
+ self._listbox.selection_set(listitems.index(textOrList))
+ else:
+ raise ValueError, 'no such item "%s"' % textOrList
+ else:
+ for item in textOrList:
+ if item in listitems:
+ self._listbox.selection_set(listitems.index(item))
+ else:
+ raise ValueError, 'no such item "%s"' % item
+
+ def setlist(self, items):
+ self._listbox.delete(0, 'end')
+ if len(items) > 0:
+ if type(items) != types.TupleType:
+ items = tuple(items)
+ apply(self._listbox.insert, (0,) + items)
+
+ # Override Tkinter.Listbox get method, so that if it is called with
+ # no arguments, return all list elements (consistent with other widgets).
+ def get(self, first=None, last=None):
+ if first is None:
+ return self._listbox.get(0, 'end')
+ else:
+ return self._listbox.get(first, last)
+
+ # ======================================================================
+
+ # Configuration methods.
+
+ def _hscrollMode(self):
+ # The horizontal scroll mode has been configured.
+
+ mode = self['hscrollmode']
+
+ if mode == 'static':
+ if not self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'none':
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ self._configureScrollCommands()
+
+ def _vscrollMode(self):
+ # The vertical scroll mode has been configured.
+
+ mode = self['vscrollmode']
+
+ if mode == 'static':
+ if not self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'none':
+ if self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ else:
+ message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ self._configureScrollCommands()
+
+ # ======================================================================
+
+ # Private methods.
+
+ def _configureScrollCommands(self):
+ # If both scrollmodes are not dynamic we can save a lot of
+ # time by not having to create an idle job to handle the
+ # scroll commands.
+
+ # Clean up previous scroll commands to prevent memory leak.
+ tclCommandName = str(self._listbox.cget('xscrollcommand'))
+ if tclCommandName != '':
+ self._listbox.deletecommand(tclCommandName)
+ tclCommandName = str(self._listbox.cget('yscrollcommand'))
+ if tclCommandName != '':
+ self._listbox.deletecommand(tclCommandName)
+
+ if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
+ self._listbox.configure(
+ xscrollcommand=self._scrollBothLater,
+ yscrollcommand=self._scrollBothLater
+ )
+ else:
+ self._listbox.configure(
+ xscrollcommand=self._scrollXNow,
+ yscrollcommand=self._scrollYNow
+ )
+
+ def _scrollXNow(self, first, last):
+ self._horizScrollbar.set(first, last)
+ self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
+
+ if self['hscrollmode'] == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+
+ def _scrollYNow(self, first, last):
+ self._vertScrollbar.set(first, last)
+ self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ def _scrollBothLater(self, first, last):
+ # Called by the listbox to set the horizontal or vertical
+ # scrollbar when it has scrolled or changed size or contents.
+
+ if self.scrollTimer is None:
+ self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+ def _scrollBothNow(self):
+ # This performs the function of _scrollXNow and _scrollYNow.
+ # If one is changed, the other should be updated to match.
+ self.scrollTimer = None
+
+ # Call update_idletasks to make sure that the containing frame
+ # has been resized before we attempt to set the scrollbars.
+ # Otherwise the scrollbars may be mapped/unmapped continuously.
+ self._scrollRecurse = self._scrollRecurse + 1
+ self.update_idletasks()
+ self._scrollRecurse = self._scrollRecurse - 1
+ if self._scrollRecurse != 0:
+ return
+
+ xview = self._listbox.xview()
+ yview = self._listbox.yview()
+ self._horizScrollbar.set(xview[0], xview[1])
+ self._vertScrollbar.set(yview[0], yview[1])
+
+ self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+ self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+ # If both horizontal and vertical scrollmodes are dynamic and
+ # currently only one scrollbar is mapped and both should be
+ # toggled, then unmap the mapped scrollbar. This prevents a
+ # continuous mapping and unmapping of the scrollbars.
+ if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+ self._horizScrollbarNeeded != self._horizScrollbarOn and
+ self._vertScrollbarNeeded != self._vertScrollbarOn and
+ self._vertScrollbarOn != self._horizScrollbarOn):
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ self._toggleVertScrollbar()
+ return
+
+ if self['hscrollmode'] == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ def _toggleHorizScrollbar(self):
+
+ self._horizScrollbarOn = not self._horizScrollbarOn
+
+ interior = self.interior()
+ if self._horizScrollbarOn:
+ self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
+ interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
+ else:
+ self._horizScrollbar.grid_forget()
+ interior.grid_rowconfigure(3, minsize = 0)
+
+ def _toggleVertScrollbar(self):
+
+ self._vertScrollbarOn = not self._vertScrollbarOn
+
+ interior = self.interior()
+ if self._vertScrollbarOn:
+ self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
+ interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
+ else:
+ self._vertScrollbar.grid_forget()
+ interior.grid_columnconfigure(3, minsize = 0)
+
+ def _handleEvent(self, event, eventType):
+ if eventType == 'double':
+ command = self['dblclickcommand']
+ elif eventType == 'key':
+ command = self['selectioncommand']
+ else: #eventType == 'release'
+ # Do not execute the command if the mouse was released
+ # outside the listbox.
+ if (event.x < 0 or self._listbox.winfo_width() <= event.x or
+ event.y < 0 or self._listbox.winfo_height() <= event.y):
+ return
+
+ command = self['selectioncommand']
+
+ if callable(command):
+ command()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)size method inherited from Tkinter.Frame.Grid.
+ def size(self):
+ return self._listbox.size()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+ def bbox(self, index):
+ return self._listbox.bbox(index)
+
+Pmw.forwardmethods(ScrolledListBox, Tkinter.Listbox, '_listbox')
+
+# ======================================================================
+
+_listboxCache = {}
+
+def _registerScrolledList(listbox, scrolledList):
+ # Register an ScrolledList widget for a Listbox widget
+
+ _listboxCache[listbox] = scrolledList
+
+def _deregisterScrolledList(listbox):
+ # Deregister a Listbox widget
+ del _listboxCache[listbox]
+
+def _handleEvent(event, eventType):
+ # Forward events for a Listbox to it's ScrolledListBox
+
+ # A binding earlier in the bindtags list may have destroyed the
+ # megawidget, so need to check.
+ if _listboxCache.has_key(event.widget):
+ _listboxCache[event.widget]._handleEvent(event, eventType)
--- /dev/null
+# Based on iwidgets2.2.0/scrolledtext.itk code.
+
+import Tkinter
+import Pmw
+
+class ScrolledText(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderframe', 0, INITOPT),
+ ('columnheader', 0, INITOPT),
+ ('hscrollmode', 'dynamic', self._hscrollMode),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('rowcolumnheader',0, INITOPT),
+ ('rowheader', 0, INITOPT),
+ ('scrollmargin', 2, INITOPT),
+ ('usehullsize', 0, INITOPT),
+ ('vscrollmode', 'dynamic', self._vscrollMode),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+
+ if self['usehullsize']:
+ interior.grid_propagate(0)
+
+ if self['borderframe']:
+ # Create a frame widget to act as the border of the text
+ # widget. Later, pack the text widget so that it fills
+ # the frame. This avoids a problem in Tk, where window
+ # items in a text widget may overlap the border of the
+ # text widget.
+ self._borderframe = self.createcomponent('borderframe',
+ (), None,
+ Tkinter.Frame, (interior,),
+ relief = 'sunken',
+ borderwidth = 2,
+ )
+ self._borderframe.grid(row = 4, column = 4, sticky = 'news')
+
+ # Create the text widget.
+ self._textbox = self.createcomponent('text',
+ (), None,
+ Tkinter.Text, (self._borderframe,),
+ highlightthickness = 0,
+ borderwidth = 0,
+ )
+ self._textbox.pack(fill = 'both', expand = 1)
+
+ bw = self._borderframe.cget('borderwidth'),
+ ht = self._borderframe.cget('highlightthickness'),
+ else:
+ # Create the text widget.
+ self._textbox = self.createcomponent('text',
+ (), None,
+ Tkinter.Text, (interior,),
+ )
+ self._textbox.grid(row = 4, column = 4, sticky = 'news')
+
+ bw = self._textbox.cget('borderwidth'),
+ ht = self._textbox.cget('highlightthickness'),
+
+ # Create the header text widgets
+ if self['columnheader']:
+ self._columnheader = self.createcomponent('columnheader',
+ (), 'Header',
+ Tkinter.Text, (interior,),
+ height=1,
+ wrap='none',
+ borderwidth = bw,
+ highlightthickness = ht,
+ )
+ self._columnheader.grid(row = 2, column = 4, sticky = 'ew')
+ self._columnheader.configure(
+ xscrollcommand = self._columnheaderscrolled)
+
+ if self['rowheader']:
+ self._rowheader = self.createcomponent('rowheader',
+ (), 'Header',
+ Tkinter.Text, (interior,),
+ wrap='none',
+ borderwidth = bw,
+ highlightthickness = ht,
+ )
+ self._rowheader.grid(row = 4, column = 2, sticky = 'ns')
+ self._rowheader.configure(
+ yscrollcommand = self._rowheaderscrolled)
+
+ if self['rowcolumnheader']:
+ self._rowcolumnheader = self.createcomponent('rowcolumnheader',
+ (), 'Header',
+ Tkinter.Text, (interior,),
+ height=1,
+ wrap='none',
+ borderwidth = bw,
+ highlightthickness = ht,
+ )
+ self._rowcolumnheader.grid(row = 2, column = 2, sticky = 'nsew')
+
+ interior.grid_rowconfigure(4, weight = 1, minsize = 0)
+ interior.grid_columnconfigure(4, weight = 1, minsize = 0)
+
+ # Create the horizontal scrollbar
+ self._horizScrollbar = self.createcomponent('horizscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (interior,),
+ orient='horizontal',
+ command=self._textbox.xview
+ )
+
+ # Create the vertical scrollbar
+ self._vertScrollbar = self.createcomponent('vertscrollbar',
+ (), 'Scrollbar',
+ Tkinter.Scrollbar, (interior,),
+ orient='vertical',
+ command=self._textbox.yview
+ )
+
+ self.createlabel(interior, childCols = 5, childRows = 5)
+
+ # Initialise instance variables.
+ self._horizScrollbarOn = 0
+ self._vertScrollbarOn = 0
+ self.scrollTimer = None
+ self._scrollRecurse = 0
+ self._horizScrollbarNeeded = 0
+ self._vertScrollbarNeeded = 0
+ self._textWidth = None
+
+ # These four variables avoid an infinite loop caused by the
+ # row or column header's scrollcommand causing the main text
+ # widget's scrollcommand to be called and vice versa.
+ self._textboxLastX = None
+ self._textboxLastY = None
+ self._columnheaderLastX = None
+ self._rowheaderLastY = None
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def destroy(self):
+ if self.scrollTimer is not None:
+ self.after_cancel(self.scrollTimer)
+ self.scrollTimer = None
+ Pmw.MegaWidget.destroy(self)
+
+ # ======================================================================
+
+ # Public methods.
+
+ def clear(self):
+ self.settext('')
+
+ def importfile(self, fileName, where = 'end'):
+ file = open(fileName, 'r')
+ self._textbox.insert(where, file.read())
+ file.close()
+
+ def exportfile(self, fileName):
+ file = open(fileName, 'w')
+ file.write(self._textbox.get('1.0', 'end'))
+ file.close()
+
+ def settext(self, text):
+ disabled = (str(self._textbox.cget('state')) == 'disabled')
+ if disabled:
+ self._textbox.configure(state='normal')
+ self._textbox.delete('0.0', 'end')
+ self._textbox.insert('end', text)
+ if disabled:
+ self._textbox.configure(state='disabled')
+
+ # Override Tkinter.Text get method, so that if it is called with
+ # no arguments, return all text (consistent with other widgets).
+ def get(self, first=None, last=None):
+ if first is None:
+ return self._textbox.get('1.0', 'end')
+ else:
+ return self._textbox.get(first, last)
+
+ def getvalue(self):
+ return self.get()
+
+ def setvalue(self, text):
+ return self.settext(text)
+
+ def appendtext(self, text):
+ oldTop, oldBottom = self._textbox.yview()
+
+ disabled = (str(self._textbox.cget('state')) == 'disabled')
+ if disabled:
+ self._textbox.configure(state='normal')
+ self._textbox.insert('end', text)
+ if disabled:
+ self._textbox.configure(state='disabled')
+
+ if oldBottom == 1.0:
+ self._textbox.yview('moveto', 1.0)
+
+ # ======================================================================
+
+ # Configuration methods.
+
+ def _hscrollMode(self):
+ # The horizontal scroll mode has been configured.
+
+ mode = self['hscrollmode']
+
+ if mode == 'static':
+ if not self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'dynamic':
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ elif mode == 'none':
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ self._configureScrollCommands()
+
+ def _vscrollMode(self):
+ # The vertical scroll mode has been configured.
+
+ mode = self['vscrollmode']
+
+ if mode == 'static':
+ if not self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ elif mode == 'none':
+ if self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+ else:
+ message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
+ raise ValueError, message
+
+ self._configureScrollCommands()
+
+ # ======================================================================
+
+ # Private methods.
+
+ def _configureScrollCommands(self):
+ # If both scrollmodes are not dynamic we can save a lot of
+ # time by not having to create an idle job to handle the
+ # scroll commands.
+
+ # Clean up previous scroll commands to prevent memory leak.
+ tclCommandName = str(self._textbox.cget('xscrollcommand'))
+ if tclCommandName != '':
+ self._textbox.deletecommand(tclCommandName)
+ tclCommandName = str(self._textbox.cget('yscrollcommand'))
+ if tclCommandName != '':
+ self._textbox.deletecommand(tclCommandName)
+
+ if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
+ self._textbox.configure(
+ xscrollcommand=self._scrollBothLater,
+ yscrollcommand=self._scrollBothLater
+ )
+ else:
+ self._textbox.configure(
+ xscrollcommand=self._scrollXNow,
+ yscrollcommand=self._scrollYNow
+ )
+
+ def _scrollXNow(self, first, last):
+ self._horizScrollbar.set(first, last)
+ self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
+
+ # This code is the same as in _scrollBothNow. Keep it that way.
+ if self['hscrollmode'] == 'dynamic':
+ currentWidth = self._textbox.winfo_width()
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ if self._horizScrollbarNeeded or \
+ self._textWidth != currentWidth:
+ self._toggleHorizScrollbar()
+ self._textWidth = currentWidth
+
+ if self['columnheader']:
+ if self._columnheaderLastX != first:
+ self._columnheaderLastX = first
+ self._columnheader.xview('moveto', first)
+
+ def _scrollYNow(self, first, last):
+ if first == '0' and last == '0':
+ return
+ self._vertScrollbar.set(first, last)
+ self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ if self['rowheader']:
+ if self._rowheaderLastY != first:
+ self._rowheaderLastY = first
+ self._rowheader.yview('moveto', first)
+
+ def _scrollBothLater(self, first, last):
+ # Called by the text widget to set the horizontal or vertical
+ # scrollbar when it has scrolled or changed size or contents.
+
+ if self.scrollTimer is None:
+ self.scrollTimer = self.after_idle(self._scrollBothNow)
+
+ def _scrollBothNow(self):
+ # This performs the function of _scrollXNow and _scrollYNow.
+ # If one is changed, the other should be updated to match.
+ self.scrollTimer = None
+
+ # Call update_idletasks to make sure that the containing frame
+ # has been resized before we attempt to set the scrollbars.
+ # Otherwise the scrollbars may be mapped/unmapped continuously.
+ self._scrollRecurse = self._scrollRecurse + 1
+ self.update_idletasks()
+ self._scrollRecurse = self._scrollRecurse - 1
+ if self._scrollRecurse != 0:
+ return
+
+ xview = self._textbox.xview()
+ yview = self._textbox.yview()
+
+ # The text widget returns a yview of (0.0, 0.0) just after it
+ # has been created. Ignore this.
+ if yview == (0.0, 0.0):
+ return
+
+ if self['columnheader']:
+ if self._columnheaderLastX != xview[0]:
+ self._columnheaderLastX = xview[0]
+ self._columnheader.xview('moveto', xview[0])
+ if self['rowheader']:
+ if self._rowheaderLastY != yview[0]:
+ self._rowheaderLastY = yview[0]
+ self._rowheader.yview('moveto', yview[0])
+
+ self._horizScrollbar.set(xview[0], xview[1])
+ self._vertScrollbar.set(yview[0], yview[1])
+
+ self._horizScrollbarNeeded = (xview != (0.0, 1.0))
+ self._vertScrollbarNeeded = (yview != (0.0, 1.0))
+
+ # If both horizontal and vertical scrollmodes are dynamic and
+ # currently only one scrollbar is mapped and both should be
+ # toggled, then unmap the mapped scrollbar. This prevents a
+ # continuous mapping and unmapping of the scrollbars.
+ if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
+ self._horizScrollbarNeeded != self._horizScrollbarOn and
+ self._vertScrollbarNeeded != self._vertScrollbarOn and
+ self._vertScrollbarOn != self._horizScrollbarOn):
+ if self._horizScrollbarOn:
+ self._toggleHorizScrollbar()
+ else:
+ self._toggleVertScrollbar()
+ return
+
+ if self['hscrollmode'] == 'dynamic':
+
+ # The following test is done to prevent continuous
+ # mapping and unmapping of the horizontal scrollbar.
+ # This may occur when some event (scrolling, resizing
+ # or text changes) modifies the displayed text such
+ # that the bottom line in the window is the longest
+ # line displayed. If this causes the horizontal
+ # scrollbar to be mapped, the scrollbar may "cover up"
+ # the bottom line, which would mean that the scrollbar
+ # is no longer required. If the scrollbar is then
+ # unmapped, the bottom line will then become visible
+ # again, which would cause the scrollbar to be mapped
+ # again, and so on...
+ #
+ # The idea is that, if the width of the text widget
+ # has not changed and the scrollbar is currently
+ # mapped, then do not unmap the scrollbar even if it
+ # is no longer required. This means that, during
+ # normal scrolling of the text, once the horizontal
+ # scrollbar has been mapped it will not be unmapped
+ # (until the width of the text widget changes).
+
+ currentWidth = self._textbox.winfo_width()
+ if self._horizScrollbarNeeded != self._horizScrollbarOn:
+ if self._horizScrollbarNeeded or \
+ self._textWidth != currentWidth:
+ self._toggleHorizScrollbar()
+ self._textWidth = currentWidth
+
+ if self['vscrollmode'] == 'dynamic':
+ if self._vertScrollbarNeeded != self._vertScrollbarOn:
+ self._toggleVertScrollbar()
+
+ def _columnheaderscrolled(self, first, last):
+ if self._textboxLastX != first:
+ self._textboxLastX = first
+ self._textbox.xview('moveto', first)
+
+ def _rowheaderscrolled(self, first, last):
+ if self._textboxLastY != first:
+ self._textboxLastY = first
+ self._textbox.yview('moveto', first)
+
+ def _toggleHorizScrollbar(self):
+
+ self._horizScrollbarOn = not self._horizScrollbarOn
+
+ interior = self.interior()
+ if self._horizScrollbarOn:
+ self._horizScrollbar.grid(row = 6, column = 4, sticky = 'news')
+ interior.grid_rowconfigure(5, minsize = self['scrollmargin'])
+ else:
+ self._horizScrollbar.grid_forget()
+ interior.grid_rowconfigure(5, minsize = 0)
+
+ def _toggleVertScrollbar(self):
+
+ self._vertScrollbarOn = not self._vertScrollbarOn
+
+ interior = self.interior()
+ if self._vertScrollbarOn:
+ self._vertScrollbar.grid(row = 4, column = 6, sticky = 'news')
+ interior.grid_columnconfigure(5, minsize = self['scrollmargin'])
+ else:
+ self._vertScrollbar.grid_forget()
+ interior.grid_columnconfigure(5, minsize = 0)
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)bbox method inherited from Tkinter.Frame.Grid.
+ def bbox(self, index):
+ return self._textbox.bbox(index)
+
+Pmw.forwardmethods(ScrolledText, Tkinter.Text, '_textbox')
--- /dev/null
+# Not Based on iwidgets version.
+
+import Pmw
+
+class SelectionDialog(Pmw.Dialog):
+ # Dialog window with selection list.
+
+ # Dialog window displaying a list and requesting the user to
+ # select one.
+
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderx', 10, INITOPT),
+ ('bordery', 10, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.Dialog.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ aliases = (
+ ('listbox', 'scrolledlist_listbox'),
+ ('label', 'scrolledlist_label'),
+ )
+ self._list = self.createcomponent('scrolledlist',
+ aliases, None,
+ Pmw.ScrolledListBox, (interior,),
+ dblclickcommand = self.invoke)
+ self._list.pack(side='top', expand='true', fill='both',
+ padx = self['borderx'], pady = self['bordery'])
+
+ if not kw.has_key('activatecommand'):
+ # Whenever this dialog is activated, set the focus to the
+ # ScrolledListBox's listbox widget.
+ listbox = self.component('listbox')
+ self.configure(activatecommand = listbox.focus_set)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)size method inherited from Tkinter.Toplevel.Grid.
+ def size(self):
+ return self.component('listbox').size()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)bbox method inherited from Tkinter.Toplevel.Grid.
+ def bbox(self, index):
+ return self.component('listbox').size(index)
+
+Pmw.forwardmethods(SelectionDialog, Pmw.ScrolledListBox, '_list')
--- /dev/null
+# A Dialog with a ScrolledText widget.
+
+import Pmw
+
+class TextDialog(Pmw.Dialog):
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('borderx', 10, INITOPT),
+ ('bordery', 10, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.Dialog.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ aliases = (
+ ('text', 'scrolledtext_text'),
+ ('label', 'scrolledtext_label'),
+ )
+ self._text = self.createcomponent('scrolledtext',
+ aliases, None,
+ Pmw.ScrolledText, (interior,))
+ self._text.pack(side='top', expand=1, fill='both',
+ padx = self['borderx'], pady = self['bordery'])
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ # Need to explicitly forward this to override the stupid
+ # (grid_)bbox method inherited from Tkinter.Toplevel.Grid.
+ def bbox(self, index):
+ return self._text.bbox(index)
+
+Pmw.forwardmethods(TextDialog, Pmw.ScrolledText, '_text')
--- /dev/null
+# Authors: Joe VanAndel and Greg McFarlane
+
+import string
+import sys
+import time
+import Tkinter
+import Pmw
+
+class TimeCounter(Pmw.MegaWidget):
+ """Up-down counter
+
+ A TimeCounter is a single-line entry widget with Up and Down arrows
+ which increment and decrement the Time value in the entry.
+ """
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ INITOPT = Pmw.INITOPT
+ optiondefs = (
+ ('autorepeat', 1, None),
+ ('buttonaspect', 1.0, INITOPT),
+ ('command', None, None),
+ ('initwait', 300, None),
+ ('labelmargin', 0, INITOPT),
+ ('labelpos', None, INITOPT),
+ ('max', None, self._max),
+ ('min', None, self._min),
+ ('padx', 0, INITOPT),
+ ('pady', 0, INITOPT),
+ ('repeatrate', 50, None),
+ ('value', None, INITOPT),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ self.arrowDirection = {}
+ self._flag = 'stopped'
+ self._timerId = None
+
+ self._createComponents(kw)
+
+ value = self['value']
+ if value is None:
+ now = time.time()
+ value = time.strftime('%H:%M:%S', time.localtime(now))
+ self.setvalue(value)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _createComponents(self, kw):
+
+ # Create the components.
+ interior = self.interior()
+
+ # If there is no label, put the arrows and the entry directly
+ # into the interior, otherwise create a frame for them. In
+ # either case the border around the arrows and the entry will
+ # be raised (but not around the label).
+ if self['labelpos'] is None:
+ frame = interior
+ if not kw.has_key('hull_relief'):
+ frame.configure(relief = 'raised')
+ if not kw.has_key('hull_borderwidth'):
+ frame.configure(borderwidth = 1)
+ else:
+ frame = self.createcomponent('frame',
+ (), None,
+ Tkinter.Frame, (interior,),
+ relief = 'raised', borderwidth = 1)
+ frame.grid(column=2, row=2, sticky='nsew')
+ interior.grid_columnconfigure(2, weight=1)
+ interior.grid_rowconfigure(2, weight=1)
+
+ # Create the down arrow buttons.
+
+ # Create the hour down arrow.
+ self._downHourArrowBtn = self.createcomponent('downhourarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downHourArrowBtn] = 'down'
+ self._downHourArrowBtn.grid(column = 0, row = 2)
+
+ # Create the minute down arrow.
+ self._downMinuteArrowBtn = self.createcomponent('downminutearrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downMinuteArrowBtn] = 'down'
+ self._downMinuteArrowBtn.grid(column = 1, row = 2)
+
+ # Create the second down arrow.
+ self._downSecondArrowBtn = self.createcomponent('downsecondarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._downSecondArrowBtn] = 'down'
+ self._downSecondArrowBtn.grid(column = 2, row = 2)
+
+ # Create the entry fields.
+
+ # Create the hour entry field.
+ self._hourCounterEntry = self.createcomponent('hourentryfield',
+ (('hourentry', 'hourentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 2)
+ self._hourCounterEntry.grid(column = 0, row = 1, sticky = 'news')
+
+ # Create the minute entry field.
+ self._minuteCounterEntry = self.createcomponent('minuteentryfield',
+ (('minuteentry', 'minuteentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 2)
+ self._minuteCounterEntry.grid(column = 1, row = 1, sticky = 'news')
+
+ # Create the second entry field.
+ self._secondCounterEntry = self.createcomponent('secondentryfield',
+ (('secondentry', 'secondentryfield_entry'),), None,
+ Pmw.EntryField, (frame,), validate='integer', entry_width = 2)
+ self._secondCounterEntry.grid(column = 2, row = 1, sticky = 'news')
+
+ # Create the up arrow buttons.
+
+ # Create the hour up arrow.
+ self._upHourArrowBtn = self.createcomponent('uphourarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upHourArrowBtn] = 'up'
+ self._upHourArrowBtn.grid(column = 0, row = 0)
+
+ # Create the minute up arrow.
+ self._upMinuteArrowBtn = self.createcomponent('upminutearrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upMinuteArrowBtn] = 'up'
+ self._upMinuteArrowBtn.grid(column = 1, row = 0)
+
+ # Create the second up arrow.
+ self._upSecondArrowBtn = self.createcomponent('upsecondarrow',
+ (), 'Arrow',
+ Tkinter.Canvas, (frame,),
+ width = 16, height = 16, relief = 'raised', borderwidth = 2)
+ self.arrowDirection[self._upSecondArrowBtn] = 'up'
+ self._upSecondArrowBtn.grid(column = 2, row = 0)
+
+ # Make it resize nicely.
+ padx = self['padx']
+ pady = self['pady']
+ for col in range(3):
+ frame.grid_columnconfigure(col, weight = 1, pad = padx)
+ frame.grid_rowconfigure(0, pad = pady)
+ frame.grid_rowconfigure(2, pad = pady)
+
+ frame.grid_rowconfigure(1, weight = 1)
+
+ # Create the label.
+ self.createlabel(interior)
+
+ # Set bindings.
+
+ # Up hour
+ self._upHourArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upHourArrowBtn:
+ s._drawArrow(button, 'up'))
+
+ self._upHourArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upHourArrowBtn:
+ s._countUp(button, 3600))
+
+ self._upHourArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upHourArrowBtn:
+ s._stopUpDown(button))
+
+ # Up minute
+ self._upMinuteArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upMinuteArrowBtn:
+ s._drawArrow(button, 'up'))
+
+
+ self._upMinuteArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upMinuteArrowBtn:
+ s._countUp(button, 60))
+
+ self._upMinuteArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upMinuteArrowBtn:
+ s._stopUpDown(button))
+
+ # Up second
+ self._upSecondArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._upSecondArrowBtn:
+ s._drawArrow(button, 'up'))
+
+
+ self._upSecondArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._upSecondArrowBtn:
+ s._countUp(button, 1))
+
+ self._upSecondArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._upSecondArrowBtn:
+ s._stopUpDown(button))
+
+ # Down hour
+ self._downHourArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downHourArrowBtn:
+ s._drawArrow(button, 'down'))
+
+ self._downHourArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._downHourArrowBtn:
+ s._countDown(button, 3600))
+ self._downHourArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downHourArrowBtn:
+ s._stopUpDown(button))
+
+
+ # Down minute
+ self._downMinuteArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downMinuteArrowBtn:
+ s._drawArrow(button, 'down'))
+
+ self._downMinuteArrowBtn.bind('<1>',
+ lambda event, s=self,button=self._downMinuteArrowBtn:
+ s._countDown(button, 60))
+ self._downMinuteArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downMinuteArrowBtn:
+ s._stopUpDown(button))
+
+ # Down second
+ self._downSecondArrowBtn.bind('<Configure>',
+ lambda event, s=self,button=self._downSecondArrowBtn:
+ s._drawArrow(button, 'down'))
+
+ self._downSecondArrowBtn.bind('<1>',
+ lambda event, s=self, button=self._downSecondArrowBtn:
+ s._countDown(button,1))
+ self._downSecondArrowBtn.bind('<Any-ButtonRelease-1>',
+ lambda event, s=self, button=self._downSecondArrowBtn:
+ s._stopUpDown(button))
+
+ self._hourCounterEntry.component('entry').bind(
+ '<Return>', self._invoke)
+ self._minuteCounterEntry.component('entry').bind(
+ '<Return>', self._invoke)
+ self._secondCounterEntry.component('entry').bind(
+ '<Return>', self._invoke)
+
+ self._hourCounterEntry.bind('<Configure>', self._resizeArrow)
+ self._minuteCounterEntry.bind('<Configure>', self._resizeArrow)
+ self._secondCounterEntry.bind('<Configure>', self._resizeArrow)
+
+ def _drawArrow(self, arrow, direction):
+ Pmw.drawarrow(arrow, self['hourentry_foreground'], direction, 'arrow')
+
+ def _resizeArrow(self, event = None):
+ for btn in (self._upHourArrowBtn, self._upMinuteArrowBtn,
+ self._upSecondArrowBtn,
+ self._downHourArrowBtn,
+ self._downMinuteArrowBtn, self._downSecondArrowBtn):
+ bw = (string.atoi(btn['borderwidth']) +
+ string.atoi(btn['highlightthickness']))
+ newHeight = self._hourCounterEntry.winfo_reqheight() - 2 * bw
+ newWidth = int(newHeight * self['buttonaspect'])
+ btn.configure(width=newWidth, height=newHeight)
+ self._drawArrow(btn, self.arrowDirection[btn])
+
+ def _min(self):
+ min = self['min']
+ if min is None:
+ self._minVal = 0
+ else:
+ self._minVal = Pmw.timestringtoseconds(min)
+
+ def _max(self):
+ max = self['max']
+ if max is None:
+ self._maxVal = None
+ else:
+ self._maxVal = Pmw.timestringtoseconds(max)
+
+ def getvalue(self):
+ return self.getstring()
+
+ def setvalue(self, text):
+ list = string.split(text, ':')
+ if len(list) != 3:
+ raise ValueError, 'invalid value: ' + text
+
+ self._hour = string.atoi(list[0])
+ self._minute = string.atoi(list[1])
+ self._second = string.atoi(list[2])
+
+ self._setHMS()
+
+ def getstring(self):
+ return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)
+
+ def getint(self):
+ return self._hour * 3600 + self._minute * 60 + self._second
+
+ def _countUp(self, button, increment):
+ self._relief = self._upHourArrowBtn.cget('relief')
+ button.configure(relief='sunken')
+ self._count(1, 'start', increment)
+
+ def _countDown(self, button, increment):
+
+ self._relief = self._downHourArrowBtn.cget('relief')
+ button.configure(relief='sunken')
+ self._count(-1, 'start', increment)
+
+ def increment(self, seconds = 1):
+ self._count(1, 'force', seconds)
+
+ def decrement(self, seconds = 1):
+ self._count(-1, 'force', seconds)
+
+ def _count(self, factor, newFlag = None, increment = 1):
+ if newFlag != 'force':
+ if newFlag is not None:
+ self._flag = newFlag
+
+ if self._flag == 'stopped':
+ return
+
+ value = (string.atoi(self._hourCounterEntry.get()) *3600) + \
+ (string.atoi(self._minuteCounterEntry.get()) *60) + \
+ string.atoi(self._secondCounterEntry.get()) + \
+ factor * increment
+ min = self._minVal
+ max = self._maxVal
+ if value < min:
+ value = min
+ if max is not None and value > max:
+ value = max
+
+ self._hour = value /3600
+ self._minute = (value - (self._hour*3600)) / 60
+ self._second = value - (self._hour*3600) - (self._minute*60)
+ self._setHMS()
+
+ if newFlag != 'force':
+ if self['autorepeat']:
+ if self._flag == 'start':
+ delay = self['initwait']
+ self._flag = 'running'
+ else:
+ delay = self['repeatrate']
+ self._timerId = self.after(
+ delay, lambda self=self, factor=factor,increment=increment:
+ self._count(factor,'running', increment))
+
+ def _setHMS(self):
+ self._hourCounterEntry.setentry('%02d' % self._hour)
+ self._minuteCounterEntry.setentry('%02d' % self._minute)
+ self._secondCounterEntry.setentry('%02d' % self._second)
+
+ def _stopUpDown(self, button):
+ if self._timerId is not None:
+ self.after_cancel(self._timerId)
+ self._timerId = None
+ button.configure(relief=self._relief)
+ self._flag = 'stopped'
+
+ def _invoke(self, event):
+ cmd = self['command']
+ if callable(cmd):
+ cmd()
+
+ def invoke(self):
+ cmd = self['command']
+ if callable(cmd):
+ return cmd()
+
+ def destroy(self):
+ if self._timerId is not None:
+ self.after_cancel(self._timerId)
+ self._timerId = None
+ Pmw.MegaWidget.destroy(self)
--- /dev/null
+# Functions for dealing with dates and times.
+
+import re
+import string
+
+def timestringtoseconds(text, separator = ':'):
+ inputList = string.split(string.strip(text), separator)
+ if len(inputList) != 3:
+ raise ValueError, 'invalid value: ' + text
+
+ sign = 1
+ if len(inputList[0]) > 0 and inputList[0][0] in ('+', '-'):
+ if inputList[0][0] == '-':
+ sign = -1
+ inputList[0] = inputList[0][1:]
+
+ if re.search('[^0-9]', string.join(inputList, '')) is not None:
+ raise ValueError, 'invalid value: ' + text
+
+ hour = string.atoi(inputList[0])
+ minute = string.atoi(inputList[1])
+ second = string.atoi(inputList[2])
+
+ if minute >= 60 or second >= 60:
+ raise ValueError, 'invalid value: ' + text
+ return sign * (hour * 60 * 60 + minute * 60 + second)
+
+_year_pivot = 50
+_century = 2000
+
+def setyearpivot(pivot, century = None):
+ global _year_pivot
+ global _century
+ oldvalues = (_year_pivot, _century)
+ _year_pivot = pivot
+ if century is not None:
+ _century = century
+ return oldvalues
+
+def datestringtojdn(text, format = 'ymd', separator = '/'):
+ inputList = string.split(string.strip(text), separator)
+ if len(inputList) != 3:
+ raise ValueError, 'invalid value: ' + text
+
+ if re.search('[^0-9]', string.join(inputList, '')) is not None:
+ raise ValueError, 'invalid value: ' + text
+ formatList = list(format)
+ day = string.atoi(inputList[formatList.index('d')])
+ month = string.atoi(inputList[formatList.index('m')])
+ year = string.atoi(inputList[formatList.index('y')])
+
+ if _year_pivot is not None:
+ if year >= 0 and year < 100:
+ if year <= _year_pivot:
+ year = year + _century
+ else:
+ year = year + _century - 100
+
+ jdn = ymdtojdn(year, month, day)
+ if jdntoymd(jdn) != (year, month, day):
+ raise ValueError, 'invalid value: ' + text
+ return jdn
+
+def _cdiv(a, b):
+ # Return a / b as calculated by most C language implementations,
+ # assuming both a and b are integers.
+
+ if a * b > 0:
+ return a / b
+ else:
+ return -(abs(a) / abs(b))
+
+def ymdtojdn(year, month, day, julian = -1, papal = 1):
+
+ # set Julian flag if auto set
+ if julian < 0:
+ if papal: # Pope Gregory XIII's decree
+ lastJulianDate = 15821004L # last day to use Julian calendar
+ else: # British-American usage
+ lastJulianDate = 17520902L # last day to use Julian calendar
+
+ julian = ((year * 100L) + month) * 100 + day <= lastJulianDate
+
+ if year < 0:
+ # Adjust BC year
+ year = year + 1
+
+ if julian:
+ return 367L * year - _cdiv(7 * (year + 5001L + _cdiv((month - 9), 7)), 4) + \
+ _cdiv(275 * month, 9) + day + 1729777L
+ else:
+ return (day - 32076L) + \
+ _cdiv(1461L * (year + 4800L + _cdiv((month - 14), 12)), 4) + \
+ _cdiv(367 * (month - 2 - _cdiv((month - 14), 12) * 12), 12) - \
+ _cdiv((3 * _cdiv((year + 4900L + _cdiv((month - 14), 12)), 100)), 4) + \
+ 1 # correction by rdg
+
+def jdntoymd(jdn, julian = -1, papal = 1):
+
+ # set Julian flag if auto set
+ if julian < 0:
+ if papal: # Pope Gregory XIII's decree
+ lastJulianJdn = 2299160L # last jdn to use Julian calendar
+ else: # British-American usage
+ lastJulianJdn = 2361221L # last jdn to use Julian calendar
+
+ julian = (jdn <= lastJulianJdn);
+
+ x = jdn + 68569L
+ if julian:
+ x = x + 38
+ daysPer400Years = 146100L
+ fudgedDaysPer4000Years = 1461000L + 1
+ else:
+ daysPer400Years = 146097L
+ fudgedDaysPer4000Years = 1460970L + 31
+
+ z = _cdiv(4 * x, daysPer400Years)
+ x = x - _cdiv((daysPer400Years * z + 3), 4)
+ y = _cdiv(4000 * (x + 1), fudgedDaysPer4000Years)
+ x = x - _cdiv(1461 * y, 4) + 31
+ m = _cdiv(80 * x, 2447)
+ d = x - _cdiv(2447 * m, 80)
+ x = _cdiv(m, 11)
+ m = m + 2 - 12 * x
+ y = 100 * (z - 49) + y + x
+
+ # Convert from longs to integers.
+ yy = int(y)
+ mm = int(m)
+ dd = int(d)
+
+ if yy <= 0:
+ # Adjust BC years.
+ yy = yy - 1
+
+ return (yy, mm, dd)
+
+def stringtoreal(text, separator = '.'):
+ if separator != '.':
+ if string.find(text, '.') >= 0:
+ raise ValueError, 'invalid value: ' + text
+ index = string.find(text, separator)
+ if index >= 0:
+ text = text[:index] + '.' + text[index + 1:]
+ return string.atof(text)
--- /dev/null
+# File to allow this directory to be treated as a python package.
--- /dev/null
+# Based on iwidgets2.2.0/tests/messagedialog.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+Pmw.aboutversion('1.0')
+Pmw.aboutcopyright('Copyright Really Good Software')
+Pmw.aboutcontact('For information about this application contact\nyour' +
+ 'system administrator')
+
+c = Pmw.AboutDialog
+
+kw_1 = {
+ 'applicationname' : 'Really Good Application',
+ 'buttonbox_padx': 30,
+}
+tests_1 = (
+ (Test.num_options, (), 14),
+ ('message_anchor', 'center'),
+ ('message_justify', 'center'),
+ ('message_wraplength', 0),
+ ('hull_background', '#d9d9d9'),
+ ('icon_bitmap', 'warning'),
+ ('hull_cursor', 'gumby'),
+ ('icon_image', Test.flagup),
+ ('message_font', Test.font['variable']),
+ ('message_foreground', 'red'),
+ ('message_padx', 15),
+ ('message_pady', 15),
+ ('icon_image', ''),
+ (c.title, 'AboutDialog 1: new title', ''),
+ (c.interior, (), Tkinter.Frame),
+ (Pmw.aboutcontact, ''),
+)
+
+kw_2 = {
+ 'applicationname' : 'Another Really Good Application',
+ 'buttonboxpos': 'e',
+ 'iconpos': 'n',
+ 'borderx': 15,
+ 'bordery': 15,
+ 'separatorwidth': 5,
+}
+tests_2 = (
+ (c.title, 'AboutDialog 2', ''),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+#!/usr/bin/env python
+
+import os
+import re
+import sys
+import Tkinter
+
+import Test
+Test.initialise()
+
+# Uncomment these to modify period between tests and how much output
+# to print:
+#Test.setdelay(1000)
+#Test.setverbose(1)
+
+# Ignore Tkinter test since it does not test any Pmw functionality
+# (only Tkinter) and it fails under MS-Windows 95 (and it hasn't been
+# kept up-to-date with changes to Tk.
+ignoreTests = ('Tkinter_test.py',)
+
+# Also ignore Blt test since it causes Blt 2.4z to core dump.
+if Tkinter.TkVersion >= 8.4:
+ ignoreTests = ignoreTests + ('Blt_test.py',)
+
+allTestData = ()
+files = os.listdir(os.curdir)
+files.sort()
+
+for file in files:
+ if file not in ignoreTests and re.search('^.+_test.py$', file) is not None:
+ test = file[:-3]
+ exec 'import ' + test
+ exec 'allTestData = allTestData + ' + test + '.testData'
+
+Test.runTests(allTestData)
--- /dev/null
+# Tests for Blt widgets.
+
+import os
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+testData = ()
+
+# Blt vector type
+
+def _setVectorItem(index, value):
+ w = Test.currentWidget()
+ w[index] = value
+
+def _getVectorItem(index):
+ w = Test.currentWidget()
+ return w[index]
+
+def _getVectorSlice(index1, index2):
+ w = Test.currentWidget()
+ return w[index1:index2]
+
+def _delVectorItem(index):
+ w = Test.currentWidget()
+ del w[index]
+
+def _vectorExpr(instanceMethod):
+ w = Test.currentWidget()
+ name = '::' + str(w)
+ if instanceMethod:
+ w.expr(name + '+ 0.5')
+ else:
+ return Pmw.Blt.vector_expr(name + '* 2')
+
+def _vectorNames():
+ name = '::' + str(Test.currentWidget())
+ names = Pmw.Blt.vector_names()
+ if name not in names:
+ return names
+ name2 = Pmw.Blt.vector_names(name)
+ if name2 != (name,):
+ return name2
+ return None
+
+if Test.haveBlt():
+ c = Pmw.Blt.Vector
+ tests = (
+ (c.set, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
+ (c.__repr__, (), '[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]'),
+ (c.set, ((1, 2, 3, 4, 5, 6, 7, 8, 9, 10),)),
+ (c.__repr__, (), '[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]'),
+ (c.__str__, (), 'PY_VEC4'),
+ (_getVectorItem, 7, 8),
+ (_getVectorSlice, (3, 6), [4.0, 5.0, 6.0]),
+ (_delVectorItem, 9),
+ (c.get, (), [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]),
+ (c.append, 10),
+ (c.get, (), [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),
+ (c.length, (), 10),
+ (c.append, 5),
+ (c.__len__, (), 11),
+ (c.count, 5, 2),
+ (c.count, 20, 0),
+ (c.search, 5, (4, 10)),
+ (c.search, 20),
+ (c.index, 5, 4),
+ # Commented tests do not work because of a bug in the blt vector command.
+ # (c.clear, ()),
+ (_getVectorItem, 4, 5),
+ #(c.remove, 5), # This causes a core in blt 2.4 under Solaris 2.5
+ #(c.index, 5, 9),
+ (c.min, (), 1.0),
+ (c.max, (), 10.0),
+ # (c.reverse, ()),
+ # (c.reverse, ()),
+ # (c.get, (), [1.0, 2.0, 3.0, 4.0, 6.0, 7.0, 8.0, 9.0, 10.0, 5.0]),
+ # (c.insert, (3, 66)),
+ # (c.search, 66, (3,)),
+ (c.get, (), [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 5.0]),
+ (c.blt_sort, ()),
+ (c.get, (), [1.0, 2.0, 3.0, 4.0, 5.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]),
+ (c.blt_sort_reverse, ()),
+ (c.get, (), [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 5.0, 4.0, 3.0, 2.0, 1.0]),
+ (_setVectorItem, (7, 77)),
+ (c.search, 77, (7,)),
+ (_setVectorItem, (2, 77)),
+ (c.search, 77, (2, 7)),
+ (_setVectorItem, (11, 77), 'TclError: can\'t set "PY_VEC4(11)": index "11" is out of range'),
+ (c.get, (), [10.0, 9.0, 77.0, 7.0, 6.0, 5.0, 5.0, 77.0, 3.0, 2.0, 1.0]),
+ (c.delete, (1, 3, 5)),
+ (c.get, (), [10.0, 77.0, 6.0, 5.0, 77.0, 3.0, 2.0, 1.0]),
+ (c.length, (), 8),
+ (c.length, (9), 9),
+ (c.get, (), [10.0, 77.0, 6.0, 5.0, 77.0, 3.0, 2.0, 1.0, 0.0]),
+ (c.range, (1, 3), [77.0, 6.0, 5.0]),
+ (_vectorExpr, 0, (20.0, 154.0, 12.0, 10.0, 154.0, 6.0, 4.0, 2.0, 0.0)),
+ (_vectorExpr, 1),
+ (c.get, (), [10.5, 77.5, 6.5, 5.5, 77.5, 3.5, 2.5, 1.5, 0.5]),
+ (_vectorNames, ()),
+ )
+ testData = testData + ((c, ((tests, {}),)),)
+
+ tests = (
+ (c.get, (), [0.0, 0.0, 0.0, 0.0]),
+ (c.length, (), 4),
+ )
+ testData = testData + ((c, ((tests, {'size' : 4}),)),)
+
+ tests = (
+ # (c.get, (), [0.0, 0.0, 0.0]), Does not work.
+ (c.length, (), 3),
+ (_getVectorItem, 2, 0),
+ (_getVectorItem, 4, 0),
+ (_getVectorItem, 5, 'IndexError: 5'),
+ )
+ testData = testData + ((c, ((tests, {'size' : '2:4'}),)),)
+
+#=============================================================================
+
+# Blt graph widget
+
+def _axisCommand(graph, value):
+ return 'XX ' + value
+
+def _createMarkerButton():
+ w = Test.currentWidget()
+ button = Tkinter.Button(w, text = 'This is\na button')
+ w.marker_create('window', coords=(10, 200), window=button)
+
+def _axisNamesSorted(pattern = None):
+ w = Test.currentWidget()
+ if pattern is None:
+ names = list(w.axis_names())
+ else:
+ names = list(w.axis_names(pattern))
+ names.sort()
+ return tuple(names)
+
+def _penNamesSorted(pattern = None):
+ w = Test.currentWidget()
+ if pattern is None:
+ names = list(w.pen_names())
+ else:
+ names = list(w.pen_names(pattern))
+ names.sort()
+ return tuple(names)
+
+if Test.haveBlt():
+ c = Pmw.Blt.Graph
+ tests = (
+ ('height', 700),
+ ('width', 900),
+ (c.pack, (), {'fill': 'both', 'expand': 1}),
+
+ (Test.num_options, (), 43),
+ (c.pen_create, 'pen1', {'fill': 'green', 'symbol': 'circle'}),
+ (c.line_create, 'line1', {'xdata': (1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 'ydata': (7, 2, 1, 4, 7, 3, 9, 3, 8, 5), 'pen': 'pen1',}),
+ (c.bar_create, 'bar1', {'xdata': Test.vector_x, 'ydata': Test.vector_y[0], 'foreground': 'blue'}),
+ (c.bar_create, 'bar2', {'xdata': Test.vector_x, 'ydata': Test.vector_y[1], 'foreground': 'magenta'}),
+ (c.line_create, 'line2', {'xdata': Test.vector_x, 'ydata': Test.vector_y[2], 'color': 'red'}),
+
+ (c.marker_create, 'text', {'coords': (25, 200), 'rotate': 45, 'text':
+ 'This is\na marker', 'name': 'myMarker1'}, 'myMarker1'),
+ (c.marker_create, 'line', {'coords': (35, 120, 15, 280), 'linewidth': 4}, 'marker1'),
+ (c.marker_create, 'polygon', {'coords': (35, 40, 45, 40, 45, 120, 35, 120), 'linewidth': 4}, 'marker2'),
+ (c.marker_create, 'bitmap', {'coords': (25, 200), 'rotate': 45, 'bitmap': 'questhead'}, 'marker3'),
+ (_createMarkerButton, ()),
+
+ (c.marker_after, 'myMarker1'),
+ (c.marker_before, ('myMarker1', 'marker3')),
+ (c.marker_create, 'text', {'coords': (10, 10), 'text':
+ 'Bye', 'name': 'myMarker2'}, 'myMarker2'),
+ (c.marker_names, 'my*', ('myMarker1', 'myMarker2')),
+ (c.marker_exists, 'myMarker2', 1),
+ (c.marker_delete, ('myMarker1', 'myMarker2', 'marker3')),
+ (c.marker_exists, 'myMarker2', 0),
+ (c.marker_names, (), ('marker1', 'marker2', 'marker4')),
+ (c.marker_type, 'marker1', 'line'),
+
+ (c.marker_cget, ('marker1', 'linewidth'), '4'),
+ (c.marker_cget, ('marker2', 'linewidth'), '4'),
+ (c.marker_configure, (('marker1', 'marker2'),), {'linewidth': 5}),
+ (c.marker_cget, ('marker1', 'linewidth'), '5'),
+ (c.marker_cget, ('marker2', 'linewidth'), '5'),
+
+ ('background', '#ffdddd'),
+ ('barmode', 'stacked'),
+ ('barwidth', 0.5),
+ ('borderwidth', 100),
+ ('borderwidth', 10),
+ ('barwidth', 0.9),
+ ('bottommargin', 100),
+ ('bufferelements', 1),
+ ('cursor', 'watch'),
+ ('font', Test.font['variable']),
+ ('foreground', 'blue'),
+ ('halo', 20),
+ ('barmode', 'aligned'),
+ ('invertxy', 1),
+ ('justify', 'left'),
+ ('leftmargin', 100),
+ ('plotbackground', 'aquamarine'),
+ ('plotborderwidth', 4),
+ ('plotrelief', 'groove'),
+ ('relief', 'ridge'),
+ ('rightmargin', 100),
+ ('takefocus', '0'),
+ ('tile', Test.earthris),
+ ('barmode', 'infront'),
+ ('title', 'Hello there\nmy little lovely'),
+ ('topmargin', 100),
+ ('invertxy', 0),
+
+ # Change colours so that axis and legend are visible against image tile.
+ (c.xaxis_configure, (), {'color': 'green'}),
+ (c.yaxis_configure, (), {'color': 'green'}),
+ (c.legend_configure, (), {'background': '#ffffcc'}),
+
+ (c.axis_cget, ('x', 'color'), 'green'),
+ (c.axis_configure, ('x2'), {'color': 'red'}),
+ (c.axis_cget, ('x2', 'color'), 'red'),
+ (c.axis_create, 'myaxis', {'rotate': 45}),
+ (c.axis_cget, ('myaxis', 'rotate'), '45.0'),
+ (_axisNamesSorted, (), ('myaxis', 'x', 'x2', 'y', 'y2')),
+ (_axisNamesSorted, ('*x*'), ('myaxis', 'x', 'x2')),
+ # Blt 2.4u returns the empty string for the axis use command
+ # (c.y2axis_use, 'myaxis', 'myaxis'),
+ (c.axis_delete, 'myaxis'),
+
+ (c.extents, 'leftmargin', 100),
+ (c.inside, (1000, 1000), 0),
+ (c.inside, (400, 400), 1),
+ (c.snap, Test.emptyimage),
+
+ (c.element_bind, ('line1', '<1>', Test.callback), Test.callback),
+ (c.element_bind, 'line1', ('<Button-1>',)),
+ (c.legend_bind, ('line1', '<1>', Test.callback), Test.callback),
+ (c.legend_bind, 'line1', ('<Button-1>',)),
+ (c.marker_bind, ('marker1', '<1>', Test.callback), Test.callback),
+ (c.marker_bind, 'marker1', ('<Button-1>',)),
+
+ (c.pen_create, 'mypen', {'type' : 'bar', 'foreground': 'red'}),
+ (c.pen_cget, ('mypen', 'foreground'), 'red'),
+ (c.pen_configure, ('mypen'), {'foreground': 'green'}),
+ (c.pen_cget, ('mypen', 'foreground'), 'green'),
+ (_penNamesSorted, (), ('activeBar', 'activeLine', 'mypen', 'pen1')),
+ (_penNamesSorted, ('*pen*'), ('mypen', 'pen1')),
+ (c.pen_delete, 'mypen'),
+
+ # These tests are not portable
+ # (c.invtransform, (0, 0), (-10.2518, 507.203)),
+ # (c.transform, (-10.2518, 507.203), (0.0, 0.0)),
+
+ # Reset margins to automatic
+ ('bottommargin', 0),
+ ('leftmargin', 0),
+ ('rightmargin', 0),
+ ('topmargin', 0),
+
+ (c.crosshairs_configure, (), {'hide': 0}),
+ (c.crosshairs_configure, (), {'position': '@300,300'}),
+ (c.crosshairs_configure, (), {'color': 'seagreen4'}),
+ (c.crosshairs_toggle, ()),
+ (c.crosshairs_cget, 'hide', 1),
+ (c.crosshairs_configure, (), {'dashes': (4, 8, 8, 8)}),
+ (c.crosshairs_configure, (), {'linewidth': 4}),
+ (c.crosshairs_toggle, ()),
+ (c.crosshairs_cget, 'hide', 0),
+ (c.crosshairs_off, ()),
+ (c.crosshairs_cget, 'hide', 1),
+ (c.crosshairs_on, ()),
+ (c.crosshairs_cget, 'hide', 0),
+
+ # Blt 2.4u gives an error with this (looks like activeBar
+ # is same as activeLine):
+ # (c.pen_configure, 'activeBar', {'foreground': '#ffffaa'}),
+
+ (c.element_configure, 'bar2', {'foreground': '#ffffaa'}),
+ # Blt 2.4u segmentation faults around here, remove tests:
+ # (c.element_activate, 'bar1'),
+ # (c.element_activate, 'bar2'),
+ # (c.element_deactivate, ('bar1', 'bar2')),
+ # (c.element_deactivate, ()),
+ # (c.element_activate, ('bar2',) + tuple(range(Test.vectorSize / 2))),
+ (c.element_configure, 'bar1', {'ydata': Test.vector_y[1]}),
+ (c.element_configure, 'bar2', {'ydata': Test.vector_y[0]}),
+
+ (c.element_cget, ('bar1', 'barwidth'), '0.0'),
+ (c.element_cget, ('bar2', 'barwidth'), '0.0'),
+ (c.element_configure, (('bar1', 'bar2'),), {'barwidth': 0.5}),
+ (c.element_cget, ('bar1', 'barwidth'), '0.5'),
+ (c.element_cget, ('bar2', 'barwidth'), '0.5'),
+
+ # These tests are not portable
+ # (c.element_closest, (330, 430), {}, {'x': 18.0, 'dist': 17.0, 'name': 'bar1', 'index': 18, 'y': 156.0}),
+ # (c.element_closest, (0, 0)),
+ # (c.element_closest, (0, 0), {'halo': 500}, {'x': 0.0, 'dist': 154.797, 'name': 'line2', 'index': 0, 'y': 359.0}),
+ # (c.element_closest, (0, 0), {'halo': 500, 'interpolate': 1}, {'x': -0.0320109, 'dist': 154.797, 'name': 'line2', 'index': 0, 'y': 358.879}),
+
+ (c.element_type, 'bar2', 'bar'),
+ (c.element_type, 'line2', 'line'),
+
+ (c.legend_activate, ('line1', 'bar2',)),
+ (c.legend_activate, ()),
+ (c.legend_deactivate, ('line1', 'bar2',)),
+ (c.legend_deactivate, ()),
+ (c.legend_configure, (), {'hide': 1}),
+ (c.legend_cget, 'hide', 1),
+ (c.legend_configure, (), {'hide': 0}),
+ (c.legend_configure, (), {'position': 'left', 'anchor': 'nw', 'ipadx': 100, 'ipady': 100}),
+ (c.legend_get, '@150,150', 'line1'),
+
+ # This probably works, but I haven't installed the prologue file
+ # (c.postscript_output, '/tmp/tmp.ps', {'landscape': 1}),
+ # (os.unlink, '/tmp/tmp.ps'),
+
+ (c.element_show, (), ('line2', 'bar2', 'bar1', 'line1')),
+ (c.element_show, (('line1', 'bar1'),), ('line1', 'bar1')),
+ (c.element_names, (), ('line1', 'line2', 'bar1', 'bar2')),
+ (c.element_names, 'line*', ('line1', 'line2')),
+ (c.element_show, (('line1', 'line2', 'bar1', 'bar2'),), ('line1', 'line2', 'bar1', 'bar2')),
+ (c.element_exists, 'bar1', 1),
+ (c.element_delete, ('bar1', 'bar2')),
+ (c.element_names, (), ('line1', 'line2')),
+ (c.element_exists, 'bar1', 0),
+ (c.element_delete, ()),
+ (c.element_names, (), ('line1', 'line2')),
+
+ (c.grid_configure, (), {'hide': 0}),
+ (c.grid_toggle, ()),
+ (c.grid_cget, 'hide', 1),
+ (c.grid_on, ()),
+ (c.grid_cget, 'hide', 0),
+ (c.grid_off, ()),
+
+ # These tests are not portable
+ # (c.xaxis_invtransform, 0, -37.1153),
+ # (c.axis_limits, 'x', (-0.98, 49.98)),
+ # (c.axis_transform, ('x', 0), 360),
+ # (c.axis_invtransform, ('y', 0), 444.198),
+ # (c.yaxis_limits, (), (-6.96, 406.96)),
+ # (c.axis_transform, ('y', 0), 620),
+ # (c.axis_invtransform, ('x2', 0), -25.1491),
+ # (c.axis_limits, 'x2', (-10.4, 10.4)),
+ # (c.x2axis_transform, 0, 598),
+ # (c.y2axis_invtransform, 0, 12.2713),
+ # (c.axis_limits, 'y2', (-10.4, 10.4)),
+ # (c.axis_transform, ('y2', 0), 341),
+ )
+
+ testData = testData + ((c, ((tests, {}),)),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/buttonbox.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.ButtonBox
+
+kw_1 = {}
+tests_1 = (
+ (c.pack, ()),
+ (c.add, 'Yes', Tkinter.Button),
+ (c.add, 'No', Tkinter.Button),
+ (c.setdefault, 'Yes'),
+ (c.alignbuttons, ()),
+ (Test.num_options, (), 5),
+ ('Button_activebackground', '#ececec'),
+ ('Button_activeforeground', 'Black'),
+ ('hull_background', '#d9d9d9'),
+ ('hull_cursor', 'gumby'),
+ ('Button_background', 'aliceblue'),
+ ('Button_disabledforeground', '#a3a3a3'),
+ ('Button_foreground', 'Black'),
+ ('Button_highlightcolor', 'Black'),
+ ('Button_highlightthickness', 2),
+ (c.index, 0, 0),
+ (c.index, Pmw.END, 1),
+ (c.index, Pmw.DEFAULT, 0),
+ (c.index, 'No', 1),
+ (c.index, 'Yes', 0),
+ (c.add, 'Maybe', Tkinter.Button),
+ (c.insert, ('Never', 0), {'text' : 'Never Never'}, Tkinter.Button),
+ (c.setdefault, 'Never'),
+ (c.invoke, 'Yes', ''),
+ (c.invoke, (), ''),
+ (c.invoke, Pmw.DEFAULT, ''),
+ (c.delete, 'Maybe'),
+ ('Yes_text', 'YES'),
+ (c.index, 12, 'ValueError: index "12" is out of range'),
+ (c.index, 'bogus', 'ValueError: bad index "bogus": ' + \
+ 'must be a name, a number, Pmw.END or Pmw.DEFAULT'),
+)
+
+kw_2 = {
+ 'orient' : 'vertical',
+ 'padx' : 30,
+ 'pady' : 30,
+ 'labelpos' : 'w',
+ 'label_text' : 'Vertical\nButtonBox',
+}
+tests_2 = (
+ (c.pack, ()),
+ (c.add, 'Hello', Tkinter.Button),
+ (c.insert, ('GoodBye', Pmw.END), Tkinter.Button),
+ (c.setdefault, 'Hello'),
+ (c.setdefault, 'GoodBye'),
+ (c.setdefault, None),
+ (c.index, Pmw.DEFAULT, 'ValueError: ButtonBox has no default'),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Tests for Pmw color handling.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+testData = ()
+
+defaultPalette = Pmw.Color.getdefaultpalette(Test.root)
+
+c = Tkinter.Button
+
+colors = ('red', 'orange', 'yellow', 'green', 'blue', 'purple', 'white')
+normalcolors = map(Pmw.Color.changebrightness,
+ (Test.root,) * len(colors), colors, (0.85,) * len(colors))
+
+kw = {}
+tests = (
+ (Pmw.Color.setscheme, (Test.root, normalcolors[0]), {'foreground' : 'white'}),
+)
+testData = testData + ((c, ((tests, kw),)),)
+
+for color in normalcolors[1:]:
+ kw = {'text' : color}
+ tests = (
+ (c.pack, ()),
+ ('state', 'active'),
+ )
+ testData = testData + ((c, ((tests, kw),)),)
+
+ kw = {}
+ tests = (
+ (Pmw.Color.setscheme, (Test.root, color), {'foreground' : 'red'}),
+ )
+ testData = testData + ((c, ((tests, kw),)),)
+
+# Restore the default colors.
+kw = {}
+tests = (
+ (Pmw.Color.setscheme, (Test.root,), defaultPalette),
+)
+testData = testData + ((c, ((tests, kw),)),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/combobox.test code.
+
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+if Tkinter.TkVersion >= 8.4:
+ expected1 = 'TclError: bad relief "bogus": must be '
+ expected2 = 'TclError: bad state "bogus": must be ' + \
+ 'disabled, normal, or readonly'
+elif Tkinter.TkVersion >= 8.3:
+ expected1 = 'TclError: bad relief "bogus": must be '
+ expected2 = 'TclError: bad state "bogus": must be disabled or normal'
+else:
+ expected1 = 'TclError: bad relief type "bogus": must be '
+ expected2 = 'TclError: bad state value "bogus": must be normal or disabled'
+
+c = Pmw.ComboBox
+
+kw_1a = {
+ 'labelpos' : 'w',
+ 'label_text' : 'Label:',
+ 'autoclear': 1,
+ 'listheight': 250,
+ 'scrolledlist_items': ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'),
+}
+
+kw_1b = {
+ 'dropdown': 0,
+ 'entry_state': 'disabled',
+ 'selectioncommand' : Test.callback1,
+ 'scrolledlist_items': ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'),
+ 'listheight' : 200,
+ 'label_text' : 'DropDown:',
+ 'labelpos' : 'w',
+ 'listbox_cursor' : 'hand1',
+ 'unique' : 1
+}
+
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 11),
+ ('entry_width', 20),
+ ('entry_textvariable', Test.stringvar),
+ ('label_text', 'ComboBox:'),
+ ('label_image', Test.flagup),
+ ('label_image', ''),
+ ('entry_borderwidth', 10),
+ ('entry_borderwidth', 2),
+ ('hull_background', 'steelblue'),
+ ('entry_foreground', 'white'),
+ ('hull_background', 'grey85'),
+ ('entry_foreground', 'Black'),
+ ('entry_textvariable', ''),
+ ('entry_state', 'disabled'),
+ ('entry_state', 'normal'),
+ ('label_font', Test.font['variable']),
+ ('entry_font', Test.font['large']),
+ ('entry_font', Test.font['variable']),
+ (c.invoke, ()),
+ ('hull_cursor', ''),
+ ('entry_relief', 'raised'),
+ ('entry_relief', 'groove'),
+ ('entry_relief', 'flat'),
+ ('entry_relief', 'sunken'),
+ ('entry_exportselection', 1),
+ ('entry_exportselection', 0),
+ ('entryfield_invalidcommand', Test.bell),
+ ('listbox_cursor', 'hand1'),
+ ('listbox_cursor', 'hand2'),
+ ('entry_selectbackground', '#b2dfee'),
+ ('listbox_selectbackground', 'steelblue'),
+ ('entry_selectborderwidth', 1),
+ ('entry_selectforeground', 'Black'),
+ ('entry_background', 'white'),
+ ('entryfield_validate', 'alphabetic'),
+ (c.setentry, '1234', 0),
+ (c.get, (), 'this is some text'),
+ ('entryfield_validate', None),
+ ('scrolledlist_hscrollmode', 'dynamic'),
+ ('scrolledlist_hscrollmode', 'dynamic'),
+ ('scrolledlist_vscrollmode', 'dynamic'),
+ ('scrolledlist_vscrollmode', 'dynamic'),
+ ('entry_borderwidth', 'bogus', 'TclError: bad screen distance "bogus"'),
+ ('entry_cursor', 'bogus', 'TclError: bad cursor spec "bogus"'),
+ ('entry_exportselection', 'bogus',
+ 'TclError: expected boolean value but got "bogus"'),
+ ('scrolledlist_hscrollmode', 'bogus',
+ 'ValueError: bad hscrollmode option "bogus": should be static, dynamic, ' +
+ 'or none'),
+ ('listbox_cursor', 'bogus', 'TclError: bad cursor spec "bogus"'),
+ ('entry_relief', 'bogus', expected1 + Test.reliefs),
+ ('entry_selectborderwidth', 'bogus', 'TclError: bad screen distance "bogus"'),
+ ('entry_state', 'bogus', expected2),
+ ('entryfield_validate', 'bogus',
+ "ValueError: bad validate value \"bogus\": must be a function or one of " +
+ "the standard validators ('alphabetic', 'alphanumeric', 'date', " +
+ "'hexadecimal', 'integer', 'numeric', 'real', 'time') or extra " +
+ "validators ()"),
+ ('scrolledlist_vscrollmode', 'bogus',
+ 'ValueError: bad vscrollmode option "bogus": should be static, dynamic, ' +
+ 'or none'),
+ ('entry_width', 'bogus', 'TclError: expected integer but got "bogus"'),
+ (c.interior, (), Tkinter.Frame),
+ (c.setentry, 'This is some text', 1),
+ (c.get, (), 'This is some text'),
+ (c.get, 2, 'C'),
+ (c.get, (2, 4), ('C', 'D', 'E')),
+ ('listbox_exportselection', 0),
+ (c.selectitem, 3),
+ (c.getcurselection, (), ('D',)),
+ (c.get, (), 'D'),
+ (c.selectitem, 'H'),
+ (c.getcurselection, (), ('H',)),
+ (c.get, (), 'H'),
+ (c.setentry, '', 1),
+ (c.size, (), 10),
+ (c.get, (), ''),
+ (c.delete, (0, 'end')),
+ (c.insert, (0, 'Test1', 'Test2', 'Test3', 'Test4')),
+ (c.insert, ('end', 'More Test')),
+ (c.size, (), 5),
+ (c.delete, (1),),
+ (c.delete, (0, 2)),
+ (c.size, (), 1),
+ (c.get, 0, 'More Test'),
+ (c.setentry, '', 1),
+ (c.get, (), ''),
+ (c.selectitem, 'More Test'),
+ (c.curselection, (), ('0',)),
+ (c.delete, (0, 'end')),
+ (c.size, (), 0),
+ (c.getcurselection, (), ()),
+ (c.insert, ('end', 'Test1', 'Test2', 'Really Long String Test')),
+ (c.size, (), 3),
+ (c.get, 0, 'Test1'),
+ (c.get, 2, 'Really Long String Test'),
+ (c.insert, (0, 'Test3', 'Test4', 'Really Long String Test')),
+ (c.size, (), 6),
+ (c.insert, (1, 'Test5', 'Test6', 'Really Long String Test')),
+ (c.size, (), 9),
+ (c.insert, (5, 'Test7', 'Test8', 'Really Long String Test')),
+ (c.size, (), 12),
+ (c.see, 0),
+ (c.see, 11),
+ (c.get, 'end', 'Really Long String Test'),
+ (c.selectitem, 5),
+ (c.curselection, (), ('5',)),
+ (c.clear, ()),
+ (c.get, (), ''),
+ (c.size, (), 0),
+)
+
+kw_2 = {
+ 'fliparrow': 1,
+ 'history': 1,
+ 'buttonaspect': 0.5,
+ 'arrowbutton_relief': 'groove',
+ 'selectioncommand' : Test.callback,
+ 'hull_background' : 'red',
+ 'scrolledlist_items' : (123, 456, 789, 101112),
+ 'listheight' : 50,
+ 'label_text' : 'Numeric Simple:',
+ 'labelpos' : 'w',
+ 'entryfield_validate' : 'numeric',
+ 'unique' : 0,
+}
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+)
+
+alltests = (
+ (tests_1, kw_1a),
+ (tests_1, kw_1b),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.CounterDialog
+
+kw_1 = {
+ 'counter_labelpos': 'n',
+ 'counter_buttonaspect': 2.0,
+ 'counter_autorepeat': 0,
+ 'counter_initwait': 1000,
+ 'counter_padx': 5,
+ 'counter_pady': 5,
+ 'counter_repeatrate': 20,
+ 'label_text' : 'Counter:',
+ 'buttons' : ('OK', 'Cancel', 'Help'),
+}
+tests_1 = (
+ (Test.num_options, (), 11),
+ ('counter_Arrow_borderwidth', 10),
+ ('counter_hull_background', 'yellow'),
+ ('command', Test.callback1),
+ ('hull_cursor', 'gumby'),
+ ('counter_datatype', 'time'),
+ ('counter_datatype', 'numeric'),
+ ('entry_borderwidth', 6),
+ ('entry_relief', 'raised'),
+ ('entry_exportselection', 0),
+ ('entry_foreground', 'blue'),
+ ('hull_highlightcolor', 'Red'),
+ ('hull_highlightthickness', 2),
+ ('counter_increment', 1),
+ ('entry_insertbackground', 'Yellow'),
+ ('entry_insertbackground', 'Black'),
+ ('entry_insertborderwidth', 1),
+ ('entry_insertborderwidth', 0),
+ ('entry_insertofftime', 400),
+ ('entry_insertontime', 700),
+ ('entry_insertwidth', 3),
+ ('entryfield_invalidcommand', Test.callback),
+ ('entry_show', '*'),
+ ('entry_background', 'red'),
+ (c.setentry, '69', 1),
+ ('entry_justify', 'right'),
+ ('entry_justify', 'center'),
+ ('entry_justify', 'left'),
+ ('label_text', 'Label'),
+ ('entry_relief', 'raised'),
+ ('entry_relief', 'sunken'),
+ ('entry_state', 'disabled'),
+ ('entry_state', 'normal'),
+ ('entry_background', 'GhostWhite'),
+ ('entryfield_validate', 'numeric'),
+ ('entryfield_validate', 'alphabetic'),
+ ('entry_width', 30),
+ ('relief', 'bogus', 'KeyError: Unknown option "relief" for CounterDialog'),
+ (c.interior, (), Tkinter.Frame),
+ (c.insert, ('end', 69)),
+ (c.increment, ()),
+ (c.decrement, ()),
+ ('defaultbutton', 1),
+ (c.invoke, (), 'Cancel'),
+ (c.clear, ()),
+ (c.get, (), ''),
+ (c.insert, ('end', 'Test String')),
+ (c.get, (), 'Test String'),
+ (c.delete, (0, 'end')),
+ (c.insert, ('end', 'Another Test')),
+ (c.icursor, 'end'),
+ (c.index, 'end', 12),
+ (c.selection_from, 0),
+ (c.selection_to, 'end'),
+ (c.xview, '3'),
+ (c.clear, ()),
+)
+
+kw_2 = {
+ 'buttonboxpos': 'e',
+ 'counter_labelpos': 'w',
+ 'label_text' : 'Another counter',
+ 'buttonbox_pady': 25,
+ 'buttons' : ('OK', 'Cancel'),
+}
+tests_2 = (
+ (c.title, 'CounterDialog 2', ''),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.Counter
+
+_myValidators = {
+ 'hello' : (lambda s: s == 'hello', len),
+}
+
+kw_1 = {
+ 'labelpos' : 'w',
+ 'label_text' : 'Counter:',
+ 'buttonaspect': 2.0,
+ 'autorepeat': 0,
+ 'initwait': 1000,
+ 'padx': 5,
+ 'pady': 5,
+ 'repeatrate': 20,
+ 'entryfield_value': 'First value',
+}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (c.get, (), 'First value'),
+ (Test.num_options, (), 12),
+ ('Arrow_borderwidth', 10),
+ ('hull_background', 'yellow'),
+ ('Arrow_background', 'green'),
+ ('label_background', 'blue'),
+ ('hull_borderwidth', 10),
+ ('entryfield_command', Test.callback),
+ ('entryfield_errorbackground', 'red'),
+ ('hull_cursor', 'gumby'),
+ ('datatype', 'time'),
+ ('datatype', 'numeric'),
+ ('entry_borderwidth', 6),
+ ('entry_relief', 'raised'),
+ ('entry_exportselection', 0),
+ ('entry_foreground', 'blue'),
+ ('hull_highlightcolor', 'Red'),
+ ('hull_highlightthickness', 2),
+ ('increment', 1),
+ ('entry_insertbackground', 'Yellow'),
+ ('entry_insertbackground', 'Black'),
+ ('entry_insertborderwidth', 1),
+ ('entry_insertborderwidth', 0),
+ ('entry_insertofftime', 400),
+ ('entry_insertontime', 700),
+ ('entry_insertwidth', 3),
+ ('entryfield_invalidcommand', Test.callback),
+ ('entry_show', '*'),
+ ('entry_background', 'red'),
+ (c.setentry, '69', Pmw.OK),
+ ('entry_show', ''),
+ ('entry_justify', 'right'),
+ ('entry_justify', 'center'),
+ ('entry_justify', 'left'),
+ ('label_text', 'Label'),
+ ('entry_relief', 'raised'),
+ ('entry_relief', 'sunken'),
+ ('entry_state', 'disabled'),
+ ('entry_state', 'normal'),
+ ('entry_background', 'GhostWhite'),
+ ('entryfield_validate', 'alphabetic'),
+ ('entryfield_validate', 'numeric'),
+ ('entry_width', 30),
+ ('relief', 'bogus', 'KeyError: Unknown option "relief" for Counter'),
+ (c.invoke, (), 1),
+ (c.interior, (), Tkinter.Frame),
+ (c.increment, ()),
+ (c.get, (), '70'),
+ ('increment', 5),
+ (c.get, (), '70'),
+ (c.decrement, ()),
+ (c.get, (), '65'),
+ (c.insert, ('end', 2)),
+ (c.get, (), '652'),
+ (c.invoke, (), 1),
+ (c.clear, ()),
+ (c.get, (), ''),
+ (c.insert, ('end', 'Test String')),
+ (c.get, (), 'Test String'),
+ (c.delete, (0, 'end')),
+ (c.insert, ('end', 'Another Test')),
+ (c.icursor, 'end'),
+ (c.index, 'end', 12),
+ (c.selection_from, 0),
+ (c.selection_to, 'end'),
+ (c.xview, '3'),
+ (c.clear, ()),
+ (c.insert, ('end', '100')),
+ ('entryfield_validate', {'validator' : 'real', 'min' : 10}),
+ (c.setentry, '50', Pmw.OK),
+ (c.setentry, 'hello', Pmw.ERROR),
+ ('entryfield_extravalidators', _myValidators),
+ ('entryfield_validate', 'hello'),
+ (c.setentry, 'hello', Pmw.OK),
+ (c.setentry, 'foo', Pmw.ERROR),
+ (c.valid, (), 1),
+ (c.cget, 'entry_background', 'GhostWhite'),
+ ('entry_textvariable', Test.stringvar),
+ (c.checkentry, (), 0),
+ (c.cget, 'entry_background', 'red'),
+ ('entryfield_validate', {'validator' : 'date', 'format' : 'dmy'}),
+ (c.valid, (), 0),
+ ('datatype', {'counter' : 'date', 'format' : 'dmy', 'yyyy' : 1}),
+ (c.setentry, '22/12/1999', Pmw.OK),
+ ('increment', 10),
+ (c.increment, ()),
+ (c.get, (), '01/01/2000'),
+
+ ('entryfield_validate', {'validator' : 'time', 'min' : '10:00:00'}),
+ (c.valid, (), 0),
+ ('increment', 60*60),
+ ('datatype', {'counter' : 'time'}),
+ (c.setentry, '11:00:00', Pmw.OK),
+ (c.decrement, ()),
+ (c.get, (), '10:00:00'),
+ (c.decrement, ()),
+ (c.get, (), '10:00:00'),
+
+ ('entryfield_validate', {'validator' : 'time', 'separator' : '.'}),
+ (c.valid, (), 0),
+ ('datatype', {'counter' : 'time', 'separator' : '.'}),
+ (c.setentry, '11.00.00', Pmw.OK),
+ (c.decrement, ()),
+ (c.get, (), '10.00.00'),
+
+ ('entryfield_validate', {'validator' : 'date', 'format' : 'dmy'}),
+ (c.valid, (), 0),
+ ('increment', 1),
+ ('datatype', {'counter' : 'date', 'format' : 'dmy'}),
+ (c.setentry, '25/12/99', Pmw.OK),
+ (c.decrement, ()),
+ (c.get, (), '24/12/99'),
+
+ ('entryfield_validate', {'validator' : 'date', 'separator' : '#@!',
+ 'max' : '99#@!12#@!26'}),
+ (c.valid, (), 0),
+ ('datatype', {'counter' : 'date', 'separator' : '#@!'}),
+ (c.setentry, '99#@!12#@!25', Pmw.OK),
+ (c.increment, ()),
+ (c.get, (), '99#@!12#@!26'),
+ ('increment', 10),
+ (c.increment, ()),
+ (c.get, (), '99#@!12#@!26'), # max exceeded
+ ('entryfield_validate', {'validator' : 'date', 'separator' : '#@!',
+ 'max' : '00#@!01#@!10'}),
+ (c.increment, ()),
+ (c.get, (), '00#@!01#@!05'), # max not exceeded
+ ('increment', 1),
+
+ ('entryfield_validate', {'validator' : 'date', 'format' : 'ymd',
+ 'separator' : '-' }),
+ (c.valid, (), 0),
+ ('datatype', {'counter' : 'date', 'format' : 'ymd', 'yyyy' : 1,
+ 'separator' : '-' }),
+ (c.setentry, '1999-12-22', 1),
+ ('increment', 10),
+ (c.increment, ()),
+ (c.get, (), '2000-01-01'),
+ ('increment', 1),
+)
+
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10}),
+)
+
+alltests = [(tests_1, kw_1)]
+
+poslist = ('nw', 'n', 'ne', 'en', 'e', 'es', 'se', 's', 'sw', 'ws', 'w', 'wn',)
+for count in range(len(poslist)):
+ pos = poslist[count]
+ margin = count * 10
+ kw_2 = {
+ 'entry_width' : 12,
+ 'labelpos' : pos,
+ 'labelmargin' : margin,
+ 'label_text' : 'Counter:',
+ }
+ alltests.append((tests_2, kw_2))
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/dialog.test code.
+
+import sys
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+if Tkinter.TkVersion >= 8.3:
+ version = sys.version_info
+ if version[0] > 2 or (version[0] == 2 and version[1] > 0):
+ expected1 = "AttributeError: Dialog instance has no attribute 'bogus'"
+ else:
+ expected1 = "AttributeError: 'Dialog' instance has no attribute 'bogus'"
+else:
+ expected1 = 'AttributeError: bogus'
+
+c = Pmw.Dialog
+
+def _addListbox():
+ global _lb
+ w = Test.currentWidget()
+ _lb = Tkinter.Listbox(w.interior(), relief = 'sunken')
+ _lb.pack(fill = 'both', expand = 'yes')
+
+def _addListEntry(text):
+ _lb.insert('end', text)
+
+def _test_deactivate(result):
+ w = Test.currentWidget()
+ w.after(Test.delay() + 4000,
+ lambda widget=w, r=result: widget.deactivate(r))
+
+def _createOtherToplevel():
+ global tempToplevel
+ Test.root.deiconify()
+ Test.root.geometry('+0+0')
+ tempToplevel = Tkinter.Toplevel()
+ tempToplevel.geometry('+0+0')
+ label = Tkinter.Label(tempToplevel, text =
+ 'The cursor should turn to a\n' +
+ 'clock over this window if the\n' +
+ 'blt busy command is available.\n' +
+ 'In any case, the button will be inactive\n' +
+ 'while the modal dialog is active.')
+ label.pack(padx=100, pady=100)
+ button = Tkinter.Button(tempToplevel, text = 'Try to press me')
+ button.pack(pady=100, expand=1)
+
+def _hideOtherToplevel():
+ global tempToplevel
+ tempToplevel.withdraw()
+ Test.root.withdraw()
+
+def _bogus():
+ w = Test.currentWidget()
+ w.bogus()
+
+kw_1 = {
+ 'buttons' : (),
+ 'buttonbox_padx': 30,
+}
+tests_1 = (
+ ('buttons', ('OK',)),
+ ('buttons', ('OK', 'Cancel',)),
+ ('defaultbutton', 'OK'),
+ (_addListbox, ()),
+ (Test.num_options, (), 9),
+ ('hull_background', '#d9d9d9'),
+ ('buttons', ('A', 'B', 'C', 'D')),
+ ('hull_cursor', 'gumby'),
+ (c.title, 'Dialog Shell', ''),
+ (c.interior, (), Tkinter.Frame),
+ ('buttons', ()),
+ ('buttons', ('OK',)),
+ ('buttons', ('OK', 'Cancel')),
+ ('buttons', ('OK', 'Cancel', 'Help')),
+ ('defaultbutton', 'OK'),
+ ('buttons', ('Apply', 'OK', 'Cancel', 'Help')),
+ ('buttons', ('Apply', 'OK', 'Cancel')),
+ ('defaultbutton', 'Cancel'),
+ (c.invoke, 'OK', 'None'),
+ ('buttonbox_OK_text', 'OOOOOKKKKK'),
+ (c.show, ()),
+ (c.withdraw, (), ''),
+ ('buttons', ('Apply', 'OK', 'Cancel', 'Foo')),
+ ('buttons', ('Apply', 'OK', 'Cancel')),
+ (c.show, ()),
+ (c.withdraw, (), ''),
+ (_createOtherToplevel, ()),
+ (_addListEntry, 'Testing application activate/deactivate'),
+ (_addListEntry, 'Please wait'),
+ (_test_deactivate, 'Hello World'),
+ (c.activate, (), 'Hello World'),
+ ('defaultbutton', ''),
+ (_addListEntry, 'Now testing global activate/deactivate'),
+ (_addListEntry, 'Please wait a bit more'),
+ (_test_deactivate, 'Hello World'),
+ (c.activate, (1), 'Hello World'),
+ (_hideOtherToplevel, ()),
+ ('buttons', ('Apply', 'OK', 'Cancel', 'Foo', '1')),
+ (c.show, (), {}),
+ (_bogus, (), expected1),
+)
+
+kw_2 = {'buttonboxpos' : 'e', 'separatorwidth' : 5}
+tests_2 = (
+ ('buttons', ('OK',)),
+ ('buttons', ('OK', 'Cancel',)),
+ ('defaultbutton', 'OK'),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/entryfield.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+_myValidators = {
+ 'hello' : (lambda s: s == 'hello', len),
+}
+
+c = Pmw.EntryField
+
+kw_1 = {'entry_width' : 12, 'labelpos' : 'n', 'label_text' : 'Entry Field:'}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 10),
+ ('errorbackground', 'red'),
+ ('hull_background', 'yellow'),
+ ('label_background', 'yellow'),
+ ('entry_background', 'yellow'),
+ ('hull_show', 'X', 'TclError: unknown option "-show"'),
+ ('entry_show', ''),
+ ('entry_borderwidth', 4),
+ ('entry_borderwidth', 2),
+ ('command', Test.callback),
+ ('hull_cursor', 'gumby'),
+ ('entry_exportselection', 0),
+ ('label_foreground', 'Green'),
+ ('entry_foreground', 'Green'),
+ ('label_foreground', 'Black'),
+ ('entry_foreground', 'Black'),
+ ('label_highlightcolor', 'Red'),
+ ('entry_highlightcolor', 'Red'),
+ ('entry_highlightthickness', 2),
+ ('entry_insertbackground', 'Yellow'),
+ ('entry_insertbackground', 'Black'),
+ ('entry_insertborderwidth', 1),
+ ('entry_insertborderwidth', 0),
+ ('entry_insertofftime', 400),
+ ('entry_insertontime', 700),
+ ('entry_insertwidth', 3),
+ ('invalidcommand', Test.callback),
+ ('entry_justify', 'right'),
+ ('entry_justify', 'center'),
+ ('entry_justify', 'left'),
+ ('label_text', 'Label'),
+ ('entry_relief', 'raised'),
+ ('entry_relief', 'sunken'),
+ ('entry_state', 'disabled'),
+ ('entry_state', 'normal'),
+ ('entry_background', 'GhostWhite'),
+ ('validate', 'numeric'),
+ ('validate', 'alphabetic'),
+ ('entry_width', 30),
+ ('validate', 'bogus',
+ "ValueError: bad validate value \"bogus\": must be a function or one " +
+ "of the standard validators ('alphabetic', 'alphanumeric', 'date', " +
+ "'hexadecimal', 'integer', 'numeric', 'real', 'time') or extra " +
+ "validators ()"),
+ ('relief', 'bogus', 'KeyError: Unknown option "relief" for EntryField'),
+ (c.invoke, (), 1),
+ (c.interior, (), Tkinter.Frame),
+ (c.clear, ()),
+ (c.get, (), ''),
+ (c.insert, ('end', 'Test String')),
+ (c.get, (), 'Test String'),
+ (c.delete, (0, 'end')),
+ (c.insert, ('end', 'Another Test')),
+ (c.icursor, 'end'),
+ (c.index, 'end', 12),
+ (c.selection_from, 0),
+ (c.selection_to, 'end'),
+ (c.xview, '3'),
+ (c.clear, ()),
+ (c.insert, ('end', '100')),
+ ('validate', {'validator' : 'real', 'min' : 10}),
+ (c.setentry, '50', 1),
+ (c.setentry, 'hello', 0),
+ ('extravalidators', _myValidators),
+ ('validate', 'hello'),
+ (c.setentry, 'hello', 1),
+ (c.setentry, 'foo', 0),
+ (c.valid, (), 1),
+ (c.cget, 'entry_background', 'GhostWhite'),
+ ('entry_textvariable', Test.stringvar),
+ (c.checkentry, (), 0),
+ (c.cget, 'entry_background', 'red'),
+)
+
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10}),
+)
+
+alltests = [(tests_1, kw_1)]
+
+poslist = ('nw', 'n', 'ne', 'en', 'e', 'es', 'se', 's', 'sw', 'ws', 'w', 'wn',)
+for pos in poslist:
+ kw_2 = {
+ 'labelpos' : pos,
+ 'label_text' : 'Entry Field',
+ }
+ alltests.append((tests_2, kw_2))
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/labeledwidget.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.LabeledWidget
+
+def _addListbox():
+ w = Test.currentWidget()
+ lb = Tkinter.Listbox(w.interior(), relief = 'sunken')
+ lb.pack(padx = 10, pady = 10)
+
+def _testalignLabels():
+ w = Test.currentWidget()
+ return Pmw.alignlabels((w,))
+
+kw_1 = {'labelpos': 'nw'}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (_addListbox, ()),
+ (Test.num_options, (), 3),
+ ('label_textvariable', Test.stringvar),
+ ('label_textvariable', ''),
+ ('label_text', 'Label'),
+ ('label_font', Test.font['small']),
+ ('label_image', Test.flagup),
+ ('label_image', ''),
+ (c.interior, (), Tkinter.Frame),
+ (_testalignLabels, (), None),
+)
+
+kw_2 = {'label_text' : 'ListBox', 'labelpos' : 's'}
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (_addListbox, ()),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+#!/bin/env python
+
+# This is a rough collection of tests that can not be automated.
+# To add a new test, create a function with name ending in '_test'.
+
+import os
+import string
+import time
+import sys
+import Test
+import Tkinter
+import Pmw
+
+# ----------------------------------------------------------------------
+
+def scrolledframeflashing_test():
+ # Script which demonstrates continuous flashing of dynamic scrollbars
+ # in Pmw.ScrolledFrame.
+ #
+ # When this script is run, the two scrollbars will be continuously
+ # mapped and unmapped and the window will continuously change size.
+
+ frame = Tkinter.Frame(root)
+ frame.pack(fill = 'both', expand = 1)
+
+ sf = Pmw.ScrolledFrame(frame, borderframe = 0)
+ sf.pack(fill = 'both', expand = 1)
+
+ inner = Tkinter.Frame(sf.interior(),
+ width = 401,
+ height = 300,
+ borderwidth = 0,
+ highlightthickness = 0,
+ )
+ inner.pack(fill = 'both', expand = 1)
+
+# ----------------------------------------------------------------------
+
+def scrolledlistboxflashing_test():
+ # Script which demonstrates continuous flashing of dynamic scrollbars
+ # in Pmw.ScrolledListBox.
+ #
+ # When this script is run, the two scrollbars will be continuously
+ # mapped and unmapped and the window will continuously change size.
+
+ frame = Tkinter.Frame(root)
+ frame.pack(fill = 'both', expand = 1)
+
+ sf = Pmw.ScrolledListBox(frame,
+ listbox_width = 20,
+ listbox_height = 10
+ )
+ sf.pack(fill = 'both', expand = 1)
+ for i in range(11):
+ sf.insert('end', '2' * 20)
+
+# ----------------------------------------------------------------------
+
+def scrolledlistboxflashing2_test():
+ # Another script which demonstrates continuous flashing of dynamic
+ # scrollbars in Pmw.ScrolledListBox under Pmw.0.8.
+ #
+ # When this script is run, the two scrollbars will be continuously
+ # mapped and unmapped and the window will continuously change size.
+ #
+ # (This did not display error when tried with Pmw.0.8, 99/8/3)
+
+ def insert():
+ sectionList = ['1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '123456789012345678901']
+ for counter in sectionList:
+ slb.insert('end', counter)
+
+ def clear():
+ slb.delete(0, 'end')
+
+ global slb
+ slb = Pmw.ScrolledListBox(root)
+ slb.pack()
+
+ root.after(2000,insert)
+ root.after(3000,clear)
+ root.after(4000,insert)
+
+ root.geometry('400x400')
+
+# ----------------------------------------------------------------------
+
+def scrolledtextflashing_test():
+ # Script which demonstrates continuous flashing of dynamic scrollbars
+ # in Pmw.ScrolledText.
+ #
+ # When this script is run, the two scrollbars will be continuously
+ # mapped and unmapped and the window will continuously change size.
+
+ frame = Tkinter.Frame(root)
+ frame.pack(fill = 'both', expand = 1)
+
+ sf = Pmw.ScrolledText(frame,
+ text_width = 20,
+ text_height = 10,
+ text_wrap = 'none',
+ borderframe = 0
+ )
+ sf.pack(fill = 'both', expand = 1)
+ for i in range(11):
+ sf.insert('end', '2' * 20)
+ if i != 10:
+ sf.insert('end', '\n')
+
+# ----------------------------------------------------------------------
+
+def scrolledcanvasflashing_test():
+ # Script which demonstrates continuous flashing of dynamic scrollbars
+ # in Pmw.ScrolledCanvas.
+ #
+ # When this script is run, the two scrollbars will be continuously
+ # mapped and unmapped and the window will continuously change size.
+
+ frame = Tkinter.Frame(root)
+ frame.pack(fill = 'both', expand = 1)
+
+ sf = Pmw.ScrolledCanvas(frame,
+ canvas_scrollregion = (0, 0, 301, 200),
+ canvas_width=300,
+ canvas_height=200,
+ borderframe = 0
+ )
+ sf.pack(fill = 'both', expand = 1)
+
+# ----------------------------------------------------------------------
+
+def scrolledframeflashing2_test():
+ # The two scrollbars will be continuously mapped and unmapped, but
+ # the toplevel window will remain the same size.
+
+ root.geometry('550x500')
+
+ frame = Tkinter.Frame()
+ frame.pack()
+
+ sf = Pmw.ScrolledFrame(frame, borderframe = 0)
+ sf.pack(fill = 'both')
+
+ inner = Tkinter.Frame(sf.interior(),
+ width = 401,
+ height = 300,
+ borderwidth = 0,
+ highlightthickness = 0,
+ )
+ inner.pack()
+
+# ----------------------------------------------------------------------
+
+def reinitialise_test():
+ global text
+ text = """
+ Demonstrates bug in Pmw.0.8.1 and earlier.
+ Click on this button, click on OK in the dialog, then Exit below.
+ When this window appears again, clicking on this button gives
+ an error:
+ TclError: can't invoke "wm" command: application has been destroyed
+ """
+ class test:
+ def __init__(self):
+ root = Tkinter.Tk()
+ Pmw.initialise(root)
+ self.messagedialog = Pmw.MessageDialog(message_text = 'Testing')
+ self.messagedialog.withdraw()
+ button = Tkinter.Button(
+ text = text, command = self.messagedialog.activate)
+ button.pack(pady = 20)
+ exit = Tkinter.Button(text = 'Exit', command = root.destroy)
+ exit.pack(pady = 20)
+ root.mainloop()
+
+ test()
+ test()
+
+# ----------------------------------------------------------------------
+
+def componentgroup_test():
+ def addbutton(bb):
+ bb.configure(Button_background = 'yellow')
+ bb.add('Apples')
+ bb.after(3000, lambda bb = bb:
+ bb.configure(Button_background = 'green'))
+
+ bb = Pmw.ButtonBox(Button_background = 'red')
+ bb.add('Bananas')
+ bb.pack()
+
+ mb = Pmw.MenuBar(Button_background = 'red')
+ mb.configure(Button_background = 'yellow')
+ mb.pack()
+
+ pw = Pmw.PanedWidget(Frame_background = 'red')
+ pw.configure(Frame_background = 'yellow')
+ pw.pack()
+
+ rs = Pmw.RadioSelect(Button_background = 'red')
+ rs.configure(Button_background = 'yellow')
+ rs.pack()
+
+ bb.after(3000, lambda bb = bb, addbutton = addbutton: addbutton(bb))
+
+# ----------------------------------------------------------------------
+
+def balloon_test():
+
+ # TODO
+
+ # Test that the balloon does not reappear if the mouse button is
+ # pressed down inside a widget and then, while the mouse button is
+ # being held down, the mouse is moved outside of the widget and
+ # then moved back over the widget.
+
+ # Test that when a widget is destroyed while a balloon is being
+ # displayed for it then the balloon is withdrawn.
+
+ # Test that when a widget is destroyed while a balloon is being
+ # displayed for another widget then the balloon is not withdrawn.
+
+ # Test that there is no eror when a widget is destroyed during the
+ # initwait period (between when the mouse enters the widget and
+ # when the initwait timer goes off).
+
+ # Test that if unbind() is called on a widget that triggered the
+ # balloon to be displayed then the balloon is withdrawn. Also
+ # test that if another widget triggered the balloon then the
+ # balloon is not withdrawn.
+
+ # Test that if tagunbind() is called on a canvas or text item that
+ # triggered the balloon to be displayed then the balloon is
+ # withdrawn. Also test that if another widget or item triggered
+ # the balloon then the balloon is not withdrawn.
+
+ pass
+
+# ----------------------------------------------------------------------
+
+# A class which prints out a message when an instance is deleted.
+class MyToplevel(Tkinter.Toplevel):
+
+ def __init__(self):
+ Tkinter.Toplevel.__init__(self)
+
+ def __del__(self):
+ print 'Window deleted'
+
+def _runMemoryLeakTest():
+ global top
+ top = MyToplevel()
+ Pmw.MegaToplevel(top)
+ Pmw.AboutDialog(top)
+ Pmw.ComboBoxDialog(top)
+ Pmw.CounterDialog(top)
+ Pmw.Dialog(top)
+ Pmw.MessageDialog(top)
+ Pmw.PromptDialog(top)
+ Pmw.SelectionDialog(top)
+ Pmw.TextDialog(top)
+
+ Pmw.ButtonBox(top).pack()
+ Pmw.ComboBox(top).pack()
+ Pmw.Counter(top).pack()
+ Pmw.EntryField(top).pack()
+ Pmw.Group(top).pack()
+ Pmw.LabeledWidget(top).pack()
+ Pmw.MenuBar(top).pack()
+ Pmw.MessageBar(top).pack()
+ Pmw.NoteBook(top).pack()
+ Pmw.OptionMenu(top).pack()
+ Pmw.PanedWidget(top).pack()
+ Pmw.RadioSelect(top).pack()
+ Pmw.ScrolledCanvas(top).pack()
+ Pmw.ScrolledField(top).pack()
+ Pmw.ScrolledFrame(top).pack()
+ Pmw.ScrolledListBox(top).pack()
+ Pmw.ScrolledText(top).pack()
+ Pmw.TimeCounter(top).pack()
+
+def _killMemoryLeakTest():
+ global top
+ top.destroy()
+ del top
+
+memoryLeakMessage = """
+Click on the "Run test" button to create instances of
+all Pmw megawidgets. Then click on the "Destroy" button.
+The message "Window deleted" should be printed to
+standard output.
+"""
+def memoryleak_test():
+ label = Tkinter.Label(text = memoryLeakMessage)
+ label.pack()
+ run = Tkinter.Button(text = 'Run test', command = _runMemoryLeakTest)
+ run.pack()
+ kill = Tkinter.Button(text = 'Destroy', command = _killMemoryLeakTest)
+ kill.pack()
+
+# ----------------------------------------------------------------------
+
+def memoryleak2_test():
+
+ print 'This test continuously creates and deletes megawidgets and'
+ print 'their components. It calls the "top" program, so'
+ print 'may not work on non-Unix operating systems. Run it for a long,'
+ print 'long time and check that the process memory size does not'
+ print 'continue to increase. Kill with <Control-C>.'
+
+ pid = os.getpid()
+
+ label = Tkinter.Label()
+ label.pack()
+
+ # Setup each test:
+
+ # 1. Create/delete all megawidgets:
+ megawidgets = (
+ Pmw.AboutDialog, Pmw.Balloon, Pmw.ButtonBox, Pmw.ComboBox,
+ Pmw.ComboBoxDialog, Pmw.Counter, Pmw.CounterDialog, Pmw.Dialog,
+ Pmw.EntryField, Pmw.Group, Pmw.HistoryText, Pmw.LabeledWidget,
+ Pmw.MainMenuBar, Pmw.MenuBar, Pmw.MessageBar, Pmw.MessageDialog,
+ Pmw.NoteBook, Pmw.OptionMenu, Pmw.PanedWidget, Pmw.PromptDialog,
+ Pmw.RadioSelect, Pmw.ScrolledCanvas, Pmw.ScrolledField,
+ Pmw.ScrolledFrame, Pmw.ScrolledListBox, Pmw.ScrolledText,
+ Pmw.SelectionDialog, Pmw.TextDialog, Pmw.TimeCounter,
+ )
+
+ # 2. Balloon binding:
+ toplevel = Tkinter.Toplevel()
+ balloon = Pmw.Balloon(toplevel)
+ button = Tkinter.Button(toplevel)
+ button.pack()
+ canvas = Tkinter.Canvas(toplevel)
+ item = canvas.create_rectangle(0, 0, 100, 100)
+ canvas.pack()
+
+ # 3. Adding and deleting menu:
+ toplevel = Tkinter.Toplevel()
+ mainmenu = Pmw.MainMenuBar(toplevel)
+ mainmenu.addmenu('Foo', 'help')
+ toplevel.configure(menu = mainmenu)
+
+ # 4. Adding and deleting notebook page:
+ toplevel = Tkinter.Toplevel()
+ notebook = Pmw.NoteBook(toplevel)
+ notebook.pack()
+
+ # 5. Adding and deleting panedwidget pane:
+ toplevel = Tkinter.Toplevel()
+ panedwidget = Pmw.PanedWidget(toplevel)
+ panedwidget.pack()
+ panedwidget.insert('Foo', size = 100)
+
+ # 6. Adding and deleting MenuBar menu:
+ toplevel = Tkinter.Toplevel()
+ menubar = Pmw.MenuBar(toplevel)
+ menubar.pack()
+
+ # 7. Setting OptionMenu items:
+ toplevel = Tkinter.Toplevel()
+ optionmenu = Pmw.OptionMenu(toplevel, items = ('XXX', 'YYY', 'ZZZ'))
+ optionmenu.pack()
+
+ # 8. Setting Tkinter.Canvas scrollcommand option:
+ toplevel = Tkinter.Toplevel()
+ scrollcanvas = Pmw.ScrolledCanvas(toplevel)
+ scrollcanvas.pack()
+
+ global prevSize
+ prevSize = -1
+
+ # Loop and run each test:
+ count = 0
+ while 1:
+ count = count + 1
+ label.configure(text = count)
+
+ # 1. Create/delete all megawidgets:
+ for widgetClass in megawidgets:
+ widget = widgetClass()
+ if widgetClass == Pmw.MainMenuBar:
+ root.configure(menu = widget)
+ elif hasattr(widgetClass, 'pack'):
+ widget.pack()
+ root.update()
+ widget.destroy()
+
+ # 2. Balloon binding:
+ balloon.bind(button, 'help')
+ balloon.tagbind(canvas, item, 'help')
+ # tagbind leaks due to a bug in Tkinter (v1.127) Canvas - it adds
+ # bindings to self._tagcommands but does not delete them.
+ root.update()
+
+ # 3. Adding and deleting MainMenuBar menu:
+ mainmenu.addmenu('File', 'help')
+ root.update()
+ mainmenu.deletemenu('File')
+ root.update()
+
+ # 4. Adding and deleting notebook page:
+ notebook.insert('File')
+ root.update()
+ notebook.delete('File')
+ root.update()
+
+ # 5. Adding and deleting panedwidget pane:
+ panedwidget.insert('File', size = 100)
+ root.update()
+ panedwidget.delete('File')
+ root.update()
+
+ # 6. Adding and deleting MenuBar menu:
+ menubar.addmenu('File', 'help')
+ root.update()
+ menubar.deletemenu('File')
+ root.update()
+
+ # 7. Setting OptionMenu items:
+ optionmenu.setitems(('aaa', 'bbb', 'ccc'))
+ root.update()
+
+ # 8. Setting Tkinter.Canvas scrollcommand option:
+ scrollcanvas.configure(hscrollmode = 'static')
+ scrollcanvas.configure(hscrollmode = 'dynamic')
+
+ # Check memory usage:
+ # lines = os.popen('top').readlines()
+ lines = os.popen('top -b -n 1 -p %d' % pid).readlines()
+ for line in lines:
+ # if string.find(line, 'python1.5.2') > 0:
+ if string.find(line, '^ *%d' % pid) > 0:
+ break
+ # size = string.atoi(string.lstrip(line[27:32]))
+ size = string.atoi(string.lstrip(line[22:29]))
+ if prevSize != size:
+ print time.strftime('%H:%M:%S', time.localtime(time.time())),
+ print line[:-1]
+ prevSize = size
+
+# ----------------------------------------------------------------------
+
+def usageExit():
+ print 'Usage:', sys.argv[0], '<test>'
+ print ' where <test> is one of:'
+ for test in tests:
+ print ' ', test
+ sys.exit()
+
+tests = []
+for name in locals().keys():
+ if name[-5:] == '_test':
+ tests.append(name)
+tests.sort()
+
+if len(sys.argv) != 2:
+ usageExit()
+
+testName = sys.argv[1]
+if testName not in tests:
+ print 'Unknown test "' + testName + '"'
+ usageExit()
+
+if testName == 'reinitialise_test':
+ # Run this by itself, since it calls Tkinter.Tk, mainloop, etc.
+ reinitialise_test()
+ sys.exit()
+
+# Use Pmw version in this distribution:
+Test.initialise()
+root = Test.root
+root.deiconify()
+
+# To use a different version of Pmw, comment out the three above lines
+# and the "import Test" line and uncomment these three:
+# root = Tkinter.Tk()
+# Pmw.setversion('1.0')
+# Pmw.initialise(root)
+
+testFunction = locals()[testName]
+testFunction()
+
+if testName != 'memoryleak2_test':
+ # This does not use mainloop.
+ root.mainloop()
--- /dev/null
+# Based on itk2.2/tests/widget.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+class TestWidget(Pmw.MegaWidget):
+
+ def __init__(self, parent = None, **kw):
+
+ # Define the megawidget options.
+ optiondefs = (
+ ('status', '', self._status),
+ ('background', 'linen', None),
+ ('borderwidth', 2, None),
+ ('foreground', 'navy', None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ self._label = self.createcomponent('label',
+ (), None,
+ Tkinter.Label, (interior,))
+ self._label.pack(side='left', padx=2)
+
+ self._button = self.createcomponent('button',
+ (), 'Mygroup',
+ Tkinter.Button, (interior,), text = 'Push Me',
+ activebackground = 'white', background = 'ivory')
+ self._button.pack(side='right', fill='x', padx=2)
+
+ # Initialise instance variables.
+ self._statusList = []
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def statusList(self, val=None):
+ if val is None:
+ return self._statusList
+ else:
+ self._statusList = val
+
+ def action(self, info):
+ self._statusList.append(info)
+
+ def _status(self):
+ self._statusList.append(self['status'])
+
+def _componentOption(component, option):
+ w = Test.currentWidget()
+ return w.component(component).cget(option)
+
+def _componentInvoke(component):
+ w = Test.currentWidget()
+ w.component(component).invoke()
+
+def _addComponent():
+ w = Test.currentWidget()
+ label2 = w.createcomponent('label2',
+ (), 'Mygroup',
+ Tkinter.Label, (w.interior(),),
+ text = 'Temporary', background = 'yellow')
+ label2.pack(fill = 'x')
+ return label2.cget('text')
+
+expectedOptions = {
+ 'background': ('background', 'background', 'Background', 'linen', 'linen'),
+ 'borderwidth': ('borderwidth', 'borderwidth', 'Borderwidth', 2, 2),
+ 'foreground': ('foreground', 'foreground', 'Foreground', 'navy', 'navy'),
+ 'status': ('status', 'status', 'Status', '', ''),
+}
+
+c = TestWidget
+tests = (
+ # Set status list to a known state, since configure(status) may have
+ # been called during contruction.
+ (c.statusList, ([''])),
+ (c.pack, ()),
+ (c.configure, (), {}, expectedOptions),
+ (c.configure, ('background'), expectedOptions['background']),
+ (c.configure, ('borderwidth'), expectedOptions['borderwidth']),
+ (c.configure, ('foreground'), expectedOptions['foreground']),
+ (c.configure, ('status'), expectedOptions['status']),
+ ('hull_background', 'red'),
+ ('label_background', 'red'),
+ ('borderwidth', 1),
+ ('button_command', Test.callback),
+ ('hull_cursor', 'trek'),
+ ('label_cursor', 'trek'),
+ ('Mygroup_foreground', 'IndianRed'),
+ ('button_activebackground', 'MistyRose'),
+ ('button_background', 'MistyRose2'),
+ ('status', 'test message'),
+ ('label_text', 'Label:'),
+ (c.components, (), ['button', 'hull', 'label']),
+ (c.component, ('hull'), Tkinter.Frame),
+ (c.component, ('label'), Tkinter.Label),
+ (c.component, ('button'), Tkinter.Button),
+ (_componentOption, ('hull', 'cursor'), 'trek'),
+ (_componentOption, ('label', 'cursor'), 'trek'),
+ (_componentOption, ('hull', 'background'), 'red'),
+ (_componentOption, ('label', 'background'), 'red'),
+ (_componentOption, ('button', 'background'), 'MistyRose2'),
+ (_componentOption, ('label', 'text'), 'Label:'),
+ (_componentOption, ('button', 'text'), 'Push Me'),
+ (c.statusList, (), ['', 'test message']),
+ ('button_command', Test.actioncallback),
+ (c.statusList, ([])),
+ (_componentInvoke, 'button'),
+ ('status', 'in between'),
+ (_componentInvoke, 'button'),
+ (c.statusList, (), ['button press', 'in between', 'button press']),
+ (_addComponent, (), 'Temporary'),
+ (c.components, (), ['button', 'hull', 'label', 'label2']),
+ (_componentOption, ('label2', 'background'), 'yellow'),
+ (c.destroycomponent, ('label2')),
+ (c.components, (), ['button', 'hull', 'label']),
+)
+testData = ((c, ((tests, {}),)),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/messagedialog.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.MessageDialog
+
+kw_1 = {
+ 'message_text' : 'Are you sure you want to do that?',
+ 'buttons' : ('OK', 'Cancel'),
+ 'icon_bitmap' : 'questhead',
+ 'iconmargin': '60',
+ 'iconpos' : 'w',
+ 'buttonbox_padx': 30,
+}
+tests_1 = (
+ (Test.num_options, (), 13),
+ ('message_anchor', 'center'),
+ ('message_justify', 'center'),
+ ('message_wraplength', 0),
+ ('hull_background', '#d9d9d9'),
+ ('icon_bitmap', 'warning'),
+ ('hull_cursor', 'gumby'),
+ ('icon_image', Test.flagup),
+ ('message_font', Test.font['variable']),
+ ('message_foreground', 'red'),
+ ('message_padx', 15),
+ ('message_pady', 15),
+ ('icon_image', ''),
+ (c.title, 'MessageDialog 1: new title', ''),
+ (c.interior, (), Tkinter.Frame),
+ ('defaultbutton', 'OK'),
+)
+
+kw_2 = {
+ 'message_text' : 'On the left',
+ 'buttons' : ('OK', 'Cancel'),
+ 'buttonboxpos': 'e',
+ 'borderx': 55,
+ 'bordery': 55,
+ 'separatorwidth': 5,
+}
+tests_2 = (
+ (c.title, 'MessageDialog 2', ''),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.NoteBook
+
+class callbackCollector:
+ def __init__(self):
+ self.list = []
+ def __call__(self, pageName):
+ self.list.append(pageName)
+ def get(self):
+ rtn = self.list
+ self.list = []
+ return rtn
+
+createCallback = callbackCollector()
+raiseCallback = callbackCollector()
+lowerCallback = callbackCollector()
+
+def checkCallbacks(clear = 0):
+ rtn = createCallback.get(), raiseCallback.get(), lowerCallback.get()
+ if not clear:
+ return rtn
+
+def _populatePage(pageName):
+ w = Test.currentWidget()
+ page = w.page(pageName)
+ text = Tkinter.Text(page)
+ text.pack()
+ return w.pagenames()[w.index(pageName)]
+
+def _getTopPageName():
+ w = Test.currentWidget()
+ return w._topPageName
+
+kw_1 = {'tabpos' : None}
+tests_1_common = (
+ (Test.num_options, (), 7),
+ ('createcommand', createCallback),
+ ('raisecommand', raiseCallback),
+ ('lowercommand', lowerCallback),
+ (checkCallbacks, 1),
+ (c.index, Pmw.END, 'ValueError: NoteBook has no pages'),
+ (c.index, Pmw.SELECT, 'ValueError: NoteBook has no pages'),
+ (c.setnaturalsize, ()),
+ (c.getcurselection, ()),
+ (c.insert, ('Temp', 0), {'page_pyclass' : Tkinter.Canvas}, Tkinter.Canvas),
+ (checkCallbacks, (), (['Temp'], ['Temp'], [])),
+ (c.getcurselection, (), 'Temp'),
+ (c.setnaturalsize, ()),
+ (c.delete, 'Temp'),
+ (checkCallbacks, (), ([], [], [])),
+ (c.getcurselection, ()),
+ (c.insert, ('Temp', Pmw.END), Tkinter.Frame),
+ (checkCallbacks, (), (['Temp'], ['Temp'], [])),
+ (c.delete, 'Temp'),
+ (c.add, 'Start', Tkinter.Frame),
+ ('Start_background', 'green'),
+ (c.insert, ('Final', Pmw.END), {'page_background' : 'blue'}, Tkinter.Frame),
+ (c.insert, ('Middle', 'Final'), Tkinter.Frame),
+ (c.index, Pmw.SELECT, 0),
+ (c.insert, ('First', 'Start'), Tkinter.Frame),
+ (c.index, Pmw.SELECT, 1),
+ (c.getcurselection, (), 'Start'),
+ (c.selectpage, Pmw.END),
+ (checkCallbacks, (), (['Start', 'Final'], ['Start', 'Final'], ['Start'])),
+ (c.index, Pmw.SELECT, 3),
+ (c.getcurselection, (), 'Final'),
+ (c.recolorborders, ()),
+ (c.selectpage, 2),
+ (c.index, Pmw.SELECT, 2),
+ (c.getcurselection, (), 'Middle'),
+ (checkCallbacks, (), (['Middle'], ['Middle'], ['Final'])),
+ (c.selectpage, 3),
+ (c.selectpage, 2),
+ (checkCallbacks, (), ([], ['Final', 'Middle'], ['Middle', 'Final'])),
+ (c.selectpage, 'Final'),
+ (c.index, Pmw.SELECT, 3),
+ (c.getcurselection, (), 'Final'),
+ (c.add, 'Last', Tkinter.Frame),
+ (c.pagenames, (), ['First', 'Start', 'Middle', 'Final', 'Last']),
+ (c.setnaturalsize, ()),
+ (_populatePage, Pmw.SELECT, 'Final'),
+ (_populatePage, 'Middle', 'Middle'),
+ (c.setnaturalsize, ()),
+ (c.add, 'Start', 'ValueError: Page "Start" already exists.'),
+ ('Page_background', 'yellow'),
+ (c.index, 1, 1),
+ (c.index, 10, 'ValueError: index "10" is out of range'),
+ (c.index, Pmw.END, 4),
+ (c.index, 'First', 0),
+ (c.index, 'Middle', 2),
+ (c.index, 'bogus', 'ValueError: bad index "bogus": ' + \
+ 'must be a name, a number, Pmw.END or Pmw.SELECT'),
+ (c.previouspage, ()),
+ (c.getcurselection, (), 'Middle'),
+ (c.previouspage, 'Start'),
+ (c.getcurselection, (), 'First'),
+ (c.nextpage, ()),
+ (c.getcurselection, (), 'Start'),
+ (c.nextpage, 'Middle'),
+ (c.getcurselection, (), 'Final'),
+ (c.delete, ('First', 'Start', 'Middle', 'Final', 'Last')),
+ (c.add, 'Temp', {'page_pyclass' : Tkinter.Button}, Tkinter.Button),
+ (c.delete, 'Temp'),
+ (c.add, 'Temp', {'page_pyclass' : Tkinter.Text}, Tkinter.Text),
+ (c.delete, 'Temp'),
+ (c.add, 'Temp', {'page_pyclass' : Pmw.ScrolledText,
+ 'page_vscrollmode' : 'static', 'page_text_state' : 'disabled'},
+ Pmw.ScrolledText),
+ ('Temp_text_background', 'red'),
+ (c.page, 'Temp', Pmw.ScrolledText),
+ (c.pagenames, (), ['Temp']),
+ (c.getcurselection, (), 'Temp'),
+ (c.delete, 'Temp'),
+ (c.getcurselection, (), None),
+ (c.add, 'Start', Tkinter.Frame),
+ (c.getcurselection, (), 'Start'),
+)
+
+tests_1 = tests_1_common + (
+ (_getTopPageName, (), None),
+ (c.pack, ()),
+ (_getTopPageName, (), 'Start'),
+ (c.delete, 'Start'),
+) + tests_1_common + (
+ (_getTopPageName, (), 'Start'),
+ (c.delete, 'Start'),
+ (c.pack_forget, ()),
+) + tests_1_common + (
+ (_getTopPageName, (), None),
+ (c.pack, ()),
+ (_getTopPageName, (), 'Start'),
+)
+
+kw_2 = {
+ 'tabpos' : None,
+ 'borderwidth' : 10,
+ 'pagemargin' : 10,
+}
+
+tests_2 = (
+ (c.pack, ()),
+ ('hull_relief', 'sunken'),
+ ('hull_borderwidth', 20),
+) + tests_1_common
+
+kw_3 = {}
+
+tests_3 = (
+ (c.pack, ()),
+) + tests_1_common + (
+ ('Tab_background', 'red'),
+ (c.add, 'One', Tkinter.Frame),
+ (c.tab, 'One', Tkinter.Button),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_1, kw_2),
+ (tests_1, kw_3),
+ (tests_2, kw_1),
+ (tests_2, kw_3),
+ (tests_3, kw_3),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.OptionMenu
+
+kw_1 = {
+ 'labelpos' : 'nw',
+ 'label_text' : 'Option Menu:',
+ 'items' : ('Chips', 'Lollies', 'Junk', 'More junk'),
+ 'initialitem' : 1,
+}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 6),
+ (c.getcurselection, (), 'Lollies'),
+ (c.index, 'Junk', 2),
+ (c.index, 'Nowhere', 'ValueError: bad index "Nowhere": must be ' +
+ 'a name, a number, Pmw.END or Pmw.SELECT'),
+ (c.index, Pmw.END, 3),
+ (c.index, Pmw.SELECT, 1),
+ (c.index, 1, 1),
+ (c.invoke, 'Chips'),
+ (c.getcurselection, (), 'Chips'),
+ ('command', Test.callback1),
+ (c.invoke, (), 'Chips'),
+ (c.invoke, 'Lollies', 'Lollies'),
+ (c.getcurselection, (), 'Lollies'),
+ ('hull_background', 'yellow'),
+ ('hull_show', 'X', 'TclError: unknown option "-show"'),
+ (c.index, Pmw.SELECT, 1),
+ (c.setitems, (('Chips', 'Junk', 'Lollies', 'More junk'),)),
+ (c.index, Pmw.SELECT, 2),
+ (c.setitems, (('Fruit', 'Vegetables', 'Cereals', 'Legumes'),)),
+ (c.index, Pmw.SELECT, 0),
+ (c.getcurselection, (), 'Fruit'),
+ (c.setitems, (('Vegetables', 'Cereals', 'Legumes'), Pmw.END)),
+ (c.getcurselection, (), 'Legumes'),
+ (c.index, 'Vegetables', 0),
+ (c.invoke, 'Legumes', 'Legumes'),
+ ('hull_cursor', 'gumby'),
+ ('label_foreground', 'Green'),
+ ('label_foreground', 'Black'),
+ ('label_highlightcolor', 'Red'),
+ ('label_text', 'Label'),
+)
+
+testData = ((c, ((tests_1, kw_1),)),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# This tests Pmw option and component handling.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+"""
+ Definitions:
+ initialisation option: an option that can be set in the call
+ to the constructor but not in configure()
+ configuration option: an option that can be set in the call
+ to the constructor and to configure()
+ option: either an initialisation option or a configuration option
+
+ Tests
+ -----
+ in constructor:
+ + define an option, its default value and whether it is an
+ initialisation or a configuration option
+ + set a callback function for a configuration option
+ + set a different default for an option of a base class
+ + set a different default for an option of a component of a base class
+ + override the callback for a configuration option of a base class
+ + create a component
+ + create an alias for a component
+ + create an alias for a sub-component
+
+ calling constructor:
+ + set an option
+ + set an option of a base class
+ + set an option of a component created in the constructor
+ + set an option of an aliased component or sub-component created in
+ the constructor
+ + set an option of one or more components via their group name
+ + use the default value of an option
+ + use the default value of an option of a base class
+ + use the default value of an option of a base class where the default
+ value is redefined in the derived class
+
+ calling configure:
+ + set a configuration option
+ + set a configuration option of a base class
+ + set a configuration option of a component
+ + set a configuration option of an aliased component or sub-component
+ + set a configuration option of one or more components via their group name
+ + set a configuration option with a callback
+ + set a configuration option of a base class with a callback in the
+ derived class
+"""
+
+class Simple(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+ optiondefs = (
+ ('initsimple1', 'initsimple1', Pmw.INITOPT),
+ ('initsimple2', 'initsimple2', Pmw.INITOPT),
+ ('optsimple1', 'optsimple1', None),
+ ('optsimple2', 'optsimple2', None),
+ )
+ self.defineoptions(kw, optiondefs)
+ Pmw.MegaWidget.__init__(self, parent)
+
+ interior = self.interior()
+ self._widget = self.createcomponent('widget',
+ (('widgy', 'widget'),), None,
+ Tkinter.Button, (interior,))
+ self._widget.grid(column=0, row=0, sticky='nsew')
+
+ self.initialiseoptions()
+
+class Complex(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+ optiondefs = (
+ ('initcomplex1', 'initcomplex1', Pmw.INITOPT),
+ ('initcomplex2', 'initcomplex2', Pmw.INITOPT),
+ ('optcomplex1', 'optcomplex1', None),
+ ('optcomplex2', 'optcomplex2', None),
+ )
+ self.defineoptions(kw, optiondefs)
+ Pmw.MegaWidget.__init__(self, parent)
+
+ interior = self.interior()
+ self._simple = self.createcomponent('simple',
+ (('widget', 'simple_widget'),), None,
+ Simple, (interior,))
+ self._simple.grid(column=0, row=0, sticky='nsew')
+
+ self.initialiseoptions()
+
+class Base(Pmw.MegaWidget):
+ def __init__(self, parent = None, **kw):
+ optiondefs = (
+ ('initbase1', 'initbase1', Pmw.INITOPT),
+ ('initbase2', 'initbase2', Pmw.INITOPT),
+ ('initbase3', 'initbase3', Pmw.INITOPT),
+ ('optbase1', 'optbase1', self._optbase1),
+ ('optbase2', 'optbase2', None),
+ ('optbase3', 'optbase3', None),
+ )
+ self.defineoptions(kw, optiondefs)
+ Pmw.MegaWidget.__init__(self, parent)
+
+ oldInterior = Pmw.MegaWidget.interior(self)
+ self._widget = self.createcomponent('basesimple',
+ (('widget', 'basesimple_widget'),), None,
+ Simple, (oldInterior,))
+ self._widget.grid(column=0, row=0, sticky='nsew')
+
+ self._child = self.createcomponent('child',
+ (), 'Mygroup',
+ Tkinter.Frame, (oldInterior,))
+ self._child.grid(column=0, row=1, sticky='nsew')
+
+ self._groupie = self.createcomponent('groupie',
+ (), 'Mygroup',
+ Tkinter.Button, (oldInterior,), text = 'XXXXX')
+ self._groupie.grid(column=0, row=2, sticky='nsew')
+
+ self.basedummy = []
+
+ self.initialiseoptions()
+
+ def _optbase1(self):
+ self.basedummy.append(self['optbase1'])
+
+ def getbasedummy(self):
+ return self.basedummy
+
+ def interior(self):
+ return self._child
+
+class Derived(Base):
+ def __init__(self, parent = None, **kw):
+ # Define the options for this megawidget.
+ optiondefs = (
+ ('initbase2', 'initbase2inderived', Pmw.INITOPT),
+ ('initderived1', 'initderived1', Pmw.INITOPT),
+ ('initderived2', 'initderived2', Pmw.INITOPT),
+ ('optbase1', 'optbase1', self._optbase1),
+ ('optderived1', 'optderived1', None),
+ ('optderived2', 'optderived2', None),
+ ('groupie_text', 'YYYYY', None),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining my options).
+ Base.__init__(self, parent)
+
+ # Create components.
+ interior = self.interior()
+ self._widget = self.createcomponent('derivedcomplex',
+ (('derivedsimple', 'derivedcomplex_simple'),), None,
+ Complex, (interior,))
+ self._widget.grid(column=0, row=0, sticky='nsew')
+
+ # Initialise instance.
+
+ # Initialise instance variables.
+ self.deriveddummy = []
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def _optbase1(self):
+ self.deriveddummy.append(self['optbase1'])
+
+ def getderiveddummy(self):
+ return self.deriveddummy
+
+testData = ()
+
+c = Simple
+kw_1 = {
+ 'hull_borderwidth' :2,
+ 'hull_relief' :'sunken',
+ 'hull_background' :'red',
+ 'widget_text' :'simple',
+ 'widgy_foreground' :'red',
+ 'initsimple1' :'initsimple1_new',
+}
+tests = (
+ (c.pack, ()),
+ (c.components, (), ['hull', 'widget']),
+ (c.componentaliases, (), [('widgy', 'widget'),]),
+ (c.options, (), [('initsimple1', 'initsimple1', 1), ('initsimple2', 'initsimple2', 1), ('optsimple1', 'optsimple1', 0), ('optsimple2', 'optsimple2', 0)]),
+ (c.cget, 'initsimple1', 'initsimple1_new'),
+ (c.cget, 'initsimple2', 'initsimple2'),
+ (c.cget, 'optsimple1', 'optsimple1'),
+ (c.cget, 'optsimple2', 'optsimple2'),
+ (c.cget, 'widget_foreground', 'red'),
+ ('optsimple1', 'optsimple1_new'),
+ (c.cget, 'optsimple1', 'optsimple1_new'),
+)
+testData = testData + ((c, ((tests, kw_1),)),)
+
+
+c = Complex
+kw_1 = {
+ 'hull_borderwidth' : 2,
+ 'hull_relief' : 'sunken',
+ 'hull_background' : 'red',
+ 'simple_widget_text' : 'complex',
+ 'widget_foreground' : 'yellow',
+}
+tests = (
+ (c.pack, ()),
+ (c.components, (), ['hull', 'simple']),
+ (c.componentaliases, (), [('widget', 'simple_widget'),]),
+ (c.options, (), [('initcomplex1', 'initcomplex1', 1), ('initcomplex2', 'initcomplex2', 1), ('optcomplex1', 'optcomplex1', 0), ('optcomplex2', 'optcomplex2', 0)]),
+)
+testData = testData + ((c, ((tests, kw_1),)),)
+
+c = Base
+kw_1 = {
+ 'hull_borderwidth' : 2,
+ 'hull_relief' : 'sunken',
+ 'hull_background' : 'red',
+ 'basesimple_widget_text' : 'base',
+ 'widget_foreground' : 'green',
+ 'initbase1' : 'initbase1_new',
+}
+tests = (
+ (c.pack, ()),
+ (c.components, (), ['basesimple', 'child', 'groupie', 'hull']),
+ (c.componentaliases, (), [('widget', 'basesimple_widget'),]),
+ (c.options, (), [('initbase1', 'initbase1', 1), ('initbase2', 'initbase2', 1), ('initbase3', 'initbase3', 1), ('optbase1', 'optbase1', 0), ('optbase2', 'optbase2', 0), ('optbase3', 'optbase3', 0)]),
+ (c.cget, 'widget_foreground', 'green'),
+ (c.cget, 'basesimple_widget_foreground', 'green'),
+ (c.cget, 'basesimple_widgy_foreground', 'green'),
+ ('widget_foreground', 'blue'),
+ (c.cget, 'widget_foreground', 'blue'),
+ (c.cget, 'basesimple_widget_foreground', 'blue'),
+ (c.cget, 'basesimple_widgy_foreground', 'blue'),
+ (c.cget, 'optbase1', 'optbase1'),
+ (c.cget, 'groupie_text', 'XXXXX'),
+ # When Test created the widget, it performed a test where it configured
+ # each option. Hence, _optbase1() has been called twice:
+ (c.getbasedummy, (), ['optbase1', 'optbase1']),
+ ('optbase1', 'basedummy_new'),
+ (c.getbasedummy, (), ['optbase1', 'optbase1', 'basedummy_new']),
+)
+testData = testData + ((c, ((tests, kw_1),)),)
+
+
+c = Derived
+kw_1 = {
+ 'hull_borderwidth' : 2,
+ 'hull_relief' : 'sunken',
+ 'hull_background' : 'red',
+ 'basesimple_widget_text' : 'base simple',
+ 'derivedcomplex_widget_text' : 'derived complex',
+ 'initderived1' : 'initderived1_new',
+ 'initbase1' : 'initbase1_new',
+ 'optbase3' : 'optbase3_new',
+ 'derivedcomplex_initcomplex1' : 'derivedcomplex_initcomplex1',
+ 'derivedsimple_initsimple1' : 'derivedsimple_initsimple1',
+ 'hull_cursor' : 'gumby',
+ 'Mygroup_borderwidth' : 2,
+ 'Mygroup_relief' : 'ridge',
+}
+tests = (
+ (c.pack, ()),
+ (c.components, (), ['basesimple', 'child', 'derivedcomplex', 'groupie', 'hull']),
+ (c.componentaliases, (), [('derivedsimple', 'derivedcomplex_simple'), ('widget', 'basesimple_widget'),]),
+ (c.options, (), [('initbase1', 'initbase1', 1), ('initbase2', 'initbase2inderived', 1), ('initbase3', 'initbase3', 1), ('initderived1', 'initderived1', 1), ('initderived2', 'initderived2', 1), ('optbase1', 'optbase1', 0), ('optbase2', 'optbase2', 0), ('optbase3', 'optbase3', 0), ('optderived1', 'optderived1', 0), ('optderived2', 'optderived2', 0), ]),
+ (c.getbasedummy, (), []),
+ (c.getderiveddummy, (), ['optbase1', 'optbase1']),
+ ('optbase1', 'derivedbasedummy_new'),
+ (c.getbasedummy, (), []),
+ (c.getderiveddummy, (), ['optbase1', 'optbase1', 'derivedbasedummy_new']),
+ (c.cget, 'optbase3', 'optbase3_new'),
+ ('optbase3', 'optbase3_newer'),
+ (c.cget, 'optbase3', 'optbase3_newer'),
+ (c.cget, 'optderived1', 'optderived1'),
+ (c.cget, 'initderived1', 'initderived1_new'),
+ (c.cget, 'initbase2', 'initbase2inderived'),
+ (c.cget, 'initbase1', 'initbase1_new'),
+ (c.cget, 'initbase3', 'initbase3'),
+ (c.cget, 'groupie_text', 'YYYYY'),
+ ('groupie_text', 'ZZZZZ'),
+ (c.cget, 'groupie_text', 'ZZZZZ'),
+ (c.cget, 'derivedcomplex_optcomplex1', 'optcomplex1'),
+ ('derivedcomplex_optcomplex1', 'optcomplex1_new'),
+ (c.cget, 'derivedcomplex_optcomplex1', 'optcomplex1_new'),
+ (c.cget, 'derivedsimple_optsimple2', 'optsimple2'),
+ ('derivedsimple_optsimple2', 'optsimple2_new'),
+ (c.cget, 'derivedcomplex_simple_optsimple2', 'optsimple2_new'),
+ ('derivedcomplex_simple_optsimple2', 'optsimple2_newer'),
+ (c.cget, 'derivedsimple_optsimple2', 'optsimple2_newer'),
+ (c.cget, 'hull_cursor', 'gumby'),
+ (c.cget, 'groupie_relief', 'ridge'),
+ (c.cget, 'Mygroup_relief', 'ridge'),
+ ('Mygroup_relief', 'sunken'),
+ (c.cget, 'groupie_relief', 'sunken'),
+ (c.cget, 'Mygroup_relief', 'sunken'),
+ ('groupie_relief', 'groove'),
+ (c.cget, 'groupie_relief', 'groove'),
+)
+testData = testData + ((c, ((tests, kw_1),)),)
+
+if __name__ == '__main__':
+ #Test.setverbose(1)
+ Test.runTests(testData)
--- /dev/null
+import os
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.PanedWidget
+
+def _setbackground(name, colour):
+ w = Test.currentWidget()
+ w.pane(name).configure(background = colour)
+
+kw_1 = {'orient': 'horizontal', 'hull_width': 400, 'hull_height': 300}
+tests_1 = (
+ (c.pack, ()),
+ (Test.num_options, (), 5),
+ (c.add, 'left', {'min' : 100}, Tkinter.Frame),
+ (_setbackground, ('left', 'red')),
+ (c.add, 'middle', {'max' : 100}, Tkinter.Frame),
+ (_setbackground, ('middle', 'green')),
+ (c.add, 'right', {'size' : 100}, Tkinter.Frame),
+ (c.insert, ('first', 'middle'), {'size' : 100}, Tkinter.Frame),
+ (_setbackground, ('right', 'yellow')),
+ (_setbackground, ('first', 'blue')),
+ (c.delete, 'middle'),
+ (c.pane, 'left', Tkinter.Frame),
+ (c.panes, (), ['left', 'first', 'right']),
+ (c.configurepane, 'first', {'size' : 200}),
+)
+
+alltests = (
+ (tests_1, kw_1),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Tests for Pmw megawidgets
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+class TestWidget(Pmw.MegaWidget):
+
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ optiondefs = ()
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ self._label = self.createcomponent('label',
+ (), None,
+ Tkinter.Label, (interior,), text = 'test')
+ self._label.pack(side='left', padx=2)
+
+ # Check keywords and initialise options.
+ self.initialiseoptions(TestWidget)
+
+ def addComponent(self, nickname):
+ self.createcomponent(nickname,
+ (), None,
+ TestComponent, (self.interior(),), status = 'create')
+
+ def addTestWidget(self, widget, option, value):
+ w = self.createcomponent('test',
+ (), None,
+ widget, (self.interior(),))
+ apply(self.configure, (), {'test_' + option : value})
+ if w.__class__.__name__ not in ('Menu', 'Toplevel'):
+ w.pack()
+ if hasattr(widget, 'geometry'):
+ w.geometry('+100+100')
+ return str(w.cget(option))
+
+ def deleteTestWidget(self):
+ w = self.component('test')
+ if hasattr(widget, 'pack'):
+ w.pack_forget()
+ self.destroycomponent('test')
+
+ def packComponent(self, nickname):
+ self.component(nickname).pack()
+
+ def getStatusList(self, nickname):
+ return self.component(nickname)._statusList
+
+ def componentOption(self, nickname, option):
+ return self.component(nickname).cget(option)
+
+class TestComponent(Pmw.MegaWidget):
+
+ def __init__(self, parent = None, **kw):
+ # Define the megawidget options.
+ optiondefs = (
+ ('status', '', self._status),
+ )
+ self.defineoptions(kw, optiondefs)
+
+ # Initialise the base class (after defining the options).
+ Pmw.MegaWidget.__init__(self, parent)
+
+ # Create the components.
+ interior = self.interior()
+ self._label = self.createcomponent('label',
+ (), None,
+ Tkinter.Label, (interior,), text = 'test')
+ self._label.pack(side='left', padx=2)
+
+ self._statusList = []
+
+ # Check keywords and initialise options.
+ self.initialiseoptions()
+
+ def action(self, info):
+ self._statusList.append(info)
+
+ def _status(self):
+ self._statusList.append(self['status'])
+
+c = TestWidget
+tests = (
+ (c.pack, ()),
+ (c.components, (), ['hull', 'label']),
+
+ (c.addComponent, 'k0'),
+ (c.getStatusList, 'k0', ['create']),
+ (c.packComponent, 'k0'),
+ ('k0_status', 'foo'),
+ (c.getStatusList, 'k0', ['create', 'foo']),
+
+ (c.addComponent, 'k1'),
+ (c.packComponent, 'k1'),
+ ('k1_status', 'bar'),
+ (c.getStatusList, 'k1', ['create', 'bar']),
+
+ (c.addComponent, 'k2'),
+ (c.packComponent, 'k2'),
+ ('k2_label_foreground', 'green'),
+ ('hull_cursor', 'gumby'),
+ ('k2_label_cursor', 'gumby'),
+ (c.componentOption, ('k2', 'label_foreground'), 'green'),
+ (c.componentOption, ('k2', 'label_cursor'), 'gumby'),
+
+ (c.addComponent, 'k3'),
+ (c.packComponent, 'k3'),
+ ('k3_label_foreground', 'red'),
+ ('hull_background', 'white'),
+ ('k3_label_background', 'white'),
+ ('hull_cursor', 'dot'),
+ ('k3_label_cursor', 'dot'),
+ (c.componentOption, ('k3', 'label_foreground'), 'red'),
+ (c.componentOption, ('k3', 'label_background'), 'white'),
+ (c.componentOption, ('k3', 'label_cursor'), 'dot'),
+ ('label_background', 'white'),
+ (c.destroycomponent, 'k0'),
+ (c.destroycomponent, 'k1'),
+ (c.destroycomponent, 'k2'),
+ (c.destroycomponent, 'k3'),
+)
+
+# Test each of the standard widgets as components.
+for widget in [Tkinter.Button, Tkinter.Checkbutton, Tkinter.Entry, Tkinter.Label, Tkinter.Listbox, \
+ Tkinter.Menu, Tkinter.Menubutton, Tkinter.Message, Tkinter.Radiobutton, Tkinter.Scale, Tkinter.Text]:
+ tests = tests + (
+ (c.addTestWidget, (widget, 'foreground', 'blue'), 'blue'),
+ (c.deleteTestWidget, ())
+ )
+
+for widget in [Tkinter.Canvas, Tkinter.Frame, Tkinter.Scrollbar, Tkinter.Toplevel]:
+ tests = tests + (
+ (c.addTestWidget, (widget, 'background', 'grey80'), 'grey80'),
+ (c.deleteTestWidget, ())
+ )
+
+# Test the logical fonts.
+for fontName in Pmw.logicalfontnames():
+ for sizeIncr in (-2, 0, 1, 2, 4, 8):
+ font = Pmw.logicalfont(fontName, sizeIncr)
+ tests = tests + (
+ ('label_text', 'Testing font\n' + fontName + ' ' + str(sizeIncr)),
+ ('label_font', font),
+ )
+fontList = (
+ (('Helvetica', 0), {}),
+ (('Times', 0), {}),
+ (('Typewriter', 0), {}),
+ (('Typewriter', 0), {'width' : 'condensed'}),
+ (('Typewriter', -1), {'width' : 'condensed'}),
+ (('Fixed', 0), {}),
+ (('Fixed', 0), {'width' : 'condensed'}),
+ (('Fixed', -1), {'width' : 'condensed'}),
+ (('Helvetica', 2), {'slant' : 'italic'}),
+ (('Helvetica', 0), {'size' : 18}),
+ (('Helvetica', 0), {'weight' : 'bold'}),
+ (('Helvetica', 12), {'weight' : 'bold', 'slant' : 'italic'}),
+ (('Typewriter', 0), {'size' : 8, 'weight' : 'bold'}),
+ (('Fixed', 0), {'size' : 8, 'weight' : 'bold'}),
+ (('Times', 0), {'size' : 24, 'weight' : 'bold', 'slant' : 'italic'}),
+)
+
+for args, dict in fontList:
+ font = apply(Pmw.logicalfont, args, dict)
+ tests = tests + (
+ ('label_text', 'Testing font\n' + str(args) + '\n' + str(dict)),
+ ('label_font', font),
+ )
+
+testData = ((c, ((tests, {'label_text' : 'Testing Pmw base classes'}),)),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/promptdialog.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.PromptDialog
+
+kw_1 = {
+ 'entryfield_labelpos': 'n',
+ 'label_text' : 'Please enter your password',
+ 'buttons' : ('OK', 'Cancel', 'Help'),
+}
+tests_1 = (
+ (Test.num_options, (), 11),
+ (c.title, 'PromptDialog 1', ''),
+ ('hull_background', '#d9d9d9'),
+ ('hull_cursor', 'gumby'),
+ ('entry_exportselection', 1),
+ ('entry_foreground', 'Black'),
+ ('entry_background', 'GhostWhite'),
+ ('entry_insertbackground', 'Black'),
+ ('entry_insertborderwidth', 1),
+ ('entry_insertborderwidth', 0),
+ ('entry_insertofftime', 400),
+ ('entry_insertontime', 700),
+ ('entry_insertwidth', 3),
+ ('label_text', 'Label'),
+ ('entry_justify', 'left'),
+ ('entry_relief', 'sunken'),
+ ('entry_state', 'disabled'),
+ ('entry_state', 'normal'),
+ ('entry_background', 'GhostWhite'),
+ ('entryfield_validate', 'numeric'),
+ ('entryfield_validate', 'alphabetic'),
+ ('entryfield_validate', 'alphanumeric'),
+ ('entry_width', 30),
+ (c.interior, (), Tkinter.Frame),
+ (c.insertentry, ('end', 'Test String')),
+ (c.get, (), 'Test String'),
+ (c.deleteentry, (0, 'end')),
+ (c.insertentry, ('end', 'Another Test')),
+ (c.icursor, 'end'),
+ (c.indexentry, 'end', 12),
+ (c.selection_from, 0),
+ (c.selection_to, 'end'),
+ (c.xview, 3),
+ (c.clear, ()),
+ (c.get, (), ''),
+ ('label_bitmap', 'warning'),
+ ('label_image', Test.flagup),
+ ('entry_font', Test.font['variable']),
+ ('entry_foreground', 'red'),
+ ('label_image', ''),
+ ('label_bitmap', ''),
+ (c.title, 'PromptDialog 1: new title', ''),
+ ('defaultbutton', 'OK'),
+)
+
+kw_2 = {
+ 'buttonboxpos': 'e',
+ 'entryfield_labelpos': 'w',
+ 'label_text' : 'Please enter your password',
+ 'buttonbox_pady': 25,
+ 'buttons' : ('OK', 'Cancel'),
+}
+tests_2 = (
+ (c.title, 'PromptDialog 2', ''),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+if Tkinter.TkVersion >= 8.4:
+ expected1 = 'TclError: bad relief "bogus": must be '
+else:
+ expected1 = 'TclError: bad relief type "bogus": must be '
+
+c = Pmw.RadioSelect
+
+kw_1 = {'labelpos' : 'nw', 'label_text' : 'Radio Select:'}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 8),
+ (c.index, Pmw.END, 'ValueError: RadioSelect has no buttons'),
+ (c.add, ('Fruit',), Tkinter.Button),
+ (c.add, ('Vegetables',), Tkinter.Button),
+ (c.add, ('CornFlakes',), {'text': 'Cereals'}, Tkinter.Button),
+ (c.add, ('Legumes',), Tkinter.Button),
+ (c.add, ('Legumes',), 'ValueError: button "Legumes" already exists'),
+ (c.index, 0, 0),
+ (c.index, Pmw.END, 3),
+ (c.index, 'Vegetables', 1),
+ (c.index, 'Fruit', 0),
+ (c.index, 12, 'ValueError: index "12" is out of range'),
+ (c.index, 'bogus', 'ValueError: bad index "bogus": ' + \
+ 'must be a name, a number or Pmw.END'),
+ ('hull_background', 'yellow'),
+ ('hull_show', 'X', 'TclError: unknown option "-show"'),
+ ('frame_relief', 'raised'),
+ ('frame_borderwidth', 4),
+ ('frame_borderwidth', 2),
+ ('command', Test.callback1),
+ (c.invoke, 'Vegetables', 'Vegetables'),
+ ('hull_cursor', 'gumby'),
+ ('Button_state', 'disabled'),
+ ('Button_background', 'Green'),
+ ('Button_cursor', 'watch'),
+ ('Button_background', 'grey85'),
+ ('label_foreground', 'Green'),
+ ('label_foreground', 'Black'),
+ ('label_highlightcolor', 'Red'),
+ ('Fruit_background', 'red'),
+ ('Vegetables_background', 'green'),
+ ('CornFlakes_background', 'yellow'),
+ ('Legumes_background', 'brown'),
+ ('Legumes_foreground', 'white'),
+ (c.add, ('Foo',), Tkinter.Button),
+ ('label_text', 'Label'),
+ ('frame_relief', 'sunken'),
+ ('frame_relief', 'bogus', expected1 + Test.reliefs),
+ (c.deleteall, ()),
+)
+
+kw_2 = {
+ 'labelpos' : 'nw',
+ 'label_text' : 'Multiple:',
+ 'selectmode' : 'multiple',
+}
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (c.add, ('Fruit',), Tkinter.Button),
+ (c.add, ('Vegetables',), Tkinter.Button),
+ (c.add, ('CornFlakes',), {'text': 'Cereals'}, Tkinter.Button),
+ (c.add, ('Legumes',), Tkinter.Button),
+ ('command', Test.callback2),
+ (c.getcurselection, (), ()),
+ (c.invoke, 'Vegetables', ('Vegetables', 1)),
+ (c.getcurselection, (), ('Vegetables',)),
+ (c.invoke, 'Legumes', ('Legumes', 1)),
+ (c.getcurselection, (), ('Vegetables', 'Legumes')),
+ (c.invoke, 'Fruit', ('Fruit', 1)),
+ (c.getcurselection, (), ('Vegetables', 'Legumes', 'Fruit')),
+ (c.invoke, 'Legumes', ('Legumes', 0)),
+ (c.getcurselection, (), ('Vegetables', 'Fruit')),
+ (c.deleteall, ()),
+ (c.add, ('Fruit',), Tkinter.Button),
+ (c.add, ('Vegetables',), Tkinter.Button),
+ (c.invoke, 'Vegetables', ('Vegetables', 1)),
+ (c.getcurselection, (), ('Vegetables',)),
+)
+
+alltests = [
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+]
+
+
+tests_3 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10}),
+ (c.add, ('Foo',), Tkinter.Button),
+ (c.add, ('Bar',), Tkinter.Button),
+)
+
+poslist = ('nw', 'n', 'ne', 'en', 'e', 'es', 'se', 's', 'sw', 'ws', 'w', 'wn',)
+for pos in poslist:
+ kw_3 = {
+ 'labelpos' : pos,
+ 'orient' : 'vertical',
+ 'padx' : 20,
+ 'pady' : 20,
+ 'label_text' : 'Radio Select',
+ }
+ alltests.append((tests_3, kw_3))
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.ScrolledCanvas
+
+def _createOvals():
+ w = Test.currentWidget()
+ w.create_oval(50, 50, 150, 100, fill = 'red')
+ w.create_oval(100, 50, 150, 150, fill = 'blue')
+ w.create_oval(50, 100, 200, 350, fill = 'yellow')
+
+def _createWindow():
+ w = Test.currentWidget()
+ lb = Pmw.ScrolledListBox(w.interior(),
+ items = range(20), listbox_height = 6)
+ w.create_window(300, 100, window = lb)
+
+def _testYView(doBottom):
+ w = Test.currentWidget()
+ top, bottom = w.yview()
+ if type(top) != type(0.0) or type(bottom) != type(0.0):
+ return 'bad type ' + str(top) + ' ' + str(bottom)
+ if doBottom:
+ if bottom != 1.0:
+ return 'bottom is ' + str(bottom)
+ else:
+ if top != 0.0:
+ return 'top is ' + str(top)
+
+kw_1 = {'labelpos': 'n', 'label_text': 'ScrolledCanvas', 'borderframe' : 1}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 8),
+ (_createOvals, ()),
+ (c.resizescrollregion, ()),
+ (_createWindow, ()),
+ (c.resizescrollregion, ()),
+ ('hull_background', 'aliceblue'),
+ ('Scrollbar_borderwidth', 3),
+ ('hull_cursor', 'gumby'),
+ ('label_text', 'Label'),
+ ('Scrollbar_repeatdelay', 200),
+ ('Scrollbar_repeatinterval', 105),
+ ('vscrollmode', 'none'),
+ ('vscrollmode', 'static'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'none'),
+ ('hscrollmode', 'static'),
+ ('hscrollmode', 'dynamic'),
+ ('Scrollbar_width', 20),
+ ('vscrollmode', 'bogus', 'ValueError: bad vscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ ('hscrollmode', 'bogus', 'ValueError: bad hscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ (c.yview, ('moveto', 0.0)),
+ (_testYView, 0),
+ (c.yview, ('moveto', 0.02)),
+ (c.yview, ('moveto', 0.04)),
+ (c.yview, ('moveto', 0.06)),
+ (c.yview, ('moveto', 0.08)),
+ (c.yview, ('moveto', 0.10)),
+ (c.yview, ('moveto', 0.12)),
+ (c.yview, ('moveto', 0.14)),
+ (c.yview, ('moveto', 0.16)),
+ (c.yview, ('moveto', 0.18)),
+ (c.yview, ('moveto', 0.20)),
+ (c.yview, ('moveto', 0.22)),
+ (c.yview, ('moveto', 0.24)),
+ (c.yview, ('moveto', 0.26)),
+ (c.yview, ('moveto', 0.28)),
+ (c.yview, ('moveto', 0.98)),
+ (_testYView, 1),
+ (c.yview, ('scroll', -1, 'page')),
+ (c.yview, ('scroll', -1, 'page')),
+ (_testYView, 0),
+ (c.yview, ('scroll', 1, 'page')),
+ (c.yview, ('scroll', 1, 'page')),
+ (_testYView, 1),
+)
+
+kw_2 = {
+ 'hscrollmode' : 'dynamic',
+ 'label_text' : 'Label',
+ 'labelpos' : 'n',
+ 'scrollmargin': 20,
+ 'canvasmargin': 20,
+ 'usehullsize': 1,
+ 'hull_width' : 500,
+ 'hull_height' : 200,
+}
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((Pmw.ScrolledCanvas, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.ScrolledField
+
+kw_1 = {'labelpos': 'nw'}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 4),
+ ('text', 'Hello World'),
+ ('label_textvariable', Test.stringvar),
+ ('label_textvariable', ''),
+ ('label_text', 'Label'),
+ ('label_font', Test.font['small']),
+ ('label_image', Test.flagup),
+ ('label_image', ''),
+ ('entry_foreground', 'red'),
+ ('text', 'Foo'),
+ ('entry_font', Test.font['small']),
+)
+
+alltests = (
+ (tests_1, kw_1),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.ScrolledFrame
+
+def _createInterior():
+ w = Test.currentWidget()
+ for i in range(3):
+ lb = Pmw.ScrolledListBox(w.interior(),
+ items = range(20), listbox_height = 6)
+ lb.pack(padx = 10, pady = 10)
+
+def _testYView(doBottom):
+ w = Test.currentWidget()
+ top, bottom = w.yview()
+ if type(top) != type(0.0) or type(bottom) != type(0.0):
+ return 'bad type ' + str(top) + ' ' + str(bottom)
+ if doBottom:
+ if bottom != 1.0:
+ return 'bottom is ' + str(bottom)
+ else:
+ if top != 0.0:
+ return 'top is ' + str(top)
+
+kw_1 = {'labelpos': 'n', 'label_text': 'ScrolledFrame'}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 11),
+ (_createInterior, ()),
+ ('hull_background', 'aliceblue'),
+ ('Scrollbar_borderwidth', 3),
+ ('hull_cursor', 'gumby'),
+ ('label_text', 'Label'),
+ ('Scrollbar_repeatdelay', 200),
+ ('Scrollbar_repeatinterval', 105),
+ ('vscrollmode', 'none'),
+ ('vscrollmode', 'static'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'none'),
+ ('hscrollmode', 'static'),
+ ('hscrollmode', 'dynamic'),
+ ('Scrollbar_width', 20),
+ ('vscrollmode', 'bogus', 'ValueError: bad vscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ ('hscrollmode', 'bogus', 'ValueError: bad hscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ (c.cget, 'vscrollmode', 'bogus'),
+ (c.cget, 'hscrollmode', 'bogus'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'dynamic'),
+ (_testYView, 0),
+ (c.yview, ('moveto', 0.02)),
+ (c.yview, ('moveto', 0.04)),
+ (c.yview, ('moveto', 0.06)),
+ (c.yview, ('moveto', 0.08)),
+ (c.yview, ('moveto', 0.10)),
+ (c.yview, ('moveto', 0.12)),
+ (c.yview, ('moveto', 0.14)),
+ (c.yview, ('moveto', 0.16)),
+ (c.yview, ('moveto', 0.18)),
+ (c.yview, ('moveto', 0.20)),
+ (c.yview, ('moveto', 0.22)),
+ (c.yview, ('moveto', 0.24)),
+ (c.yview, ('moveto', 0.26)),
+ (c.yview, ('moveto', 0.28)),
+ (c.yview, ('moveto', 0.98)),
+ (_testYView, 1),
+ (c.yview, ('scroll', -1, 'page')),
+ (c.yview, ('scroll', -1, 'page')),
+ (c.yview, ('scroll', -1, 'page')),
+ (_testYView, 0),
+ (c.yview, ('scroll', 1, 'page')),
+ (c.yview, ('scroll', 1, 'page')),
+ (c.yview, ('scroll', 1, 'page')),
+ (_testYView, 1),
+)
+
+kw_2 = {
+ 'hscrollmode' : 'dynamic',
+ 'label_text' : 'Label',
+ 'labelpos' : 'n',
+ 'scrollmargin': 20,
+}
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((Pmw.ScrolledFrame, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/scrolledlistbox.test code.
+
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.ScrolledListBox
+
+def _testYView(doBottom):
+ w = Test.currentWidget()
+ top, bottom = w.yview()
+ if type(top) != type(0.0) or type(bottom) != type(0.0):
+ return 'bad type ' + str(top) + ' ' + str(bottom)
+ if doBottom:
+ if bottom != 1.0:
+ return 'bottom is ' + str(bottom)
+ else:
+ if top != 0.0:
+ return 'top is ' + str(top)
+
+kw_1 = {
+ 'labelpos': 'n',
+ 'label_text': 'Start',
+ 'listbox_height' : 20,
+ 'listbox_width' : 40
+}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 9),
+ ('label_text', 'ScrolledListBox'),
+ ('listbox_height', 6),
+ ('listbox_width', 20),
+ ('listbox_borderwidth', 3),
+ ('hscrollmode', 'none'),
+ ('hscrollmode', 'static'),
+ ('hscrollmode', 'dynamic'),
+ (c.delete, (0, 'end')),
+ (c.insert, ('end', 'Hello', 'World')),
+ ('listbox_relief', 'raised'),
+ ('listbox_relief', 'sunken'),
+ ('Scrollbar_width', 20),
+ ('Scrollbar_width', 15),
+ ('listbox_background', 'GhostWhite'),
+ ('listbox_selectborderwidth', 3),
+ ('listbox_selectforeground', 'blue'),
+ ('listbox_selectmode', 'browse'),
+ ('listbox_selectmode', 'extended'),
+ ('listbox_selectmode', 'single'),
+ ('listbox_selectmode', 'multiple'),
+ ('listbox_font', Test.font['small']),
+ ('vscrollmode', 'none'),
+ ('vscrollmode', 'static'),
+ ('vscrollmode', 'dynamic'),
+ ('listbox_width', 30),
+ ('listbox_height', 20),
+ ('vscrollmode', 'bogus', 'ValueError: bad vscrollmode option "bogus": ' + \
+ 'should be static, dynamic, or none'),
+ ('hscrollmode', 'bogus', 'ValueError: bad hscrollmode option "bogus": ' + \
+ 'should be static, dynamic, or none'),
+ (c.cget, 'vscrollmode', 'bogus'),
+ (c.cget, 'hscrollmode', 'bogus'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'dynamic'),
+ (c.insert, (0, 'Test', 'Test', 'Test', 'Test')),
+ (c.insert, ('end', 'More Test')),
+ (c.delete, 1),
+ (c.delete, (0, 3)),
+ ('listbox_exportselection', 0),
+ (c.select_set, 0),
+ (c.select_set, (0, 1)),
+ (c.getcurselection, (), ('World', 'More Test')),
+ (c.select_clear, (0, 'end')),
+ (c.getcurselection, (), ()),
+ (c.delete, (0, 'end')),
+ (c.get, (0, 'end'), ()),
+ (c.insert, ('end', 'Test', 'Test', 'Long String Test')),
+ (c.get, (0, 'end'), ('Test', 'Test', 'Long String Test')),
+ (c.insert, (0, 'Test', 'Test A')),
+ (c.get, (0, 'end'), ('Test', 'Test A', 'Test', 'Test', 'Long String Test')),
+ (c.insert, (1, 'Test', 'Test', 'Long String Test')),
+ (c.get, (0, 4), ('Test', 'Test', 'Test', 'Long String Test', 'Test A')),
+ (c.insert, (5, 'Test', 'Test',
+ 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')),
+ (c.get, 7, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),
+ (c.get, 'end', 'Long String Test'),
+ (c.size, (), 11),
+ (c.delete, (3, 2)),
+ (c.size, (), 11),
+ (c.delete, (3, 3)),
+ (c.size, (), 10),
+ (c.clear, ()),
+ (c.size, (), 0),
+ (c.get, (), ()),
+ (c.yview, ('moveto', 0.0)),
+ (_testYView, 0),
+ (c.insert, ('end', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10')),
+ (c.insert, ('end', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10')),
+ (c.insert, ('end', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10')),
+ (c.insert, ('end', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10')),
+ (_testYView, 0),
+ (c.yview, ('moveto', 0.02)),
+ (c.yview, ('moveto', 0.04)),
+ (c.yview, ('moveto', 0.06)),
+ (c.yview, ('moveto', 0.08)),
+ (c.yview, ('moveto', 0.10)),
+ (c.yview, ('moveto', 0.12)),
+ (c.yview, ('moveto', 0.14)),
+ (c.yview, ('moveto', 0.16)),
+ (c.yview, ('moveto', 0.18)),
+ (c.yview, ('moveto', 0.20)),
+ (c.yview, ('moveto', 0.22)),
+ (c.yview, ('moveto', 0.24)),
+ (c.yview, ('moveto', 0.26)),
+ (c.yview, ('moveto', 0.28)),
+ (c.yview, ('moveto', 0.98)),
+ (_testYView, 1),
+ (c.yview, ('scroll', -1, 'page')),
+ (c.yview, ('scroll', -1, 'page')),
+ (c.yview, ('scroll', -1, 'page')),
+ (_testYView, 0),
+ (c.yview, ('scroll', 1, 'page')),
+ (c.yview, ('scroll', 1, 'page')),
+ (c.yview, ('scroll', 1, 'page')),
+ (_testYView, 1),
+)
+
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+)
+
+alltests = [(tests_1, kw_1)]
+
+poslist = ('nw', 'n', 'ne', 'en', 'e', 'es', 'se', 's', 'sw', 'ws', 'w', 'wn',)
+for pos in poslist:
+ kw_2 = {
+ 'listbox_selectmode' : 'extended',
+ 'items' : ('Hello', 'Out There', 'World'),
+ 'vscrollmode' : 'static',
+ 'hscrollmode' : 'dynamic',
+ 'label_text' : 'List',
+ 'labelpos' : pos,
+ 'scrollmargin': 10,
+ }
+ alltests.append((tests_2, kw_2))
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Based on iwidgets2.2.0/tests/scrolledtext.test code.
+
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.ScrolledText
+
+def _testYView(doBottom):
+ w = Test.currentWidget()
+ top, bottom = w.yview()
+ if type(top) != type(0.0) or type(bottom) != type(0.0):
+ return 'bad type ' + str(top) + ' ' + str(bottom)
+ if doBottom:
+ if bottom != 1.0:
+ return 'bottom is ' + str(bottom)
+ else:
+ if top != 0.0:
+ return 'top is ' + str(top)
+
+kw_1 = {'labelpos': 'n', 'label_text': 'ScrolledText'}
+tests_1 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (Test.num_options, (), 10),
+ (c.importfile, 'ScrolledText_test.py'),
+ ('hull_background', 'aliceblue'),
+ ('text_borderwidth', 3),
+ ('Scrollbar_borderwidth', 3),
+ ('hull_cursor', 'gumby'),
+ ('text_exportselection', 0),
+ ('text_exportselection', 1),
+ ('text_foreground', 'Black'),
+ ('text_height', 10),
+ ('text_width', 20),
+ ('text_insertbackground', 'Black'),
+ ('text_insertborderwidth', 1),
+ ('text_insertofftime', 200),
+ ('text_insertontime', 500),
+ ('text_insertwidth', 3),
+ ('label_text', 'Label'),
+ ('text_relief', 'raised'),
+ ('text_relief', 'sunken'),
+ ('Scrollbar_repeatdelay', 200),
+ ('Scrollbar_repeatinterval', 105),
+ ('vscrollmode', 'none'),
+ ('vscrollmode', 'static'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'none'),
+ ('hscrollmode', 'static'),
+ ('hscrollmode', 'dynamic'),
+ ('Scrollbar_width', 20),
+ ('text_selectborderwidth', 2),
+ ('text_state', 'disabled'),
+ ('text_state', 'normal'),
+ ('text_background', 'GhostWhite'),
+ ('text_wrap', 'char'),
+ ('text_wrap', 'none'),
+ ('vscrollmode', 'bogus', 'ValueError: bad vscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ ('hscrollmode', 'bogus', 'ValueError: bad hscrollmode ' +
+ 'option "bogus": should be static, dynamic, or none'),
+ (c.cget, 'vscrollmode', 'bogus'),
+ (c.cget, 'hscrollmode', 'bogus'),
+ ('vscrollmode', 'dynamic'),
+ ('hscrollmode', 'dynamic'),
+ (c.insert, ('end', 'Hello there\n')),
+ (_testYView, 0),
+ (c.yview, ('moveto', 0.02)),
+ (c.yview, ('moveto', 0.04)),
+ (c.yview, ('moveto', 0.06)),
+ (c.yview, ('moveto', 0.08)),
+ (c.yview, ('moveto', 0.10)),
+ (c.yview, ('moveto', 0.12)),
+ (c.yview, ('moveto', 0.14)),
+ (c.yview, ('moveto', 0.16)),
+ (c.yview, ('moveto', 0.18)),
+ (c.yview, ('moveto', 0.20)),
+ (c.yview, ('moveto', 0.22)),
+ (c.yview, ('moveto', 0.24)),
+ (c.yview, ('moveto', 0.26)),
+ (c.yview, ('moveto', 0.28)),
+ (c.yview, ('moveto', 0.98)),
+ (_testYView, 1),
+ (c.yview, ('scroll', -1, 'page')),
+ (c.yview, ('scroll', -50, 'page')),
+ (_testYView, 0),
+ (c.yview, ('scroll', 1, 'page')),
+ (c.yview, ('scroll', 50, 'page')),
+ (_testYView, 1),
+ (c.clear, ()),
+ (c.get, (), '\n'),
+)
+
+kw_2 = {
+ 'hscrollmode' : 'dynamic',
+ 'label_text' : 'Label',
+ 'labelpos' : 'n',
+ 'scrollmargin': 20,
+}
+tests_2 = (
+ (c.pack, (), {'padx' : 10, 'pady' : 10, 'fill' : 'both', 'expand' : 1}),
+ (c.importfile, 'ScrolledText_test.py'),
+ ('text_relief', 'raised'),
+ ('text_relief', 'sunken'),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((Pmw.ScrolledText, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.SelectionDialog
+
+kw_1 = {
+ 'scrolledlist_labelpos': 'n',
+ 'label_text' : 'Please select one',
+ 'buttons' : ('OK', 'Cancel'),
+ 'buttonbox_padx': 30,
+}
+tests_1 = (
+ (Test.num_options, (), 11),
+ ('hull_background', '#d9d9d9'),
+ (c.insert, ('end', 'Calling', 'all', 'cars')),
+ ('label_bitmap', 'warning'),
+ ('hull_cursor', 'gumby'),
+ ('label_image', Test.flagup),
+ ('listbox_font', Test.font['variable']),
+ ('listbox_foreground', 'red'),
+ ('listbox_selectmode', 'multiple'),
+ ('label_image', ''),
+ ('label_bitmap', ''),
+ (c.title, 'SelectionDialog 1: new title', ''),
+ (c.interior, (), Tkinter.Frame),
+ ('defaultbutton', 'OK'),
+ (c.delete, (0, 'end')),
+ (c.get, (0, 'end'), ()),
+ (c.insert, ('end', 'Test', 'Test', 'Long String Test')),
+ (c.get, (0, 'end'), ('Test', 'Test', 'Long String Test')),
+ (c.insert, (0, 'Test', 'Test A')),
+ (c.get, (0, 'end'), ('Test', 'Test A', 'Test', 'Test', 'Long String Test')),
+ (c.insert, (1, 'Test', 'Test', 'Long String Test')),
+ (c.get, (0, 4), ('Test', 'Test', 'Test', 'Long String Test', 'Test A')),
+ (c.insert, (5, 'Test', 'Test',
+ 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')),
+ (c.get, 7, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),
+ (c.get, 'end', 'Long String Test'),
+ (c.size, (), 11),
+ (c.delete, (3, 2)),
+ (c.size, (), 11),
+ (c.delete, (3, 3)),
+ (c.size, (), 10),
+ (c.clear, ()),
+ (c.size, (), 0),
+ (c.get, (), ()),
+)
+
+kw_2 = {
+ 'buttons' : ('OK', 'Cancel'),
+ 'buttonboxpos': 'e',
+ 'scrolledlist_labelpos': 'n',
+}
+tests_2 = (
+ (c.title, 'SelectionDialog 2', ''),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Functions used by widget tests.
+
+import imp
+import os
+import re
+import string
+import sys
+import traceback
+import types
+import Tkinter
+import _tkinter
+
+if Tkinter.TkVersion >= 8.4:
+ refcountafterdestroy = 7
+else:
+ refcountafterdestroy = 6
+
+script_name = imp.find_module(__name__)[1]
+if not os.path.isabs(script_name):
+ script_name = os.path.join(os.getcwd(), script_name)
+
+while 1:
+ script_dir = os.path.dirname(script_name)
+ if not os.path.islink(script_name):
+ break
+ script_name = os.path.join(script_dir, os.readlink(script_name))
+script_dir = os.path.join(os.getcwd(), script_dir)
+script_dir = os.path.normpath(script_dir)
+
+# Add the '../../..' directory to the path.
+package_dir = os.path.dirname(script_dir)
+package_dir = os.path.dirname(package_dir)
+package_dir = os.path.dirname(package_dir)
+sys.path[:0] = [package_dir]
+
+import Pmw
+# Need to import TestVersion, rather than do its work here, since
+# __name__ will be known there.
+import TestVersion
+
+# Set this to 1 to generate tracebacks on exceptions, rather than
+# catching them and continuing with the tests.
+# This is useful for debugging the test scripts.
+dont_even_try = 0
+
+_delay = 1
+_verbose = 0
+_printTraceback = 0
+_initialised = 0
+
+##############################################################################
+# Public functions:
+
+rand = 12345
+def random():
+ global rand
+ rand = (rand * 125) % 2796203
+ return rand
+
+def initialise():
+ global _initialised, font, flagup, earthris, emptyimage, \
+ stringvar, floatvar, root, reliefs
+ if not _initialised:
+ root = Tkinter.Tk(className = 'PmwTest')
+ root.withdraw()
+ if os.name == 'nt':
+ size = 16
+ else:
+ size = 12
+ Pmw.initialise(root, size = size, fontScheme = 'pmw2')
+ font = {}
+ font['small'] = '6x13'
+ font['large'] = '10x20'
+ font['variable'] = '-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*'
+ flagup = Tkinter.BitmapImage(file = 'flagup.bmp')
+ earthris = Tkinter.PhotoImage(file = 'earthris.gif')
+ emptyimage = Tkinter.PhotoImage()
+ stringvar = Tkinter.StringVar()
+ stringvar.set('this is some text')
+ floatvar = Tkinter.DoubleVar()
+ floatvar.set(50.0)
+ if haveBlt():
+ global vectorSize, vector_x, vector_y
+ vector_x = Pmw.Blt.Vector()
+ vector_y = []
+ for y in range(3):
+ vector_y.append(Pmw.Blt.Vector())
+ vectorSize = 50
+ for index in range(vectorSize):
+ vector_x.append(index)
+ vector_y[0].append(random() % 100)
+ vector_y[1].append(random() % 200)
+ vector_y[2].append(random() % 100 + 300)
+
+ # "solid" relief type was added to 8.0
+ if Tkinter.TkVersion >= 8.0:
+ reliefs = 'flat, groove, raised, ridge, solid, or sunken'
+ else:
+ reliefs = 'flat, groove, raised, ridge, or sunken'
+
+ _initialised = 1
+
+def haveBlt():
+ return Pmw.Blt.haveblt(root)
+
+def bell():
+ root.bell()
+
+def setdelay(newdelay):
+ global _delay
+ _delay = newdelay
+
+def setverbose(newverbose):
+ global _verbose
+ _verbose = newverbose
+
+def printtraceback(newprintTraceback = 1):
+ global _printTraceback
+ _printTraceback = newprintTraceback
+
+def num_options(widget):
+ return len(widget.configure())
+
+def callback():
+ return 1
+
+def callback1(dummy):
+ # Callback taking 1 argument
+ return dummy
+
+def callback2(dummy1, dummy2):
+ # Callback taking 2 arguments
+ return (dummy1, dummy2)
+
+def callbackN(*args):
+ # Callback taking zero or more arguments
+ return args
+
+def actioncallback():
+ w = currentWidget()
+ w.action('button press')
+
+def currentWidget():
+ return _currentWidget
+
+def delay():
+ return _delay
+
+def set_geom(width, height):
+ _currentToplevel.geometry(str(width) + 'x' + str(height))
+
+def runTests(allTestData):
+ root.after(_delay, _runTest, None, None, allTestData, 0, -1, -1)
+ root.mainloop()
+
+_pattern = None
+
+##############################################################################
+# Private functions:
+
+def _print_results(result, expected, description):
+ if type(expected) == types.ClassType:
+ if hasattr(result, '__class__'):
+ ok = (result.__class__ == expected)
+ else:
+ ok = 0
+ else:
+ ok = (result == expected)
+
+ # Megawidgets return a value of the correct type. Tk widgets
+ # always return a string, so convert the string and check again.
+ if not ok:
+ if type(expected) == types.InstanceType:
+ if result == str(expected) and (
+ expected is earthris or expected is stringvar or
+ expected is floatvar or expected is flagup):
+ ok = 1
+ elif hasattr(_tkinter, 'Tcl_Obj') and \
+ type(result) == _tkinter.Tcl_Obj:
+ ok = (str(stringvar) == result.string)
+ elif type(expected) == types.IntType:
+ if type(result) is types.StringType:
+ try:
+ ok = (string.atoi(result) == expected)
+ except ValueError:
+ pass
+ elif hasattr(_tkinter, 'Tcl_Obj') and \
+ type(result) == _tkinter.Tcl_Obj:
+ ok = (string.atoi(str(result)) == expected)
+ elif type(expected) == types.FloatType:
+ if type(result) is types.StringType:
+ try:
+ ok = (string.atof(result) == expected)
+ except ValueError:
+ pass
+ elif expected == callback:
+ ok = re.search('^[0-9]*callback$', str(result)) is not None
+ elif expected == callback1:
+ ok = re.search('^[0-9]*callback1$', str(result)) is not None
+ elif expected == callback2:
+ ok = re.search('^[0-9]*callback2$', str(result)) is not None
+ elif expected == actioncallback:
+ ok = re.search('^[0-9]*actioncallback$',str(result)) is not None
+
+ if not ok or _verbose > 0:
+ print '====', description
+ if not ok or _verbose > 1:
+ print '==== result was:'
+ print result, type(result)
+ if ok:
+ if _verbose > 1:
+ print '++++ PASSED'
+ else:
+ print '---- result should have been:'
+ print expected, type(expected)
+ if _printTraceback:
+ traceback.print_exc()
+ print '---- FAILED'
+ print
+
+def _destroyToplevel(top, title):
+ if _verbose > 0:
+ print '==== destruction of Toplevel for', title
+ top.destroy()
+
+def _Toplevel(title):
+ if _verbose > 0:
+ print '==== construction of Toplevel for', title
+ top = Tkinter.Toplevel()
+ top.geometry('+100+100')
+ top.title(title)
+ return top
+
+def _constructor(isWidget, top, classCmd, kw):
+ if _verbose > 0:
+ print '====', classCmd.__name__, 'construction'
+ if isWidget:
+ if dont_even_try:
+ w = apply(classCmd, (top,), kw)
+ else:
+ try:
+ w = apply(classCmd, (top,), kw)
+ except:
+ print 'Could not construct', classCmd.__name__
+ traceback.print_exc()
+ print 'Can not continue'
+ print 'Bye'
+ return None
+
+ isMegaWidget = hasattr(classCmd, 'defineoptions')
+ # Check the option types:
+ options = w.configure()
+ option_list = options.keys()
+ option_list.sort()
+ for option in option_list:
+ # Some of the options (like bd, bg and fg) have only two parts
+ # and are just abbreviations. Only check 'real' options.
+ if len(options[option]) == 5:
+ initoption = isMegaWidget and w.isinitoption(option)
+ if dont_even_try:
+ value = w.cget(option)
+ if option not in ('class', 'container') and not initoption:
+ apply(w.configure, (), {option : value})
+ newvalue = w.cget(option)
+ if newvalue != value:
+ print '====', classCmd.__name__, 'widget', \
+ '\'' + option + '\'', 'option'
+ print '---- setting option returns different value'
+ print '==== new value was:'
+ print newvalue, type(newvalue)
+ print '---- set value was:'
+ print value, type(value)
+ print '---- FAILED'
+ print
+ else:
+ try:
+ value = w.cget(option)
+ if option not in ('class', 'container') and not initoption:
+ try:
+ apply(w.configure, (), {option : value})
+ newvalue = w.cget(option)
+ if hasattr(_tkinter, 'Tcl_Obj') and \
+ (
+ (type(newvalue) == _tkinter.Tcl_Obj
+ and str(newvalue) != str(value))
+ or
+ (type(newvalue) != _tkinter.Tcl_Obj
+ and newvalue != value)
+ ) or \
+ (
+ not hasattr(_tkinter, 'Tcl_Obj') and
+ newvalue != value
+ ):
+ print '====', classCmd.__name__, 'widget', \
+ '\'' + option + '\'', 'option'
+ print '---- setting option returns different value'
+ print '==== new value was:'
+ print `newvalue`, type(newvalue)
+ print '---- set value was:'
+ print `value`, type(value)
+ print '---- FAILED'
+ print
+ except:
+ print '====', classCmd.__name__, 'widget', \
+ '\'' + option + '\'', 'option'
+ print '---- could not set option'
+ print '---- FAILED'
+ print
+ except KeyError:
+ print '====', classCmd.__name__, 'widget', \
+ '\'' + option + '\'', 'option'
+ print '---- unknown option'
+ print '---- FAILED'
+ print
+
+ if hasattr(classCmd, 'geometry'):
+ w.geometry('+100+100')
+ w.title(classCmd.__name__)
+ else:
+ w = apply(classCmd, (), kw)
+ return w
+
+def _destructor(widget, isWidget):
+ if _verbose > 0:
+ print '====', widget.__class__.__name__, 'destruction'
+ if isWidget:
+ if dont_even_try:
+ widget.destroy()
+ else:
+ try:
+ widget.destroy()
+ ref = sys.getrefcount(widget)
+ if ref != refcountafterdestroy:
+ print '====', widget.__class__.__name__, 'destructor'
+ print '---- refcount', ref, 'not zero after destruction'
+ print '---- FAILED'
+ print
+ except:
+ print 'Could not destroy', widget.__class__.__name__
+ traceback.print_exc()
+ print 'Can not continue'
+ print 'Bye'
+ return None
+ return 1
+
+# Structure of allTestData:
+# (
+# (
+# 'ButtonBox',
+# (
+# (
+# Pmw.ButtonBox,
+# {},
+# (
+# (c.pack, ()),
+# (c.pack, ()),
+# (c.pack, ()),
+# ...
+# )
+# ),
+# (
+# Pmw.ButtonBox,
+# {},
+# (
+# (c.pack, ()),
+# (c.pack, ()),
+# (c.pack, ()),
+# ...
+# )
+# ),
+# ...
+# )
+# ),
+# (
+# 'ButtonBox',
+# (
+# (
+# Pmw.ButtonBox,
+# {},
+# (
+# (c.pack, ()),
+# (c.pack, ()),
+# (c.pack, ()),
+# ...
+# )
+# ),
+# (
+# Pmw.ButtonBox,
+# {},
+# (
+# (c.pack, ()),
+# (c.pack, ()),
+# (c.pack, ()),
+# ...
+# )
+# ),
+# ...
+# )
+# ),
+# ...
+# )
+
+def _runTest(top, w, allTestData, index0, index1, index2):
+ if index0 >= len(allTestData):
+ root.quit()
+ return
+ classCmd, fileTests = allTestData[index0]
+ if classCmd == Tkinter.Menu:
+ isToplevel = 1
+ else:
+ isToplevel = hasattr(classCmd, 'userdeletefunc')
+ isWidget = hasattr(classCmd, 'cget')
+ title = classCmd.__name__
+
+ if index1 == -1:
+ if isToplevel:
+ top = None
+ else:
+ top = _Toplevel(title)
+ global _currentToplevel
+ _currentToplevel = top
+ index1 = 0
+ elif index1 >= len(fileTests):
+ if not isToplevel:
+ _destroyToplevel(top, title)
+ index1 = -1
+ index0 = index0 + 1
+ else:
+ methodTests, kw = fileTests[index1]
+ if index2 == -1:
+ w = _constructor(isWidget, top, classCmd, kw)
+ if w is None:
+ root.quit()
+ return
+ global _currentWidget
+ _currentWidget = w
+ index2 = 0
+ elif index2 >= len(methodTests):
+ if _destructor(w, isWidget) is None:
+ root.quit()
+ return
+ index2 = -1
+ index1 = index1 + 1
+ else:
+ methodTestData = methodTests[index2]
+ if type(methodTestData[0]) == types.StringType:
+ _configureTest(w, methodTestData)
+ else:
+ _methodTest(w, methodTestData)
+ index2 = index2 + 1
+ root.update()
+ root.after(_delay, _runTest, top, w, allTestData, index0, index1, index2)
+
+def _configureTest(w, testData):
+ option = testData[0]
+ value = testData[1]
+ if dont_even_try:
+ apply(w.configure, (), {option: value})
+ result = w.cget(option)
+ else:
+ try:
+ apply(w.configure, (), {option: value})
+ result = w.cget(option)
+ except:
+ result = _getErrorValue()
+ if len(testData) > 2:
+ expected = testData[2]
+ else:
+ expected = value
+ _print_results(result, expected, \
+ w.__class__.__name__ + ' option ' + str(testData))
+
+def _getErrorValue():
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ if type(exc_type) == types.ClassType:
+ # Handle python 1.5 class exceptions.
+ exc_type = exc_type.__name__
+ if type(exc_value) == types.StringType:
+ return exc_type + ': ' + exc_value
+ else:
+ exc_value_str = str(exc_value)
+ if exc_value_str[:1] == "'" and exc_value_str[-1:] == "'":
+ exc_value_str = exc_value_str[1:-1]
+ return exc_type + ': ' + exc_value_str
+
+def _methodTest(w, testData):
+ func = testData[0]
+ args = testData[1]
+ kw = {}
+ expected = None
+ if len(testData) == 3:
+ if type(testData[2]) == types.DictionaryType:
+ kw = testData[2]
+ else:
+ expected = testData[2]
+ elif len(testData) > 3:
+ kw = testData[2]
+ expected = testData[3]
+ if type(args) != types.TupleType:
+ args = (args,)
+ if func is num_options:
+ args = (w,) + args
+ origArgs = args
+ if type(func) == types.MethodType and func.im_self is None:
+ args = (w,) + args
+ if dont_even_try:
+ result = apply(func, args, kw)
+ else:
+ try:
+ result = apply(func, args, kw)
+ except:
+ result = _getErrorValue()
+ if hasattr(func, 'im_func'):
+ name = w.__class__.__name__ + ' method ' + \
+ func.im_func.func_code.co_name
+ else:
+ name = 'function ' + func.__name__
+ name = name + ' ' + str(origArgs)
+ if kw:
+ name = name + ' ' + str(kw)
+ _print_results(result, expected, name)
--- /dev/null
+# Set the version of Pmw to use for the tests based on the directory
+# name.
+
+import imp
+import os
+import string
+import Pmw
+
+file = imp.find_module(__name__)[1]
+if not os.path.isabs(file):
+ file = os.path.join(os.getcwd(), file)
+file = os.path.normpath(file)
+
+dir = os.path.dirname(file)
+dir = os.path.dirname(dir)
+dir = os.path.basename(dir)
+
+version = string.replace(dir[4:], '_', '.')
+Pmw.setversion(version)
--- /dev/null
+import Tkinter
+import Test
+import Pmw
+
+Test.initialise()
+
+c = Pmw.TextDialog
+
+kw_1 = {
+ 'scrolledtext_labelpos': 'n',
+ 'label_text' : 'Here is the news',
+ 'buttons' : ('OK', 'Cancel'),
+ 'buttonbox_padx': 30,
+}
+tests_1 = (
+ (Test.num_options, (), 11),
+ ('text_wrap', 'none'),
+ ('text_state', 'disabled'),
+ ('hull_background', '#d9d9d9'),
+ ('label_bitmap', 'warning'),
+ ('hull_cursor', 'gumby'),
+ ('label_image', Test.flagup),
+ ('text_font', Test.font['variable']),
+ ('text_foreground', 'red'),
+ ('text_padx', 15),
+ ('text_pady', 15),
+ ('label_image', ''),
+ ('label_bitmap', ''),
+ (c.title, 'TextDialog 1: new title', ''),
+ (c.interior, (), Tkinter.Frame),
+ ('defaultbutton', 'OK'),
+ (c.clear, ()),
+ (c.get, (), '\n'),
+)
+
+kw_2 = {
+ 'buttons' : ('OK', 'Cancel'),
+ 'buttonboxpos': 'e',
+ 'scrolledtext_labelpos': 'n',
+}
+tests_2 = (
+ (c.title, 'TextDialog 2', ''),
+)
+
+alltests = (
+ (tests_1, kw_1),
+ (tests_2, kw_2),
+)
+
+testData = ((c, alltests),)
+
+if __name__ == '__main__':
+ Test.runTests(testData)
--- /dev/null
+# Tests for basic Tkinter widgets.
+
+import Tkinter
+import Test
+
+Test.initialise()
+testData = ()
+
+if Tkinter.TkVersion >= 8.0:
+ button_num = 31
+ frame_num = 16
+ menu_num = 20
+ menubutton_num = 32
+else:
+ button_num = 30
+ frame_num = 15
+ menu_num = 19
+ menubutton_num = 31
+
+c = Tkinter.Button
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), button_num),
+ ('text', 'Hello World'),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ ('command', Test.callback),
+ (c.flash, ()),
+ (c.invoke, (), '1'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Canvas
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 27),
+ ('background', 'aliceblue'),
+ (c.create_oval, (100, 100, 200, 200),
+ {'fill' : 'lightsteelblue1', 'tags' : 'circle'}, 1),
+ (c.create_rectangle, (200, 100, 300, 200),
+ {'fill' : 'lightsteelblue2', 'tags' : 'square'}, 2),
+ (c.create_text, (0, 200),
+ {'text' : 'Hello, world', 'tags' : 'words', 'anchor' : 'w'}, 3),
+ (c.addtag_withtag, ('lightsteelblue1', 'circle')),
+ (c.bbox, ('circle', 'square'), (99, 99, 301, 201)),
+ (c.tag_bind, ('circle', '<1>', Test.callback)),
+ (c.tag_bind, 'circle', '<Button-1>'),
+ (c.tag_unbind, ('circle', '<1>')),
+ (c.canvasx, 100, 100.0),
+ (c.canvasy, 100, 100.0),
+ (c.coords, 'circle', [100.0, 100.0, 200.0, 200.0]),
+ (c.coords, ('circle', 0, 0, 300, 300), []),
+ (c.coords, 'circle', [0.0, 0.0, 300.0, 300.0]),
+ (c.find_withtag, 'lightsteelblue1', (1,)),
+ (c.focus, 'circle', ''),
+ (c.gettags, 'circle', ('circle', 'lightsteelblue1')),
+ (c.icursor, ('words', 7)),
+ (c.index, ('words', 'insert'), 7),
+ (c.insert, ('words', 'insert', 'cruel ')),
+ (c.itemconfigure, 'circle', {'fill': 'seagreen4'}),
+ (c.itemcget, ('circle', 'fill'), 'seagreen4'),
+ (c.lower, 'words'),
+ (c.move, ('square', -50, -50)),
+ (c.tkraise, ('words', 'circle')),
+ (c.scale, ('circle', 150, 150, 1.0, 0.5)),
+ (c.select_from, ('words', 0)),
+ (c.select_to, ('words', 'end')),
+ (c.delete, 'square'),
+ (c.type, 'circle', 'oval'),
+ (c.dtag, 'lightsteelblue1'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Checkbutton
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 36),
+ ('text', 'Hello World'),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ ('command', Test.callback),
+ (c.flash, ()),
+ (c.invoke, (), '1'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Entry
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 28),
+ ('background', 'lightsteelblue1'),
+ (c.insert, ('insert', 'Hello, Brian!')),
+ (c.delete, (7, 12)),
+ (c.icursor, 7),
+ (c.insert, ('insert', 'world')),
+ (c.get, (), 'Hello, world!'),
+ (c.index, 'insert', 12),
+ (c.selection_from, 7),
+ (c.selection_to, '12'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Frame
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), frame_num),
+ ('background', 'lightsteelblue1'),
+ ('width', 300),
+ ('height', 50),
+ ('background', 'lightsteelblue1'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Label
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 25),
+ ('text', 'Hello World'),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ ('image', Test.earthris),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Listbox
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 23),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ (c.insert, (0, 'ABC', 'DEF', 'GHI', 'XXXXXXXXXXXX')),
+ (c.activate, 1),
+ (c.select_set, (2, 3)),
+ (c.curselection, (), ('2', '3')),
+ (c.delete, 1),
+ (c.get, 1, 'GHI'),
+ (c.get, (0, 1), ('ABC', 'GHI')),
+ (c.index, 'end', 3),
+ (c.nearest, 1, 0),
+ (c.see, 1),
+ (c.size, (), 3),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Menu
+tests = (
+ (Test.num_options, (), menu_num),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ (c.add_command, (),
+ {'background': 'lightsteelblue2', 'label': 'Hello World'}),
+ (c.add_checkbutton, (),
+ {'background': 'lightsteelblue2', 'label': 'Charm'}),
+ (c.post, (100, 100)),
+ (c.activate, 1),
+ (c.entryconfigure, 'Hello World', {'background': 'aliceblue'}),
+ (c.entrycget, ('Hello World', 'background'), 'aliceblue'),
+ (c.index, 'end', 2),
+ ('tearoff', 0),
+ (c.index, 'end', 1),
+ (c.insert_radiobutton, 'Charm',
+ {'background': 'lightsteelblue2', 'label': 'Niceness',
+ 'command': Test.callback}),
+ (c.invoke, 'Niceness', '1'),
+ (c.delete, 'Charm'),
+ (c.type, 'Hello World', 'command'),
+ (c.yposition, 'Hello World', 2),
+ (c.unpost, ()),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Menubutton
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), menubutton_num),
+ ('text', 'Hello World'),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Message
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 21),
+ ('text', 'Hello World'),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ ('text', 'Hello\nCruel Cruel World'),
+ ('borderwidth', 100),
+ ('justify', 'center'),
+ ('justify', 'right'),
+ ('justify', 'left'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Radiobutton
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 35),
+ ('text', 'Hello World'),
+ ('value', 'Foo Bar'),
+ ('variable', Test.stringvar),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ ('text', 'Hello\nCruel Cruel World'),
+ ('command', Test.callback),
+ (c.select, ()),
+ (Test.stringvar.get, (), 'Foo Bar'),
+ (c.flash, ()),
+ (c.invoke, (), '1'),
+ (c.deselect, ()),
+ (Test.stringvar.get, (), ''),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Scale
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 33),
+ ('showvalue', 1),
+ ('orient', 'horizontal'),
+ ('from', 100.0),
+ ('to', 200.0),
+ ('variable', Test.floatvar),
+ ('background', 'lightsteelblue1'),
+ ('foreground', 'seagreen4'),
+ ('command', Test.callback1),
+ (c.set, 150.0),
+ (c.get, (), 150.0),
+ (c.get, 123, 'TypeError: too many arguments; expected 1, got 2'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Scrollbar
+tests = (
+ (c.pack, (), {'fill': 'x'}),
+ (Test.num_options, (), 20),
+ ('orient', 'horizontal'),
+ (Test.set_geom, (300, 50)),
+ (c.set, (0.3, 0.7)),
+ ('background', 'lightsteelblue1'),
+ ('troughcolor', 'aliceblue'),
+ (c.get, (), (0.3, 0.7)),
+ (c.activate, 'slider'),
+ (c.set, (0.5, 0.9)),
+ (c.delta, (0, 0), 0),
+ (c.fraction, (0, 0), 0),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+c = Tkinter.Text
+tests = (
+ (c.pack, ()),
+ (Test.num_options, (), 35),
+ ('background', 'lightsteelblue1'),
+ (c.insert, ('end', 'This little piggy is bold.', 'bold', '\n')),
+ (c.insert, ('end', 'This little piggy is in green.', 'green', '\n')),
+ (c.insert, ('end', 'This line is a mistake.\n')),
+ (c.insert, ('end', 'This little piggy is crossed out.', 'overstrike', '\n')),
+ (c.insert, ('end', 'This little piggy is raised.', 'raised', '\n')),
+ (c.insert, ('end', 'This little piggy is underlined.', 'underline', '\n')),
+ (c.tag_configure, 'bold', {'font': Test.font['variable']}),
+ (c.tag_configure, 'green', {'background': 'seagreen1'}),
+ (c.tag_configure, 'overstrike', {'overstrike': 1}),
+ (c.tag_configure, 'raised',
+ {'background': 'aliceblue', 'borderwidth': 2, 'relief': 'raised'}),
+ (c.tag_configure, 'underline', {'underline': 1}),
+ (c.compare, ('2.0', '<', 'end'), 1),
+ (c.delete, ('3.0', '4.0')),
+ (c.get, ('1.0', '1.4'), 'This'),
+ (c.index, 'end', '7.0'),
+ (c.mark_set, ('my_mark', '4.9')),
+ (c.mark_gravity, ('my_mark', 'right'), ''),
+ (c.mark_gravity, 'my_mark', 'right'),
+ (c.mark_names, (), ('my_mark', 'insert', 'current')),
+ (c.mark_unset, 'my_mark'),
+ (c.insert, ('end', '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n')),
+ (c.insert, ('end', 'This is the last line.')),
+ (c.scan_mark, (0, 20)),
+ (c.scan_dragto, (0, 0)),
+ (c.scan_dragto, (0, 20)),
+ (c.tag_add, ('green', '1.0', '1.4')),
+ (c.tag_cget, ('raised', 'background'), 'aliceblue'),
+ (c.tag_lower, 'green'),
+ (c.tag_names, (),
+ ('green', 'sel', 'bold', 'overstrike', 'raised', 'underline')),
+ (c.tag_nextrange, ('raised', '0.0'), ('4.0', '4.28')),
+ (c.tag_raise, 'green'),
+ (c.tag_ranges, 'green', ('1.0', '1.4', '2.0', '2.30')),
+ (c.tag_remove, ('green', '1.0', '1.4')),
+ (c.tag_ranges, 'green', ('2.0', '2.30')),
+ (c.tag_delete, 'green'),
+ (c.search, ('Gre.n', '0.0'), {'regexp': 1, 'nocase': 1}, '2.24'),
+ (c.search, ('Gre.n', '3.0', 'end'), {'regexp': 1, 'nocase': 1}, ''),
+ (c.see, 'end'),
+ (c.see, '0.0'),
+)
+testData = testData + ((c, ((tests, {}),)),)
+
+#=============================================================================
+
+# Grid command
+
+def _makeGridButtons():
+ w = Test.currentWidget()
+ b1 = Tkinter.Button(w, text = 'Button 1')
+ b2 = Tkinter.Button(w, text = 'Button 2')
+ b3 = Tkinter.Button(w, text = 'Button 3')
+ b4 = Tkinter.Button(w, text = 'Button 4')
+ b5 = Tkinter.Button(w, text = 'Button 5')
+ b6 = Tkinter.Button(w, text = 'Button 6')
+ b7 = Tkinter.Button(w, text = 'Button 7')
+ b8 = Tkinter.Button(w, text = 'Button 8')
+
+ b1.grid(column=0, row=0)
+ b2.grid(column=1, row=0)
+ b3.grid(column=2, row=0, ipadx=50, ipady=50, padx=50, pady=50, sticky='nsew')
+ b4.grid(column=3, row=0)
+ b5.grid(column=0, row=1)
+ b6.grid(column=2, row=1, columnspan=2, rowspan=2, sticky='nsew')
+ b7.grid(column=0, row=2)
+ b8.grid(column=0, row=3, columnspan=4, padx=50, sticky='ew')
+
+def _checkGridSlaves():
+ w = Test.currentWidget()
+ return len(w.grid_slaves())
+
+def _checkGridInfo():
+ w = Test.currentWidget()
+ b8 = w.grid_slaves(column=0, row=3)[0]
+ info = b8.grid_info()
+ if info['in'] == w:
+ rtn = {}
+ for key, value in info.items():
+ if key != 'in':
+ rtn[key] = value
+ return rtn
+ return 'BAD'
+
+def _checkGridForget():
+ w = Test.currentWidget()
+ b8 = w.grid_slaves(column=0, row=3)[0]
+ b8.grid_forget()
+ return w.grid_size()
+
+# The -pad grid option was added in Tk 4.2.
+# Could not do columnconfigure(0) before Tk 4.2.
+if Tkinter.TkVersion >= 4.2:
+ padTest = {'pad': 25}
+ colTest = {'minsize': 100, 'pad': 25, 'weight': 1}
+ rowTest = {'minsize': 100, 'pad': 0, 'weight': 1}
+else:
+ padTest = {'minsize': 100}
+ colTest = 'TclError: wrong # args: should be "grid columnconfigure master index ?-option value...?"'
+ rowTest = 'TclError: wrong # args: should be "grid rowconfigure master index ?-option value...?"'
+
+c = Tkinter.Frame
+tests = (
+ (c.pack, (), {'fill': 'both', 'expand': 1}),
+ (_makeGridButtons, ()),
+ # (c.grid_bbox, (1, 2), (85, 268, 85, 34)),
+ (c.grid_columnconfigure, (0, 'minsize'), 0),
+ (c.grid_columnconfigure, (0, 'weight'), 0),
+ (c.grid_columnconfigure, 0, {'minsize': 100, 'weight': 1}),
+ (c.grid_columnconfigure, 0, padTest),
+ (c.grid_columnconfigure, 0, {}, colTest),
+ (c.grid_columnconfigure, (0, 'minsize'), 100),
+ (c.grid_columnconfigure, (0, 'weight'), 1),
+ (c.location, (200, 100), (2, 0)),
+ (c.grid_propagate, (), 1),
+ (c.grid_propagate, 0),
+ (c.grid_propagate, (), 0),
+ (c.grid_rowconfigure, (0, 'minsize'), 0),
+ (c.grid_rowconfigure, (0, 'weight'), 0),
+ (c.grid_rowconfigure, 0, {'minsize': 100, 'weight': 1}),
+ (c.grid_rowconfigure, 0, {}, rowTest),
+ (c.grid_size, (), (4, 4)),
+ (_checkGridSlaves, (), 8),
+ (_checkGridInfo, (), {}, {'column': '0', 'columnspan': '4',
+ 'ipadx': '0', 'ipady': '0', 'padx': '50', 'pady': '0',
+ 'row': '3', 'rowspan': '1', 'sticky': 'ew',
+ }),
+ (_checkGridForget, (), (4, 3)),
+ (_checkGridSlaves, (), 7),
+)
+
+testData = testData + ((c, ((tests, {}),)),)
+
+if __name__ == '__main__':
+ #Test.setverbose(1)
+ #Test.setdelay(1000)
+ Test.runTests(testData)
--- /dev/null
+#define flagup_width 48
+#define flagup_height 48
+static char flagup_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xef, 0x6a, 0x00,
+ 0x00, 0x00, 0xc0, 0x7b, 0x75, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0x6a, 0x00,
+ 0x00, 0x00, 0x30, 0x60, 0x75, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x7f, 0x00,
+ 0x00, 0x00, 0x0c, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x06, 0xe0, 0x04, 0x00,
+ 0x00, 0x00, 0x03, 0xe0, 0x04, 0x00, 0x00, 0x80, 0x01, 0xe0, 0x06, 0x00,
+ 0x00, 0xc0, 0x1f, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x7f, 0xe0, 0x07, 0x00,
+ 0x00, 0x70, 0xe0, 0xe0, 0x05, 0x00, 0x00, 0x38, 0x80, 0xe1, 0x04, 0x00,
+ 0x00, 0x18, 0x80, 0xf1, 0x04, 0x00, 0x00, 0x0c, 0x00, 0xfb, 0x04, 0x00,
+ 0x00, 0x0c, 0x00, 0xff, 0x04, 0x00, 0x00, 0x86, 0x1f, 0xee, 0x04, 0x00,
+ 0x00, 0x06, 0x06, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0xe6, 0x04, 0x00,
+ 0x00, 0x06, 0x00, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0x66, 0x04, 0x00,
+ 0x7f, 0x56, 0x52, 0x06, 0xe4, 0xff, 0x00, 0x76, 0x55, 0x06, 0x04, 0x00,
+ 0x00, 0x56, 0x57, 0x06, 0x04, 0x00, 0x00, 0x56, 0x55, 0x06, 0x06, 0x00,
+ 0x00, 0x56, 0xd5, 0x06, 0x03, 0x00, 0x00, 0x06, 0x00, 0x86, 0x01, 0x00,
+ 0x54, 0x06, 0x00, 0xc6, 0x54, 0x55, 0xaa, 0x06, 0x00, 0x66, 0xaa, 0x2a,
+ 0x54, 0x06, 0x00, 0x36, 0x55, 0x55, 0xaa, 0x06, 0x00, 0xbe, 0xaa, 0x2a,
+ 0x54, 0xfe, 0xff, 0x6f, 0x55, 0x55, 0xaa, 0xfc, 0xff, 0xa7, 0xaa, 0x2a,
+ 0x54, 0x01, 0x88, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x50, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa8, 0xaa, 0x2a,
+ 0x54, 0x55, 0x95, 0x54, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a,
+ 0x54, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
--- /dev/null
+
+ Python megawidgets
+
+ Pmw is a toolkit for building high-level compound widgets in
+ Python using the Tkinter module.
+
+ All documentation about Pmw is in the form of html files stored in
+ the 'doc' directory of each release of Pmw. Please use your Web
+ browser to view the file 'doc/index.html' in the directory
+ containing the most recent release.
--- /dev/null
+# This file is executed when the Pmw package is imported. It creates
+# a lazy importer/dynamic loader for Pmw and replaces the Pmw module
+# with it. Even though the loader from the most recent installed
+# version of Pmw is used, the user is able to specify which version of
+# Pmw megawidgets to load by using the setversion() function of the
+# loader.
+
+# This is the only file in Pmw which is not part of a particular Pmw
+# release.
+
+import sys
+import os
+import re
+
+def _hasLoader(dir):
+ # Only accept Pmw_V_R_P with single digits, since ordering will
+ # not work correctly with multiple digits (for example, Pmw_10_0
+ # will be before Pmw_9_9).
+ if re.search('^Pmw_[0-9]_[0-9](_[0-9])?$', dir) is not None:
+ for suffix in ('.py', '.pyc', '.pyo'):
+ path = os.path.join(_dir, dir, 'lib', 'PmwLoader' + suffix)
+ if os.path.isfile(path):
+ return 1
+ return 0
+
+# First get a list of all subdirectories containing versions of Pmw.
+_dir = __path__[0]
+_listdir = os.listdir(_dir)
+_instdirs = filter(_hasLoader, _listdir)
+_instdirs.sort()
+_instdirs.reverse()
+
+# Using the latest version import the dynamic loader.
+_loader = 'Pmw.' + _instdirs[0] + '.lib.PmwLoader'
+__import__(_loader)
+_mod = sys.modules[_loader]
+
+# Create the dynamic loader and install it into sys.modules.
+sys.modules['_Pmw'] = sys.modules['Pmw']
+sys.modules['Pmw'] = _mod.PmwLoader(_dir, _instdirs, _listdir)