├── .gitignore ├── MANIFEST.in ├── README.rst ├── cute.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | # PyBuilder 60 | target/ 61 | ### Linux template 62 | *~ 63 | 64 | # KDE directory preferences 65 | .directory 66 | 67 | # Linux trash folder which might appear on any partition or disk 68 | .Trash-* 69 | ### OSX template 70 | .DS_Store 71 | .AppleDouble 72 | .LSOverride 73 | 74 | # Icon must end with two \r 75 | Icon 76 | 77 | # Thumbnails 78 | ._* 79 | 80 | # Files that might appear in the root of a volume 81 | .DocumentRevisions-V100 82 | .fseventsd 83 | .Spotlight-V100 84 | .TemporaryItems 85 | .Trashes 86 | .VolumeIcon.icns 87 | 88 | # Directories potentially created on remote AFP share 89 | .AppleDB 90 | .AppleDesktop 91 | Network Trash Folder 92 | Temporary Items 93 | .apdisk 94 | ### Windows template 95 | # Windows image file caches 96 | Thumbs.db 97 | ehthumbs.db 98 | 99 | # Folder config file 100 | Desktop.ini 101 | 102 | # Recycle Bin used on file shares 103 | $RECYCLE.BIN/ 104 | 105 | # Windows Installer files 106 | *.cab 107 | *.msi 108 | *.msm 109 | *.msp 110 | 111 | # Windows shortcuts 112 | *.lnk 113 | ### JetBrains template 114 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 115 | 116 | *.iml 117 | 118 | ## Directory-based project format: 119 | .idea/ 120 | # if you remove the above rule, at least ignore the following: 121 | 122 | # User-specific stuff: 123 | # .idea/workspace.xml 124 | # .idea/tasks.xml 125 | # .idea/dictionaries 126 | 127 | # Sensitive or high-churn files: 128 | # .idea/dataSources.ids 129 | # .idea/dataSources.xml 130 | # .idea/sqlDataSources.xml 131 | # .idea/dynamic.xml 132 | # .idea/uiDesigner.xml 133 | 134 | # Gradle: 135 | # .idea/gradle.xml 136 | # .idea/libraries 137 | 138 | # Mongo Explorer plugin: 139 | # .idea/mongoSettings.xml 140 | 141 | ## File-based project format: 142 | *.ipr 143 | *.iws 144 | 145 | ## Plugin-specific files: 146 | 147 | # IntelliJ 148 | /out/ 149 | 150 | # mpeltonen/sbt-idea plugin 151 | .idea_modules/ 152 | 153 | # JIRA plugin 154 | atlassian-ide-plugin.xml 155 | 156 | # Crashlytics plugin (for Android Studio and IntelliJ) 157 | com_crashlytics_export_strings.xml 158 | crashlytics.properties 159 | crashlytics-build.properties 160 | 161 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Cute 2 | ==== 3 | 4 | Cross-Qt compatibility module for IDAPython. 5 | 6 | What is it? 7 | ----------- 8 | 9 | Cute is a tiny compatibility module, to allow IDAPython code to use 10 | PySide/Qt4 and PyQt/Qt5 seamlessly. 11 | 12 | Motivation 13 | ---------- 14 | 15 | Cute was originally a part of 16 | `Sark `__ 17 | (``sark.qt``). Since no-one likes dependencies, especially for things 18 | this small, no-one used it. So I decided to release it as a separate 19 | module as well, so that people can just take the file as-is and use it 20 | in their own code. 21 | 22 | Usage 23 | ----- 24 | 25 | Qt Modules 26 | ~~~~~~~~~~ 27 | 28 | Importing ``QtCore``, ``QtGui`` and ``QtWidgets`` works for all Qt 29 | versions. For Qt4, ``QtWidgets`` is an alias for ``QtGui``, so Qt5 code 30 | with ``QtWidgets`` / ``QtGui`` separation will work on Qt4 as well. 31 | 32 | :: 33 | 34 | from cute import QtCure, QtGui, QtWidgets 35 | 36 | Connecting to Signals 37 | ~~~~~~~~~~~~~~~~~~~~~ 38 | 39 | Cute offers a ``cute.connect(...)`` method to mitigate the difference 40 | between Qt4 and Qt5. 41 | 42 | :: 43 | 44 | # Qt4 Code: 45 | QtCore.QObject.connect(my_object, QtCore.SIGNAL('error(QProcess::ProcessError)'), my_callback) 46 | 47 | # Qt5 Code: 48 | my_object.error.connect(my_callback) 49 | 50 | # Cute Code: 51 | cute.connect(my_object, 'error(QProcess::ProcessError)', my_callback) 52 | 53 | The API for disconnecting is the same, just use the 54 | ``cute.disconnect(...)`` function. 55 | 56 | Form to Widget 57 | ~~~~~~~~~~~~~~ 58 | 59 | IDA has 2 APIs for getting the widget associated with a TForm. One for 60 | PyQt and one for PySide. Cute wraps them both in one function. 61 | 62 | :: 63 | 64 | my_widget = cute.form_to_widget(my_tform) 65 | 66 | Which Qt Should I Use? 67 | ~~~~~~~~~~~~~~~~~~~~~~ 68 | 69 | Sometimes, you *do* need to to know the Qt version your code uses. For 70 | those cases, ask the ``use_qt5`` variable. 71 | 72 | :: 73 | 74 | if cute.use_qt5: 75 | print 'Use Qt5' 76 | else: 77 | print 'Use Qt4' 78 | 79 | In a Project 80 | ~~~~~~~~~~~~ 81 | 82 | There are two recommended ways to use Cute: 83 | 84 | #. Copy it into your own project. This way *does* create code duplication, but avoid dependencies. 85 | #. Install it as a package - ``pip install idacute``. 86 | 87 | 88 | Licensing 89 | --------- 90 | 91 | Cute is released under the MIT license, so you are free to use it in any 92 | project whatsoever. 93 | 94 | FAQ 95 | --- 96 | 97 | **Q:** Why did you name it "cute"? 98 | 99 | **A:** For years I was sure "Qt" is pronounced "Q T". This is my effort 100 | to remind myself it is not. 101 | -------------------------------------------------------------------------------- /cute.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Cute - a crossQt compatibility module for IDAPython. 3 | 4 | Feel free to copy this code into your own projects. 5 | 6 | Latest version can be found at https://github.com/tmr232/Cute 7 | 8 | 9 | 10 | The MIT License (MIT) 11 | 12 | Copyright (c) 2015 Tamir Bahar 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | ''' 32 | import os 33 | import sys 34 | 35 | import idaapi 36 | 37 | # This nasty piece of code is here to force the loading of IDA's PySide. 38 | # Without it, Python attempts to load PySide from the site-packages directory, 39 | # and failing, as it does not play nicely with IDA. 40 | old_path = sys.path[:] 41 | try: 42 | ida_python_path = os.path.dirname(idaapi.__file__) 43 | sys.path.insert(0, ida_python_path) 44 | if idaapi.IDA_SDK_VERSION >= 690: 45 | from PyQt5 import QtGui, QtCore, QtWidgets 46 | import sip 47 | 48 | use_qt5 = True 49 | else: 50 | from PySide import QtGui, QtCore 51 | from PySide import QtGui as QtWidgets 52 | 53 | use_qt5 = False 54 | finally: 55 | sys.path = old_path 56 | 57 | 58 | def connect(sender, signal, callback): 59 | '''Connect a signal. 60 | Use this function only in cases where code should work with both Qt5 and Qt4, as it is an ugly hack. 61 | Args: 62 | sender: The Qt object emitting the signal 63 | signal: A string, containing the signal signature (as in Qt4 and PySide) 64 | callback: The function to be called upon receiving the signal 65 | ''' 66 | if use_qt5: 67 | return getattr(sender, signal.split('(', 1)[0]).connect(callback) 68 | else: 69 | return sender.connect(QtCore.SIGNAL(signal), callback) 70 | 71 | def disconnect(sender, signal, callback): 72 | '''Disconnect a signal. 73 | Use this function only in cases where code should work with both Qt5 and Qt4, as it is an ugly hack. 74 | Args: 75 | sender: The Qt object emitting the signal 76 | signal: A string, containing the signal signature (as in Qt4 and PySide) 77 | callback: The function to be called upon receiving the signal 78 | ''' 79 | if use_qt5: 80 | return getattr(sender, signal.split('(', 1)[0]).disconnect(callback) 81 | else: 82 | return sender.disconnect(QtCore.SIGNAL(signal), callback) 83 | 84 | 85 | def form_to_widget(tform): 86 | '''Get the tform's widget. 87 | IDA has two different form-to-widget functions, one for PyQt and one for PySide. 88 | This function uses the relevant one based on the version of IDA you are using. 89 | Args: 90 | tform: The IDA TForm to get the widget from. 91 | ''' 92 | class Ctx(object): 93 | QtGui = QtGui 94 | if use_qt5: 95 | QtWidgets = QtWidgets 96 | sip = sip 97 | 98 | if use_qt5: 99 | return idaapi.PluginForm.FormToPyQtWidget(tform, ctx=Ctx()) 100 | else: 101 | return idaapi.PluginForm.FormToPySideWidget(tform, ctx=Ctx()) 102 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup 4 | 5 | def read(*paths): 6 | """Build a file path from *paths* and return the contents.""" 7 | with open(os.path.join(*paths), 'r') as f: 8 | return f.read() 9 | 10 | 11 | setup( 12 | name='idacute', 13 | version='1.0.1', 14 | py_modules=['cute'], 15 | url='https://github.com/tmr232/Cute', 16 | license='MIT', 17 | author='Tamir Bahar', 18 | author_email='', 19 | description='Cross-Qt compatibility module for IDAPython', 20 | long_description=(read('README.rst')), 21 | ) 22 | --------------------------------------------------------------------------------