How can I get/show name of an object(variable name) on gui?
This is object name "frame"
set frame [.c1 create rectangle 50 50 200 200 -width 4 -outline "red"]
when i click on this, it will show its name in a text box or with mouse pointer. How can it be possible? please help me.
The easiest way to show a varying string in the GUI is to use a label with the -textvariable option set. Then you can just set a variable and the string will appear.
To respond to a click, a canvas item should have an event handler script bound to it. The most common events to bind to are <ButtonPress> — often written <1> for <ButtonPress-1>, which is the main mouse button — and <Enter> and <Leave>, which handle tracking of what the mouse is over. The current canvas tag tracks the current item (if any).
Combining these:
pack [canvas .c1]
# make some items here...
.c1 create rectangle 50 50 200 200 -width 4 -outline "red"
# ...
pack [frame .f1 -textvariable msg]
.c1 bind all <1> {set msg "Click on %W:[%W finditem current]"}
.c1 bind all <Enter> {set msg "Entered %W:[%W finditem current]"}
.c1 bind all <Leave> {set msg ""}
When you're experimenting with this, be sure to check out entirely transparent items (particularly fully transparent polygons). They're useful for defining hot areas that are invisible to the user, a pretty useful technique especially if they're put on top of several other items that are visible…
I'm not sure exactly what you are doing, or what you want to happen.
I assume that you have created a canvas .c1, that you have successfully displayed it in a toplevel window somewhere and that you want to respond when the user clicks on the border of the rectangle you have created.
In that case, you need to specify a binding for the click on the item
.c1 bind $frame <button-1> {puts stdout "The frame has been clicked"}
This will just print a message on the console, which is enough to prove the mechanism. You'll need to decide how you want to display it.
Alternatively, use the tooltip package from tklib, documented here, via
tooltip::tooltip .c1 -items $frame "The frame has been hovered over"
which will display "The frame has been..." if the user hovers over the border of the frame.
Related
I try to make popup menus that are in part defined by the widget that it was opened on. I can't seem to find a way to find which widget the menu is opened on. For example:
.f.canvas bind all <3> {
puts stderr "%W just gives me '.f.canvas'"
}
The widget name would be used for lookup in another table to change properties of the object related to the specific widget.
Try
.f.canvas bind all <3> {
puts stderr [%W find closest %x %y]
}
It should show the id of the item you clicked on.
When looking for some other stuff in the Tk documentation for canvas I came across the current tag:
The tag current is managed automatically by Tk; it applies to the
current item, which is the topmost item whose drawn area covers the
position of the mouse cursor (different item types interpret this in
varying ways; see the individual item type documentation for details).
If the mouse is not in the canvas widget or is not over an item, then
no item has the current tag.
Example of using it:
.f.canvas bind all <3> {
puts stderr "widget [%W find withtag current] says hello"
}
So I have this type of button with this options.
button .but1 -text "Sample" -width 10 -height 2 -background $btnColor -activeforeground $actForegrd -state normal -activebackground $activbtnColor -relief flat -highlightthickness 0
is there a way to make a global variable with those options for button, I know about style configure, but in order to use style, I need to change my buttons to ttk::button and those don't have the same options as the regular button.
I try putting all the options in a string and pass it when creating a button but it doesn't work
example:
set style "-width 10 -height 2 -background $btnColor -activeforeground $actForegrd -state normal -activebackground $activbtnColor -relief flat -highlightthickness 0"
button .but1 $style
There's two basic possibilities.
1. Use expansion
What you tried is almost right. Try this:
set style "-width 10 -height 2 -background $btnColor -activeforeground $actForegrd -state normal -activebackground $activbtnColor -relief flat -highlightthickness 0"
button .but1 {*}$style
The {*} (which entered Tcl in 8.5) makes what follows it be expanded to many arguments.
2. Use the option "database"
Instead, you can put all those values into the option database using the option command:
option add *.Button.width 10
option add *.Button.height 2
option add *.Button.background $btnColor
option add *.Button.activeForeground $actForegrd
option add *.Button.state normal
option add *.Button.activeBackground $activbtnColor
option add *.Button.relief flat
option add *.Button.highlightThickness 10
## You can also pull the above from a separate file with:
# option readfile ~/thefile.opts
button .but1
Using the option database right and well is tricky, but it lets you set the defaults for many widgets at once (or a single one, or just widgets that are children of a particular window, or of a class of windows, or …) You'd be truly splitting the style from the code.
I wouldn't normally have the state set by this mechanism though, as that's more of a semantic option; if a button is going to be disabled or not really does matter to code (just as the callback to run when that happens is another one I'd not set via options).
It should be noted that the Ttk style/theme mechanism largely supersedes this, as it allows for much more substantial modifications and cleaner runtime switching between them. But there are a number of effects that are quite a bit harder to do that way; we still maintain the classic button, etc. for a reason.
I'm trying to pack (or place) a widget which is child of the root toplevel . inside another toplevel, that is a child of . itself. That is,
% toplevel .tl
.tl
% frame .f
.f
% pack .f -in .tl
can't pack .f inside .tl
However, I've found this code almost works:
% frame .tl
.tl
% frame .f
.f
% pack .f -in .tl
% wm manage .tl
I said almost, because .f is not visible. It's a bit strange, because if I put a button inside .f, such as
button .f.b -text FooBar
pack .f.b
I see the empty space reserved by the geometry manager, but no widget is visible.
I'm sure I'm doing something wrong, but I don't know what and why, and the pack, grid and place man pages don't help.
Edit: some details about what I'm doing
I'm trying to build a snit widget which automates some toplevel creation stuff. One thing I usually do is putting a ttk::frame inside every toplevel I create, and managing it using a pack ... -fill both -expand true command.
My snidget should always do it, but I'd like to hide it from the user perspective, so that any change to the implementation wouldn't break existing code.
The simple way is this
snit::widget Toplevel {
hulltype toplevel
component f
constructor {args} {
set f [ttk::frame $self.f -padding 2]
pack $f -fill both -expand 1
$self configurelist $args
}
}
but the user must know about the f component, and create other widgets as children of it.
So, I tried another solution: I use a ttk::frame widget as hull type, then build a sibling toplevel of the hull, and try to put the hull inside the toplevel.
The code I tried is similar to the following:
snit::widget Toplevel {
hulltype ttk::frame
component tl
constructor {args} {
set segments [split $self .]
set wname [join [lreplace $segments end end _[lindex $segments end]] .]
set tl [frame $wname -width 100 -height 100]
pack $self -in $tl -fill both -expand 1
wm manage $tl
$self configurelist $args
}
}
If it would work as expected, the user could write something like this:
% Toplevel .t
.t
% button .t.b -text Foobar
.t.b
% pack .t.b
and would get a button inside the toplevel .t build using the snidget.
Widgets except for toplevels are arranged in a strict hierarchy of containment; the widget .foo contains the widget .foo.bar, which in turn contains .foo.bar.grill, etc. Toplevel widgets are actually all children of the root window.
There are exactly two things you can do to change this.
You can use wm manage (requires 8.5 or later) to turn a standard frame widget into a toplevel, and wm forget to reverse this (you'll have to also then re-pack/re-grid it too). (I don't think this is supported on OSX/Aqua.)
frame .foo
# Define its contents...
wm manage .foo
# Later...
wm forget .foo
pack .foo
This is a useful pair of operations for making torn-off windows like toolbars. There's even a demo in the Tk widget demo.
You can turn a frame (or toplevel) into a container widget at creation time (set the -container option to true in the creation options; it can't be done later). You can then get the ID of the window widget (with winfo id) and tell a toplevel at creation time to use that widget ID as its parent via the -use option.
frame .foo -container 1
set id [winfo id .foo]
toplevel .bar -use $id
Note that in this case, on Unix/X11 those IDs can be passed to other processes and used, and need not actually refer to windows created by Tk. (Some applications can also do the reverse, embedding themselves inside a window given by ID that was defined by Tk.) On Windows and OSX/Aqua, the IDs are only guaranteed to work within a single process.
What I don't know is whether the operation you want to perform can be done by either of these two possible operations; you're not quite clear about the higher-level perspective of what you're trying to do. They don't mix particularly well, as -container and -use are creation-time-only options, and are subsequently read-only, and you can't change the name hierarchy (that's totally fixed after creation). Really sophisticated stuff may require you to step back and formally define a model that you have multiple widget layouts being views of.
What you're trying to do isn't possible, Marco. All widgets must be children of their top-level containers.
The docs for Ttk::button's -default option state that it's supposed to be used in dialog boxes, however the only dialog box function I know of is tk_dialog, which can't take buttons as arguments but only the button titles.
Does anyone have a working example of a Ttk::button with it's -default option set to active, where upon running the app and the user hitting the enter key this button is invoked? Here are my attempts:
I've attempted to lay out a button directly in the main window:
package require Tk
ttk::button .button -text "text" -default active -command "puts sometext"
bind .button <Return> { .button invoke }
pack .button
Pressing enter does nothing by default, I would first have to tab to select the button and then hitting enter will work.
I've also tried injecting buttons into tk_dialog, thinking the following might work:
package require Tk
tk_dialog .dg "Title" "Question" "" "" \
[ttk::button .button1 -text "Yes" -default disabled] \
[ttk::button .button2 -text "No" -default active]
But that just creates two buttons ".button1" and ".button2", and neither of them are the default-selected one. (this makes sense since the 4th parameter is empty). tk_dialog itself can specify the default button, but I cannot pass custom buttons to it, it only seems to accept strings for the button names.
The reason I'm asking this is because I'm writing a language binding to Tk and have to figure out which settings should be exposed. I've looked at Tkinter for Python, and it doesn't seem to wrap the -default option for buttons. Is this option ever used in Tk, and if so could you give me a proper working example? Thanks.
The -default option is indeed just a display indicator. What it does depends on the theme you are using. On Windows XP and above, the default active button will be bright blue. On other themes it may have a highlighted border or different edge relied. On the old classic theme it was some huge sunken border.
To actually have something happen when you hit either Enter or Escape you must bind the <Return> and <Escape> events to the relevant buttons as mentioned already.
Don't use tk_dialog. Its really old and very useless and the style doesn't conform to any modern windowing system at all.
Here's a snippet of code I use in one app to make dialogs look sensible:
proc ::tkchat::Dialog {w args} {
lappend args -class Dialog
set dlg [eval [linsert $args 0 toplevel $w]]
catch {wm attributes $w -type dialog}
wm transient $dlg [winfo parent $dlg]
wm group $dlg .
return $dlg
}
This shows a few useful things to be done. First, we have a default class for the toplevel so we can allow default configuration options for dialogs. We also set the -type attribute where supported which will set the Extended Window Manager property that lets modern X window managers style the window as a dialog and not some other kind of transient window. We also then mark it as transient for the parent window -- so the window managers know that this toplevel is actually associated with the given parent or owner window (the taskbar can avoid showing it as another application).
This would be used in something like the following:
set dlg [Dialog .options]
wm withdraw $dlg
wm title $dlg "Options"
# ... create child windows and pack / grid them
set b_ok [ttk::button $dlg.ok -text OK -underline 0 -default active \
-command [list [namespace origin EditOptionsClose] $dlg ok $pages]]
set b_cn [ttk::button $dlg.cancel -text Cancel -underline 0 \
-command [list [namespace origin EditOptionsClose] $dlg cancel $pages]]
bind $dlg <Return> [list $b_ok invoke]
bind $dlg <Escape> [list $b_cn invoke]
bind $dlg <Alt-o> [list focus $b_ok]
bind $dlg <Alt-c> [list focus $b_cn]
wm protocol $dlg WM_DELETE_WINDOW [list $b_cn invoke]
wm resizable $dlg 0 0
catch {::tk::PlaceWindow $dlg widget .}
wm deiconify $dlg
tkwait visibility $dlg
focus $b_ok ; grab $dlg
tkwait variable [namespace which -variable _editoptions]
grab release $dlg
destroy $dlg
So quite a lot going on there. We create then withdraw the dialog. This improves the performance as we place lots of children into the toplevel as by being withdrawn we can defer the geometry calculations until we have to map the whole thing. Then create the buttons and everything else and get them placed onto the toplevel. I've shown just the buttons to illustrate the -default option and also the use of -underline to show the accelerator keys.
Then bindings - Return and Escape should always be handled along with the WM_DELETE_WINDOW protocol message (thats the X button provided by the window manager or Alt-F4 on Windows). This one is also then made non-resizable and we place it over the application window using the Tk library helper function tk::PlaceWindow. Finally, we map the window and set the keyboard focus onto the active widget.
The above covers pretty much everything for a well behaved dialog I think.
The documentation (for -default) is poor and out of date.
-default active was only a display option, and did not affect what the key did. Using the old style button, -default active will turn on the sunken relief surrounding the button in the highlight background area, as an indicator that the button was the default. It's also a little confusing in that 'active' has nothing to do with the button state, and everything to do with the highlight.
The highlight background area is no longer supported for ttk:: widgets, and though the configuration options are accepted, they are not used.
If you want the key to do something particular in your dialog, bind it to the main window, rather than to a particular button:
bind . <Return> {.button2 invoke}
As patthoyts said, it is just a display option. On my machine it looks like this (with Tile)
The "Selected" button has currently the focus.
The script I used to create this is simple:
grid [ttk::button .b -default active -text Ok] [ttk::button .c -default disabled -text Cancel] -sticky nesw
grid [ttk::button .b -text Normal] [ttk::button .c -text Selected] -sticky nesw
grid rowconfigure . all -weight 1
grid columnconfigure . all -weight 1
I have been looking through several online resources trying to get my checkbutton to grey out when another button is selected. However, with all of my attempts, I cannot seem to get it to work.
The following is an example at work:
3 checkboxes called chkAll, chkBalanced, and chkFXO. Let's say that when chkAll is selected and in the active state then chkBalanced, and chkFXO are then unchecked. I've tried to use an if-statement that looks at the state of chkAll but it complains about having the state issue in the conditional portion of the block. Should I consider using variables, if so how do I implement them? I know that checkbuttons have a -variable option but I also don't really know how to use it
If you're trying to make an exclusive choice, you should not be using checkbuttons. What you are describing is the behavior of radiobuttons.
To use radiobuttons you create two or more and give them all the same variable. You will then only be able to pick one at a time.
foreach value {chkAll chkBalanced chkFXO} {
radiobutton .rb-$value -text $value -variable myvar \
-justify left -value $value
pack .rb-$value -side top -fill x -anchor w
}
If you're just wanting to have three widgets that present a mutually-exclusive choice, for goodness' sake use radiobuttons for the task. That's what they're there for. That's what users expect. (Even better, put them in a labelframe; that improves the usability for little effort.)
If you've got something else going on and need to “turn on and off” widgets based on a checkbutton, you do this by putting a write trace on the variable referenced by the checkbutton's -variable option. That trace should then enable (change to -state normal) or disable (change to -state disabled) the other widgets when it fires, based on the value of the variable.
checkbutton .cb -variable bools(cb) -text "Foo Bar"
button .other -text "Example extra content"
button .widget -text "Second ordinary button"
# NOTE the ;# at the end; IMPORTANT HACK to discard trace arguments
trace add variable bools(cb) {changed bools(cb) .other .widget;#}
proc changed {varName args} {
upvar "#0" $varName var
foreach w $args {
if {$var} {
$w configure -state normal
} else {
$w configure -state disabled
}
}
}
set bools(cb) 0; # <<< Initialize to known state
pack .cb .other .widget
This sort of thing can get quite complicated, and produce very nice GUIs. You can also mix it in with radiobuttons (with appropriate adjustments as the state variables are no longer boolean). But don't do it just for simple pick-one-from-a group; use a straight collection of checkbuttons for that.