Page 1 of 2

DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 05 Oct 2017, 12:55
by ahk7
DPI() can either return the scaling factor or calculate position/values for AHK controls (font size, position (x y), width, height).

Code and Documentation

https://github.com/hi5/dpi

What is it?

At work I have the opportunity to work on a 4K monitor from time to time. I have quite a few scripts with GUIs and even though the scaling is set to 150% and AutoHotkey scales the GUI automatically - see https://autohotkey.com/docs/commands/Gui.htm#DPIScale - I still find it often too small / inconvenient to use.

As I didn't want to redo all the GUI sizes, control positions, etc or scale all the coordinates manually so the GUI would be usable on various DPI settings, I wrote a simple function to do all the work dynamically. I still have to modify the GUI code but now not that much editing is needed and it will scale the GUI depending on the DPI setting set by the user.

Example: here is a GUI at 144 DPI before and after applying dpi():
Image
(at 96 dpi the GUI will probably look huge)

If you want to disable the scaling you can set the dpi by simply calling the function see setdpi in the documentation.

Simple GUI code before DPI()

Code: Select all

Gui, Font, s10
Gui, Add, Text, x5 y5, Hello
Gui, Add, Edit, xp yp+20 w200 h100 vVar, Goodbye
Gui, Add, Button, xp yp+110 w100 gMyLabel, OK
Gui, Add, Button, xp+110 yp w50 gGuiClose, Cancel
Gui, Show, w220 h200, DPI() test GUI
Return
You can simply call the dpi() function by wrapping it around the options like so:

Code: Select all

Gui, Font, % dpi("s10")
Gui, Add, Text, % dpi("x5 y5"), Hello
Gui, Add, Edit, % dpi("xp yp+20 w200 h100 vVar"), Goodbye
Gui, Add, Button, % dpi("xp yp+110 w100 gMyLabel"), OK
Gui, Add, Button, % dpi("xp+110 yp w50 gGuiClose"), Cancel
Gui, Show, % dpi("w220 h200"), DPI() test GUI
Return

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 05 Oct 2017, 17:10
by Helgef
Very convenient, it works well, thank you very much for sharing. :thumbup:
About the example in the readme, the code with using dpi() sets gui height 220, while the one without set height 180, so it will look like there is some poor scaling going on, but there isn't :D
You do not handle the case when height or width is -1, for example for the picture control, you can specify h-1 to keep aspect ratio. Quick fix:

Code: Select all

RegExMatch(option,"\K((-|)\d+)",number)
newnumber:= number == -1 ? -1 : r ? Round(number*factor) : number*factor
Personal preference: instead of the global variable, I'd use a static variable which can be set, eg, via a third parameter, dpi(,,setDpi:=37).

Cheers.

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 06 Oct 2017, 05:11
by just me
I almost ever used 96 DPI, so I don't know why you are dissatisfied with the built-in scaling.

A few remarks:
  • You should not scale the rows option.
  • You should elaborate the RegEx functions. Examples:

    Code: Select all

    Gui, Add, Edit, % dpi("xp yp+20 w200 h100 vVar hwndEdit10ID"), Goodbye
    GuiControl, , MyPic, % dpi("*icon2 *w100 *h-1 C:\My Application.exe")
  • You should consider to return either Round() or Floor(), though AHK currently ignores the fractional part. The internal scaling is eqivalent to Round().
@Helgef: There are cases when -1 should be scaled dependent on the DPI, e.g. yp-1.

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 06 Oct 2017, 05:41
by Helgef
There are cases when -1 should be scaled dependent on the DPI, e.g. yp-1.
Good point :thumbup:
Also, you might want to consider tabs when parsing, probably space will suffice for99.99 % of the time, but it is easy to add,

Code: Select all

Loop, parse, in, %A_Space%%A_Tab%
Cheers.

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 06 Oct 2017, 13:11
by ahk7
Thanks for the useful feedback which I will try to incorporate.

I've prepared a test script to see if it can correctly catch "all" options that need to be processed ",1" should be processed e.g. x5 -> x10 and ",0" should be skipped and kept as is e.g. hwndEdit10ID (should not be changed to hwndEdit20ID, good catch just me)
Do I miss something obvious?

