Fideliox2
Fideliox2

Reputation: 33

TCL canvas scrollbar misbehaviour

I tried to implement a scrollable frame in TCL with the help of canvas. I somehow managed to get it to work, however it is behaving kind of weirdly: I can only scroll by pressing on the arrows, and the scroll bar is always completely filled, so I have no slider representing how far I am scrolled. Also, I can scroll up and down way too much, over the boundaries of the widgets I added to the frame. I tried to add the line $base.fr.can configure -scrollregion [$base.fr.can bbox all], however that lead to me not even being able to scroll all the way down, but way too much up. Also, the size of the canvas / outer frame is not matching the size of the inner frame, meaning if I resize the root window, the canvas adjusts to a certain point but then I get just grey area after that. Which I thought was the point of using columnconfigure and rowconfigure, so that doesn't happen. What I wanted to achieve: Originally I just had a frame with widgets. But during program execution, more widgets can be added and I came to a point with too much widgets to display them all, so I wanted to have a scroll bar for that frame. Code:

proc name_space::gui_create {root} {
variable base [expr {($root eq ".") ? "" : $root}]
variable fontSmall "Helvetica 12"
variable fontMedium "Helvetica 14"
variable fontLarge "Helvetica 16"
variable fontLog "Courier 10"
variable fontCheckmark "Helvetica 24"



#menu
menu .mainm
menu .mainm.filem
menu .mainm.filem.options
.mainm add cascade -menu .mainm.filem
.mainm.filem add cascade -menu .mainm.filem.options -label 
.mainm.filem.options add checkbutton -variable var_state -command [namespace code [list some_random_proc]] \
        -label "Do stuff"

#main scrollbar
ttk::frame $base.fr
canvas $base.fr.can -yscrollcommand "$base.fr.yscroll set" -width 800 -height 400
ttk::scrollbar $base.fr.yscroll -command "$base.fr.can yview"

grid $base.fr -row 0 -column 0 -sticky nwes
grid columnconfigure $root $base.fr -weight 1
grid rowconfigure $root $base.fr -weight 1
grid $base.fr.can -row 0 -column 0 -sticky nswe
grid $base.fr.yscroll -row 0 -column 1 -sticky ns
grid columnconfigure $base.fr $base.fr.can -weight 1
grid rowconfigure $base.fr $base.fr.can -weight 1

ttk::frame $base.fr.can.fr_inner
$base.fr.can create window 0 0 -anchor nw -window $base.fr.can.fr_inner

set base $base.fr.can.fr_inner

# widgets: workspace management
ttk::frame $base.wf
ttk::label $base.wf.l -font $fontSmall -text "Workspace:"
ttk::combobox $base.wf.selection -state readonly -values $ws_list \
        -exportselection 0 -font $fontSmall
bind $base.wf.selection <<ComboboxSelected>> \
        [namespace code [list on_workspace_selected %W 1]]
ttk::button $base.wf.create -text "Create..." \
        -command [namespace code [list on_create_workspace_clicked]]
ttk::button $base.wf.backup -text "Backup Workspace to Desktop" \
        -command [namespace code [list on_backup_workspace_clicked]]
ttk::button $base.wf.restore -text "Restore Backup" \
        -command [namespace code [list on_restore_workspace_clicked]]


# widgets: slots
ttk::frame $base.f
ttk::button $base.f.t1 -text "text1" \
        -command [namespace code [list some_proc]]
ttk::button $base.f.t2 -text "text2" \
        -command [namespace code [list some_proc_2]]
ttk::button $base.f.t3 -text "text3" \
        -command [namespace code [list some_proc_3]]
ttk::button $base.f.t4 -text "text4" \
        -command [namespace code [list some_proc_4]]

# widgets: log window
ttk::frame $base.lf
ttk::scrollbar $base.lf.scroll -command [list $base.lf.log yview]
text $base.lf.log \
        -yscrollcommand [list $base.lf.scroll set] \
        -font $fontLog \
        -height 10 \
        -state disabled
$base.lf.log tag configure stdout -background white
$base.lf.log tag configure stderr -background #ffcccc
$base.lf.log tag configure info -background #ccffff \
        -font [concat $fontLog "bold"]
$base.lf.log tag configure error -background #ccffff \
        -font [concat $fontLog "bold"] -foreground #ff0000

# layout: workspace management
grid $base.wf -column 0 -row 0 -sticky nwe -pady {0 20}
grid columnconfigure $base $base.wf -weight 1
grid $base.wf.l -column 0 -row 0 -sticky w -padx 15
grid $base.wf.selection -column 1 -columnspan 2 -row 0 -sticky we
grid columnconfigure $base.wf $base.wf.selection -weight 1
grid $base.wf.create -column 3 -row 0 -sticky e -padx 15
grid $base.wf.backup -column 1 -row 1 -sticky we -padx 5 -pady {3 0}
grid $base.wf.restore -column 2 -row 1 -sticky we -padx 5 -pady {3 0}

# layout: slots
grid $base.f -column 0 -row 1 -sticky nwes
grid columnconfigure $base $base.f -weight 1
grid $base.f.t1 -column 0 -row 0 -sticky w -pady 5
grid $base.f.t2 -column 0 -row 1 -sticky w -pady 5
grid $base.f.t3 -column 0 -row 2 -sticky w -pady 5
grid $base.f.t4 -column 0 -row 3 -sticky w -pady 5


#show on workspace selected
grid remove $base.f
# layout: log window
grid $base.lf -column 0 -row 2 -sticky nwes
grid rowconfigure $base $base.lf -weight 1
grid $base.lf.log -column 0 -row 0 -sticky nwes
grid rowconfigure $base.lf $base.lf.log -weight 1
grid columnconfigure $base.lf $base.lf.log -weight 1
grid $base.lf.scroll -column 1 -row 0 -sticky nes
grid rowconfigure $base.lf $base.lf.scroll -weight 1
}

Upvotes: 0

Views: 344

Answers (1)

Schelte Bron
Schelte Bron

Reputation: 4813

Your code is long and doesn't work. You don't seem to have spent any effort to make a minimal working example. For example, the #menu part contains a bug (value for "-label" missing) and has no added value because the menu is never used. This is probably one of the reasons nobody has responded yet.

As you suspected, configuring the canvas scroll region is the fix to your issue. But at the point you are doing that, the frame still has a size of 1x1 pixels. You have to run the command when the inner frame is complete and the geometry manager had a chance to work out the final size. If you ever change the size of the inner frame at a later point, e.g. by adding or removing widgets, the scroll region should then be reconfigured.

Both of these things can be accomplished by binding to the <Configure> event of the inner frame:

bind $base.fr.can.fr_inner <Configure> [list apply [list can {
    $can configure -scrollregion [$can bbox all]
}] $base.fr.can]

As this has obviously been done before, you can also take advantage of existing code to create a scrolled frame. I particularly like the ulis/KJN version at https://wiki.tcl-lang.org/page/A+scrolled+frame, which doesn't use a canvas at all.

Upvotes: 0

Related Questions