1from __future__ import nested_scopes 2 3from _pydev_imps._pydev_saved_modules import threading 4import os 5 6def set_trace_in_qt(): 7 from _pydevd_bundle.pydevd_comm import get_global_debugger 8 debugger = get_global_debugger() 9 if debugger is not None: 10 threading.current_thread() # Create the dummy thread for qt. 11 debugger.enable_tracing() 12 13 14_patched_qt = False 15def patch_qt(qt_support_mode): 16 ''' 17 This method patches qt (PySide, PyQt4, PyQt5) so that we have hooks to set the tracing for QThread. 18 ''' 19 if not qt_support_mode: 20 return 21 22 if qt_support_mode is True or qt_support_mode == 'True': 23 # do not break backward compatibility 24 qt_support_mode = 'auto' 25 26 if qt_support_mode == 'auto': 27 qt_support_mode = os.getenv('PYDEVD_PYQT_MODE', 'auto') 28 29 # Avoid patching more than once 30 global _patched_qt 31 if _patched_qt: 32 return 33 34 _patched_qt = True 35 36 if qt_support_mode == 'auto': 37 38 patch_qt_on_import = None 39 try: 40 import PySide2 # @UnresolvedImport @UnusedImport 41 qt_support_mode = 'pyside2' 42 except: 43 try: 44 import Pyside # @UnresolvedImport @UnusedImport 45 qt_support_mode = 'pyside' 46 except: 47 try: 48 import PyQt5 # @UnresolvedImport @UnusedImport 49 qt_support_mode = 'pyqt5' 50 except: 51 try: 52 import PyQt4 # @UnresolvedImport @UnusedImport 53 qt_support_mode = 'pyqt4' 54 except: 55 return 56 57 if qt_support_mode == 'pyside2': 58 try: 59 import PySide2.QtCore # @UnresolvedImport 60 _internal_patch_qt(PySide2.QtCore, qt_support_mode) 61 except: 62 return 63 64 elif qt_support_mode == 'pyside': 65 try: 66 import PySide.QtCore # @UnresolvedImport 67 _internal_patch_qt(PySide.QtCore, qt_support_mode) 68 except: 69 return 70 71 elif qt_support_mode == 'pyqt5': 72 try: 73 import PyQt5.QtCore # @UnresolvedImport 74 _internal_patch_qt(PyQt5.QtCore) 75 except: 76 return 77 78 elif qt_support_mode == 'pyqt4': 79 # Ok, we have an issue here: 80 # PyDev-452: Selecting PyQT API version using sip.setapi fails in debug mode 81 # http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html 82 # Mostly, if the user uses a different API version (i.e.: v2 instead of v1), 83 # that has to be done before importing PyQt4 modules (PySide/PyQt5 don't have this issue 84 # as they only implements v2). 85 patch_qt_on_import = 'PyQt4' 86 def get_qt_core_module(): 87 import PyQt4.QtCore # @UnresolvedImport 88 return PyQt4.QtCore 89 _patch_import_to_patch_pyqt_on_import(patch_qt_on_import, get_qt_core_module) 90 91 else: 92 raise ValueError('Unexpected qt support mode: %s' % (qt_support_mode,)) 93 94 95def _patch_import_to_patch_pyqt_on_import(patch_qt_on_import, get_qt_core_module): 96 # I don't like this approach very much as we have to patch __import__, but I like even less 97 # asking the user to configure something in the client side... 98 # So, our approach is to patch PyQt4 right before the user tries to import it (at which 99 # point he should've set the sip api version properly already anyways). 100 101 dotted = patch_qt_on_import + '.' 102 original_import = __import__ 103 104 from _pydev_imps._pydev_sys_patch import patch_sys_module, patch_reload, cancel_patches_in_sys_module 105 106 patch_sys_module() 107 patch_reload() 108 109 def patched_import(name, *args, **kwargs): 110 if patch_qt_on_import == name or name.startswith(dotted): 111 builtins.__import__ = original_import 112 cancel_patches_in_sys_module() 113 _internal_patch_qt(get_qt_core_module()) # Patch it only when the user would import the qt module 114 return original_import(name, *args, **kwargs) 115 116 import sys 117 if sys.version_info[0] >= 3: 118 import builtins # Py3 119 else: 120 import __builtin__ as builtins 121 122 builtins.__import__ = patched_import 123 124 125def _internal_patch_qt(QtCore, qt_support_mode='auto'): 126 _original_thread_init = QtCore.QThread.__init__ 127 _original_runnable_init = QtCore.QRunnable.__init__ 128 _original_QThread = QtCore.QThread 129 130 class FuncWrapper: 131 def __init__(self, original): 132 self._original = original 133 134 def __call__(self, *args, **kwargs): 135 set_trace_in_qt() 136 return self._original(*args, **kwargs) 137 138 class StartedSignalWrapper(QtCore.QObject): # Wrapper for the QThread.started signal 139 140 try: 141 _signal = QtCore.Signal() # @UndefinedVariable 142 except: 143 _signal = QtCore.pyqtSignal() # @UndefinedVariable 144 145 def __init__(self, thread, original_started): 146 QtCore.QObject.__init__(self) 147 self.thread = thread 148 self.original_started = original_started 149 if qt_support_mode in ('pyside', 'pyside2'): 150 self._signal = original_started 151 else: 152 self._signal.connect(self._on_call) 153 self.original_started.connect(self._signal) 154 155 def connect(self, func, *args, **kwargs): 156 if qt_support_mode in ('pyside', 'pyside2'): 157 return self._signal.connect(FuncWrapper(func), *args, **kwargs) 158 else: 159 return self._signal.connect(func, *args, **kwargs) 160 161 def disconnect(self, *args, **kwargs): 162 return self._signal.disconnect(*args, **kwargs) 163 164 def emit(self, *args, **kwargs): 165 return self._signal.emit(*args, **kwargs) 166 167 def _on_call(self, *args, **kwargs): 168 set_trace_in_qt() 169 170 class ThreadWrapper(QtCore.QThread): # Wrapper for QThread 171 172 def __init__(self, *args, **kwargs): 173 _original_thread_init(self, *args, **kwargs) 174 175 # In PyQt5 the program hangs when we try to call original run method of QThread class. 176 # So we need to distinguish instances of QThread class and instances of QThread inheritors. 177 if self.__class__.run == _original_QThread.run: 178 self.run = self._exec_run 179 else: 180 self._original_run = self.run 181 self.run = self._new_run 182 self._original_started = self.started 183 self.started = StartedSignalWrapper(self, self.started) 184 185 def _exec_run(self): 186 set_trace_in_qt() 187 self.exec_() 188 return None 189 190 def _new_run(self): 191 set_trace_in_qt() 192 return self._original_run() 193 194 class RunnableWrapper(QtCore.QRunnable): # Wrapper for QRunnable 195 196 def __init__(self, *args): 197 _original_runnable_init(self, *args) 198 199 self._original_run = self.run 200 self.run = self._new_run 201 202 203 def _new_run(self): 204 set_trace_in_qt() 205 return self._original_run() 206 207 QtCore.QThread = ThreadWrapper 208 QtCore.QRunnable = RunnableWrapper 209