Code: Select all

; dpi() test case
; 0 = Failed (do not process option)
; 1 = OK (these should be processed)

testoptions=
(join|
x5,1
xp,0
xp+1,1
xp-1,1
xm,0
xs,0
y5,1
yp,0
yp+5,1
yp-5,1
ym,0
ys,0
*w5,1
*w0,0
*h0,0
w5,1
h5,1
h-1,0
*h-1,0
hwndEdit10ID,0
hwndEdit1,0
icon10,0
*icon10,0
s1,1
r1,0
vVar,0
Checked1,0
Choose1,0
Number,0
)

Loop, parse, testoptions, |
	{
	 test:=StrSplit(A_LoopField,",")
	 If RegExMatch(test[1],"i)(w0|h0|h-1|xp|yp|xs|ys|xm|ym)$") or RegExMatch(test[1],"i)(icon|hwnd)") ; these need to be bypassed, test[2] = 0
		{
		 testresult .= test[2] ? test[1] "`tFailed1`n" : test[1] "`tOK1`n" 
		 continue
		}
	 else If (RegExMatch(test[1],"i)^\*{0,1}(x|xp|y|yp|w|h|s)([-+]){0,1}\d")) ; should be processed, test[2] = 1
		{
		 testresult .= test[2] ? test[1] "`tOK2`n" : test[1] "`tFailed2`n" 
		 continue
		} ; other bypass options here, test[2] = 0
	 testresult .= test[2] ? test[1] "`tFialed3`n" : test[1] "`tOK3`n" 
	}

MsgBox % testresult "`n"


Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 06 Oct 2017, 16:14
by ahk7
A development branch is (temporarily) available here https://github.com/hi5/dpi/tree/v0.3

Changes
  • Replaced super global variable ###dpiset with static variable within dpi() to set dpi, usage: dpi(,120)
  • Removed r parameter, always use Round()
  • No longer scales the Rows option and others that should be skipped (h-1, *w0, hwnd etc)

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 09 Oct 2017, 11:42
by ahk7
Merged the development branch, v0.31 is now the master branch https://github.com/hi5/dpi (refactored the code somewhat)

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 Oct 2017, 02:50
by Helgef
Excellent updates, I really like it :thumbup:

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 Oct 2017, 07:58
by DataLife
Concerning your github example 3, on the DropDownList you did not add vVar inside the parenthesis.

Code: Select all

Gui, Add, Edit,     % dpi("x25 yp-5 w300 h20 Number vVar"), EditText

SelectMenuPos:=2
Gui, Add, DropDownList, % dpi("xp yp+10 w200 h25 r4 Choose" SelectMenuPos) " vVar", 1 - Option|2 - Option|3 - Option
; Choose + the SelectMenuPos selects "2 - Option" in the DropDownList

Check:=1
Gui, Add, Checkbox, % dpi("x25 yp+20 w200 h16 Checked" Check " vVar"), Option
; Checked + the Check variable ticks the checkbox
Is vVar added within the parenthesis or not?

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 Oct 2017, 08:10
by Helgef
You can do it either way DataLife, the function DPI() will scale the relevant numbers in the option string, and leave the appropriate options alone, then return and the options will be concatenated. Leaving it outside means one less thing to parse in the function, you will never notice, but you might feel good about it :ugeek: .

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 May 2018, 05:21
by OCP
this is very use full thank you just what i was looking for

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 May 2018, 08:19
by Flipeador
I was wondering, why is this function necessary? Should not this be done automatically by AHK? Whats wrong with AHK?

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 May 2018, 09:07
by ahk7-nli
@Flipeador - in the first post you can see a screenshot of the difference. Image on the left is automatic DPIScale by AutoHotkey at 144dpi (4K monitor at 150%), the right one is after dpi() has been applied (so easier to read, easier to click buttons).

