Skip to content

Commit

Permalink
Merge pull request #221 from spyoungtech/gui
Browse files Browse the repository at this point in the history
gui methods
  • Loading branch information
spyoungtech authored Jul 31, 2023
2 parents 0feb2d1 + 4fc794d commit cc95ca3
Show file tree
Hide file tree
Showing 11 changed files with 716 additions and 4 deletions.
17 changes: 16 additions & 1 deletion ahk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,23 @@
from ._sync import AHK
from ._sync import Control
from ._sync import Window
from ._utils import MsgBoxButtons
from ._utils import MsgBoxDefaultButton
from ._utils import MsgBoxIcon
from ._utils import MsgBoxModality

__all__ = ['AHK', 'Window', 'AsyncWindow', 'AsyncAHK', 'Control', 'AsyncControl']
__all__ = [
'AHK',
'Window',
'AsyncWindow',
'AsyncAHK',
'Control',
'AsyncControl',
'MsgBoxButtons',
'MsgBoxDefaultButton',
'MsgBoxIcon',
'MsgBoxModality',
]

_global_instance: Optional[AHK] = None

Expand Down
185 changes: 185 additions & 0 deletions ahk/_async/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@

from .._hotkey import Hotkey
from .._hotkey import Hotstring
from .._utils import MsgBoxButtons
from .._utils import MsgBoxDefaultButton
from .._utils import MsgBoxIcon
from .._utils import MsgBoxModality
from .._utils import MsgBoxOtherOptions
from .._utils import type_escape
from ..directives import Directive

Expand All @@ -35,8 +40,11 @@
from .transport import AsyncTransport
from .window import AsyncControl
from .window import AsyncWindow

# from .window import AsyncGui
from ahk.message import Position


async_sleep = asyncio.sleep # unasync: remove
sleep = time.sleep

