DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

Post your working scripts, libraries and tools for AHK v1.1 and older
ahk7
Posts: 574
Joined: 06 Nov 2013, 16:35

DPI() - writing friendlier DPI-Aware AutoHotkey GUIs

05 Oct 2017, 12:55

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
Last edited by ahk7 on 09 Oct 2017, 11:41, edited 1 time in total.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

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

05 Oct 2017, 17:10

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.
just me
Posts: 9425
Joined: 02 Oct 2013, 08:51
Location: Germany

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

06 Oct 2017, 05:11

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.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

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

06 Oct 2017, 05:41

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.
ahk7
Posts: 574
Joined: 06 Nov 2013, 16:35

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

06 Oct 2017, 13:11

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"

Last edited by ahk7 on 09 Oct 2017, 11:41, edited 1 time in total.
ahk7
Posts: 574
Joined: 06 Nov 2013, 16:35

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

06 Oct 2017, 16:14

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)
ahk7
Posts: 574
Joined: 06 Nov 2013, 16:35

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

09 Oct 2017, 11:42

Merged the development branch, v0.31 is now the master branch https://github.com/hi5/dpi (refactored the code somewhat)
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

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

17 Oct 2017, 02:50

Excellent updates, I really like it :thumbup:
User avatar
DataLife
Posts: 445
Joined: 29 Sep 2013, 19:52

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

17 Oct 2017, 07:58

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?
Check out my scripts. (MyIpChanger) (ClipBoard Manager) (SavePictureAs)
All my scripts are tested on Windows 10, AutoHotkey 32 bit Ansi unless otherwise stated.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

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

17 Oct 2017, 08:10

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: .
OCP
Posts: 98
Joined: 28 Mar 2018, 19:28

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

17 May 2018, 05:21

this is very use full thank you just what i was looking for
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

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

17 May 2018, 08:19

I was wondering, why is this function necessary? Should not this be done automatically by AHK? Whats wrong with AHK?
ahk7-nli

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

17 May 2018, 09:07

@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 :-)
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

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

17 May 2018, 09:09

i too am confused just like Flipeador. are we supposed to use Gui -DPIScale and then use this function instead?

ahk7-nli

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

17 May 2018, 09:32

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 :-)
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

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

17 May 2018, 09:48

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?
ahk7
Posts: 574
Joined: 06 Nov 2013, 16:35

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

19 May 2018, 03:14

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:
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

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

19 May 2018, 07:07

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.
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

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

20 May 2018, 17:43

thanks a lot for the explanations

User avatar
lmstearn
Posts: 688
Joined: 11 Aug 2016, 02:32
Contact:

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

22 May 2019, 09:14

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
:arrow: itros "ylbbub eht tuO kaerB" a ni kcuts m'I pleH

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: laoyugong and 144 guests