tk scaling

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.

Note that the [tk scaling] value set at startup by Tk seems to use a reference screen having a resolution density of 96 pixels/inch (this value seems to come from the OS). If you monitor has 72 pixels/inch, you are off by a factor of 0.75 (see this discussion on c.l.t ).

If you want to set the value correctly, so that an inch or a centimeter given as a Tk distance corresponds to an inch or centimeter on the screen/panel of the monitor, you can only do this correctly when knowing the actual height or with of the screen/panel and the number of pixels in that direction. With these values you can calculate the resolution as "dots per inch" (DPI), e.g. like this:

 set DPIvalue [expr {[winfo screenwidth .]/double($physical_screenwidth_inch)}]
 set DPIvalue [expr {[winfo screenwidth .]/double($physical_screenwidth_centimeter) * 2.54}]

Then, as [tk scaling] is the number of pixels per point and one point is 1/72 of an inch, you just need to divide the DPI value by 72 and you will get the proper scaling:

 tk scaling [expr {$DPIvalue/72.0}]

The challenge is to actually retrieve the physical size of the screen without taking a ruler, measure it and manually enter it into a piece of software that wants to show proper scaling ...

For a Mac there seems to be a way to retrieve this value with some Cocoa code: see Stackoverflow: How to read the physical screen size of OSX? .

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

tablelist or scrollutil sets tk scaling

HaO 2022-01-15: The packages tablelist and scrollutil set tk scaling in an intelligent way supporting many Unix display managers, Windows and Android. Thanks to Csaba for this great work. For the interest, the functionality is implemented in the file utils/scaleutil.tcl.


See also