Expand Down Expand Up @@ -3486,6 +3494,183 @@ async def reg_read(
args.append(value_name)
return await self._transport.function_call('AHKRegRead', args, blocking=blocking)

# fmt: off
@overload
async def msg_box(self, text: str = '', title: str = 'Message', buttons: MsgBoxButtons = MsgBoxButtons.OK, icon: Optional[MsgBoxIcon] = None, default_button: Optional[MsgBoxDefaultButton] = None, modality: Optional[MsgBoxModality] = None, help_button: bool = False, text_right_justified: bool = False, right_to_left_reading: bool = False, timeout: Optional[int] = None) -> str: ...
@overload
async def msg_box(self, text: str = '', title: str = 'Message', buttons: MsgBoxButtons = MsgBoxButtons.OK, icon: Optional[MsgBoxIcon] = None, default_button: Optional[MsgBoxDefaultButton] = None, modality: Optional[MsgBoxModality] = None, help_button: bool = False, text_right_justified: bool = False, right_to_left_reading: bool = False, timeout: Optional[int] = None, *, blocking: Literal[False]) -> AsyncFutureResult[str]: ...
@overload
async def msg_box(self, text: str = '', title: str = 'Message', buttons: MsgBoxButtons = MsgBoxButtons.OK, icon: Optional[MsgBoxIcon] = None, default_button: Optional[MsgBoxDefaultButton] = None, modality: Optional[MsgBoxModality] = None, help_button: bool = False, text_right_justified: bool = False, right_to_left_reading: bool = False, timeout: Optional[int] = None, *, blocking: Literal[True]) -> str: ...
@overload
async def msg_box(self, text: str = '', title: str = 'Message', buttons: MsgBoxButtons = MsgBoxButtons.OK, icon: Optional[MsgBoxIcon] = None, default_button: Optional[MsgBoxDefaultButton] = None, modality: Optional[MsgBoxModality] = None, help_button: bool = False, text_right_justified: bool = False, right_to_left_reading: bool = False, timeout: Optional[int] = None, *, blocking: bool = True) -> Union[str, AsyncFutureResult[str]]: ...
# fmt: on
async def msg_box(
self,
text: str = '',
title: str = 'Message',
buttons: MsgBoxButtons = MsgBoxButtons.OK,
icon: Optional[MsgBoxIcon] = None,
default_button: Optional[MsgBoxDefaultButton] = None,
modality: Optional[MsgBoxModality] = None,
help_button: bool = False,
text_right_justified: bool = False,
right_to_left_reading: bool = False,
timeout: Optional[int] = None,
*,
blocking: bool = True,
) -> Union[str, AsyncFutureResult[str]]:
options: int = int(buttons)
for opt in (icon, default_button, modality):
if opt is not None:
options += opt
if help_button:
options += MsgBoxOtherOptions.HELP_BUTTON
if text_right_justified:
options += MsgBoxOtherOptions.TEXT_RIGHT_JUSTIFIED
if right_to_left_reading:
options += MsgBoxOtherOptions.RIGHT_TO_LEFT_READING_ORDER

args = [str(options), title, text]
if timeout is not None:
args.append(str(timeout))
return await self._transport.function_call('AHKMsgBox', args, blocking=blocking)

# fmt: off
@overload
async def input_box(self, prompt: str = '', title: str = 'Input', default: str = '', hide: bool = False, width: Optional[int] = None, height: Optional[int] = None, x: Optional[int] = None, y: Optional[int] = None, locale: bool = True, timeout: Optional[int] = None) -> Union[None, str]: ...
@overload
async def input_box(self, prompt: str = '', title: str = 'Input', default: str = '', hide: bool = False, width: Optional[int] = None, height: Optional[int] = None, x: Optional[int] = None, y: Optional[int] = None, locale: bool = True, timeout: Optional[int] = None, *, blocking: Literal[False]) -> Union[AsyncFutureResult[str], AsyncFutureResult[None]]: ...
@overload
async def input_box(self, prompt: str = '', title: str = 'Input', default: str = '', hide: bool = False, width: Optional[int] = None, height: Optional[int] = None, x: Optional[int] = None, y: Optional[int] = None, locale: bool = True, timeout: Optional[int] = None, *, blocking: Literal[True]) -> Union[str, None]: ...
@overload
async def input_box(self, prompt: str = '', title: str = 'Input', default: str = '', hide: bool = False, width: Optional[int] = None, height: Optional[int] = None, x: Optional[int] = None, y: Optional[int] = None, locale: bool = True, timeout: Optional[int] = None, *, blocking: bool = True) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]: ...
# fmt: on
async def input_box(
self,
prompt: str = '',
title: str = 'Input',
default: str = '',
hide: bool = False,
width: Optional[int] = None,
height: Optional[int] = None,
x: Optional[int] = None,
y: Optional[int] = None,
locale: bool = True,
timeout: Optional[int] = None,
*,
blocking: bool = True,
) -> Union[None, str, AsyncFutureResult[str], AsyncFutureResult[None]]:
"""
Like AHK's ``InputBox``
If the user presses Cancel or closes the box, ``None`` is returned.
Otherwise, the user's input is returned.
Raises a ``TimeoutError`` if a timeout is specified and expires.
"""
args = [title, prompt]
if hide:
args.append('hide')
else:
args.append('')
for opt in (width, height, x, y):
if opt is not None:
args.append(str(opt))
else:
args.append('')
if locale:
args.append('Locale')
else:
args.append('')
if timeout is not None:
args.append(str(timeout))
else:
args.append('')
args.append(default)
return await self._transport.function_call('AHKInputBox', args, blocking=blocking)

# fmt: off
@overload
async def file_select_box(self, title: str = 'Select File', multi: bool = False, root: str = '', filter: str = '', save_button: bool = False, file_must_exist: bool = False, path_must_exist: bool = False, prompt_create_new_file: bool = False, prompt_override_file: bool = False, follow_shortcuts: bool = True) -> Union[None, str]: ...
@overload
async def file_select_box(self, title: str = 'Select File', multi: bool = False, root: str = '', filter: str = '', save_button: bool = False, file_must_exist: bool = False, path_must_exist: bool = False, prompt_create_new_file: bool = False, prompt_override_file: bool = False, follow_shortcuts: bool = True, *, blocking: Literal[False]) -> Union[AsyncFutureResult[str], AsyncFutureResult[None]]: ...
@overload
async def file_select_box(self, title: str = 'Select File', multi: bool = False, root: str = '', filter: str = '', save_button: bool = False, file_must_exist: bool = False, path_must_exist: bool = False, prompt_create_new_file: bool = False, prompt_override_file: bool = False, follow_shortcuts: bool = True, *, blocking: Literal[True]) -> Union[str, None]: ...
@overload
async def file_select_box(self, title: str = 'Select File', multi: bool = False, root: str = '', filter: str = '', save_button: bool = False, file_must_exist: bool = False, path_must_exist: bool = False, prompt_create_new_file: bool = False, prompt_override_file: bool = False, follow_shortcuts: bool = True, *, blocking: bool = True) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]: ...
# fmt: on
async def file_select_box(
self,
title: str = 'Select File',
multi: bool = False,
root: str = '',
filter: str = '',
save_button: bool = False,
file_must_exist: bool = False,
path_must_exist: bool = False,
prompt_create_new_file: bool = False,
prompt_override_file: bool = False,
follow_shortcuts: bool = True,
*,
blocking: bool = True,
) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]:
opts = 0
if file_must_exist:
opts += 1
if path_must_exist:
opts += 2
if prompt_create_new_file:
opts += 8
if prompt_override_file:
opts += 8
if not follow_shortcuts:
opts += 32
options = ''
if multi:
options += 'M'
if save_button:
options += 'S'
if opts:
options += str(opts)
args = [options, root, title, filter]
return await self._transport.function_call('AHKFileSelectFile', args, blocking=blocking)

