Back to AutoHotkey Tools
RadMode Tools
Automation scripts and utilities for radiology workflows
How to Use
Complete AutoHotkey v2 script for radiology workflow automation
- 1Download all three files and place them in the same folder. Double-click the Runner (.exe) to launch — a setup GUI window will appear.
- 2Configure your hotkeys and settings in the setup GUI. The launcher minimizes to the system tray after setup — close it to stop the script.
- 3Press Caps + F to enter Rads Mode for dictation shortcuts, and Caps + D to exit.
- 4Hold Alt + Z to trigger Smart Break Glass using the hammer icon. All scripts intended for EPIC, PowerScribe, and Visage workflows.
Quick Reference
Key shortcuts — full list available in the script GUI
Global Shortcuts
Caps LockTrackball Scroll (Hold)
Alt + ZSmart Break Glass
Shift + Alt + ZForce Manual Entry
Alt + PLogin site
Caps + FEnable Rads Mode
Caps + DExit Rads Mode
Rads Mode — Dictation
DToggle dictation
HSave draft (F9)
FNext field
GPrevious field
SBackspace
ANew line
QClose window (Ctrl+W)
Mouse & Pick Lists
RLeft click
EMiddle click
WRight click
Alt + 1–5Pick list choice
Visage viewer:
Z, X, C, VSend to Visage
Download Files
All three files are required — place them in the same folder
Hammer Icon
.pnghammer.png
Required for the "Break the Glass" automation — must be in the same folder as the script.
Download IconFull RadMode Automation Script
View the complete source code or copy it to your clipboard
1#Requires Autohotkey v2
2#SingleInstance Force
3CoordMode "ToolTip", "Screen"
4CoordMode "Mouse", "Screen"
5CoordMode "Pixel", "Screen"
6SetTitleMatchMode 2
7
8; ==============================================================================
9; Version & Configuration Defaults
10; ==============================================================================
11global VERSION := "v22.19" ; Incremented version to reflect global clipboard injection
12global INI_FILE := "rad_helper_settings.ini"
13
14; --- User Variables (Runtime) ---
15global USER := ""
16global PASS := ""
17global KEEPAWAKE := 0
18global TRACKBALL_SCROLL := 1
19global DEBUG_MODE := 0
20
21; --- Advanced Settings (Loaded from INI) ---
22global BTG_TAB_DELAY := 80
23global BTG_POPUP_DELAY := 500
24global SCROLL_THRESHOLD := 15
25global SCROLL_LOCK_RATIO := 2.0
26
27; --- Marker Customization ---
28global MARKER_SIZE := 5
29global MARKER_COLOR := "FF0000"
30global MARKER_ALPHA := 180
31
32; --- Reliability Settings ---
33SetKeyDelay 10, 10
34global LOGIN_STEP_DELAY := 150
35
36; --- Window Titles ---
37global EPIC_TITLE := "Prod"
38global PS := "PowerScribe"
39global VISAGE := "Visage"
40global VISAGE_LOGIN := "Client Login ahk_class Qt51510QWindowIcon"
41
42global RADSMODE := false
43global MarkerGui := unset
44
45; ==============================================================================
46; Initialization
47; ==============================================================================
48LoadSettings()
49CreateCursorMarker()
50
51; ==============================================================================
52; Main GUI
53; ==============================================================================
54myGui := Gui()
55myGui.Title := "Radiology Helper " . VERSION
56myGui.BackColor := "FBFBFB"
57myGui.SetFont("s10", "Segoe UI")
58
59; --- Login Section ---
60myGui.Add("GroupBox", "x20 y10 w460 h80", "Login Credentials")
61myGui.Add("Text", "x40 y40 w70 h25 +0x200", "Username:")
62Edit1 := myGui.Add("Edit", "x115 y40 w130 h25", USER)
63
64myGui.Add("Text", "x255 y40 w70 h25 +0x200", "Password:")
65Edit2 := myGui.Add("Edit", "x330 y40 w110 h25 +Password", PASS)
66
67; --- Action Button ---
68LaunchButton := myGui.Add("Button", "x20 y100 w260 h50 +Default", "Launch Visage System")
69LaunchButton.OnEvent("Click", LaunchButtonHandler)
70LaunchButton.SetFont("s11 bold")
71
72; --- Config Button ---
73ConfigButton := myGui.Add("Button", "x290 y100 w190 h50", "Advanced Config")
74ConfigButton.OnEvent("Click", OpenConfigMenu)
75
76; --- Options Section ---
77CheckBox1 := myGui.Add("CheckBox", "x40 y160 w150 h25 Checked" . KEEPAWAKE, "Prevent Sleep")
78CheckBox2 := myGui.Add("CheckBox", "x220 y160 w240 h25 Checked" . TRACKBALL_SCROLL, "Hold CapsLock to scroll")
79CheckBox3 := myGui.Add("CheckBox", "x40 y185 w150 h25 Checked" . DEBUG_MODE, "Debug Mode")
80
81; --- Divider ---
82myGui.Add("Text", "x20 y215 w460 h2 0x10")
83
84; --- Reference Guide (Reformatted) ---
85myGui.SetFont("s9", "Segoe UI")
86RefGroup := myGui.Add("GroupBox", "x20 y220 w460 h185", "Quick Reference Guide")
87
88; Column 1: Global
89myGui.SetFont("s9 w700")
90myGui.Add("Text", "x40 y245 w180", "Global Shortcuts")
91myGui.SetFont("s9 w400")
92myGui.Add("Text", "x40 y265 w200", "Caps + f : Rads Mode ON")
93myGui.Add("Text", "x40 y285 w200", "Caps + d : Rads Mode OFF")
94myGui.Add("Text", "x40 y305 w200", "Alt + p : Auto-Login Site")
95myGui.Add("Text", "x40 y325 w200", "Alt + z : Fast Break Glass")
96
97; Column 2: Rads Mode
98myGui.SetFont("s9 w700")
99myGui.Add("Text", "x260 y245 w180", "Rads Mode (Vim-Style)")
100myGui.SetFont("s9 w400")
101myGui.Add("Text", "x260 y265 w200", "d : Toggle Dictation")
102myGui.Add("Text", "x260 y285 w200", "h : Save Draft (F9)")
103myGui.Add("Text", "x260 y305 w200", "f / g : Next / Prev Field")
104myGui.Add("Text", "x260 y325 w200", "s / a : Backspace / Enter")
105myGui.Add("Text", "x260 y345 w200", "Alt + 1-5 : Pick List Choice")
106
107; Logic Note
108myGui.SetFont("s8 italic c555555")
109myGui.Add("Text", "x40 y370 w420", "Logic Note: Logins utilize a clipboard-injection method to instantly paste credentials, bypassing standard keystroke lag.")
110
111; Footer
112myGui.SetFont("s8 c777777")
113myGui.Add("Text", "x20 y415 w460 Center", "Note: Red dot indicator appears when Rads Mode is active.")
114
115myGui.Show("w500 h445")
116
117; ==============================================================================
118; Cursor Marker Logic
119; ==============================================================================
120CreateCursorMarker() {
121 global MarkerGui, MARKER_SIZE, MARKER_COLOR, MARKER_ALPHA
122 if IsSet(MarkerGui)
123 MarkerGui.Destroy()
124 MarkerGui := Gui("+AlwaysOnTop -Caption +ToolWindow +E0x20")
125 MarkerGui.BackColor := MARKER_COLOR
126 D := MARKER_SIZE
127 WinSetRegion("0-0 W" D " H" D " R" D "-" D, MarkerGui.Hwnd)
128 MarkerGui.Opt("+LastFound")
129 WinSetTransparent(MARKER_ALPHA, MarkerGui.Hwnd)
130}
131
132UpdateMarkerPos() {
133 global RADSMODE, MarkerGui, MARKER_SIZE
134 if (RADSMODE) {
135 MouseGetPos(&mX, &mY)
136 MarkerGui.Show("x" (mX + 12) " y" (mY + 12) " w" MARKER_SIZE " h" MARKER_SIZE " NoActivate")
137 } else {
138 MarkerGui.Hide()
139 }
140}
141
142; ==============================================================================
143; Advanced Config Menu
144; ==============================================================================
145OpenConfigMenu(*) {
146 advGui := Gui()
147 advGui.Title := "Advanced Configuration"
148 advGui.SetFont("s9", "Segoe UI")
149
150 advGui.Add("GroupBox", "x10 y10 w380 h100", "Rads Mode Indicator (Dot)")
151 advGui.Add("Text", "x30 y35 w100 h20", "Dot Size (px):")
152 Ctrl_Size := advGui.Add("Edit", "x130 y32 w60 h20 Number", MARKER_SIZE)
153 advGui.Add("Text", "x210 y35 w80 h20", "Hex Color:")
154 Ctrl_Color := advGui.Add("Edit", "x290 y32 w80 h20", MARKER_COLOR)
155 advGui.Add("Text", "x30 y65 w100 h20", "Opacity (0-255):")
156 Ctrl_Alpha := advGui.Add("Edit", "x130 y62 w60 h20 Number", MARKER_ALPHA)
157
158 advGui.Add("GroupBox", "x10 y120 w380 h130", "Workflow Settings")
159 advGui.Add("Text", "x30 y145 w150 h20", "Tab Delay (ms):")
160 Ctrl_TabDelay := advGui.Add("Edit", "x240 y142 w80 h20 Number", BTG_TAB_DELAY)
161 advGui.Add("Text", "x30 y175 w150 h20", "Scroll Sensitivity:")
162 Ctrl_Sens := advGui.Add("Edit", "x240 y172 w80 h20 Number", SCROLL_THRESHOLD)
163 advGui.Add("Text", "x30 y205 w150 h20", "Axis Lock Ratio:")
164 Ctrl_Lock := advGui.Add("Edit", "x240 y202 w80 h20", SCROLL_LOCK_RATIO)
165
166 SaveBtn := advGui.Add("Button", "x90 y270 w100 h30", "Save")
167 CancelBtn := advGui.Add("Button", "x200 y270 w100 h30", "Cancel")
168 SaveBtn.OnEvent("Click", SaveHandler)
169 CancelBtn.OnEvent("Click", (*) => advGui.Destroy())
170 advGui.Show()
171
172 SaveHandler(*) {
173 global BTG_TAB_DELAY, SCROLL_THRESHOLD, SCROLL_LOCK_RATIO
174 global MARKER_SIZE, MARKER_COLOR, MARKER_ALPHA
175 MARKER_SIZE := Ctrl_Size.Value
176 MARKER_COLOR := Ctrl_Color.Value
177 MARKER_ALPHA := Ctrl_Alpha.Value
178 BTG_TAB_DELAY := Ctrl_TabDelay.Value
179 SCROLL_THRESHOLD := Ctrl_Sens.Value
180 SCROLL_LOCK_RATIO := Number(Ctrl_Lock.Value)
181 SaveSettings()
182 CreateCursorMarker()
183 advGui.Destroy()
184 MsgBox("Settings Saved!", "Configuration", "Iconi T1")
185 }
186}
187
188; ==============================================================================
189; INI Persistence Functions
190; ==============================================================================
191LoadSettings() {
192 global BTG_TAB_DELAY, SCROLL_THRESHOLD, SCROLL_LOCK_RATIO
193 global MARKER_SIZE, MARKER_COLOR, MARKER_ALPHA
194 if FileExist(INI_FILE) {
195 try {
196 BTG_TAB_DELAY := IniRead(INI_FILE, "Citrix", "TabDelay", 80)
197 SCROLL_THRESHOLD := IniRead(INI_FILE, "Physics", "Sensitivity", 15)
198 SCROLL_LOCK_RATIO := IniRead(INI_FILE, "Physics", "AxisLock", 2.0)
199 MARKER_SIZE := IniRead(INI_FILE, "Marker", "Size", 5)
200 MARKER_COLOR := IniRead(INI_FILE, "Marker", "Color", "FF0000")
201 MARKER_ALPHA := IniRead(INI_FILE, "Marker", "Alpha", 180)
202 }
203 }
204}
205
206SaveSettings() {
207 IniWrite(BTG_TAB_DELAY, INI_FILE, "Citrix", "TabDelay")
208 IniWrite(SCROLL_THRESHOLD, INI_FILE, "Physics", "Sensitivity")
209 IniWrite(SCROLL_LOCK_RATIO, INI_FILE, "Physics", "AxisLock")
210 IniWrite(MARKER_SIZE, INI_FILE, "Marker", "Size")
211 IniWrite(MARKER_COLOR, INI_FILE, "Marker", "Color")
212 IniWrite(MARKER_ALPHA, INI_FILE, "Marker", "Alpha")
213}
214
215; ==============================================================================
216; GUI Handlers
217; ==============================================================================
218UpdateGlobals(*) {
219 global USER := Edit1.Value, PASS := Edit2.Value, KEEPAWAKE := CheckBox1.Value
220 global TRACKBALL_SCROLL := CheckBox2.Value, DEBUG_MODE := CheckBox3.Value
221}
222LaunchButtonHandler(*) {
223 LaunchVisage()
224 myGui.Minimize()
225}
226CloseHandler(*) => ExitApp()
227
228; ==============================================================================
229; Core Logic
230; ==============================================================================
231SetTimer StopSleep, 100000
232SetTimer UpdateMarkerPos, 15
233
234LaunchVisage() {
235 if WinExist(VISAGE) {
236 WinActivate(VISAGE)
237 return
238 }
239 if WinExist(VISAGE_LOGIN) {
240 VisageLoginSequence()
241 return
242 }
243 targetLnk := ""
244 loop files A_Desktop "\*Visage*.lnk" {
245 targetLnk := A_LoopFileFullPath
246 break
247 }
248 if (targetLnk == "") {
249 loop files A_DesktopCommon "\*Visage*.lnk" {
250 targetLnk := A_LoopFileFullPath
251 break
252 }
253 }
254 if (targetLnk) {
255 Run(targetLnk)
256 } else {
257 fallbackPath := "C:\Program Files\Visage Imaging\Visage 7.1\bin\arch-Win\vsclient.exe"
258 if FileExist(fallbackPath) {
259 Run(fallbackPath)
260 } else {
261 MsgBox("Could not find Visage executable.", "Error", "Icon!")
262 return
263 }
264 }
265 if WinWait(VISAGE_LOGIN,, 20) {
266 VisageLoginSequence()
267 }
268}
269
270VisageLoginSequence() {
271 global VISAGE_LOGIN, LOGIN_STEP_DELAY
272 global Edit1, Edit2
273
274 if !WinExist(VISAGE_LOGIN)
275 return
276 WinActivate(VISAGE_LOGIN)
277 if !WinWaitActive(VISAGE_LOGIN,, 3)
278 return
279
280 local safeUser := Edit1.Value
281 local safePass := Edit2.Value
282
283 SetKeyDelay 10, 10
284
285 WinGetPos(,, &winW, &winH, VISAGE_LOGIN)
286 oldMode := A_CoordModeMouse
287 CoordMode "Mouse", "Window"
288
289 MouseMove(winW * 0.60, winH * 0.40)
290 Sleep(LOGIN_STEP_DELAY)
291 Click()
292 Sleep(LOGIN_STEP_DELAY)
293
294 ; Save the current clipboard contents to memory
295 ClipSaved := ClipboardAll()
296 A_Clipboard := ""
297
298 SendEvent("^a{Delete}")
299 Sleep(LOGIN_STEP_DELAY)
300 A_Clipboard := "VISAGE.NYUMC.ORG"
301 Sleep(50)
302 Send("^v")
303 Sleep(LOGIN_STEP_DELAY)
304 SendEvent("{Tab}")
305 Sleep(LOGIN_STEP_DELAY)
306
307 SendEvent("^a{Delete}")
308 Sleep(LOGIN_STEP_DELAY)
309 A_Clipboard := safeUser
310 Sleep(50)
311 Send("^v")
312 Sleep(LOGIN_STEP_DELAY)
313 SendEvent("{Tab}")
314 Sleep(LOGIN_STEP_DELAY)
315
316 A_Clipboard := safePass
317 Sleep(50)
318 Send("^v")
319 Sleep(LOGIN_STEP_DELAY + 200)
320
321 SendEvent("{Enter}")
322
323 ; Wait slightly before restoring so the final paste completes
324 Sleep(150)
325 A_Clipboard := ClipSaved
326
327 CoordMode "Mouse", oldMode
328}
329
330; --- Fast Break Glass (Clipboard Injection) ---
331fastBreakGlass(*) {
332 global BTG_TAB_DELAY, Edit2
333
334 ; Save the current clipboard contents to memory
335 ClipSaved := ClipboardAll()
336 A_Clipboard := ""
337
338 ; Inject the Indication
339 A_Clipboard := "Provision"
340 Sleep(50) ; Brief pause allows Windows clipboard to update
341 Send("^v")
342 Sleep(BTG_TAB_DELAY)
343
344 Send("{Tab}")
345 Sleep(BTG_TAB_DELAY)
346 Send("{Tab}")
347 Sleep(BTG_TAB_DELAY)
348
349 ; Inject the Password
350 A_Clipboard := Edit2.Value
351 Sleep(50)
352 Send("^v")
353 Sleep(BTG_TAB_DELAY)
354
355 ; Accept
356 Send("!a")
357
358 ; Wait slightly before restoring so the final paste completes
359 Sleep(150)
360 A_Clipboard := ClipSaved
361}
362
363loginSite() {
364 global Edit1, Edit2
365
366 ClipSaved := ClipboardAll()
367 A_Clipboard := ""
368
369 A_Clipboard := Edit1.Value
370 Sleep(50)
371 Send("^v")
372 Sleep(200)
373 Send("{Tab}")
374 Sleep(200)
375
376 A_Clipboard := Edit2.Value
377 Sleep(50)
378 Send("^v")
379 Sleep(200)
380 Send("{Enter}")
381
382 Sleep(150)
383 A_Clipboard := ClipSaved
384}
385
386; ==============================================================================
387; CAPSLOCK SCROLL
388; ==============================================================================
389TrackballScroll(*) {
390 global SCROLL_THRESHOLD, SCROLL_LOCK_RATIO
391 oldMode := A_CoordModeMouse
392 CoordMode "Mouse", "Screen"
393 MouseGetPos(&startX, &startY)
394 bankX := 0, bankY := 0
395 Loop {
396 if !GetKeyState("CapsLock", "P")
397 break
398 MouseGetPos(¤tX, ¤tY)
399 moveX := currentX - startX, moveY := currentY - startY
400 if (moveX != 0 || moveY != 0) {
401 DllCall("SetCursorPos", "int", startX, "int", startY)
402 }
403 bankX += moveX, bankY += moveY
404 absX := Abs(bankX), absY := Abs(bankY)
405 if (absX > (absY * SCROLL_LOCK_RATIO)) {
406 if (absX >= SCROLL_THRESHOLD) {
407 ticks := Integer(absX / SCROLL_THRESHOLD)
408 direction := (bankX > 0) ? "{WheelRight}" : "{WheelLeft}"
409 Loop ticks
410 Send(direction)
411 bankX := Mod(bankX, SCROLL_THRESHOLD), bankY := 0
412 }
413 } else if (absY > absX) {
414 if (absY >= SCROLL_THRESHOLD) {
415 ticks := Integer(absY / SCROLL_THRESHOLD)
416 direction := (bankY > 0) ? "{WheelDown}" : "{WheelUp}"
417 Loop ticks
418 Send(direction)
419 bankY := Mod(bankY, SCROLL_THRESHOLD), bankX := 0
420 }
421 }
422 Sleep(5)
423 }
424 CoordMode "Mouse", oldMode
425}
426
427; ==============================================================================
428; UTILS & HOTKEYS
429; ==============================================================================
430GetTargetControl() {
431 global PS
432 largestArea := 0, bestControl := ""
433
434 ; STRATEGY: Find the largest visible RichEdit control first.
435 try {
436 ctrlList := WinGetControls(PS)
437 for ctrl in ctrlList {
438 if InStr(ctrl, "RICHEDIT") {
439 try {
440 if ControlGetVisible(ctrl, PS) {
441 ControlGetPos(,, &w, &h, ctrl, PS)
442 if (w * h > largestArea) {
443 largestArea := w * h, bestControl := ctrl
444 }
445 }
446 }
447 }
448 }
449 }
450
451 if (bestControl != "")
452 return bestControl
453
454 try {
455 focusedCtrl := ControlGetFocus(PS)
456 if InStr(focusedCtrl, "RICHEDIT")
457 return focusedCtrl
458 }
459
460 return ""
461}
462
463ActivateAndSend(key, targetSpecificControl := true) {
464 global PS
465 if WinExist(PS) {
466 if !WinActive(PS) {
467 WinActivate(PS), WinWaitActive(PS,, 1), Sleep(50)
468 }
469 target := targetSpecificControl ? GetTargetControl() : ""
470 if (target)
471 ControlSend(key, target, PS)
472 else
473 SendEvent(key)
474 }
475}
476
477StopSleep(*) {
478 if (KEEPAWAKE) {
479 oldMode := A_CoordModeMouse
480 CoordMode("Mouse", "Screen")
481 MouseGetPos(&cX, &cY)
482 MouseMove(cX + 1, cY, 0), MouseMove(cX, cY, 0)
483 CoordMode("Mouse", oldMode)
484 }
485}
486
487pick(num) {
488 global PS
489 MouseGetPos &startX, &startY
490 CoordMode "Mouse", "Client"
491 heightRow := 17
492 if WinExist(PS) {
493 try {
494 for ctrl in WinGetControls(PS) {
495 try {
496 txt := ControlGetText(ctrl, PS)
497 } catch {
498 continue
499 }
500 if (txt == "Pick List Choices") {
501 ControlGetPos(&x, &y, &w, &h, ctrl, PS)
502 targetY := y + Integer(heightRow/2) + (heightRow * num)
503 targetX := x + 20
504 if !WinActive(PS) {
505 WinActivate(PS)
506 WinWaitActive(PS,, 1)
507 }
508 Click(targetX, targetY)
509 Sleep(50)
510 Click(targetX, targetY)
511 CoordMode "Mouse", "Screen"
512 MouseMove(startX, startY)
513 Sleep(50)
514 ActivateAndSend("{Tab}", true)
515 return
516 }
517 }
518 }
519 }
520 CoordMode "Mouse", "Screen"
521}
522
523; -- Common Rads Logic --
524toggleDictation(*) => ActivateAndSend("{F4}", false)
525saveDraft(*) => ActivateAndSend("{F9}", false)
526nextField(*) => ActivateAndSend("{Tab}", true)
527prevField(*) => ActivateAndSend("+{Tab}", true)
528backspace(*) => ActivateAndSend("{Backspace}", true)
529newLine(*) => ActivateAndSend("{Enter}", true)
530
531enableRadsMode(*) {
532 global RADSMODE := true
533}
534exitRadsMode(*) {
535 global RADSMODE := false
536}
537
538#HotIf TRACKBALL_SCROLL
539 $CapsLock::TrackballScroll()
540#HotIf
541
542#HotIf GetKeyState("CapsLock", "P")
543 f:: enableRadsMode()
544 d:: exitRadsMode()
545#HotIf
546
547; -- Global Hotkeys --
548!z:: fastBreakGlass()
549!p:: loginSite()
550
551#HotIf RADSMODE
552 r::LButton
553 e::MButton
554 w::RButton
555 $d:: toggleDictation()
556 $h:: saveDraft()
557 $f:: nextField()
558 $g:: prevField()
559 $s:: backspace()
560 $a:: newLine()
561 $q:: Send("^w")
562
563 ; -- Pick List Hotkeys --
564 $!1:: pick(1)
565 $!2:: pick(2)
566 $!3:: pick(3)
567 $!4:: pick(4)
568 $!5:: pick(5)
569
570 $z:: (WinActive(PS) ? "" : Send("z"))
571 $x:: (WinActive(PS) ? "" : Send("x"))
572 $c:: (WinActive(PS) ? "" : Send("c"))
573 $v:: (WinActive(PS) ? "" : Send("v"))
574#HotIfThis is the full source code of the RadMode.ahk file. You can copy this into a new .ahk file or download the file directly using the button above.