Yes, AutoHotkey does DPIScale (unless you turn it off) but that doesn't mean a 10pt letter on a normal monitor can be read on a 4K monitor which is set to 150% (144dpi) if you don't have perfect vision - so DPI() is actually increasing the DPIScale done by AutoHotkey. I wrote it to help me read the text(s) and make it easier to click various controls in a AutoHotkey GUI in a portable fashion. There is no need for it, but I do find it useful :-)

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 May 2018, 09:09
by guest3456
i too am confused just like Flipeador. are we supposed to use Gui -DPIScale and then use this function instead?

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 May 2018, 09:32
by ahk7-nli
Turning off DPIScale would defeat the purpose - if you do that and then apply dpi() you would end up with the same GUI as AutoHotkey would do it.
Basically dpi() doubles* the DPIScale done by AutoHotkey. I find that the automatic scaling is too small for me. So I can read a 10pt text at a standard monitor perfectly well, I can't read that same GUI text on a 4K monitor and the buttons are "too small" to click.
So that 10pt text would have to become 14pt for me to read, so dpi() does the math for me 10 -> dpi() -> 14* - presto I can read it.
(again that is my personal experience)

If you have a 4K monitor AND have changed the scaling to 125% or higher you can simply test it by copying the example GUI code from the first post and see the difference between the GUIs before and after - that should be similar to the example image in the first post, same GUI only bigger.


* its a cheat - so in my code I say "use 10pts", using dpi() I tell it to actually "use 14pts", but AutoHotkey actually uses DPIScale to increase that 14pts yet again so that is why it more or less "doubles". You can do this manually of course and tell AutoHotkey to use "18pts" but as I use different monitors on different computers I would have to do that manually for all my scripts which is too much work and it would look odd on "normal" monitors - now I just wrap it in dpi() and I'm done.

If it is still to confusing just ignore this thread :-)

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 17 May 2018, 09:48
by Flipeador
I have seen images where only half of the text is seen in the controls, so it is inefficient. This function seems to correct that. I think AHK should have an 'smarter' automatic scale, if this would be possible.
Edit: With this method, can we ensure that all of our GUIs are displayed correctly on all monitors?

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 19 May 2018, 03:14
by ahk7
Just to be clear:
AutoHotkey already does DPI scaling of GUIs. So if you're happy with the way your GUI looks on your monitor(s) (4K or not) there is no need for your use this dpi() function.
Only use it if you "can't read it" or have trouble with controls being too small/incovenient to click. That way you don't have to worry about which monitor your GUI will be displayed on.

@Flipeador:
I personally haven't had any issues using dpi() as I'm using it now on nearly all my GUIs.
As mentioned in the doc: "Define a font size for all your GUIs, that way you can easily use DPI() to also scale the font size." If you don't it may increase the size of the controls but still use a small font. There is a code snippet by SKAN in the docs as well.
If text/controls are misaligned/chopped it is most likely a GUI design error.

Room for improvement?

I don't have a multi-monitor setup so I can not check or test it but from what I have found is that for recent editions of Windows 10 you can now set the DPI scaling per monitor.
Currently dpi() will use the DPI scaling reported by the system in use for the primary screen. So in theory it could be improved by trying to detect which monitor the GUI will appear on first (or create a helper function for example) and try to read the DPI from the correct registry key:

HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings\

Further reading:

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 19 May 2018, 07:07
by Flipeador
Thanks ahk7!
I will be reading the links. I would like to have another monitor with high DPI to be able to test :cry:
:wave:
I hate this DPI thing, I would have to scale the images too, running the risk that they look bad.

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 20 May 2018, 17:43
by guest3456
thanks a lot for the explanations

Re: DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Posted: 22 May 2019, 09:14
by lmstearn
Thanks for the article. The actual function for GetSystemMetrics is GetSystemMetricsForDpi for a multi-monitor setup:
This function returns the same result as GetSystemMetrics but scales it according to an arbitrary DPI you provide if appropriate.
Sounds like fun. A whole lot more DPI stuff is found in WinUser.h including SystemParametersInfoForDpi function which handles dpi for icons and non client regions.
From the above linked Blog article a few salient points:
GDI+ content doesn’t DPI scale
DX content doesn’t DPI scale
Bitmap-based content won’t be crisp