# fmt: off
@overload
async def folder_select_box(self, prompt: str = 'Select Folder', root: str = '', chroot: bool = False, enable_new_directories: bool = True, edit_field: bool = False, new_dialog_style: bool = False) -> Union[None, str]: ...
@overload
async def folder_select_box(self, prompt: str = 'Select Folder', root: str = '', chroot: bool = False, enable_new_directories: bool = True, edit_field: bool = False, new_dialog_style: bool = False, *, blocking: Literal[False]) -> Union[AsyncFutureResult[str], AsyncFutureResult[None]]: ...
@overload
async def folder_select_box(self, prompt: str = 'Select Folder', root: str = '', chroot: bool = False, enable_new_directories: bool = True, edit_field: bool = False, new_dialog_style: bool = False, *, blocking: Literal[True]) -> Union[str, None]: ...
@overload
async def folder_select_box(self, prompt: str = 'Select Folder', root: str = '', chroot: bool = False, enable_new_directories: bool = True, edit_field: bool = False, new_dialog_style: bool = False, *, blocking: bool = True) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]: ...
# fmt: on
async def folder_select_box(
self,
prompt: str = 'Select Folder',
root: str = '',
chroot: bool = False,
enable_new_directories: bool = True,
edit_field: bool = False,
new_dialog_style: bool = False,
*,
blocking: bool = True,
) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]:
if not chroot:
starting_folder = '*'
else:
starting_folder = ''
starting_folder += root
if enable_new_directories:
opts = 1
else:
opts = 0
if edit_field:
opts += 2
if new_dialog_style:
opts += 4
args = [starting_folder, str(opts), prompt]
return await self._transport.function_call('AHKFileSelectFolder', args, blocking=blocking)

