Version 21 of tk scaling

Updated 2021-09-14 20:38:55 by SEH
tk scaling ?-displayof window? ?number''?

Sets and queries the current scaling factor used by Tk to convert between physical units (for example, points, inches, or millimeters) and pixels. The number argument is a floating point number that specifies the number of pixels per point on window's display. If the window argument is omitted, it defaults to the main window. If the number argument is omitted, the current value of the scaling factor is returned.

A "point" is a unit of measurement equal to 1/72 inch. A scaling factor of 1.0 corresponds to 1 pixel per point, which is equivalent to a standard 72 dpi monitor. A scaling factor of 1.25 would mean 1.25 pixels per point, which is the setting for a 90 dpi monitor; setting the scaling factor to 1.25 on a 72 dpi monitor would cause everything in the application to be displayed 1.25 times as large as normal. The initial value for the scaling factor is set when the application starts, based on properties of the installed monitor, but it can be changed at any time. Measurements made after the scaling factor is changed will use the new scaling factor, but it is undefined whether existing widgets will resize themselves dynamically to accomodate the new scaling factor.

bll 2018-9-29: Unfortunately, The ~/.wishrc file is not loaded in a consistent manner (on Linux):

/usr/bin/wishloaded
#!/usr/bin/wishnot loaded
#!/usr/bin/env wishnot loaded
#!/usr/bin/tclsh; package require Tknot loaded

It is best to implement the adjustment and restoration of 'tk scaling' within your application.

MGS - The tk scaling value should be set to its correct value as early as possible. I do this in my ~/.wishrc file as follows:

   tk scaling -displayof . [expr {101.6 / 72.0}]

I know my monitor is 101.6 DPI by measuring with a ruler and dividing into the screen resolution.

wdb - In my Kanotix, I've tried to do the same with tclkit, but as there, Tk isn't required automatically on startup, the procedure tk is not yet defined such that an error occurs. So, I've written this one way procedure to my script ~/.tclkitrc:

 proc initTk {} {
        tk scaling -displayof . [expr {101.6 / 72.0}]
        uplevel [list trace remove variable tk_version write "initTk ;#"]
        rename initTk ""
 }
 trace add variable tk_version write "initTk ;#"

You can manually convert measurements into pixel values by using the winfo pixels and winfo fpixels commands. e.g. to get the DPI of your monitor (as set by the current value of tk scaling):

   set dpi [winfo fpixels . 1i]

