Look at result of this script:
canvas .c -bg white
grid .c
set x1 20
set x2 22
set y2 105
for {set f 0} {$f<50} {incr f} {
set y1 [expr {$y2-0.05*$f}]
.c create rectangle $x1 $y1 $x2 $y2 -fill black
incr x1 2
incr x2 2
}
On Windows XP I see that at left side of figure bottom margin is one pixel lower than at right side. But it shouldn't happen as y2 is the same (105) for all rectangles. What do you think?
I think it has to do with the effort of TK
to draw a rectangle of a least 1 pixel in size.
In the code I can see, that y2 is incremeted by
1 if it's equal to y1 after rounding to short integer.
Logging your creation statements one can see, that the pixel jump
occurs between f=10 and f=11. That is the point where
y1 and y2 become unequal and no adjust takes place:
f=10 .c create rectangle 40 104.5 42 105 -fill black
rounded: y1=105 y2=105
adjusted: y1=105 y2=106
f=11 .c create rectangle 42 104.45 44 105 -fill black
rounded: y1=104 y2=105
no adjustment
That explains the pixel jump.
IMO you should file a bug on this.
Related
length of x = 1000,
length of y = 1000
plot(x, y, '+');
xlabel ("p");
ylabel ("Q(p)");
title('Custom plot');
set(gca, 'xtick', [0.00000 0.20000 0.40000 0.60000 0.80000 1.00000])
This set command creates 6 tics in X axis, but I like to create 20 ticks so that there are 50 elements in between 2 tics.
Thanks in advance.
From your comment I guess you don't know the concept behind Octaves "range". see here
In your case if you want the xticks go from 0 to 1000 and the difference between the steps is 20 just use:
set(gca, 'xtick', 0:20:1000);
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!