Warm tip: This article is reproduced from stackoverflow.com, please click
itcl tcl tk ttk

Is there a possibility to get the content of the popdown list of a ttk::combobox before it is opened

发布于 2020-03-27 10:17:30

I create a mask which shows only a combobox. I want to color certain elements in red.

For several reasons I have to hold a given structure, so that I need three files for it. The first creates the toplevel, in the second the mask is created and there are also the methods to fill the combobox. The third file includes all methods to create and to handle the combobox.

So this is the first file

(vmHelmert2.tcl):


    #!/bin/sh
    #\
    exec vmwish "$0" ${1+"$@"}

    package require itcl
    auto_mkindex . vmCombobox2.itcl vmMaskHelmert2.itcl
    lappend auto_path /myPath

    namespace eval vmHelmert2 {
       variable helmert
    }

    proc vmHelmert2::grundmaske {} {

       set top [toplevel .top  -class Helmert]
       set frMain [frame $top.centrum]
       pack $frMain -expand 1 -fill both

       set helmertWidget [vmMaskHelmert2 #auto $frMain]
       set helmert(widget) [$helmertWidget getThis]

    }

    vmHelmert2::grundmaske

This is the second file

(vmMaskHelmert2.itcl)


    package require Itcl

    ::itcl::class vmMaskHelmert2 {

       public method getThis {}

       private method createMaskHelmert {w}
       private method setAnsatzList {liste}
       private method faerbeAnsatzListe {}

       private variable pfd
       private variable data


       constructor {w} {
          createMaskHelmert $w
          return $this
       }

        destructor {
          #puts "DESTRUCTOR wird jetzt gestartet."
       }
    }

    ::itcl::body vmMaskHelmert2::getThis {} {
       return $this
    }

    ::itcl::body vmMaskHelmert2::createMaskHelmert {w} {
       set pfd(frMain) [frame $w.frMain]
       pack $pfd(frMain) -anchor nw -expand 1 -fill both
       set pfd(c_ansatznr) [vmCombobox2 $pfd(frMain).c_ansatznr \
                                -state normal \
                                -width 15\
                                -justify right]
       pack $pfd(c_ansatznr) -side left
       [$pfd(c_ansatznr) component combobox] configure -postcommand "[itcl::code $this faerbeAnsatzListe]"

       set data(ansatzList) [list 1 0 2 1 3 1]

       setAnsatzList $data(ansatzList)

    }

    ::itcl::body vmMaskHelmert2::setAnsatzList {liste} {
       # Alle Inhalte vorher loeschen
       $pfd(c_ansatznr) delete entry 0 end
       $pfd(c_ansatznr) delete list 0 end
       foreach {einElement status} $liste {
          $pfd(c_ansatznr) insert list end $einElement
       }
       return
    }

    ::itcl::body vmMaskHelmert2::faerbeAnsatzListe {} {
       foreach {elem state} $data(ansatzList) {
          if { $state } {
    #            puts "TODO: Farbe Ansatz $elem verändern!!!"
                $pfd(c_ansatznr) itemconfigure $elem red
          }
       }
    }

And this is the last file for the combobox

(vmCombobox2.itcl):


    package require Itcl
    package require Iwidgets
    itcl::class vmCombobox2 {
       inherit itk::Widget
       constructor {args} {}
       destructor {}

       public method insert {component index args}
       public method delete {component first {last {}}}
       public method itemconfigure {bez farbe}

       private variable popdown

       private method create {top}

       protected method _deleteList {first {last {}}}
    }

    itcl::body vmCombobox2::constructor {args} {
       ttk::style configure Combobox$this.TCombobox\
          -selectbackground #52719c\
          -borderwidth 1\
          -insertwidth 2\
          -selectforeground white\
          -fieldbackground white
       ttk::style map Combobox$this.TCombobox -background [list disabled #a3a3a3 readonly #a3a3a3]
       ttk::style map Combobox$this.TCombobox -foreground [list disabled #d9d9d9 readonly #d9d9d9]
       ttk::style map Combobox$this.TCombobox -arrowcolor [list disabled darkgrey readonly black]
       create $itk_interior
       itk_initialize {*}$args
       # wenn -values vor -textvariable steht wird die Variable nicht initialisiert deshalb:
       set idx [lsearch $args "-textvariable"]
       if {$idx != -1} {
          setVar [lindex [$itk_component(combobox) cget -values] end]
       }
    }

    itcl::body vmCombobox2::create {top} {
    #   puts "createCombobox"
       # Label
       itk_component add label {
          set label [label $top.label -anchor w]
          set label
       } {
          rename -font -labelfont labelFont Font
       }
       # Frame fuer highlightthickness
       itk_component add frame {
          set frame [frame $top.frame -highlightcolor black]
          set frame
       } {
       }   

       # combobox
       itk_component add combobox {
          set combobox [ttk::combobox $top.frame.combo -style Combobox$this.TCombobox]
          set combobox
       } {
          keep -textvariable -values -cursor -exportselection -justify -height -state -width -takefocus -postcommand\
             -invalidcommand -foreground
          rename -validate -validateart validateArt ValidateArt
       }

       grid $itk_component(label) -row 0 -column 0 -sticky ne 
       grid $itk_component(frame) -row 0 -column 1 -sticky ew
       pack $itk_component(combobox) -fill x -expand 1
       grid columnconfigure $top 1 -weight 1
       grid rowconfigure $top 0 -weight 1

       # aufgeklappte Liste
       set pd [ttk::combobox::PopdownWindow $itk_component(combobox)]
       set popdown $pd.f.l
    }

    itcl::body vmCombobox2::_deleteList {first {last {}}} {

        if {$last == {}} {
           set last $first
        }
        set valueList [$itk_component(combobox) cget -values]
        set newValuesList [lreplace $valueList $first $last]

        # remove the item if it is no longer in the list
        set text [$itk_component(combobox) get]
        if {$text != ""} {
           set index [lsearch -exact $newValuesList $text]
           if {$index == -1} {
              $itk_component(combobox) set ""
             }
        }   
        $itk_component(combobox) configure -values $newValuesList
        return
    }

    itcl::body vmCombobox2::delete {component first {last {}}} {
        switch -- $component {
             entry {
                if {$last == {}} {
                   #set last [expr {$first + 1}]
                   set last $first
                }
              set text [$itk_component(combobox) get]
              set newText [string replace $text $first $last]
              $itk_component(combobox) set $newText
             }
             list {
                _deleteList $first $last
             }
             default {
                error "falsches Combobox component \"$component\":\
                         zugelassen sind: entry or list."
             }
       }
    }

    itcl::body vmCombobox2::insert {component index args} {
       set nargs [llength $args]

       if {$nargs == 0} {
            error "Kein Einfuegestring fuer parameter \"string\" in function\"vmCombobox2::insert\""
       } 

       switch -- $component {
          list {
             if {$itk_option(-state) == "normal"} {
                set aktuell [$itk_component(combobox) cget -values]
                  if {[lsearch -exact $aktuell $args] != -1} {
                     return
                   }
                set neu [linsert $aktuell $index [join $args]]
                $itk_component(combobox) configure -values $neu
             }
          }
          default {error "Falsches vmCombobox2 component \"$component\": zugelassen be entry oder list."}
       }
    }

    itcl::body vmCombobox2::itemconfigure {bez farbe} {

       puts "content popdownListe >>[$popdown get 0 end]<<"
       # index des Elements in popDownListe
       set index [lsearch [$popdown get 0 end] $bez]
       try {
          $popdown itemconfigure $index -foreground red
       } on error {err errOpts} {
          puts "Error >>$err<<"
       } 
    }

In the method vmCombobox2::itemconfigure I put the content of the popDownList. If the popDownList is opened for the first time, the content is empty and none of the elements are colored red (

content popdownListe

. I got the error

item number "-1" out of range

(for sure, the popDownList is empty). If I open it for the second time, the elements 2 and 3 are colored red as expected.

Is there a way to fill content to the popdown list before it is opened the first time?

Questioner
A.Pitt
Viewed
34
A.Pitt 2019-07-09 17:32

To illustrate, what was meant with the answer here the changes I made in vmCombobox2.itcl and vmMaskHelmert2.itcl

vmMaskHelmert2.itcl

I added a new option -postcallback to the combobox and configured it in faerbeAnsatzListe.

:itcl::body vmMaskHelmert2::faerbeAnsatzListe {} {
   set listIndex [list ]
   foreach {elem state} $data(ansatzList) {
      if { $state } {
#         puts "TODO: Farbe Ansatz $elem verändern!!!"
          set values [$pfd(c_ansatznr) getValues]
          set index [lsearch $values $elem]
          lappend listIndex $index
      }
   }
   set pd [$pfd(c_ansatznr) getPopdown]
   $pfd(c_ansatznr) configure -postcallback [list configureLB $pd $listIndex]
}

getValues is a method, which lists all items of the list in the combobox.

vmCombobox2.itcl

I added the option postcallback in vmCombobox2.itcl

itcl::class vmCombobox {
...
itk_option define -postcallback postCallback PostCallback ""
...
}

In constructor I added the following lines:

itcl::body vmCombobox::constructor {args} {
...
if {$idx != -1} {
   setVar [lindex [$itk_component(combobox) cget -values] end]
}
...
set pd [ttk::combobox::PopdownWindow $itk_component(combobox)]
set oldTags [bindtags $pd]
set tagList [concat $oldTags "callBack$this"]
bind callBack$this <Map> [itcl::code $this popUpCallback]
bindtags $pd $tagList
bind $pd <Unmap> [::itcl::code $this clearAfterSelect]

And I added three more methods (I also declared them in class as public method)

itcl::body vmCombobox::getPopdown {} {
   return $popdown
} 
itcl::body vmCombobox::popUpCallback {} {
   if {$itk_option(-postcallback) != ""} {
      eval $itk_option(-postcallback)
   }
}

::itcl::body vmCombobox::configureLB {pd listIndex} {
   foreach index $listIndex {
      $pd itemconfigure $index -foreground red
   }
}

For me it worked, with the changes I can colour certain items.