async def block_forever(self) -> NoReturn:
"""
Blocks (sleeps) forever. Utility method to prevent script from exiting.
Expand Down
16 changes: 15 additions & 1 deletion ahk/_async/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,24 @@ def result(self, timeout: Optional[float] = None) -> T_SyncFuture:
'AHKControlGetPos',
'AHKControlGetText',
'AHKControlSend',
'AHKFileSelectFile',
'AHKFileSelectFolder',
'AHKGetClipboard',
'AHKGetClipboardAll',
'AHKGetCoordMode',
'AHKGetSendLevel',
'AHKGetTitleMatchMode',
'AHKGetTitleMatchSpeed',
'AHKGetVolume',
'AHKGuiNew',
'AHKImageSearch',
'AHKInputBox',
'AHKKeyState',
'AHKKeyWait',
'AHKMenuTrayIcon',
'AHKMenuTrayShow',
'AHKMenuTrayTip',
'AHKMsgBox',
'AHKMouseClickDrag',
'AHKMouseGetPos',
'AHKMouseMove',
Expand Down Expand Up @@ -591,7 +596,16 @@ async def function_call(self, function_name: Literal['AHKMenuTrayTip'], args: Op
async def function_call(self, function_name: Literal['AHKMenuTrayIcon'], args: Optional[List[str]] = None, *, blocking: bool = True) -> Union[None, AsyncFutureResult[None]]: ...
@overload
async def function_call(self, function_name: Literal['AHKMenuTrayShow'], args: Optional[List[str]] = None, *, blocking: bool = True) -> Union[None, AsyncFutureResult[None]]: ...

@overload
async def function_call(self, function_name: Literal['AHKGuiNew'], args: List[str], *, engine: AsyncAHK) -> str: ...
@overload
async def function_call(self, function_name: Literal['AHKMsgBox'], args: Optional[List[str]] = None, *, blocking: bool = True) -> Union[str, AsyncFutureResult[str]]: ...
@overload
async def function_call(self, function_name: Literal['AHKInputBox'], args: Optional[List[str]] = None, *, blocking: bool = True) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]: ...
@overload
async def function_call(self, function_name: Literal['AHKFileSelectFile'], args: Optional[List[str]] = None, *, blocking: bool = True) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]: ...
@overload
async def function_call(self, function_name: Literal['AHKFileSelectFolder'], args: Optional[List[str]] = None, *, blocking: bool = True) -> Union[str, None, AsyncFutureResult[str], AsyncFutureResult[None]]: ...
# fmt: on

async def function_call(
Expand Down
95 changes: 95 additions & 0 deletions ahk/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2644,6 +2644,101 @@
return FormatNoValueResponse()
}
AHKGuiNew(ByRef command) {
global STRINGRESPONSEMESSAGE
options := command[2]
title := command[3]
Gui, New, %options%, %title%
return FormatResponse(STRINGRESPONSEMESSAGE, hwnd)
}
AHKMsgBox(ByRef command) {
global TIMEOUTRESPONSEMESSAGE
global STRINGRESPONSEMESSAGE
options := command[2]
title := command[3]
text := command[4]
timeout := command[5]
MsgBox,% options, %title%, %text%, %timeout%
IfMsgBox, Yes
ret := FormatResponse(STRINGRESPONSEMESSAGE, "Yes")
IfMsgBox, No
ret := FormatResponse(STRINGRESPONSEMESSAGE, "No")
IfMsgBox, OK
ret := FormatResponse(STRINGRESPONSEMESSAGE, "OK")
IfMsgBox, Cancel
ret := FormatResponse(STRINGRESPONSEMESSAGE, "Cancel")
IfMsgBox, Abort
ret := FormatResponse(STRINGRESPONSEMESSAGE, "Abort")
IfMsgBox, Ignore
ret := FormatResponse(STRINGRESPONSEMESSAGE, "Ignore")
IfMsgBox, Retry
ret := FormatResponse(STRINGRESPONSEMESSAGE, "Retry")
IfMsgBox, Continue
ret := FormatResponse(STRINGRESPONSEMESSAGE, "Continue")
IfMsgBox, TryAgain
ret := FormatResponse(STRINGRESPONSEMESSAGE, "TryAgain")
IfMsgBox, Timeout
ret := FormatResponse(TIMEOUTRESPONSEMESSAGE, "MsgBox timed out")
return ret
}
AHKInputBox(ByRef command) {
global STRINGRESPONSEMESSAGE
global TIMEOUTRESPONSEMESSAGE
title := command[2]
prompt := command[3]
hide := command[4]
width := command[5]
height := command[6]
x := command[7]
y := command[8]
locale := command[9]
timeout := command[10]
default := command[11]
InputBox, output, %title%, %prompt%, %hide%, %width%, %height%, %x%, %y%, %locale%, %timeout%, %default%
if (ErrorLevel = 2) {
ret := FormatResponse(TIMEOUTRESPONSEMESSAGE, "Input box timed out")
} else if (ErrorLevel = 1) {
ret := FormatNoValueResponse()
} else {
ret := FormatResponse(STRINGRESPONSEMESSAGE, output)
}
return ret
}
AHKFileSelectFile(byRef command) {
global STRINGRESPONSEMESSAGE
options := command[2]
root := command[3]
title := command[4]
filter := command[5]
FileSelectFile, output, %options%, %root%, %title%, %filter%
if (ErrorLevel = 1) {
ret := FormatNoValueResponse()
} else {
ret := FormatResponse(STRINGRESPONSEMESSAGE, output)
}
return ret
}
AHKFileSelectFolder(byRef command) {
global STRINGRESPONSEMESSAGE
starting_folder := command[2]
options := command[3]
prompt := command[4]
FileSelectFolder, output, %starting_folder%, %options%, %prompt%
if (ErrorLevel = 1) {
ret := FormatNoValueResponse()
} else {
ret := FormatResponse(STRINGRESPONSEMESSAGE, output)
}
return ret
}
b64decode(ByRef pszString) {
; TODO load DLL globally for performance
; REF: https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptstringtobinaryw
Expand Down
Loading

0 comments on commit cc95ca3

Please sign in to comment.