Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CallTips.py 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. # CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that
  2. # displays parameter information as you open parens.
  3. import inspect
  4. import string
  5. import sys
  6. import traceback
  7. class CallTips:
  8. menudefs = []
  9. keydefs = {
  10. "<<paren-open>>": ["<Key-parenleft>"],
  11. "<<paren-close>>": ["<Key-parenright>"],
  12. "<<check-calltip-cancel>>": ["<KeyRelease>"],
  13. "<<calltip-cancel>>": ["<ButtonPress>", "<Key-Escape>"],
  14. }
  15. windows_keydefs = {}
  16. unix_keydefs = {}
  17. def __init__(self, editwin):
  18. self.editwin = editwin
  19. self.text = editwin.text
  20. self.calltip = None
  21. if hasattr(self.text, "make_calltip_window"):
  22. self._make_calltip_window = self.text.make_calltip_window
  23. else:
  24. self._make_calltip_window = self._make_tk_calltip_window
  25. def close(self):
  26. self._make_calltip_window = None
  27. # Makes a Tk based calltip window. Used by IDLE, but not Pythonwin.
  28. # See __init__ above for how this is used.
  29. def _make_tk_calltip_window(self):
  30. import CallTipWindow
  31. return CallTipWindow.CallTip(self.text)
  32. def _remove_calltip_window(self):
  33. if self.calltip:
  34. self.calltip.hidetip()
  35. self.calltip = None
  36. def paren_open_event(self, event):
  37. self._remove_calltip_window()
  38. arg_text = get_arg_text(self.get_object_at_cursor())
  39. if arg_text:
  40. self.calltip_start = self.text.index("insert")
  41. self.calltip = self._make_calltip_window()
  42. self.calltip.showtip(arg_text)
  43. return "" # so the event is handled normally.
  44. def paren_close_event(self, event):
  45. # Now just hides, but later we should check if other
  46. # paren'd expressions remain open.
  47. self._remove_calltip_window()
  48. return "" # so the event is handled normally.
  49. def check_calltip_cancel_event(self, event):
  50. if self.calltip:
  51. # If we have moved before the start of the calltip,
  52. # or off the calltip line, then cancel the tip.
  53. # (Later need to be smarter about multi-line, etc)
  54. if self.text.compare(
  55. "insert", "<=", self.calltip_start
  56. ) or self.text.compare("insert", ">", self.calltip_start + " lineend"):
  57. self._remove_calltip_window()
  58. return "" # so the event is handled normally.
  59. def calltip_cancel_event(self, event):
  60. self._remove_calltip_window()
  61. return "" # so the event is handled normally.
  62. def get_object_at_cursor(
  63. self,
  64. wordchars="._"
  65. + string.ascii_uppercase
  66. + string.ascii_lowercase
  67. + string.digits,
  68. ):
  69. # XXX - This needs to be moved to a better place
  70. # so the "." attribute lookup code can also use it.
  71. text = self.text
  72. chars = text.get("insert linestart", "insert")
  73. i = len(chars)
  74. while i and chars[i - 1] in wordchars:
  75. i = i - 1
  76. word = chars[i:]
  77. if word:
  78. # How is this for a hack!
  79. import __main__
  80. namespace = sys.modules.copy()
  81. namespace.update(__main__.__dict__)
  82. try:
  83. return eval(word, namespace)
  84. except:
  85. pass
  86. return None # Can't find an object.
  87. def _find_constructor(class_ob):
  88. # Given a class object, return a function object used for the
  89. # constructor (ie, __init__() ) or None if we can't find one.
  90. try:
  91. return class_ob.__init__
  92. except AttributeError:
  93. for base in class_ob.__bases__:
  94. rc = _find_constructor(base)
  95. if rc is not None:
  96. return rc
  97. return None
  98. def get_arg_text(ob):
  99. # Get a string describing the arguments for the given object.
  100. argText = ""
  101. if ob is not None:
  102. if inspect.isclass(ob):
  103. # Look for the highest __init__ in the class chain.
  104. fob = _find_constructor(ob)
  105. if fob is None:
  106. fob = lambda: None
  107. else:
  108. fob = ob
  109. if inspect.isfunction(fob) or inspect.ismethod(fob):
  110. try:
  111. argText = str(inspect.signature(fob))
  112. except:
  113. print("Failed to format the args")
  114. traceback.print_exc()
  115. # See if we can use the docstring
  116. if hasattr(ob, "__doc__"):
  117. doc = ob.__doc__
  118. try:
  119. doc = doc.strip()
  120. pos = doc.find("\n")
  121. except AttributeError:
  122. ## New style classes may have __doc__ slot without actually
  123. ## having a string assigned to it
  124. pass
  125. else:
  126. if pos < 0 or pos > 70:
  127. pos = 70
  128. if argText:
  129. argText = argText + "\n"
  130. argText = argText + doc[:pos]
  131. return argText
  132. #################################################
  133. #
  134. # Test code
  135. #
  136. if __name__ == "__main__":
  137. def t1():
  138. "()"
  139. def t2(a, b=None):
  140. "(a, b=None)"
  141. def t3(a, *args):
  142. "(a, *args)"
  143. def t4(*args):
  144. "(*args)"
  145. def t5(a, *args):
  146. "(a, *args)"
  147. def t6(a, b=None, *args, **kw):
  148. "(a, b=None, *args, **kw)"
  149. class TC:
  150. "(self, a=None, *b)"
  151. def __init__(self, a=None, *b):
  152. "(self, a=None, *b)"
  153. def t1(self):
  154. "(self)"
  155. def t2(self, a, b=None):
  156. "(self, a, b=None)"
  157. def t3(self, a, *args):
  158. "(self, a, *args)"
  159. def t4(self, *args):
  160. "(self, *args)"
  161. def t5(self, a, *args):
  162. "(self, a, *args)"
  163. def t6(self, a, b=None, *args, **kw):
  164. "(self, a, b=None, *args, **kw)"
  165. def test(tests):
  166. failed = []
  167. for t in tests:
  168. expected = t.__doc__ + "\n" + t.__doc__
  169. if get_arg_text(t) != expected:
  170. failed.append(t)
  171. print(
  172. "%s - expected %s, but got %s"
  173. % (t, repr(expected), repr(get_arg_text(t)))
  174. )
  175. print("%d of %d tests failed" % (len(failed), len(tests)))
  176. tc = TC()
  177. tests = t1, t2, t3, t4, t5, t6, TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6
  178. test(tests)