Here is a cute script written by Kevin Kenny for helping compute the scaling factor that 'tk scaling' needs. - DL

   # Determine screen width and height, screen diagonal, and screen aspect.
   # Phi is the angle that the screen diagonal makes with the horizontal,
   # assuming square pixels.

   set screenwd [winfo screenwidth .]
   set screenht [winfo screenheight .]

   set screendiag [expr {sqrt ($screenwd*$screenwd + $screenht*$screenht)}]

   set cos_phi [expr {1.0 * $screenwd / $screendiag}]
   set sin_phi [expr {1.0 * $screenht / $screendiag}]

   # Make a canvas to display the ruler

   set winwd [expr 0.8*$screenwd]
   set winht [expr 0.8*$screenht]

   grid [canvas .c -width $winwd -height $winht] -columnspan 2

   # Compute ruler length, start, and end.  Draw the ruler.

   set rlen_nominal [expr {0.8 * $screendiag - 150}]
   set rlen [expr {50 * int($rlen_nominal / 50)}]

   set x0 [expr {($winwd - $rlen * $cos_phi) / 2}]
   set y0 [expr {($winht - $rlen * $sin_phi) / 2}]

   set xend [expr {$x0 + $rlen * $cos_phi}]
   set yend [expr {$y0 + $rlen * $sin_phi}]

   .c create line $x0 $y0 $xend $yend

   # Draw ticks on the ruler, and label the major ticks.

   for {set p 0} {$p < $rlen+12} {incr p 50} {
      set h0 [expr {$x0 + $p * $cos_phi}]
      set k0 [expr {$y0 + $p * $sin_phi}]
      set h1 [expr {$h0 + 20.0 * $sin_phi}]
      set k1 [expr {$k0 - 20.0 * $cos_phi}]
      .c create line $h0 $k0 $h1 $k1
      .c create text $h1 $k1 -anchor sw -text $p
   }
   for {set p 0} {$p < $rlen+5} {incr p 10} {
      if {$p % 50} {
          set h0 [expr {$x0 + $p * $cos_phi}]
          set k0 [expr {$y0 + $p * $sin_phi}]
          set h1 [expr {$h0 + 10.0 * $sin_phi}]
          set k1 [expr {$k0 - 10.0 * $cos_phi}]
          .c create line $h0 $k0 $h1 $k1
      }
   }
   for {set p 5} {$p < $rlen+2} {incr p 10} {
      set h0 [expr {$x0 + $p * $cos_phi}]
      set k0 [expr {$y0 + $p * $sin_phi}]
      set h1 [expr {$h0 + 5.0 * $sin_phi}]
      set k1 [expr {$k0 - 5.0 * $cos_phi}]
      .c create line $h0 $k0 $h1 $k1
   }

   # Make a handy-dandy calculator for resolution.

   grid [label .l1 -text "Pixels:"] [entry .e1 -textvariable pixels] -sticky w
   grid [label .l2 -text "Inches:"] [entry .e2 -textvariable inches] -sticky w
   grid [label .l3 -text "Pixels/inch"] \
          [entry .e3 -state disabled -textvariable ppi] -sticky w
   grid [label .l4 -text "Pixels/point (tk scaling factor)"] \
          [entry .e4 -state disabled -textvariable ppp] -sticky w
   grid columnconfigure . 1 -weight 100

   label .l5 -text " Instructions: Lay a ruler along the line\n\
                     shown on the screen.  Enter a measurement\n\
                     in inches and the corresponding measurement\n\
                     in pixels. Read out the screen pitch and\n\
                     Tk scale factor." -anchor e -justify right
   grid .l5 -row 1 -column 1 -rowspan 4 -sticky se
                 

   trace variable pixels w update_ppi
   trace variable inches w update_ppi

   proc update_ppi args {
      uplevel #0 {
          catch {
              set ppi [expr {1.0 * $pixels / $inches}]
              set ppp [expr {$pixels / ($inches * 72.0)}]
          }
      }
   }

bll 2014-11-15 Here is a simpler script I use in the BallroomDJ program to get the scaling factor.

#!/usr/bin/tclsh
#
# Originally written by Brad Lanam
# This code is in the public domain.
#

package require Tk

variable sz

proc setSize { args } {
  variable sz
  .bluebox configure -height $sz -width $sz
}

proc doexit { } {
  variable sz
  puts [expr {double($sz) / 144.0}]
  exit
}

set exitval {}

set sz [expr {round(144.0 * [tk scaling])}]

if { $exitval == "-exit" } {
  doexit
}

focus .

ttk::frame .l
pack .l -side left -expand true -fill both -pady 5 -padx 5

ttk::style configure Box.TFrame -background darkblue
ttk::frame .bluebox -padding {0 0 0 0} \
    -height $sz -width $sz -style Box.TFrame
pack .bluebox -in .l -side top

ttk::frame .szbox
pack .szbox -in .l -side top -expand true -fill x -pady 5 -padx 5

ttk::label .szlab -text "Size: "
ttk::spinbox .szentry -textvariable sz \
    -from 1 -to 800 -increment 1.0 -font default -width 5
.szentry configure -font default
trace add variable sz write setSize
grid .szlab .szentry -in .szbox -sticky se

set inst {Instructions: Using a ruler,
and taking great care not to damage your screen,
adjust the size of the box until it is exactly
2 inches or 5.1 cm in height on the screen.
Select 'Save' to save the font scale factor.}
regsub -all "\n" $inst { } inst

ttk::frame .r -width 200
pack .r -side right -expand false -fill both -pady 5 -padx 5

ttk::label .inst -text $inst -wraplength 300 -justify left
pack .inst -in .r -anchor sw
ttk::button .exit -text Save -command doexit
pack .exit -in .r -anchor se

See also