_native_win.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. # This module contains all the Windows-specific code to create native
  2. # message boxes using the winapi.
  3. # If you'd like to learn more about calling the winapi functions from
  4. # Python, you can check out my other module "nicewin" to see nicely-documented
  5. # examples. It is at https://github.com/asweigart/nicewin
  6. # The documentation for the MessageBox winapi is at:
  7. # https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-messagebox
  8. from __future__ import annotations
  9. from typing import Optional, Any
  10. import sys, ctypes
  11. import pymsgbox
  12. MB_OK = 0x0
  13. MB_OKCANCEL = 0x1
  14. MB_ABORTRETRYIGNORE = 0x2
  15. MB_YESNOCANCEL = 0x3
  16. MB_YESNO = 0x4
  17. MB_RETRYCANCEL = 0x5
  18. MB_CANCELTRYCONTINUE = 0x6
  19. NO_ICON = 0
  20. STOP = MB_ICONHAND = MB_ICONSTOP = MB_ICONERRPR = 0x10
  21. QUESTION = MB_ICONQUESTION = 0x20
  22. WARNING = MB_ICONEXCLAIMATION = 0x30
  23. INFO = MB_ICONASTERISK = MB_ICONINFOMRAITON = 0x40
  24. MB_DEFAULTBUTTON1 = 0x0
  25. MB_DEFAULTBUTTON2 = 0x100
  26. MB_DEFAULTBUTTON3 = 0x200
  27. MB_DEFAULTBUTTON4 = 0x300
  28. MB_SETFOREGROUND = 0x10000
  29. MB_TOPMOST = 0x40000
  30. IDABORT = 0x3
  31. IDCANCEL = 0x2
  32. IDCONTINUE = 0x11
  33. IDIGNORE = 0x5
  34. IDNO = 0x7
  35. IDOK = 0x1
  36. IDRETRY = 0x4
  37. IDTRYAGAIN = 0x10
  38. IDYES = 0x6
  39. runningOnPython2 = sys.version_info[0] == 2
  40. if runningOnPython2:
  41. messageBoxFunc = ctypes.windll.user32.MessageBoxA # pyright: ignore[reportAttributeAccessIssue]
  42. else: # Python 3 functions.
  43. messageBoxFunc = ctypes.windll.user32.MessageBoxW # pyright: ignore[reportAttributeAccessIssue]
  44. def alert(
  45. text: str = "",
  46. title: str = "",
  47. button: str = pymsgbox.OK_TEXT,
  48. root: Optional[Any] = None,
  49. timeout: Optional[int] = None,
  50. icon: int = NO_ICON,
  51. _tkinter: bool = False,
  52. ) -> str:
  53. """Displays a simple message box with text and a single OK button. Returns the text of the button clicked on."""
  54. text = str(text)
  55. if (_tkinter) or (timeout is not None) or (button != pymsgbox.OK_TEXT):
  56. # Timeouts are not supported by Windows message boxes.
  57. # Call the original tkinter alert function, not this native one:
  58. return pymsgbox._alertTkinter(text, title, button, root, timeout)
  59. messageBoxFunc(0, text, title, MB_OK | MB_SETFOREGROUND | MB_TOPMOST | icon)
  60. return button
  61. def confirm(
  62. text: str = "",
  63. title: str = "",
  64. buttons: tuple = (pymsgbox.OK_TEXT, pymsgbox.CANCEL_TEXT),
  65. root: Optional[Any] = None,
  66. timeout: Optional[int] = None,
  67. icon: int = QUESTION,
  68. _tkinter: bool = False,
  69. ) -> Optional[str]:
  70. """Displays a message box with OK and Cancel buttons. Number and text of buttons can be customized. Returns the text of the button clicked on, or None if the dialog box was closed."""
  71. text = str(text)
  72. buttonFlag = None
  73. if len(buttons) == 1:
  74. if buttons[0] == pymsgbox.OK_TEXT:
  75. buttonFlag = MB_OK
  76. elif len(buttons) == 2:
  77. if buttons[0] == pymsgbox.OK_TEXT and buttons[1] == pymsgbox.CANCEL_TEXT:
  78. buttonFlag = MB_OKCANCEL
  79. elif buttons[0] == pymsgbox.YES_TEXT and buttons[1] == pymsgbox.NO_TEXT:
  80. buttonFlag = MB_YESNO
  81. elif buttons[0] == pymsgbox.RETRY_TEXT and buttons[1] == pymsgbox.CANCEL_TEXT:
  82. buttonFlag = MB_RETRYCANCEL
  83. elif len(buttons) == 3:
  84. if (
  85. buttons[0] == pymsgbox.ABORT_TEXT
  86. and buttons[1] == pymsgbox.RETRY_TEXT
  87. and buttons[2] == pymsgbox.IGNORE_TEXT
  88. ):
  89. buttonFlag = MB_ABORTRETRYIGNORE
  90. elif (
  91. buttons[0] == pymsgbox.CANCEL_TEXT
  92. and buttons[1] == pymsgbox.TRY_AGAIN_TEXT
  93. and buttons[2] == pymsgbox.CONTINUE_TEXT
  94. ):
  95. buttonFlag = MB_CANCELTRYCONTINUE
  96. elif (
  97. buttons[0] == pymsgbox.YES_TEXT
  98. and buttons[1] == pymsgbox.NO_TEXT
  99. and buttons[2] == pymsgbox.CANCEL_TEXT
  100. ):
  101. buttonFlag = MB_YESNOCANCEL
  102. if (_tkinter) or (timeout is not None) or (buttonFlag is None):
  103. # Call the original tkinter confirm() function, not this native one:
  104. return pymsgbox._confirmTkinter(text, title, buttons, root, timeout)
  105. retVal = messageBoxFunc(
  106. 0, text, title, buttonFlag | MB_SETFOREGROUND | MB_TOPMOST | icon
  107. )
  108. if retVal == IDOK or len(buttons) == 1:
  109. return pymsgbox.OK_TEXT
  110. elif retVal == IDCANCEL:
  111. return pymsgbox.CANCEL_TEXT
  112. elif retVal == IDYES:
  113. return pymsgbox.YES_TEXT
  114. elif retVal == IDNO:
  115. return pymsgbox.NO_TEXT
  116. elif retVal == IDTRYAGAIN:
  117. return pymsgbox.RETRY_TEXT
  118. elif retVal == IDRETRY:
  119. return pymsgbox.RETRY_TEXT
  120. elif retVal == IDIGNORE:
  121. return pymsgbox.IGNORE_TEXT
  122. elif retVal == IDCONTINUE:
  123. return pymsgbox.CONTINUE_TEXT
  124. elif retVal == IDABORT:
  125. return pymsgbox.ABORT_TEXT
  126. else:
  127. assert False, "Unexpected return value from MessageBox: %s" % (retVal)
  128. '''
  129. def prompt(text='', title='' , default=''):
  130. """Displays a message box with text input, and OK & Cancel buttons. Returns the text entered, or None if Cancel was clicked."""
  131. pass
  132. def password(text='', title='', default='', mask='*'):
  133. """Displays a message box with text input, and OK & Cancel buttons. Typed characters appear as *. Returns the text entered, or None if Cancel was clicked."""
  134. pass
  135. '''