I trying to place window at center on my desktop. But I could not find the way how to find height/width of work area (i.e. respect taskbar or other system's areas).
My first attempt was:
wm withdraw $window
update idletasks
set width [winfo reqwidth $window]
set height [winfo reqheight $window]
set x [expr { ([winfo screenwidth $window] - $width) / 2}]
set y [expr { ([winfo screenheight $window] - $height) / 2}]
wm geometry $window ${width}x${height}+${x}+${y}
wm deiconify $window
But it doesn't work correctly because winfo screenwidth/height returns values based on screen resolution, but not on desktop work area.
My another attempt was from https://stackoverflow.com/a/36387629/1980049
wm withdraw $window
update idletasks
set width [winfo reqwidth $window]
set height [winfo reqheight $window]
toplevel [set testWin ".__test_screen_size__[incr UID]"]
wm withdraw $testWin
wm state $testWin zoomed
update idletasks
set x [expr { ([winfo width $testWin] - $width) / 2 }]
set y [expr { ([winfo height $testWin] - $height) / 2 }]
destroy $testWin
wm geometry $window ${width}x${height}+${x}+${y}
wm deiconify $window
It works as expected and correct x/y values were obtained. But the $testWin blinked on screen, so this method also not suitable. Without update idletasks it doesn't work also.
I think you can use:
winfo screenmmheight .
Returns a decimal string giving the height of window's screen, in millimeters.
winfo screenmmwidth .
Returns a decimal string giving the width of window's screen, in millimeters.
package require Tk
set window_1_width 350 ; set window_1_height 250
set x_1 [ expr {([ winfo vrootwidth . ] - $window_1_width ) / 2 }]
set y_1 [ expr {([ winfo vrootheight . ] - $window_1_height ) / 2 }]
wm geometry . ${window_1_width}x${window_1_height}+$x_1+$y_1
tested on windows 7 tcl/tk 8.6
Related
This formula will be coded in tcl.
Y= Intercept + Slope1*(X – X0) + (Slope2 – Slope1)*Delta*ln(1+exp((X-X0)/Delta))
I searched that ln() in this formula can be coded using log() in tcl.
However, I cannot get the correct output in tcl.
Here is what I coded, could you please take a look? Any comments? Thank you.
set Intercept 0.7416
set Slope1 52.42
set x0 0.01491
set Slope2 0.2533
set Delta 0.002275
set y_frac [expr {
$Intercept + $Slope1*($x/$tend - $x0/$tend)
+ ($Slope2-$Slope1) * $Delta * log10(1.0 + exp(($x/$tend - $x0/$tend)/$Delta))
}]
set y [expr {$y_frac *100.0}]
The log() function computes the natural logarithm of its argument (just as the exp() function is its inverse, computing ex). That means your expression becomes (with the simplest of transformations) just:
set Y [expr {
$Intercept + $Slope1*($X – $X0) + ($Slope2 – $Slope1)*$Delta*log(1+exp(($X-$X0)/$Delta))
}]
I'd refactor the expression a little, as deltas are natural things to extract:
set dX [expr { $X - $X0 }]
set dSlope [expr { $Slope2 - $Slope1 }]
set Y [expr { $Intercept + $Slope1*$dX + $dSlope*$Delta*log(1+exp($dX/$Delta)) }]
At this point, it's probably easier to define a procedure to encapsulate this.
proc computeY {y} {
global Intercept Slope1 Slope2 Delta x0
set dX [expr { $x - $x0 }]
set dSlope [expr { $Slope2 - $Slope1 }]
set Y [expr { $Intercept + $Slope1*$dX + $dSlope*$Delta*log(1+exp($dX/$Delta)) }]
return [expr { $Y * 100.0 }]; # Your final rescaling
}
Why my script does not work. I am using while for looping process.
Hope anyone could help me for my case. The script as below;
Set global
global xmin xmax ymin ymax xVer yVer x1 y1 Count
Set paramaters
set xmin 0
set xmax 51
set ymin 0
set ymax 51
set x1 2
set y1 2
set xVer 2
set yVer 2
set Count 1
set goToVer "n"
Do looping process
while {$x1 > $xmin && $x1 < $xmax && $y1 > $ymin && $y1 < $ymax} {
# For horizontal axis
while {$x1 > $xmin && $x1 < $xmax} {
set azi [expr (45+90)]
set dip 0
set length 2
set dist [expr (cos($dip) * $length)]
set x1 [expr ($x1 + (sin($azimuth) * $dist))]
set y1 [expr ($y1 + (cos($azimuth) * $dist))]
set goToVer "y"
incr Count
}
# For vertical axis
if {$goToVer == "y"} {
set azi 45
set dip 0
set length 5
set dist [expr (cos($dip) * $length)]
set x1 [expr ($xVer + (sin($azimuth) * $dist))]
set y1 [expr ($yVer + (cos($azimuth) * $dist))]
set xVer $x1
set yVer $y1
incr Count
}
}
Thanks in advance!
I don't know what the problem is, but there are some things that we can do to make everything better. First step: let's ry factoring out the coordinate conversion code itself into a little procedure (I've fixed the braces around the expressions too):
proc convert {x y length azimuth dip} {
set dist [expr {cos($dip) * $length}]
set x1 [expr {$x + sin($azimuth) * $dist}]
set y1 [expr {$y + cos($azimuth) * $dist}]
return [list $x1 $y1]
}
while {$x1 > $xmin && $x1 < $xmax && $y1 > $ymin && $y1 < $ymax} {
# For horizontal axis
while {$x1 > $xmin && $x1 < $xmax} {
set azi [expr (45+90)]
set dip 0
set length 2
lassign [convert $x1 $y1 $length $azi $dip] x1 y1
set goToVer "y"
incr Count
}
# For vertical axis
if {$goToVer == "y"} {
set azi 45
set dip 0
set length 5
lassign [convert $xVer $yVer $length $azi $dip] x1 y1
incr Count
}
}
Next, the value of azi in the inner loop is suspicious; it looks like it is in degrees but Tcl's trigonometry functions (like those in most other programming languages) take their argument in radians. Multiply it by π/180°.
Finally, the logic of the loops is weird. I'm not saying it is wrong… but I really find it hard to comprehend what you'd use looping like that for. To loop a pair of coordinates over some space using equal steps on the axes, you use for loops with integer iterator variables and then apply a conversion to get your floating point coordinates (this is best because it limits cumulative errors):
set azi [expr {(45 + 90) * 3.1415927/180}]
set dip 0
set length 2
for {set x $xmin} {$x <= $xmax} {incr x} {
for {set y $ymin} {$y <= $ymax} {incr y} {
set dist [expr {cos($dip) * $length}]
set x1 [expr {$x + sin($azimuth) * $dist}]
set y1 [expr {$y + cos($azimuth) * $dist}]
# I assume you want to do something with $x1,$y1 here…
}
}
Alternatively, you could use regular spacing in polar coordinates, or any other regular scheme; it's just that good code exploits regularity and you're strongly recommended to work that way if you can. But that might not be what you were trying to do at all. Your code is confusing in its intent.
Which brings me to your actual bugs, which appear to revolve around state management. The logic with the goToVer was confused, BTW, and that might've been the problem you were having. You were setting it in the inner loop, but from that point on it was always set. I recommend not doing things like that as it is quite difficult to debug (there are cases where it can make sense, but it doesn't look like you're doing them) and instead sticking to regular grids, but they can work. I'm guessing that you are missing a reset of the variable to 0 at some point in the outer loop, probably just before the inner loop starts.
I want to create a window with two text boxes one on top of another with first occupying 25% of height and next occupying 75% of height.
I tried to calculate relative height/width of toplevel win and pass into text command but didn't work (I am guessing because the units of dimension returned by wm geometry is not the same as when passed to text command)
Following is my code:
toplevel .t
wm geometry .t 1500x800+10+10
update
proc topAspect {args} {
regexp {(\d+)} $args -> relAspect
regexp {([^\d|%]+)} $args -> aspect
regexp {(.*)x(.*)[+-](.*)[+-](.*)} [wm geometry .t] -> width height x y
puts "width->$width height->$height x->$x y->$y"
switch -regexp [string tolower $aspect] {
x {
return [expr $x + $relAspect]
}
y {
return [expr $y + $relAspect]
}
w {
return [expr $width * $relAspect / 100]
}
h {
return [expr $height * $relAspect / 100]
}
default {
log::log error "Unsupported relative aspect $aspect cannot be determined for top level window"
}
}
}
text .t.text1 -height [topAspect -width 25%] -width [topAspect -width 99%]
grid .t.text1 -sticky news
text .t.text2 -height [topAspect -width 75%] -width [topAspect -width 99%]
grid .t.text2 -sticky news
When I tried following - it did give me some decent GUI:
text .t.text1 -height 20 -width [topAspect -width 99%]
grid .t.text1 -sticky news
text .t.text2 -height 20 -width [topAspect -width 99%]
grid .t.text2 -sticky news
But i want to use the relative options. How to make it work?
The easiest way to solve this is to use the grid geometry manager with weights in the right ratio and a uniform group. It will even work fine when you resize the window; Tk knows the policy itself and maintains it for you. (Internally, grid is a fairly sophisticated constraint solver; you can do some really complicated stuff with it.)
toplevel .t
grid [text .t.text1 -bg red] -sticky news
grid [text .t.text2 -bg green] -sticky news
# The group name is just an arbitrary non-empty string.
# So long as it is the same on the two rows it will work as desired.
# The weights give a ratio of 1:3, i.e., 25% to one and 75% to the other.
grid rowconfigure .t .t.text1 -weight 1 -uniform group1
grid rowconfigure .t .t.text2 -weight 3 -uniform group1
(If you're using Tk 8.5, you'll need to specify the rows to rowconfigure by number instead of the often-more-convenient name of a widget in the row.)
Yes, the -height and -width options for the text widget are given in character units, not screen units. You can fix that by further dividing by font width and height (I set them to constant values below). Remember that this is integer division!
Oooh, all those regexes… I’ve cleaned up a bit, you can take it or leave it.
proc topAspect {aspect relAspect} {
set relAspect [string trimright $relAspect %]
scan [wm geometry .t] "%dx%d%d%d" width height x y
set fontWidth 15
set fontHeight 15
switch -regexp [string tolower $aspect] {
x {
return [expr {$x + $relAspect}]
}
y {
return [expr {$y + $relAspect}]
}
w {
return [expr {($width * $relAspect / 100) / $fontWidth}]
}
h {
return [expr {($height * $relAspect / 100) / $fontHeight}]
}
default {
log::log error "Unsupported relative aspect $aspect cannot be determined for top level window"
}
}
}
Also, you used -width as an argument to topAspect for both -height and -width: I presume that was a mistake.
text .t.text1 -height [topAspect -height 25%] -width [topAspect -width 99%]
grid .t.text1 -sticky news
text .t.text2 -height [topAspect -height 75%] -width [topAspect -width 99%]
grid .t.text2 -sticky news
Otherwise, I recommend Donal Fellows’s solution.
Documentation:
* (operator),
+ (operator),
/ (operator),
expr,
for,
grid,
proc,
return,
scan,
set,
string,
switch,
text (widget),
wm,
Syntax of Tcl regular expressions
Place worked best in this case - even on resizing the following held out the proportions well:
place .t.text1 -in .t -relheight .25 -relwidth .98 -relx .003 -rely .003
place .t.text2 -in .t -relheight .75 -relwidth .98 -relx .003 -rely .254
Is there any pitfall that anyone sees in this approach as compared to grid.
Thank you
I have a canvas widget which shows a gradient. This is done by drawing lines from its top to bottom each with a slightly different color. To achieve this, in the function that draws the line I check the height of the canvas and draw lines according to it. The problem is, that the first time its drawn, or when the widget is resized (when it's resized, I call the drawing function) the result I get from the command winfo height $legendCanvas is wrong and the drawing is bad, only when I recall the function again, it gets the right value and the drawing results are good. I've tried adding update idletasks at the start of the method, it doesn't work.
The relevant canvas is called legendCanvas
itcl::body siReportAttackersMatrix::setThreshold {{val ""}} {
update idletasks
# some unrelated code here
# ...
#redraw the legend
$legendCanvas delete line all
set range [expr {$maxVal*1.0-$minVal}]
set step [expr {$range/[winfo height $legendCanvas]}]
for {set y 0} {$y < [winfo height $legendCanvas]} {incr y} {
# some unrelated code that calculated the color
set id [$legendCanvas create line 0 $y [winfo width $legendCanvas] $y -fill $color]
}
set textX [expr {[winfo width $legendCanvas]/2}]
set id [$legendCanvas create text $textX 0 -anchor n -text [expr {int($maxVal * 1000)}]]
set id [$legendCanvas create text $textX [winfo height $legendCanvas] -anchor s -text [expr {int($minVal * 1000)}]]
foreach fraction [list 2 4 [expr {4/3.0}]] {
set textY [expr {int([winfo height $legendCanvas]*1.0/$fraction)}]
set textValue [expr {int(($maxVal-$minVal)*(1-1.0/$fraction)*1000)}]
set id [$legendCanvas create text $textX $textY -anchor center -text $textValue]
}
}
in order to conserve space I've removed code that is irellevent to the problem, like calculating the color, some more functions that the method does and bindings on the different items in the canvas
Screen pics of the results:
On creation (on the left), After recalling the method(on the right):
On resize (on the left), After recalling the method (on the right):
The simplest way of fixing this is to recompute the gradient whenever that canvas widget receives a <Configure> event. In particular, the %h and %w substitutions in the <Configure> event tell you what the size of the widget is being set to, though the basic Tk infrastructure will also save those values into the widget record (where winfo height and winfo width can retrieve them).
# Something like this; you might want to tweak the binding
bind $legendCanvas <Configure> { doRescale %W %w %h }
You're advised to have a procedure (or method) that just handles this; other operations that require the rescaling (such as the initial setup code) can just call it as necessary.
Gridded toplevel windows in Windows XP doesn't seem to restrict the user from resizing in multiples of a number. It works in X, but not in Windows. I can resize to any pixel size.
Doesn't the Windows windows manager support it? Can i do it manually, maybe by binding some commands to the resize event of the toplevel?
You're correct that it doesn't really work on Windows (this is also true for Mac OS X/Aqua) and it is because the window manager itself doesn't support the feature. You have to synthesize it with some scripts. However the real complication is that a typical gridded window is more than just the one gridded window; there's some extra space around it which can make the updated size go into the next grid size up and that makes the window expand a bit and resize again… and again and again…
Thus, we need a two-stage initialization, first measuring the real size of the extra space we have to allow for and only then applying the enforcement code.
package require Tk
proc initGrid {window w h} {
# Do nothing for subwindows
if {$window ne [winfo toplevel $window]} return
lassign [wm grid $window] xcount ycount xstep ystep
set wExtra [expr {$w - $xcount*$xstep}]
set hExtra [expr {$h - $ycount*$ystep}]
bind $window <Configure> [list resizeGrid $wExtra $hExtra %W %w %h]
}
proc resizeGrid {wExtra hExtra window w h} {
# Do nothing for subwindows
if {$window ne [winfo toplevel $window]} return
lassign [wm grid $window] xcount ycount xstep ystep
if {$w-$wExtra != $xcount*$xstep || $h-$hExtra != $ycount*$ystep} {
set w [expr {($w - $wExtra)/$xstep}]
set h [expr {($h - $hExtra)/$ystep}]
wm grid $window $w $h $xstep $ystep
}
}
pack [text .t -setgrid 1] -fill both -expand 1
bind . <Configure> {initGrid %W %w %h}
As you can see, this isn't particularly simple to get right!