├── .gitignore ├── .no-sublime-package ├── Context.sublime-menu ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── Default.sublime-commands ├── LICENSE.txt ├── Main.sublime-menu ├── README.markdown ├── Side Bar.sublime-menu ├── Snippets ├── Connect-New.sublime-snippet ├── Connect-Old.sublime-snippet ├── Signal.sublime-snippet ├── Slot.sublime-snippet ├── qml_alias.sublime-snippet ├── qml_anchors.sublime-snippet ├── qml_anchors_all.sublime-snippet ├── qml_anchors_centerin.sublime-snippet ├── qml_anchors_fill.sublime-snippet ├── qml_anchors_horizontal.sublime-snippet ├── qml_anchors_margins.sublime-snippet ├── qml_anchors_vertical.sublime-snippet ├── qml_color.sublime-snippet ├── qml_component_on_completed.sublime-snippet ├── qml_connections.sublime-snippet ├── qml_fun.sublime-snippet ├── qml_layout_all.sublime-snippet ├── qml_layout_height.sublime-snippet ├── qml_layout_width.sublime-snippet ├── qml_mousearea.sublime-snippet ├── qml_property.sublime-snippet ├── qml_property_default.sublime-snippet ├── qml_property_readonly.sublime-snippet ├── qml_qtobject.sublime-snippet ├── qml_rect.sublime-snippet ├── qml_signal.sublime-snippet └── qml_stack_status.sublime-snippet ├── SublimePySide.sublime-settings ├── Support ├── Comments.tmPreferences ├── QML.JSON-tmLanguage ├── QML.sublime-build └── QML.tmLanguage ├── XML.sublime-settings ├── converter ├── __init__.py ├── base.py ├── parser.py ├── pyqt2pyside.py └── pyside2pyqt.py ├── data ├── designer │ ├── templates.json │ └── templates │ │ ├── dialog_with_buttons_bottom.ui │ │ ├── dialog_with_buttons_right.ui │ │ ├── dialog_without_buttons.ui │ │ ├── main_window.ui │ │ └── widget.ui └── templates │ ├── qt_console_application │ └── main.py │ ├── qt_gui_application │ ├── application.py │ ├── application.qrc │ ├── application_rc.py │ └── images │ │ ├── copy.png │ │ ├── cut.png │ │ ├── new.png │ │ ├── open.png │ │ ├── paste.png │ │ └── save.png │ ├── qt_quick_application │ ├── main.py │ └── view.qml │ ├── qt_quick_ui │ ├── main.qml │ └── quickui.qmlproject │ ├── qt_unit_test │ └── test_main.py │ ├── template.sublime-project │ └── templates.lst └── sublime_pyside.py /.gitignore: -------------------------------------------------------------------------------- 1 | .ropeproject/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /.no-sublime-package: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/.no-sublime-package -------------------------------------------------------------------------------- /Context.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "PySide", 4 | "id": "pyside_commands", 5 | "children": 6 | [ 7 | { 8 | "caption": "Create Project", 9 | "command": "create_qt_project" 10 | }, 11 | { 12 | "command": "open_file_in_designer", 13 | "caption": "Open file with Qt Designer" 14 | }, 15 | { 16 | "caption": "Create new Qt Designer file from template", 17 | "command": "new_dialog" 18 | }, 19 | { 20 | "caption": "Open file with Qt Linguist", 21 | "command": "open_in_linguist" 22 | }, 23 | { 24 | "caption": "Compile resource file", 25 | "command": "compile_resource" 26 | }, 27 | { 28 | "caption": "Preview UI file", 29 | "command": "preview_ui" 30 | }, 31 | { 32 | "caption": "Compile UI", 33 | "command": "compile_ui" 34 | }, 35 | { 36 | "caption": "Convert to PyQt4 Syntax", 37 | "command": "convert_py_side2_py_qt4" 38 | }, 39 | { 40 | "caption": "Convert to PySide Syntax", 41 | "command": "convert_py_qt42_py_side" 42 | } 43 | ] 44 | } 45 | ] -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+shift+q"], "command": "create_qt_project" }, 3 | { "keys": ["ctrl+shift+c", "ctrl+shift+q"], "command": "convert_py_side2_py_qt4"}, 4 | { "keys": ["ctrl+shift+c", "ctrl+shift+p"], "command": "convert_py_qt42_py_side"} 5 | ] 6 | -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+super+q"], "command": "create_qt_project" }, 3 | { "keys": ["ctrl+super+c", "ctrl+super+q"], "command": "convert_py_side2_py_qt4"}, 4 | { "keys": ["ctrl+super+c", "ctrl+super+p"], "command": "convert_py_qt42_py_side"} 5 | ] 6 | -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+alt+q"], "command": "create_qt_project" }, 3 | { "keys": ["ctrl+shift+c", "ctrl+shift+q"], "command": "convert_py_side2_py_qt4"}, 4 | { "keys": ["ctrl+shift+c", "ctrl+shift+p"], "command": "convert_py_qt42_py_side"} 5 | ] 6 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "SublimePySide: Create new Qt Project from template", 4 | "command": "create_qt_project" 5 | }, 6 | { 7 | "caption": "SublimePySide: Convert PyQt4 to PySide syntax", 8 | "command": "convert_py_qt42_py_side" 9 | }, 10 | { 11 | "caption": "SublimePySide: Convert PySide to PyQt4 syntax", 12 | "command": "convert_py_side2_py_qt4" 13 | }, 14 | { 15 | "caption": "SublimePySide: Open file with Qt Designer", 16 | "command": "open_file_in_designer" 17 | }, 18 | { 19 | "caption": "SublimePySide: Create new Qt Designer file from template", 20 | "command": "new_dialog" 21 | }, 22 | { 23 | "caption": "SublimePySide: Open QDBusViewer", 24 | "command": "open_qdbusviewer" 25 | }, 26 | { 27 | "caption": "SublimePySide: Open Qt Linguist", 28 | "command": "open_linguist" 29 | }, 30 | { 31 | "caption": "SublimePySide: Open file with Qt Linguist", 32 | "command": "open_in_linguist" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "tools", 4 | "children": 5 | [ 6 | { 7 | "caption": "PySide Qt", 8 | "id": "pyside", 9 | "children": 10 | [ 11 | { 12 | "caption": "Create Project", 13 | "command": "create_qt_project" 14 | }, 15 | { 16 | "caption": "Convert to PySide Syntax", 17 | "command": "convert_py_qt42_py_side" 18 | }, 19 | { 20 | "caption": "Convert to PyQt4 Syntax", 21 | "command": "convert_py_side2_py_qt4" 22 | }, 23 | { 24 | "caption": "Open file with Qt Designer", 25 | "command": "open_file_in_designer" 26 | }, 27 | { 28 | "caption": "SublimePySide: Create new Qt Designer file from template", 29 | "command": "new_dialog" 30 | }, 31 | { 32 | "caption": "SublimePySide: Open Qt Linguist", 33 | "command": "open_linguist" 34 | }, 35 | { 36 | "caption": "SublimePySide: Open file with Qt Linguist", 37 | "command": "open_in_linguist" 38 | }, 39 | { 40 | "caption": "SublimePySide: Open QDBusViewer application", 41 | "command": "open_qdbusviewer" 42 | } 43 | ] 44 | } 45 | ] 46 | }, 47 | { 48 | 49 | "id": "preferences", 50 | "children": 51 | [ 52 | { 53 | "caption": "Package Settings", 54 | "mnemonic": "P", 55 | "id": "package-settings", 56 | "children": 57 | [ 58 | { 59 | "caption": "SublimePySide", 60 | "children": 61 | [ 62 | { 63 | "command": "open_file", 64 | "args": {"file": "${packages}/PySide/README.markdown"}, 65 | "caption": "README" 66 | }, 67 | { "caption": "-" }, 68 | { 69 | "command": "open_file", 70 | "args": {"file": "${packages}/PySide/SublimePySide.sublime-settings"}, 71 | "caption": "Settings - Default" 72 | }, 73 | { 74 | "command": "open_file", 75 | "args": {"file": "${packages}/User/SublimePySide.sublime-settings"}, 76 | "caption": "Settings – User" 77 | }, 78 | { 79 | "command": "open_file_settings", 80 | "caption": "Settings – Syntax Specific – User" 81 | }, 82 | { "caption": "-" }, 83 | { 84 | "command": "open_file", 85 | "args": { 86 | "file": "${packages}/PySide/Default (OSX).sublime-keymap", 87 | "platform": "OSX" 88 | }, 89 | "caption": "Key Bindings – Default" 90 | }, 91 | { 92 | "command": "open_file", 93 | "args": { 94 | "file": "${packages}/PySide/Default (Linux).sublime-keymap", 95 | "platform": "Linux" 96 | }, 97 | "caption": "Key Bindings – Default" 98 | }, 99 | { 100 | "command": "open_file", 101 | "args": { 102 | "file": "${packages}/PySide/Default (Windows).sublime-keymap", 103 | "platform": "Windows" 104 | }, 105 | "caption": "Key Bindings – Default" 106 | }, 107 | { 108 | "command": "open_file", 109 | "args": { 110 | "file": "${packages}/User/Default (OSX).sublime-keymap", 111 | "platform": "OSX" 112 | }, 113 | "caption": "Key Bindings – User" 114 | }, 115 | { 116 | "command": "open_file", 117 | "args": { 118 | "file": "${packages}/User/Default (Linux).sublime-keymap", 119 | "platform": "Linux" 120 | }, 121 | "caption": "Key Bindings – User" 122 | }, 123 | { 124 | "command": "open_file", 125 | "args": { 126 | "file": "${packages}/User/Default (Windows).sublime-keymap", 127 | "platform": "Windows" 128 | }, 129 | "caption": "Key Bindings – User" 130 | }, 131 | { "caption": "-" } 132 | ] 133 | } 134 | ] 135 | } 136 | ] 137 | } 138 | 139 | ] 140 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | **Sublime PySide** 2 | ================ 3 | 4 | status: beta 5 | 6 | Overview 7 | ======== 8 | 9 | Sublime PySide adds Qt (PySide and PyQt4) support for Sublime Text 2 and Sublime Text 3 on Python. 10 | 11 | Python support is build for PySide and PyQt4 as well. This has been tested on Linux and Mac OSX 12 | 13 | **Sublime Text 3**: This plugin works on Sublime Text 3 as well as Sublime Text 2 you should only install it from Package Control as usual. 14 | 15 | Copyright (C) 2012 - 2013 Oscar Campos 16 | 17 | **WARNING**: SublimeRope features doesn't work in Sublime Text 3 but you can use [Anaconda](https://github.com/DamnWidget/anaconda) to get full auto completion. 18 | 19 | 20 | Getting Started 21 | --------------- 22 | 23 | Unzip / git clone the SublimePySide directory into your ST2's Packages directory. To create a new PySide Qt project just use your Operating System keybindings: 24 | 25 | ctrl+shift+q on Linux 26 | ctrl+super+q on Mac OSX 27 | ctrl+alt+q on Windows 28 | 29 | Then select the type of project you want to create and answer the questions. 30 | 31 | You can also use the Tools menu at the toolbar to create a new project. You can configure SublimePySide to always use PySide or PyQt4 in the plugin settings file or just let it asks you when you generate a new project. 32 | 33 | To convert PySide to PyQt4 syntax you can use the keybindings: 34 | 35 | ctrl+shift+c, ctrl+shift+q on Linux 36 | ctrl+super+c, ctrl+super+q on Mac OSX 37 | ctrl+shift+c, ctrl+shift+q on Windows 38 | 39 | To convert PyQt4 to PySide syntax you can use the keybindings: 40 | 41 | ctrl+shift+c, ctrl+shift+p on Linux 42 | ctrl+super+c, ctrl+super+p on Mac OSX 43 | ctrl+shift+c, ctrl+shift+p on Windows 44 | 45 | 46 | **NOTES**: Conversion from PyQt4 API 1 QVariant toWhatever methods to PySide is not automatic yet so maybe you should edit your code by hand after conversion. PySide only converts to PyQt4 API 2. 47 | 48 | 49 | **IMPORTANT**: This plugin use SublimeRope if installed to generate Rope projects in an automatic way. Note that this behaviour is only true in Sublime Text 2, in Sublime Text 3 you can use [Anaconda](https://github.com/DamnWidget/anaconda) to get full autocompletion. 50 | 51 | Features 52 | ---------- 53 | 54 | PySide features are describe below: 55 | 56 | #### Syntax Helpers 57 | 58 | * QML file syntax highligth 59 | * QMLProject file syntax highlight 60 | * QML snippets 61 | * PySide and PyQt4 project creation 62 | * PySide and PyQt4 autocompletion via SublimeRope 63 | * PySide to PyQt4 syntax conversion 64 | * PyQt4 to PySide syntax conversion 65 | 66 | #### Qt Designer related 67 | 68 | * Open ui files with Qt Designer if installed (and it's path is configured) 69 | * Create new UI files for Qt Designer and open it automatically 70 | * Compile UI (available as side bar and context menus) 71 | * Preview UI (available as context menu) 72 | 73 | #### Qt Linguist and friends 74 | 75 | * Open Qt Linguist from Sublime Text 76 | * Open TS files that we have already open in Sublime Text with Qt Linguist (it only works with TS or QM files) 77 | * Generate project (by Qt project file or by python sources) linguist TS files (available as side bar context menu) 78 | 79 | #### Other Tools 80 | 81 | * Compile resource file with pyside-rcc (available in context and side bar menus) 82 | * Open QDBusViewer from Sublime Text 83 | 84 | Supported Templates 85 | -------------------- 86 | 87 | * Qt Quick Application (Python + QML) 88 | * Qt Quick UI (Pure QML) 89 | * Qt Gui Application (Pure Python) 90 | * Qt Console Application (Pure Python) 91 | * Qt Unit Test (dumb skeleton) 92 | 93 | License: 94 | -------- 95 | This program is free software; you can redistribute it and/or modify 96 | it under the terms of the GNU General Public License as published by 97 | the Free Software Foundation; either version 2 of the License, or 98 | (at your option) any later version. 99 | 100 | This program is distributed in the hope that it will be useful, 101 | but WITHOUT ANY WARRANTY; without even the implied warranty of 102 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 103 | GNU General Public License for more details. 104 | 105 | You should have received a copy of the GNU General Public License along 106 | with this program; if not, write to the Free Software Foundation, Inc., 107 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 108 | 109 | Have a look at "LICENSE.txt" file for more information. 110 | 111 | Donate 112 | ------ 113 | 114 | [][0] 115 | 116 | [0]: http://flattr.com/thing/1765346/ 117 | -------------------------------------------------------------------------------- /Side Bar.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "PySide", 4 | "id": "pyside_commands", 5 | "children": 6 | [ 7 | { 8 | "caption": "Create Project", 9 | "command": "create_qt_project" 10 | }, 11 | { 12 | "caption": "Open file with Qt Designer", 13 | "command": "open_file_in_designer" 14 | }, 15 | { 16 | "caption": "Create new Qt Designer file from template", 17 | "command": "new_dialog", 18 | "args": {"dirs": []} 19 | }, 20 | { 21 | "caption": "Open file with Qt Linguist", 22 | "command": "open_in_linguist" 23 | }, 24 | { 25 | "caption": "Run lupdate to generate TS files", 26 | "command": "generate_translations", 27 | "args": {"files": [], "dirs": []} 28 | }, 29 | { 30 | "caption": "Compile resource file", 31 | "command": "compile_resource", 32 | "args": {"files": []} 33 | }, 34 | { 35 | "caption": "Compile UI", 36 | "command": "compile_ui", 37 | "args": {"files": []} 38 | }, 39 | { 40 | "caption": "Convert to PyQt4 Syntax", 41 | "command": "convert_py_side2_py_qt4" 42 | }, 43 | { 44 | "caption": "Convert to PySide Syntax", 45 | "command": "convert_py_qt42_py_side" 46 | } 47 | ] 48 | } 49 | ] -------------------------------------------------------------------------------- /Snippets/Connect-New.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | connect 4 | source.python 5 | Signal/Slot >> New Syntax 6 | 7 | -------------------------------------------------------------------------------- /Snippets/Connect-Old.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | connect 4 | source.python 5 | Signal/Slot >> Odl Syntax 6 | 7 | -------------------------------------------------------------------------------- /Snippets/Signal.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | signal 4 | source.python 5 | QtCore.Signal(...) 6 | 7 | -------------------------------------------------------------------------------- /Snippets/Slot.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | slot 4 | source.python 5 | @QtCore.Slot 6 | 7 | -------------------------------------------------------------------------------- /Snippets/qml_alias.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | alias 6 | source.qml 7 | QML property alias 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_anchors.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | anchors 6 | source.qml 7 | (simple) 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_anchors_all.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 8 | anchors 9 | source.qml 10 | (all sides) 11 | 12 | -------------------------------------------------------------------------------- /Snippets/qml_anchors_centerin.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | anchors.centerIn 6 | source.qml 7 | centerIn 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_anchors_fill.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | anchors.fill 6 | source.qml 7 | fill 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_anchors_horizontal.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | anchors.horizontalCenter 6 | source.qml 7 | horizontal 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_anchors_margins.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | anchors.margins 6 | source.qml 7 | margins 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_anchors_vertical.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | anchors.verticalCenter 6 | source.qml 7 | vertical 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_color.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | color 6 | source.qml 7 | #... 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_component_on_completed.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | component 6 | source.qml 7 | Component.onCompleted{} 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_connections.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 8 | connections 9 | source.qml 10 | Connections{} 11 | 12 | -------------------------------------------------------------------------------- /Snippets/qml_fun.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | fun 6 | source.qml 7 | function{} 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_layout_all.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 6 | layout 7 | source.qml 8 | (all) 9 | 10 | -------------------------------------------------------------------------------- /Snippets/qml_layout_height.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | layout.fillHeight 6 | source.qml 7 | height 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_layout_width.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | layout.fillWidth 6 | source.qml 7 | width 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_mousearea.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 13 | mouse 14 | source.qml 15 | MouseArea{} 16 | 17 | -------------------------------------------------------------------------------- /Snippets/qml_property.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | property 6 | source.qml 7 | QML property 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_property_default.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | default property 6 | source.qml 7 | QML default property 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_property_readonly.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | readonly property 6 | source.qml 7 | QML readonly property 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_qtobject.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 7 | obj 8 | source.qml 9 | QtObject{} 10 | 11 | -------------------------------------------------------------------------------- /Snippets/qml_rect.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 9 | rectangle 10 | source.qml 11 | Rectangle{} 12 | 13 | -------------------------------------------------------------------------------- /Snippets/qml_signal.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 5 | signal 6 | source.qml 7 | QML signal 8 | 9 | -------------------------------------------------------------------------------- /Snippets/qml_stack_status.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 10 | stack.onStatusChanged 11 | source.qml 12 | onStatusChanged{} 13 | 14 | -------------------------------------------------------------------------------- /SublimePySide.sublime-settings: -------------------------------------------------------------------------------- 1 | /* 2 | SublimePySide default settings 3 | */ 4 | { 5 | /* 6 | Sets the path to PySide Tools 7 | */ 8 | "sublimepyside_tools_map": 9 | { 10 | "uic": "/usr/bin/pyside-uic", 11 | "lupdate": "/usr/bin/pyside-lupdate", 12 | "rcc": "/usr/bin/pyside-rcc" 13 | }, 14 | 15 | /* 16 | Sets the path to Qt Tools 17 | */ 18 | "sublimepyside_qt_tools_map": 19 | { 20 | "linguist": "/usr/bin/linguist", 21 | "designer": "/usr/bin/designer", 22 | "qdbusviewer": "/usr/bin/qdbusviewer" 23 | }, 24 | 25 | /* 26 | Sets the package name (do not edit) 27 | */ 28 | "sublimepyside_package": "PySide", 29 | 30 | /* 31 | Sets the data directory (do not edit) 32 | */ 33 | "sublimepyside_data_dir": "data", 34 | 35 | /* 36 | Sets the Qt Library to use 37 | */ 38 | "sublimepyside_library": "PySide", 39 | 40 | /* 41 | If is set to true, SublimePySide should ask about which libraary to use 42 | PySide or PyQt4 43 | */ 44 | "sublimepyside_library_ask": true, 45 | 46 | /* 47 | Options for RCC command 48 | */ 49 | "sublimepyside_rcc_options": { 50 | /* 51 | if output_file is set as same_rc, SublimePySide will automatically 52 | save the output to a file called _rc.py it will 53 | ask you for a name otherwise 54 | */ 55 | "output_file": "same_rc", 56 | 57 | /* 58 | compression_level could be a valid value between 0 and 9, de default 59 | is just -1 which specifies zlib's default compression 60 | 61 | for more information look at: http://qt-project.org/doc/qt-4.8/qbytearray.html#qCompress 62 | */ 63 | "compression_level": -1, 64 | 65 | /* 66 | when no_compress is set as true, rcc doesn't compress files at all 67 | */ 68 | "no_compress": false, 69 | 70 | /* 71 | when root_path is set, all the resources paths are prefixed with it 72 | */ 73 | "root_path": "" 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Support/Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Comments 7 | scope 8 | source.qml 9 | settings 10 | 11 | shellVariables 12 | 13 | 14 | name 15 | TM_COMMENT_START 16 | value 17 | // 18 | 19 | 20 | name 21 | TM_COMMENT_START_2 22 | value 23 | /* 24 | 25 | 26 | name 27 | TM_COMMENT_END_2 28 | value 29 | */ 30 | 31 | 32 | name 33 | TM_COMMENT_DISABLE_INDENT_2 34 | value 35 | yes 36 | 37 | 38 | 39 | uuid 40 | 8C784EC4-7566-4FE3-B3D3-62409EBBB9EC 41 | 42 | 43 | -------------------------------------------------------------------------------- /Support/QML.JSON-tmLanguage: -------------------------------------------------------------------------------- 1 | // [PackageDev] target_format: plist, ext: tmLanguage 2 | { 3 | "name": "QML", 4 | "scopeName": "source.qml", 5 | "fileTypes": ["qml", "qmlproject"], 6 | "uuid": "13a281e0-0507-45b4-bb6c-a57177630f10", 7 | 8 | "patterns": [ 9 | { 10 | "name": "comment.block.documentation.qml", 11 | "begin": "/\\*(?!/)", 12 | "end": "\\*/", 13 | "comment": "Block comment." 14 | }, 15 | 16 | { 17 | "name": "comment.line.double-slash.qml", 18 | "match": "//.*$", 19 | "comment": "Line comment." 20 | }, 21 | 22 | { 23 | "name": "meta.import.qml", 24 | "begin": "\\b(import)\\s+", 25 | "beginCaptures": { 26 | "1": { "name": "keyword.other.import.qml" } 27 | }, 28 | "patterns": [ 29 | { 30 | "name": "meta.import.namespace.qml", 31 | "match": "([\\w\\d\\.]+)\\s+(\\d+\\.\\d+)(?:\\s+(as)\\s+([A-Z][\\w\\d]*))?", 32 | "captures": { 33 | "1": { "name": "entity.name.class.qml" }, 34 | "2": { "name": "constant.numeric.qml" }, 35 | "3": { "name": "keyword.other.import.qml" }, 36 | "4": { "name": "entity.name.class.qml" } 37 | }, 38 | "comment": "import Namespace VersionMajor.VersionMinor [as SingletonTypeIdentifier]" 39 | }, 40 | { 41 | "name": "meta.import.dirjs.qml", 42 | "match": "(\\\"[^\\\"]+\\\")(?:\\s+(as)\\s+([A-Z][\\w\\d]*))?", 43 | "captures": { 44 | "1": { "name": "string.quoted.double.qml" }, 45 | "2": { "name": "keyword.other.import.qml" }, 46 | "3": { "name": "entity.name.class.qml" } 47 | }, 48 | "comment": "import [as Script]" 49 | } 50 | ], 51 | "end": "$", 52 | "comment": "import statement." 53 | }, 54 | 55 | { 56 | "name": "support.class.qml", 57 | "match": "\\b[A-Z]\\w*\\b", 58 | "comment": "Capitalized word (class or enum)." 59 | }, 60 | 61 | { 62 | "name": "support.class.qml", 63 | "match": "(((^|\\{)\\s*)|\\b)on[A-Z]\\w*\\b", 64 | "comment": "onSomething - handler." 65 | }, 66 | 67 | { 68 | "name": "meta.id.qml", 69 | "match": "(?:^|\\{)\\s*(id)\\s*\\:\\s*([^;\\s]+)\\b", 70 | "captures": { 71 | "1": { "name": "keyword.other.qml" }, 72 | "2": { "name": "storage.modifier.qml" } 73 | }, 74 | "comment": "id: " 75 | }, 76 | 77 | { 78 | "name": "meta.propertydef.qml", 79 | "match": "^\\s*(?:(default|readonly)\\s+)?(property)\\s+(?:(alias)|([\\w\\<\\>]+))\\s+(\\w+)", 80 | "captures": { 81 | "1": { "name": "keyword.other.qml" }, 82 | "2": { "name": "keyword.other.qml" }, 83 | "3": { "name": "keyword.other.qml" }, 84 | "4": { "name": "storage.type.qml" }, 85 | "5": { "name": "entity.other.attribute-name.qml" } 86 | }, 87 | "comment": "property definition." 88 | }, 89 | 90 | { 91 | "name": "meta.signal.qml", 92 | "begin": "\\b(signal)\\s+(\\w+)\\s*", 93 | "beginCaptures": { 94 | "1": {"name": "keyword.other.qml"}, 95 | "2": {"name": "support.function.qml"} 96 | }, 97 | "patterns": [ 98 | { 99 | "name": "meta.signal.parameters.qml", 100 | "match": "(\\w+)\\s+(\\w+)", 101 | "captures": { 102 | "1": { "name": "storage.type.qml" }, 103 | "2": { "name": "variable.parameter.qml" } 104 | } 105 | } 106 | ], 107 | "end": ";|(?=/)|$", 108 | "comment": "signal [([ [, ...]])]" 109 | }, 110 | 111 | { 112 | "name": "meta.keyword.qml", 113 | "match": "(?:\\b|\\s+)(?:(true|false|null|undefined)|(var|void)|(on|as|enum|connect|break|case|catch|continue|debugger|default|delete|do|else|finally|for|if|in|instanceof|new|return|switch|this|throw|try|typeof|while|with))\\b", 114 | "captures": { 115 | "1": { "name": "constant.language.qml" }, 116 | "2": { "name": "storage.type.qml" }, 117 | "3": { "name": "keyword.control.qml" } 118 | }, 119 | "comment": "js keywords." 120 | }, 121 | 122 | { 123 | "name": "meta.function.qml", 124 | "match": "\\b(function)\\s+([\\w_]+)\\s*(?=\\()", 125 | "captures": { 126 | "1": { "name": "storage.type.qml" }, 127 | "2": { "name": "entity.name.function.untitled" } 128 | }, 129 | "comment": "function definition." 130 | }, 131 | 132 | { 133 | "name": "support.function.qml", 134 | "match": "\\b[\\w_]+\\s*(?=\\()", 135 | "comment": "function call." 136 | }, 137 | 138 | { 139 | "name": "entity.other.attribute-name.qml", 140 | "match": "(?:^|\\{|;)\\s*[a-z][\\w\\.]*\\s*(?=\\:)", 141 | "comment": "property (property: )." 142 | }, 143 | 144 | { 145 | "name": "entity.other.attribute-name.qml", 146 | "match": "(?<=\\.)\\b\\w*", 147 | "comment": "property of the variable (name.property)." 148 | }, 149 | { 150 | "name": "variable.parameter", 151 | "match": "\\b([a-z_]\\w*)\\b", 152 | "comment": "All non colored words are assumed to be variables." 153 | }, 154 | 155 | {"include": "source.js" } 156 | ] 157 | } 158 | -------------------------------------------------------------------------------- /Support/QML.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": ["qmlscene", "$file"], 3 | "selector": "source.qml", 4 | "file_regex": "^file:\/\/(.+):([0-9]+):() (.*)$", 5 | "windows": { 6 | "file_regex": "^file:\/\/\/(.+):([0-9]+):() (.*)$" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Support/QML.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | qml 8 | qmlproject 9 | 10 | name 11 | QML 12 | patterns 13 | 14 | 15 | begin 16 | /\*(?!/) 17 | comment 18 | Block comment. 19 | end 20 | \*/ 21 | name 22 | comment.block.documentation.qml 23 | 24 | 25 | comment 26 | Line comment. 27 | match 28 | //.*$ 29 | name 30 | comment.line.double-slash.qml 31 | 32 | 33 | begin 34 | \b(import)\s+ 35 | beginCaptures 36 | 37 | 1 38 | 39 | name 40 | keyword.other.import.qml 41 | 42 | 43 | comment 44 | import statement. 45 | end 46 | $ 47 | name 48 | meta.import.qml 49 | patterns 50 | 51 | 52 | captures 53 | 54 | 1 55 | 56 | name 57 | entity.name.class.qml 58 | 59 | 2 60 | 61 | name 62 | constant.numeric.qml 63 | 64 | 3 65 | 66 | name 67 | keyword.other.import.qml 68 | 69 | 4 70 | 71 | name 72 | entity.name.class.qml 73 | 74 | 75 | comment 76 | import Namespace VersionMajor.VersionMinor [as SingletonTypeIdentifier] 77 | match 78 | ([\w\d\.]+)\s+(\d+\.\d+)(?:\s+(as)\s+([A-Z][\w\d]*))? 79 | name 80 | meta.import.namespace.qml 81 | 82 | 83 | captures 84 | 85 | 1 86 | 87 | name 88 | string.quoted.double.qml 89 | 90 | 2 91 | 92 | name 93 | keyword.other.import.qml 94 | 95 | 3 96 | 97 | name 98 | entity.name.class.qml 99 | 100 | 101 | comment 102 | import <string> [as Script] 103 | match 104 | (\"[^\"]+\")(?:\s+(as)\s+([A-Z][\w\d]*))? 105 | name 106 | meta.import.dirjs.qml 107 | 108 | 109 | 110 | 111 | comment 112 | Capitalized word (class or enum). 113 | match 114 | \b[A-Z]\w*\b 115 | name 116 | support.class.qml 117 | 118 | 119 | comment 120 | onSomething - handler. 121 | match 122 | (((^|\{)\s*)|\b)on[A-Z]\w*\b 123 | name 124 | support.class.qml 125 | 126 | 127 | captures 128 | 129 | 1 130 | 131 | name 132 | keyword.other.qml 133 | 134 | 2 135 | 136 | name 137 | storage.modifier.qml 138 | 139 | 140 | comment 141 | id: <something> 142 | match 143 | (?:^|\{)\s*(id)\s*\:\s*([^;\s]+)\b 144 | name 145 | meta.id.qml 146 | 147 | 148 | captures 149 | 150 | 1 151 | 152 | name 153 | keyword.other.qml 154 | 155 | 2 156 | 157 | name 158 | keyword.other.qml 159 | 160 | 3 161 | 162 | name 163 | keyword.other.qml 164 | 165 | 4 166 | 167 | name 168 | storage.type.qml 169 | 170 | 5 171 | 172 | name 173 | entity.other.attribute-name.qml 174 | 175 | 176 | comment 177 | property definition. 178 | match 179 | ^\s*(?:(default|readonly)\s+)?(property)\s+(?:(alias)|([\w\<\>]+))\s+(\w+) 180 | name 181 | meta.propertydef.qml 182 | 183 | 184 | begin 185 | \b(signal)\s+(\w+)\s* 186 | beginCaptures 187 | 188 | 1 189 | 190 | name 191 | keyword.other.qml 192 | 193 | 2 194 | 195 | name 196 | support.function.qml 197 | 198 | 199 | comment 200 | signal <signalName>[([<type> <parameter>[, ...]])] 201 | end 202 | ;|(?=/)|$ 203 | name 204 | meta.signal.qml 205 | patterns 206 | 207 | 208 | captures 209 | 210 | 1 211 | 212 | name 213 | storage.type.qml 214 | 215 | 2 216 | 217 | name 218 | variable.parameter.qml 219 | 220 | 221 | match 222 | (\w+)\s+(\w+) 223 | name 224 | meta.signal.parameters.qml 225 | 226 | 227 | 228 | 229 | captures 230 | 231 | 1 232 | 233 | name 234 | constant.language.qml 235 | 236 | 2 237 | 238 | name 239 | storage.type.qml 240 | 241 | 3 242 | 243 | name 244 | keyword.control.qml 245 | 246 | 247 | comment 248 | js keywords. 249 | match 250 | (?:\b|\s+)(?:(true|false|null|undefined)|(var|void)|(on|as|enum|connect|break|case|catch|continue|debugger|default|delete|do|else|finally|for|if|in|instanceof|new|return|switch|this|throw|try|typeof|while|with))\b 251 | name 252 | meta.keyword.qml 253 | 254 | 255 | captures 256 | 257 | 1 258 | 259 | name 260 | storage.type.qml 261 | 262 | 2 263 | 264 | name 265 | entity.name.function.untitled 266 | 267 | 268 | comment 269 | function definition. 270 | match 271 | \b(function)\s+([\w_]+)\s*(?=\() 272 | name 273 | meta.function.qml 274 | 275 | 276 | comment 277 | function call. 278 | match 279 | \b[\w_]+\s*(?=\() 280 | name 281 | support.function.qml 282 | 283 | 284 | comment 285 | property (property: <something>). 286 | match 287 | (?:^|\{|;)\s*[a-z][\w\.]*\s*(?=\:) 288 | name 289 | entity.other.attribute-name.qml 290 | 291 | 292 | comment 293 | property of the variable (name.property). 294 | match 295 | (?<=\.)\b\w* 296 | name 297 | entity.other.attribute-name.qml 298 | 299 | 300 | comment 301 | All non colored words are assumed to be variables. 302 | match 303 | \b([a-z_]\w*)\b 304 | name 305 | variable.parameter 306 | 307 | 308 | include 309 | source.js 310 | 311 | 312 | scopeName 313 | source.qml 314 | uuid 315 | 13a281e0-0507-45b4-bb6c-a57177630f10 316 | 317 | 318 | -------------------------------------------------------------------------------- /XML.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "extensions": 3 | [ 4 | "qrc", 5 | "ui" 6 | ] 7 | } -------------------------------------------------------------------------------- /converter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/converter/__init__.py -------------------------------------------------------------------------------- /converter/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | 3 | # Copyright (C) 2012 - Oscar Campos 4 | # This plugin is Free Software see LICENSE file for details 5 | 6 | """ 7 | Base class for converters 8 | """ 9 | 10 | sip_api_2 = '''# PyQT4 API 2 SetUp. Comment or remove if you are using Python 3 11 | import sip 12 | 13 | sip.setapi('QString', 2) 14 | sip.setapi('QTextStream', 2) 15 | sip.setapi('QVariant', 2) 16 | sip.setapi('QTime', 2) 17 | sip.setapi('QDate', 2) 18 | sip.setapi('QDateTime', 2) 19 | sip.setapi('QUrl', 2) 20 | ''' 21 | 22 | 23 | class BaseConverter(object): 24 | """ 25 | Base class for PySide <--> PyQt4 converters 26 | """ 27 | 28 | def __init__(self, view, pattern): 29 | super(BaseConverter, self).__init__() 30 | self.view = view 31 | self.pattern = pattern 32 | 33 | def convert(self, st_edit): 34 | """Try to convert the file""" 35 | 36 | edit = st_edit if st_edit is not None else self.view.begin_edit() 37 | 38 | for key in self.pattern: 39 | matches = self.view.find_all(key) 40 | matches.reverse() 41 | 42 | for item in matches: 43 | self.view.replace(edit, item, self.pattern[key]) 44 | 45 | if st_edit is None: 46 | self.view.end_edit(edit) 47 | -------------------------------------------------------------------------------- /converter/parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | 3 | # Copyright (C) 2012 - Oscar Campos 4 | # This plugin is Free Software see LICENSE file for details 5 | 6 | """ 7 | Conversion parser 8 | """ 9 | 10 | 11 | -------------------------------------------------------------------------------- /converter/pyqt2pyside.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | 3 | # Copyright (C) 2012 - Oscar Campos 4 | # This plugin is Free Software see LICENSE file for details 5 | 6 | """ 7 | Converts a PyQt4 script to PySide 8 | """ 9 | 10 | import sys 11 | 12 | if sys.version_info < (3, 3): 13 | from converter.base import BaseConverter 14 | else: 15 | from PySide.converter.base import BaseConverter 16 | 17 | 18 | class Converter(BaseConverter): 19 | """ 20 | Converts a PyQt file to PySide syntax 21 | """ 22 | 23 | def __init__(self, view): 24 | pattern = { 25 | 'PyQt4': 'PySide', 26 | 'pyqtSignal': 'Signal', 27 | 'pyqtSlot': 'Slot', 28 | 'pyqtProperty': 'Property', 29 | 'pyuic4': 'pyside-uic', 30 | 'pyrcc4': 'pyside-rcc', 31 | 'pylupdate4': 'pyside-lupdate' 32 | } 33 | super(Converter, self).__init__(view, pattern) 34 | 35 | def convert(self, edit): 36 | """Convert a PySide syntax file to PyQt4""" 37 | 38 | return super(Converter, self).convert(edit) 39 | 40 | def original_file(self): 41 | """Returns the original (unchanged) file""" 42 | 43 | return self.filebuffer 44 | -------------------------------------------------------------------------------- /converter/pyside2pyqt.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | 3 | # Copyright (C) 2012 - Oscar Campos 4 | # This plugin is Free Software see LICENSE file for details 5 | 6 | """ 7 | Converts a PySide script to PyQt 8 | """ 9 | 10 | import sys 11 | 12 | if sys.version_info < (3, 3): 13 | from converter.base import BaseConverter 14 | else: 15 | from PySide.converter.base import BaseConverter 16 | 17 | 18 | class Converter(BaseConverter): 19 | """ 20 | Converts a PySide file to PyQt syntax 21 | """ 22 | 23 | def __init__(self, view): 24 | pattern = { 25 | 'PySide': 'PyQt4', 26 | 'Signal': 'pyqtSignal', 27 | 'Slot': 'pyqtSlot', 28 | 'Property': 'pyqtProperty', 29 | 'pyside-uic': 'pyuic4', 30 | 'pyside-rcc': 'pyrcc4', 31 | 'pyside-lupdate': 'pylupdate4' 32 | } 33 | super(Converter, self).__init__(view, pattern) 34 | 35 | def convert(self, edit): 36 | """Convert a PySide syntax file to PyQt4""" 37 | 38 | return super(Converter, self).convert(edit) 39 | 40 | def original_file(self): 41 | """Returns the original (unchanged) file""" 42 | 43 | return self.filebuffer 44 | -------------------------------------------------------------------------------- /data/designer/templates.json: -------------------------------------------------------------------------------- 1 | { 2 | "templates_list": [ 3 | "Dialog with buttons bottom", 4 | "Dialog with buttons right", 5 | "Dialog without buttons", 6 | "Main window", 7 | "Widget" 8 | ], 9 | "widgets": [] 10 | } -------------------------------------------------------------------------------- /data/designer/templates/dialog_with_buttons_bottom.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dialog 6 | 7 | 8 | 9 | 0 10 | 0 11 | 400 12 | 300 13 | 14 | 15 | 16 | Dialog 17 | 18 | 19 | 20 | 21 | 30 22 | 240 23 | 341 24 | 32 25 | 26 | 27 | 28 | Qt::Horizontal 29 | 30 | 31 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | buttonBox 40 | accepted() 41 | Dialog 42 | accept() 43 | 44 | 45 | 248 46 | 254 47 | 48 | 49 | 157 50 | 274 51 | 52 | 53 | 54 | 55 | buttonBox 56 | rejected() 57 | Dialog 58 | reject() 59 | 60 | 61 | 316 62 | 260 63 | 64 | 65 | 286 66 | 274 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /data/designer/templates/dialog_with_buttons_right.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dialog 6 | 7 | 8 | 9 | 0 10 | 0 11 | 400 12 | 300 13 | 14 | 15 | 16 | Dialog 17 | 18 | 19 | 20 | 21 | 290 22 | 20 23 | 81 24 | 241 25 | 26 | 27 | 28 | Qt::Vertical 29 | 30 | 31 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | buttonBox 40 | accepted() 41 | Dialog 42 | accept() 43 | 44 | 45 | 248 46 | 254 47 | 48 | 49 | 157 50 | 274 51 | 52 | 53 | 54 | 55 | buttonBox 56 | rejected() 57 | Dialog 58 | reject() 59 | 60 | 61 | 316 62 | 260 63 | 64 | 65 | 286 66 | 274 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /data/designer/templates/dialog_without_buttons.ui: -------------------------------------------------------------------------------- 1 | 2 | Dialog 3 | 4 | 5 | 6 | 0 7 | 0 8 | 400 9 | 300 10 | 11 | 12 | 13 | Dialog 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /data/designer/templates/main_window.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainWindow 6 | 7 | 8 | MainWindow 9 | 10 | 11 | 12 | 0 13 | 0 14 | 800 15 | 600 16 | 17 | 18 | 19 | MainWindow 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /data/designer/templates/widget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Form 6 | 7 | 8 | Form 9 | 10 | 11 | 12 | 0 13 | 0 14 | 400 15 | 300 16 | 17 | 18 | 19 | Form 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /data/templates/qt_console_application/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | ${PyQT_API_CHECK}from ${QT_LIBRARY} import QtCore 4 | 5 | if __name__ == '__main__': 6 | 7 | app = QtCore.QCoreApplication(sys.argv) 8 | print "Initializing the main events loop" 9 | sys.exit(app.exec_()) 10 | -------------------------------------------------------------------------------- /data/templates/qt_gui_application/application.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | ${PyQT_API_CHECK}from ${QT_LIBRARY} import QtCore, QtGui 5 | 6 | import application_rc 7 | 8 | 9 | class MainWindow(QtGui.QMainWindow): 10 | def __init__(self): 11 | super(MainWindow, self).__init__() 12 | 13 | self.curFile = '' 14 | 15 | self.textEdit = QtGui.QTextEdit() 16 | self.setCentralWidget(self.textEdit) 17 | 18 | self.createActions() 19 | self.createMenus() 20 | self.createToolBars() 21 | self.createStatusBar() 22 | 23 | self.readSettings() 24 | 25 | self.textEdit.document().contentsChanged.connect( 26 | self.documentWasModified) 27 | 28 | self.setCurrentFile('') 29 | self.setUnifiedTitleAndToolBarOnMac(True) 30 | 31 | def closeEvent(self, event): 32 | if self.maybeSave(): 33 | self.writeSettings() 34 | event.accept() 35 | else: 36 | event.ignore() 37 | 38 | def newFile(self): 39 | if self.maybeSave(): 40 | self.textEdit.clear() 41 | self.setCurrentFile('') 42 | 43 | def open(self): 44 | if self.maybeSave(): 45 | fileName, filtr = QtGui.QFileDialog.getOpenFileName(self) 46 | if fileName: 47 | self.loadFile(fileName) 48 | 49 | def save(self): 50 | if self.curFile: 51 | return self.saveFile(self.curFile) 52 | 53 | return self.saveAs() 54 | 55 | def saveAs(self): 56 | fileName, filtr = QtGui.QFileDialog.getSaveFileName(self) 57 | if fileName: 58 | return self.saveFile(fileName) 59 | 60 | return False 61 | 62 | def about(self): 63 | QtGui.QMessageBox.about( 64 | self, 65 | "About ${APP_NAME}", 66 | "The ${APP_NAME} example demonstrates how to write " 67 | "modern GUI applications using Qt, with a menu bar, " 68 | "toolbars, and a status bar.") 69 | 70 | def documentWasModified(self): 71 | self.setWindowModified(self.textEdit.document().isModified()) 72 | 73 | def createActions(self): 74 | self.newAct = QtGui.QAction(QtGui.QIcon(':/images/new.png'), "&New", 75 | self, shortcut=QtGui.QKeySequence.New, 76 | statusTip="Create a new file", 77 | triggered=self.newFile) 78 | 79 | self.openAct = QtGui.QAction( 80 | QtGui.QIcon(':/images/open.png'), '&Open...', self, 81 | shortcut=QtGui.QKeySequence.Open, 82 | statusTip="Open an existing file", triggered=self.open) 83 | 84 | self.saveAct = QtGui.QAction( 85 | QtGui.QIcon(':/images/save.png'), '&Save', self, 86 | shortcut=QtGui.QKeySequence.Save, 87 | statusTip="Save the document to disk", triggered=self.save) 88 | 89 | self.saveAsAct = QtGui.QAction( 90 | 'Save &As...', self, shortcut=QtGui.QKeySequence.SaveAs, 91 | statusTip="Save the document under a new name", 92 | triggered=self.saveAs) 93 | 94 | self.exitAct = QtGui.QAction( 95 | 'E&xit', self, shortcut="Ctrl+Q", statusTip="Exit the application", 96 | triggered=self.close) 97 | 98 | self.cutAct = QtGui.QAction( 99 | QtGui.QIcon(':/images/cut.png'), 'Cu&t', self, 100 | shortcut=QtGui.QKeySequence.Cut, 101 | statusTip="Cut the current selection's contents to the clipboard", 102 | triggered=self.textEdit.cut) 103 | 104 | self.copyAct = QtGui.QAction( 105 | QtGui.QIcon(':/images/copy.png'), '&Copy', self, 106 | shortcut=QtGui.QKeySequence.Copy, 107 | statusTip="Copy the current selection's contents to the clipboard", 108 | triggered=self.textEdit.copy) 109 | 110 | self.pasteAct = QtGui.QAction( 111 | QtGui.QIcon(':/images/paste.png'), '&Paste', self, 112 | shortcut=QtGui.QKeySequence.Paste, 113 | statusTip="Paste the clipboard's contents into " 114 | "the current selection", 115 | triggered=self.textEdit.paste) 116 | 117 | self.aboutAct = QtGui.QAction( 118 | '&About', self, statusTip="Show the application's About box", 119 | triggered=self.about) 120 | 121 | self.aboutQtAct = QtGui.QAction( 122 | 'About &Qt', self, statusTip="Show the Qt library's About box", 123 | triggered=QtGui.qApp.aboutQt) 124 | 125 | self.cutAct.setEnabled(False) 126 | self.copyAct.setEnabled(False) 127 | self.textEdit.copyAvailable.connect(self.cutAct.setEnabled) 128 | self.textEdit.copyAvailable.connect(self.copyAct.setEnabled) 129 | 130 | def createMenus(self): 131 | self.fileMenu = self.menuBar().addMenu("&File") 132 | self.fileMenu.addAction(self.newAct) 133 | self.fileMenu.addAction(self.openAct) 134 | self.fileMenu.addAction(self.saveAct) 135 | self.fileMenu.addAction(self.saveAsAct) 136 | self.fileMenu.addSeparator() 137 | self.fileMenu.addAction(self.exitAct) 138 | 139 | self.editMenu = self.menuBar().addMenu("&Edit") 140 | self.editMenu.addAction(self.cutAct) 141 | self.editMenu.addAction(self.copyAct) 142 | self.editMenu.addAction(self.pasteAct) 143 | 144 | self.menuBar().addSeparator() 145 | 146 | self.helpMenu = self.menuBar().addMenu("&Help") 147 | self.helpMenu.addAction(self.aboutAct) 148 | self.helpMenu.addAction(self.aboutQtAct) 149 | 150 | def createToolBars(self): 151 | self.fileToolBar = self.addToolBar("File") 152 | self.fileToolBar.addAction(self.newAct) 153 | self.fileToolBar.addAction(self.openAct) 154 | self.fileToolBar.addAction(self.saveAct) 155 | 156 | self.editToolBar = self.addToolBar("Edit") 157 | self.editToolBar.addAction(self.cutAct) 158 | self.editToolBar.addAction(self.copyAct) 159 | self.editToolBar.addAction(self.pasteAct) 160 | 161 | def createStatusBar(self): 162 | self.statusBar().showMessage("Ready") 163 | 164 | def readSettings(self): 165 | settings = QtCore.QSettings("Trolltech", "${APP_NAME}") 166 | pos = settings.value("pos", QtCore.QPoint(200, 200)) 167 | size = settings.value("size", QtCore.QSize(400, 400)) 168 | self.resize(size) 169 | self.move(pos) 170 | 171 | def writeSettings(self): 172 | settings = QtCore.QSettings("Trolltech", "${APP_NAME}") 173 | settings.setValue("pos", self.pos()) 174 | settings.setValue("size", self.size()) 175 | 176 | def maybeSave(self): 177 | if self.textEdit.document().isModified(): 178 | ret = QtGui.QMessageBox.warning( 179 | self, "${APP_NAME}", 180 | "The document has been modified.\nDo you want to save " 181 | "your changes?", 182 | QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | 183 | QtGui.QMessageBox.Cancel) 184 | if ret == QtGui.QMessageBox.Save: 185 | return self.save() 186 | elif ret == QtGui.QMessageBox.Cancel: 187 | return False 188 | return True 189 | 190 | def loadFile(self, fileName): 191 | file = QtCore.QFile(fileName) 192 | if not file.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text): 193 | QtGui.QMessageBox.warning( 194 | self, "${APP_NAME}", "Cannot read file %s:\n%s." % ( 195 | fileName, file.errorString())) 196 | return 197 | 198 | inf = QtCore.QTextStream(file) 199 | QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) 200 | self.textEdit.setPlainText(inf.readAll()) 201 | QtGui.QApplication.restoreOverrideCursor() 202 | 203 | self.setCurrentFile(fileName) 204 | self.statusBar().showMessage("File loaded", 2000) 205 | 206 | def saveFile(self, fileName): 207 | file = QtCore.QFile(fileName) 208 | if not file.open(QtCore.QFile.WriteOnly | QtCore.QFile.Text): 209 | QtGui.QMessageBox.warning( 210 | self, "${APP_NAME}", "Cannot write file %s:\n%s." % ( 211 | fileName, file.errorString())) 212 | return False 213 | 214 | outf = QtCore.QTextStream(file) 215 | QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) 216 | outf << self.textEdit.toPlainText() 217 | QtGui.QApplication.restoreOverrideCursor() 218 | 219 | self.setCurrentFile(fileName) 220 | self.statusBar().showMessage("File saved", 2000) 221 | return True 222 | 223 | def setCurrentFile(self, fileName): 224 | self.curFile = fileName 225 | self.textEdit.document().setModified(False) 226 | self.setWindowModified(False) 227 | 228 | if self.curFile: 229 | shownName = self.strippedName(self.curFile) 230 | else: 231 | shownName = 'untitled.txt' 232 | 233 | self.setWindowTitle("%s[*] - ${APP_NAME}" % shownName) 234 | 235 | def strippedName(self, fullFileName): 236 | return QtCore.QFileInfo(fullFileName).fileName() 237 | 238 | 239 | if __name__ == '__main__': 240 | 241 | import sys 242 | 243 | app = QtGui.QApplication(sys.argv) 244 | mainWin = MainWindow() 245 | mainWin.show() 246 | sys.exit(app.exec_()) 247 | -------------------------------------------------------------------------------- /data/templates/qt_gui_application/application.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/copy.png 4 | images/cut.png 5 | images/new.png 6 | images/open.png 7 | images/paste.png 8 | images/save.png 9 | 10 | 11 | -------------------------------------------------------------------------------- /data/templates/qt_gui_application/application_rc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Resource object code 4 | # 5 | # Created: sáb may 5 16:36:37 2012 6 | # by: The Resource Compiler for PySide (Qt v4.7.4) 7 | # 8 | # WARNING! All changes made in this file will be lost! 9 | 10 | from ${QT_LIBRARY} import QtCore 11 | 12 | qt_resource_data = "\x00\x00\x03T\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04gAMA\x00\x00\xd6\xd8\xd4OX2\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x02\xe6IDATX\xc3\xd5\x97\xcdN\x13a\x14\x86\xeb5\x94\x95{q\xe1\xd2\xc4\xe0\x05\xb8\xe2\x0e\x5c\xb8\xf4\x02\x5c\xb10\xea\x05\x18\x96&bX\xb8\xb0\x91X \xd1\x9d\xbf\x89\xa4\x14\xb1R\xa4HE\x94\xfe\xd0\x02C\xff\xa6\x9d\x19\xa6e\x80\xe3y{\xfa\x85QJ\x82\xc9!\x86I\xde\x9c3\xa7\xf3\xcd\xfb\x9c\xf3M\x9bN\x84\x88\x22\xffS\x91s\x01\xc0\xc7\xd5\x90n\xff\xa5\xfb\xac\xc7==d\x0d\xa9\x02\xf012<<\xbcj4::\xba\x19V<\x1e\xaf&\x93\xc9V:\x9dv\x13\x89Dk`` \xcdknh\x02\xa48\xd2\xe1\xe1q\x99\xba\xef\xb7\xc9\xb2,\xda\xdf\xdf'\x86\xf1x\xcd\x18\xeb\x8a\x1a@?\xf3\xb0\x1c\xc7\xa5Lf\xb9\x0b\x14\x04\x01\xc5b\xb1:\xaf{p\x1a\x88S\x01\x1c\x1c\x10ww\xb2l\xdb\xa1\xf9\xf9\xcfd\x0e\xd7u\xe9\xf9\xc4D\x17B\x05\x00&{\xc1\xc9\xaa7\x1cJ\xce\xcdS\xf8p]\x0f\x8b\x17T\x00\x82\x10@gO\x14\xce\xed\xa6G\x1fgf\xe9\xf5\x9b\xb7\x14\x9f\x9c\xa4\xa9\xa9iz\xf7\xfe\x03E\xa3\xd1e^\x7fA\x05\xc0\xef\x10\xed\xb6%\x86\x85\x9a\xe3\x05\x94]\xcd\xd1\xe4\xf4+z2\xfe\x94\x9e\xc5^\xd0Lb\x0e\x8b\x17U\x00\xda\x81\x18\xf5\x13 <\xff\x90j\xcd6\x157\xab\x94/nS\x89c\x8d\xb7\x85\xd7~Q\x01\xf0y\xcc\xcd]\x1e\xb5\xc7{\xdb\xee\x9f;\xbe\xe4\x88]\xb8\xbd\xee\xe2\x94\xca3\xe0u\xe4\xc6uWb\xd8\x109\xea\xe63D\xd4\x01\xa7\x06\xe0\xf4:\xad9\x22\x98\x98hr\x80\x98kPS\x9d\x00\x00*-\xb91\xe2NS\x8c\x10\x0d\x04\xf2m\xfb(\xb6|E\x00\x9b;\xdbj\xfci\x8e\xfb\xc5S(\xf0C\xb8fI\xf7k\xf9R\x87\xd7\xbeT\x01\xc8U\x8f\xbaN\xadK\x0e\x90\xaf\x85\xde\xb7\xc2\x92=O\xa6\xb3\xde\xa3\xb1q\xeb\xda\xd0\xf5\x15\x98\xb3n\xa9\x00l4\xa4k\x18\xff\xe0\x11\x7fZ\x17S\xd4\x13\x0bYo\xe4\xee\xbd\xe2\xa5\xc1\xcbK|m\x8cu\x875\xa8\xfa\xb7\x1c\xdde\xd9<\x8f\x1f\x19\xfe\x9e\xcf\x1e7\xbd\xc9\xbax&oF\x00h\xf2\xff\x81\x99\x94\x9e\xe9?\xbf\x19\x01B\xd3\xf4\xfc\xbd\x9c\x9e\xa5~\x03Ql%\xa1\x92\x95\x0aw\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x05:\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04gAMA\x00\x00\xd6\xd8\xd4OX2\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x04\xccIDATX\xc3\xb5\x97]L[e\x1c\xc6wo\xbc\xd9\xe5\x12I q\xd7&\xe3N\x13\xb8p\xd1\x85D\xbdP\xe3\x10\x18\xe5+.&J\x04'\x86\xaa\x8b\x99\xe0\xd0\xa2l\x19\x869\x17\xdc\x1a\x16\x98\x80@l\xa6C\xca +\x83\x1e(\xcc\xda\xd1\x96\xd2\xd2J{\xfa\x01\xa5\xd0\xef\x16\x1e\xdf\xff\xdb\x1d\xc7\xcc\x04*\x87\x93<9o!\x9c\xe7\xf7<\xefG\x0f\x87\x00\x1c\xcaF\xcf\xbd\xfa\xe9\xbbLZ&a\x0fj`\xca\xd9\xe9y\xd9\x9a?]P\xf2\xa5\xc1\xe9\x8f\xa7W\xc3@0\x02\x84\xa2\x19\xad\xc72\x8a'\x81X\x22s\xbfyk\xdaK\x10r\x02\x1c{\xe7\xac\xda\x1c\xd8\xc8\x98\x12@\x84\x99\x85\xe3\x19\x911)\x1aKa%\x94D8\x9aBs\x87\xc6\xbe\x13\xc4\xff\x02\x90\x12\x93y$\xf1\xc8X\x92\xcf\x1f\x84]\x8c\xc2\xe5\x09\x22\x12K\xa3\xf4\xc3\xefM4uY\x01\xb0\xeb\xd86\xd5\x90\x9e:\xfc\xcc\xb9\xe7_.\x11?V\x9eEEU\x0d*\x99\xde\xaf\xad\xc3\x9d\xb1\x89\xc7\x00\xac\xb6%\xfc\xb9\xe8\x87k\x15X\xf6\x04\x10\x08\xc6\xd2\xaf\x9c\xbep\x9fA\x1c\xd9\x15\x80]\x87\x99\x1a\x8a\x8a\x8a\xcc\x92Z[[\xdd\xa4\xafU\xad\xfe\xafT\xdf\xa6\x06\x06\x06195\x85\xd9\xb99\xe8&&PPP\x80!\xcdo|\xdeI\xa6\xf9\x05\xcc\x98\x5c\x1c\xc0\xe1OA\xf4\x85\xf0C\xaf\xce\xcd\x00j\xf6\x02PCf\xd8\xe5\x8a\xc7\xe3\xf0z\xbdH\xa7\xd3\x98\x9c\x9cDee5fg\x8d\xbc\x81\x07f\x1bt\xd3\x16\x0e@2-x\xf0\xdd\x8dQ\x8f\xac\x00\xe1p\x18F\xa3\x91\x8fS\xa9\x14~\xea\xedE\xe3'\x9fa\x86A8\x96\xdcPwu\xe3LC#\xce5\x9d\xc7\xed\x91q\x5c\xbc>,/\xc0\xc6\xc6\x06\xf4z\xfdc@}}\xfdP2\x88\xd0F\x1cf\x9b\x0b\x82\xc1\x88\xa9\x19\x13\xac\x0e\x11\x97\xbadn\x80\x00\xa6\xd8:\xd8~E\x22\x11\x94+*0\xae\x13@\xe7\x04mW\xda\xaa4\xbe|S\xe65@f:\x9d\x0e\xc3\xc3\xc3\xe8e\xf5\xf7\xf7\xf7C\xab\xd5\xa2\xaa\xba\x06cw\xf5\x90\x0e*w\x90\xed\x04\xb6\x0e\xda\xbbe\x06\xa0y\xb7\xdb\xed\x18\x1a\x1aBgg'zzz8PIi\x19ni\xf5\x10\xd7\x00o\x08\xb0\xf9\x00g\x00\xb8\xd0%3\xc0\xd6\xd6\x16\xdf\x09\x81@\x00\xa2(\xc2\xef\xf7cmm\x0d\xa7\x14\x95\xd0\xfc\xae\xe7\xa9\xc9|\xc1\x0b\x98=@\x9b\xdc\x00\xdbA677\xf9v\xa4V\x14\x15\xd5\xe8\xfbU\xe0\xa9\x1d\x81G\x00\xe7;\x0f\x00\x80\xcc%\x80$3O$\x12(+\xaf\xe2\x00\x7f\xb8\x00\x8b\x98\x01\xa06Z\xd5\x070\x05\xff\x98'\x93<=MI\xc9\xa9J\x0e\xa0\xb7\xb3\x03\x89=\xc5\xf8\x170\xb1\x00|q\xf5\x00\x00\xa4\xea\xc9\x98\x14\x8b\xc5P\xa6\xa8\x82zH\xc0\x98\x19\xb8k\x05\xe6\x9c\x99\xfb\xe7Wd\x04\x90\xd2Sj\x02\x88F\xa3\xdc<\x14\x0a\xa1\xb8\xb4\x02\xd7\x06\x05\xdcf\x87\xe4\xa0\x01\x1cd\xc4\x04(;d\x06H=\x9cs\x12\x99\xd3\xb9@ \xc5eU\xb8\xd8-\xa0\x7f:c\xae}\x90i\xe0\xa3v\x99\x00\xfe]=\xa5&\xad\xae\xaer\x88\xb7J*p\xb9W\xc0=\x1b\xb8~\x9e\x01\xee\xcc\x03g.\xed\x13@\xaa\x9dD\x8b\x8e\x92\xd3qL\xdf\x01+++X__\xe7\x10'Y\x03\xdft\x09PO\x00\xbf\xcce\x1a\xb82\x064\xec\xa7\x01\xc9X\xda\xebdNi)9\x1dD\x04@\xf5\xd3\xcf\xde|[\x81\x96\xeb\x02O~u\x1c\xb8q\x0f\xf8q,\x9e~\xbdNm\xa67\xaa\xac\x00\x9ed,m72%\x00\xd1#\xf2\xe4\x12\xcc\x1b'\x15h\xef\x11\xa0\xbcf[\x7fO5\xe2\xc9xG\x00\x95J\xc5\x01\xa4\x15.\xcd7\x19RR:\xf7)\xb5\xc3\xe1\xe0\x22\xe3\xc5\xc5E\x0e\xf5\xe2\xf1\x97\x5c\xf4\x1e\xb9\x93\xe9\xae\x00---n\xe9`\xa1\xd4\xd2\x97\x0d\x8d\x97\x97\x97\xe1\xf3\xf9`\xb3\xd9\xf8}ii\x89C\x10\x00\x8d\x0b\x0b\x0b\xcd\xb2\x00\xd0\xa2\x92R\x93\x11\x8d\xe9N\xdfxT;5`\xb5Zy\xf5\xd4\x0a\xfd\xce`0$\xf2\xf2\xf2\xee\xb3g\x1c\xd9\x17@SS\x93[\x9agJO\x22\x13\xaa\x9a\xc6\x16\x8b\x997@\x9fGGG#mmm\xde\xfc\xfc|\x13\xfb\xdbA\xa6\xb2\xbd\x9a\xff'@ss3\x9f\x02JG\x10T?U???\xcf\xeb\xd6h4\x91\xba\xba:\xe7\xc3\xb4]L\x1f0\x1d\xcd\xc6xG\x00\xa5R\xe9v:\x9d\xbcbJJo>\x94\xb4\xbe\xbe\xde\x99\x93\x93#\x99\x16gSuV\x00\x8d\x8d\x8dn\x8b\xc5\x82\x81\x81\x81Hmm\xad377WV\xd3\xdd\x00\xf8\x7fFL\xc2A\x99n\xd7\xdfC9V\x18\x85p\xc8\x04\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x05+\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04gAMA\x00\x00\xd6\xd8\xd4OX2\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x04\xbdIDATX\xc3\xedWkL\x93W\x18>#q\xc92\xe9\x16\x97\xa8Te8\x9d\x02\x15\xf6\x03\x872\x93\x01f,[p\xc40\xff`\xa2.\x1a:\x1dN\x03\xba1\x89[\xb3\x80\xd9\x0c\x84\x02\x19X\x1c\x14\x8b\x85\xb2\x82\x95^\xe4f\x0b\x8e1\xf8\xc3F\xcb-\x81\x15\xdc\xa8\xc2\x1c\x1b\xb7ji\x91\xf2\xee\xbc\x87\xaf\x0c\xdc\xb8\x0da\xd9\xb2\x93<\xed\x97\xf3}\xfd\xde\xe7\xbc\xef\xf3^J\x00\x80\xfc\x93 \xff\x0a\x02t\x09(D\x14\xd9\x14q\x14\x01+F\x80\xae\xddd\xdd\xc6f\x22L\xf8\x95\xc4\x8bG\xc8\xa1\xd3\xf7\xc8\x8e\x97;82a+A \x85\x9c\xbe0H.\xdd\x80\x19@2\xabyM\xf4\xbe\xfbr\x13hd\x06\x91\x04^\xa3Q\xf4\x06\xee\x85G\xf5\xd0\xbd\x83\xcbM \x9b\x9d\xf6@t/\xbd\x162= \x89?H\xa5,\x1b\x01\x8c1y\xc1\xbb\x9d\x88K\xc6\xd7\xc6&\x0e\xa0\x10\xb9\xfdB\xfe\xc5+6F\x8c\x12\x5cN\x02\x93\xa7\xa7\xa7\x0d\xcc\xd39\xb9\x98c6\x14\x0a\xd2\xe4\xa3+A \x8c)\x9e*\xdf7G\xeb\xdc{\xb5\xcc\x89\x9e@D\x96T\x83+,\x0b6FH\x08\x13\xf5d*{.T\x03\x01\xf8\x037\xbf\xc0\x0e4*T\xdfb\x88R\xd5,X\x03t\x1d\x16\x08\x04zEU\xf5\xc8\xa0mt\xc2\xd4s\xf7!\xbesQ\x95\x90\xae\x8f\xd0\x13\xcf\xe5\x94\x83\x87\xb4\x02\x9e\xcc.\x03\xd4\x06\xdd\xaf\x99\xcb\xb0\xaf\xaf\xaf>\xbf\xd2`\xb5\xdb\xed\x80\xf8y\xe4>\xc4^\xab\xb4\xb9\x88/\x86\x80'\xd3\xc0g\xf9\x8e\x19\xf5`\xd7^3\xbav\xdas\xeeh\xd8\xc7\xc7G\x9f\xab\xab\xb0\x0e\x0f\x0d\xc1\x10\x87\xb2\xf6.\xe7\x967\xf7wsa\xd8\xbd\xe8^\x80/f\x9a\xa0\x86\xdf\xa96B\xf7\xf0\x03\xd8\x19\x9f\xd4\xcf\xa5\xe7\x1a\x8a\x98-~\xfem\x97T\x1ak__\x1f\xb8\xd0\xd1s\x07br\x15VN\xc4\x87\x97\xd4\x8c0\x14\xe9\x15\xb7\x1e8\x1c\x0e@\xa4\xd6\x191\x9e\x85\x9b\x05~m\xa9%\x1a[\x97\xd9\x0c\xe6.\x0a\xf3$\x14\xdf6\x8e{\xbd\x1e\xd1\xcdB\xc8\x09o\xa9\x04<\xd1\xbdV\xab\x15\x10w\x7f\x1b\x84\xf3\x92\x5c\xbbR\xa9\x84\xfa\xfaz0\x99L\x0cu\xdf5\xc1Q\xb1d\x18\xc9QD>\xb6v\xcc\xb4@O\x93_~\xd3\xd6\xdf\xdf\x0f2\x99\x0cD\x22\x11\xa8T*\x90J\xa5\xa0\xd1h K[9\xbe\xe9\x95\xe0\x1f\xb8S\xafy,\xf3\x00\x97\x8e\x22\x9e\xc7\x86\xe6S)\x19\xf6\x82\x82\x02\xe6\xe2\xa0\xa0 \xe0\xf1x`\xb1X@[^\x01\xfb\xcf&\x0c-\xa6S\xceg\x94\xcf\x09L\x83\xe2[{\xe6\xc2`\x9a\xb2\x14\x14\x0a\x05\x88\xc5b\xc8\xcc\xcc\x84\xa2\xa2\x22P\xab\xd5\xd0\xd9\xd9\xc9`\xec\xfe\xc9\xb9\xc9\xdb\xa7u.\xb7\xcfK\x80\xae\xb7\xd8)p\x0e\xc0j\x97\xacx\x88\xca\x7f\x82\xe2)\x89\x0e>\x97+![\x96\x0f\x07c\xe3G\x84\x1f&\xd8\x92rd\x8eo\x1a\xbf\x07\xa3\xd1\x08-\xad-\xf0\xcb\xc0 \x1c8\xf1\xbe\x05\xb3b\xc1\x04\x5ci\x84\x85\x85\x84F\xdc&\xe72\xac,\xcf3\xb5\x13\xec;\xe3\xba\xd33\xaf\x82\xe5\xfez\x89\x06\x9e\xde\xfcb\x1b\xf7<\x92\x8d{f\xabO[\xca5\xedXCC=444\x80\xa5\xb7\x172\x14\xc5\xc3\xf3\xe9\xc0e<\x92\xe5(\x9e6]\xe5\x9c*2x}\xf4\x83.Zl\x121\x0c\x1b%\xeaq\xf7/\xcb'\xef\x05\x87_\xfe\xd3\xe4D\x0bLh\xf4\xc9>u\x95\x1e\x0c\x06\x03\xb4\xb7\xb7\xc3\xd7\xc6\x961\xae\x81\x09f\xf16m8h\xed\xf7\x08\x1e*>]\xe5X\xaa\xf1GZ\xf5\xb6Y\x0b\x11\x1d\xb3C\xc9\x918\x099\xf9\xa9\x96!\xfa\x5c\x1a\x0d\xcf\xb3\xff\xff7\xfcO\x13\xf8\x1d\xe7\x87\x19\xb9D\xc3\x01\xcf\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x04\xa3\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04gAMA\x00\x00\xd6\xd8\xd4OX2\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x045IDATX\xc3\xe5\x97\xcd\x8fTE\x14\xc5\x7f\xb7\xea\xd6{\xaf\xdbn\xc7\xf9@\x9d\x89FM4\x99D\x8d\x1aH\x98\xc4\x8c\x1f\x1b\xfe\x02L\x5c\xf1\x07\x18\x16.M\x5ckX\xc3\x8e\xc4\x8d\x1b\x17\xce\x82htA\x5c\x18\x0d\xe2\xc4\xc6\x00=`PQ\x19`\x02\xa2\x0e\x0c\x83\xd3\xfd^\xf7\x94\x8b\xaa\xee\xf9`\xe6\x0d\x84Q\x16VR\xa9\xce{\xb7\xeb\x9e:\xf7\xd4\xa9z\xea\xbd\xe7~6\xe5>\xb7>\x80]\xbbv\xbd\x03\xec\xfd\x8f\xf2N5\x1a\x8d\x03\xeb\x19\xd8\xbb\xef\xbd\xa3;\x1f\x1fv\x00\x9c<:\xcf\xcc\x977X\x9c\xef\xdcS\xa6\xda\xa0\xf2\xdck\x03\xbc\xb8g\x10\x80\x8b\x7f\x16|\xf8\xee\x1e\x80\xdb\x00p\xfc\xec\x1c\xdf?0\x04x.\xfd\xb8\xc0\xfe\xb7\xceo\xcbr\x0f\x1dy\x9a\x0b#\x96\xd3\x9f\x1fd\xfc\xd5}\x9bk@E\xb0\x16@xp,#\xcb\xb2m\x0100\x96a\x8dP\x1b|\x14#%\x22\x14+\xd8\x18\x91\xd5\x95s\xe7\xce\x83*\xb8\x04\xd2\x14\xb2\x0c\xd2,\x8cI\x0aI\x12\xdew:\x90\xe7\x90\xb7\xa1\xd5\x82v+\x8em(r\xb2\xfa8\xd6\x0a\xe3\xaf\xbcIk\xf1\xfa\xe6\x00\xac\x15\xac\x15\x04\xb0F\xd8\xbd{\xe7\x16k\xeb\x86\xae\x80Z\xa8V\x81\xeamQ\x8d\xaf\x04\xb5\x82\xf7\xa0\xa6\x84\x01g\x055\x82\x08\xa8\x0a\x95,\xc3# \x1e\x08\xc0\xf0\x1e/\x02\xde#\x12&\x15|\x88#\xc4!\x1e\xd0\xaf\x16\xaa\x1b\x8b\xf6\xd8'aa\xbd\x1c%% \x00\xf0\x81\x8d4M\xa3:\xc3\xb3\x98\x11\x89l\x07\xdac\x09V\x98_)F\xfca\xcdr\x7fa\x1d-\xd1\x80:\x09TI\x18O4/\xe0\x9d\x85\xc4!\x89\xc3g\x09\x92i\xd8\x11\x89\xe2\x13\x87X\x8b\xefv\x91\xbc\x80\xbc\x03\xed\x02\xdfj#\xed\x02\xf2\x02\x9fwP\x1dE\xd5 x:\xebTx\x9b\x06\x9c3x\x0f\x03\x8f$\xbc\xfe\xf2\xf3wh\xe86h\xa4\xbe\xf1\xeb\xc6\xfc\xdf\xb1\x04R^\x82DM_\x84\x8f\x0d\xa58\xe7\xb6\xc5\x88\x9e\x18K\xb9v\xb3\x03\x08\x9dR\x11\xaa\x90\xb8P\xefZ\xc50}\xb1\xcb@\xc5\xb0\x0e\xf4&\xadW\xf9U.\xe1\xe1\xc6\xd22\xf5\xccp}\xc9\x84-\xe9J\x19\x10\x9c\x1a\xc0s\xe5f\x97+7\xbb\xacQW?\xd7\xaad~\xc5'\xa2)\xac\x05\x15\xc3\x9c\x0b\xb5w\xa6l\x17\xa8\xc1\xa9 \xc8\x1a5\xaf\x9b5\x1a\x8fY1\x9e\xfe{\xe9\xef\x14\x00\xf1\x82\xef\x9bX0+WV\x02U!\xd1\x90\xfc\xe7S\xdf\xf2\xeb\x99\x13,-\xde\xb8\xa7\xfaWj\x03<\xf5\xecN\x9eya\x02\x0f\xa83[1\x10\x03|\x87\xf7\xf7\xbf\xc1\xc2\xc2\x02\xb7n\xdd\xa2(\x0aD\x04k-\xd6ZT\x15U\xc59\x87\xaab\xad\xc5\x98\xf0\xdf\xe5\xe5e\xf2<\xef\xf7#\xcd\xf9\xb8\xf2-\x18pVP\x17\x18\xdc1:\xb6rO8~\x9c\xe9\xe9i\x8c1x\xef\x99\x98\x98`rr\xf2\x8eY\xd81:\xd6\xdf\x86\xae\xd4\x09Up6\xac\xa2V\xaf\xf7k933\xc3\xd0\xd0\x10\xd6Z\xbc\xf74\x9b\xcd\xbb\x02P\xab\xd7p\xd1\x88\xb4\xd4\x88\x14\x9c\x0b'\x5c\xa0*\x00\xa8V\xabdY\xd6\xa7\xb87\xdeis\x1a\xa9\x17AK\xad8\x1e\xc7\xbd#\xb4\xd7\x8c1\x88D\xdf\x8f:\xb8\xab\x9b\xaf5\xa8\x0d\xf3\xf6\x18.=\x8e\x83)m\xe3\xd5\xdb\x12\xa9\xf7\xe5Vl\xad\xf4\x91\x0e\x8e\x0c\xc3\xf2\xef\xdb\x02\xe0\xa1\x91a\xd4\xc2\xb5+\x97Y\x9c\xbf\xbe\x05\x036\xf8\xc0`\xad\x02\x0b\xdb\xc3\xc0P\xad\xc2\xec\xc5K\x9c\xfd\xee\x1b\xce\x9f\x9c\x9e\x03\xa66\x04`$^J\x05\x12\x0b\xed\x91'\xa9=\x0co\x1f8\xc8f\xc7\x81':\xf1*\xe75\x1e2\x81\x14(\xbap\xf9\xeaU\xce4\x8e\xd1\xfc\xfa\x8b\xb9\xd9\x1fN\x1d\x02\x0eo\x08\xe0\xb3\x8f>\xe0\xa7\xd3'W\x99\xe9\xda\xa3\x86U\xe6\xbb\x1e\x04\x1b<_\x1do|w\xee\x8f\xd9_\x0e\x01\x87\x1b\x8d\xc6_\x1b\x01\x98\x9a\xfe\xf4\xe3\x7f\xf5sl}\xf25\x00\xe2\xb7\xda\x81\xff\xdd\xd7\xf1?M\xf0K\xb9\xe8F\x89\xaf\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x06m\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x064IDATx^\xad\x97[lT\xc7\x1d\xc6\x7fs\xce\xd9\x8b\xbd\xf6\xfa\x16\xa0\xbe\x00\x0e\xb2ic$BJ!\x22\xa1-\x95b\xa5/\xeeKh+\x95\xa6U\xa5\xc6`U\xaa\xda\xb4\xaa\xfaV\x09U\xca\x03\x94'\xda\x07\x84\x14)\xad\xc4\x8b\xa5R\x83y\x08\xc5\x189\x0ei\xd3\x84\x9a\x9bcj\xec\xb2\x04\x1b;\xbb\xf6z\x8f\xbd\xbb\xde\xb3g\xa6\xc3h\x85\xe5rl\x88\xc9'}\xfa\x9f\x9d\x87\xfd~\xf3\x9f\x99s\x11J)\x82$\x84x\x05x\x9e\xc7kH)\xf5w\xd6(' \xb8C\xbb\x01h\x97R\xbe\xc6cdY\xd6\x07\x1a\xf6\xbb@\xb7\x069\xff\x14\x00&\xfc\xb7\xed\xf5\xe2`]DDn\xce\x89\x8a+W\xaeP]S\x8d@\x00\xa0P\x08e(A)f\xd3i^\xa9\x17/\xbc\xb4Nl;\xf1\x1f\xb9G\x83|[CL;\x8f\x85D\x952\xe2\xb6\xc4\xb6\x04!!p>Sl\x8c;\x80D*\x04\xf0\x9c\x10\x02\xe0\xcb@\x05P\x0f4`\xc4Hi\x9f$\x02\x01N\x9c8!\x00\x81\x05\xd2\x87\x96\x96g\x09em\x14\xe5(\xa5\xb4A\x08XW\x19%\xe2\xd8DB\x16\xc3\x13s\x5c\xbc=A\xf7X\x8e\x5c$\xbe\xa9\xbd}\xf7\xef-\xcbZ\xdc\xb1cGYUU\x95\xd3\xd8\xd8\x18~\xe0\x86\x86\x86\xd0\xa5K\x97\xdc\xae\xae\xae\x08\xf0\xd6\xaa\x1d\x00\x13DU,\xc2s\xd51\xf2\x9eO\xa1(\x91Ja\x09A\xd8\xb1\x88\x86l\xe6r\x05\x12\xa2\x8e?\x9f\xff+\x0dM\x1b\x01\x22\xc0f\x96\x84\xef\xfbx\x9eGuu\xb5\x9ePK\xf4\xea\xd5\xab\x87\x84\x10(\xa5\xdeZ\x11\xc0\xb2A\x00\xb6-\x90\xda\xb6\x148\x08\xa4\x12X\xc2\x8c\x1b\x8fL\xb9\xec{\xf5;\xd476\x11|/\xc1\x84g2\x19\xca\xcb\xcb\xcdf>v\xec\xd8&\xbd\x7f\x0e.A,\x01\xd0\xd9\xd9\xa9\x0e\x1d:\xa4l!\x08Y\x10\xb6-\x1c\xc7\xc6BP\xb4\xcd\x1a\x1b\x00\xc7\xb2\x888\x96\xae\x02`Yx\x10\xc0\xdc\xdc\x1c555\x06 \x1a\x8dr\xe4\xc8\x91\xcd\xc0\x03\x88\x1b\x1a\xa2\xc7b\xb9\xb0mt0f\x8d\xcb#6\xb1\xa8\xa3\xc7,2\x8b\x1e\x93\x99\x1cc\xa9y\xee\xcc.\xe8\xdfEr\xf9<\xab\xc8,A6\x9b5\xa7f\xe9\xffm\x0e\x1c8\xb0\x1e\xe8\x00X\x06\xa0\xb4t\x16\x8e\x0d\xe1\x90\xc0S\x8a\xb1\xa4\xcb\x8d\x8c\x83\xd3\xb2\x97\xa6}\xaf\xb3\xb5\xe3\x17\xac\xdb\xfb:\x0d/\xb4s\xfb\xce$\xfd\xfd\xfd$\x93I\x94R\xe6\xfa\xf8\xf1\xe3\xe8\xba\xac3\xe7\xce\x9d\xe3\xe8\xd1\xa3\x1c>|\x98\xde\xde^\x12\x89\x84\x04,\xa1\x15\xdc\x01\xed\xff\xce\xe6\xf8\xe7\x94Ok\xc7\xcf\xf8\xe6/\xdf&\xf6\xf57\x99|\xa6\x83k\xfe.\xae\xf1-dk\x17\xad{\x7fN^Vs\xfaog\xd1wM\xee\xdc\x9d\xe2\x1b\xafvr\xfd\xfau\x03\xa0gk\xd6?\x16\x8b\x99\xebx<\x8e\xe38%8\x04\xc0#\x00\x96%\x98\xcaA:\xde\xca\xfe\xdf\xbdM\xd5\xae\xd7(\x84b\x08\xdbBY\x82lAr\x7ff\x91O\xeef\x18\xb8\xear\xfa\x1fad\xd5^\xae\x8f\xdcg2\xd7\xc6\x85\x0f\xee\x9b\x00\xed\x87\xa1\xcd\xcd\xcd\xb4\xb5\xb5\x19755\xa1\xa1\x14 \x83\x1fF\x16\xdcq\x15\xdf\xff\xe9o\xa8l\xd8H\xe2\xec;L\x8f^\xc3\x89\x94\xb1\xb5y\x07\x9b[\xb6\xf3Iy%c\x09\x97\xcff\xf2\xdc\x9d\xce2\xa1\xed\x88\x0dL'\xe7\xd8\xb7+\xca\xfa%\x003{=k\xea\xea\xea\x00\xccu*\x952\x00J+\x10\xa0\xb9Zp\xe1\x9dc(,\xca\xe6\xc6\xd9\x10\x8fR\x94\x92{\xc3}$e\x05\xdb\xda\x7fLM\xdb\xcb|<\x9cf\xd2_\xc0\xcdx,\xcck/x \x00\xb5t:B\xa1\x90\x09-\xdd\xea\x1f\x8e\x01*\xf8>`\xc1\xc6\xb8\xa0P\x1c#\x1c\x8bS\xb7\xa5\x96\x92xv}\x05\xe9\xac\xc7h\xff\x9f\x98\xae\xbcL\xcb\xf6\x83\xb8\x0ba\xbc\x82\xa4X\x94x\xda!\xc7B-\xaa\x80\xe3i\xa0\x96\xd5\x15\x01\x00\xd6\xc7C\x84\xca#\xfc\xbfjc!\x9e\xa9\x0cs\xe1\xdf\x83\xec\xd9\xf9\x13\xca\xa3\x0e\xb92G\x03(\x03ak\x00\x16K!\xa5\x1c%0*\x15\xa4\x5c\x05@X\xa5*\xcc\xf5#\xfapl\x86\xf1Y\x8f\xef\xfd\xfa\x8f\xdc\xca\xd4\xe0D\x5c\xa2\x11\x1b\xcf\x93\x14=\x07\xd3\x01\xa5\x90R\xf2PjY\x01V\x05\x10\x08L\x0d\x04\x18\x9dv\xf9\xd5_\x86\x18\xbd\xb7\x80=\x93g\xd3\xba2\xf2y_\xbbh\xea\xce\xaf\xd4p\xf9\xdd\xe0%\x00\x9ex\x09L\xb8\x10<\xa2\xd6/U\xf2\x87\x1f>\xcf\xf5O3D\x1b\xb7\xb1\xf3\xc5\x97Y\x12\x5cN`\x8e\xdbS\x01(\xc0\x12%\x00m\xd4R}\xb1\xb5\x96\xdd[\xe2t\xbf\x97\xa5j\xf7W\xf9\xd1\x1bo\x10\xa0\xb5\x03\x98\xb57\xd5\xd8\x08\x01\xd2\xcbSpSx\xf33\x14\xb3i\x0a\x19\x1f%\xfd\xd5\x82\xd6\x08\xf0\xf0)\xe7\xe3\xe73\x14\xe6u\xa8\x0e\xd6\x00\xcb\xf7\x89\x10\xc13}\xfa\xd7r\x8c\xb2\x137\x03\xc7\x01\xb2\x1e\xfe\xad\x94\xcco\xf7DT\x03\xd8_p\x07\x08\x92\x09\xfd\xd7=?\xfd~B\xa6\xcf\xdf\xf6\xef\x02\xeev;\xfc\x92\x06\xa8\xe3s\xcau]\x1fpW\xed\x00@2\xab\x0a\x1f~*\xd3\xbd\xb7\xfc\xd4\xcdi9\x05\xf4\x03\x97th\xbf\x10\xa2\xd3\xb6\xed\xaf}\x9e%XXX\xf0\x07\x06\x06\xd2'O\x9e\x9c\x06\xba\x83\x00>\x1aI\xca\xad\xe3\xb3*\xd7;\xe2\xa7nL\xcb\xd1R\xe8Y\x1dt\x8b\x00=\x09\xc0\xd0\xd0\x90\xdb\xd3\xd3\x93\xd2N\xcf\xce\xce\x9e.\xbd\x1d\xdf\x08\x02\xe8\xee\xea)\x00\x8c\x04\x84\x06\x85\xaf\x08055U\xd0/\x22\xa9S\xa7N%\xc7\xc7\xc7/\x03g\x81~\x1d\xec\xae\xb8\x09K\xdfv\xdaO&\x85\x01@\x08@aZ\xfc\xde\xe0`\xba\xbb\xbb;\xa5\xdf\x8a\xcc$\xd0^\xeds\xcda\xed\x9aw3n\x11`p\xf0\xfdt___\xfa\xcc\x993\xa6\xc5\xa5\xd0\x8fx\x02\x89\xb5\x9ec!D\x18x\x13\xd8Ois\x06\xb4\xf8\xb1\xfa\x1f\xbd\xfa*_\xf2\xd8\x15\x9d\x00\x00\x00\x00IEND\xaeB`\x82\x00\x00\x08\x19\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x00\x04gAMA\x00\x00\xd6\xd8\xd4OX2\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\xc9e<\x00\x00\x07\xabIDATX\xc3\xadW[P\x93g\x1a\xf6\xca\xce\xec\xcc\xf6b/\xbc\xd9\xe9\xce\xecn\xbd\xda\xd9\x9b\xb5\xce\xba;{\xb0\xad\xccz\xb1\xce\xce:\xb3vTpu\xdb\xe2\x81\xd6\xb6T\x04\xbb\xa5 m\xc1\x82\x06\x08\x07QB\x80\x80\x80\x02!\x81\x10\x92@H\x10s$!gr\x80\x04B \x9c\x09G\xb5Tx\xf6\xfb~\x13\x160X\x8b}g\x9e\xf9/\x92\xfc\xcf\xfb>\xcf\xfb\xbe\xdf\x97]\x00v\xfd\x98 \xf1\x0b\x82\x14\x02\x03\xc1u\x82\x03\xcf\xfd\xfe\x8fH\xbc\x9b \xe1W\xaf\xef\xb5*\x8c\xd6e\xdb\x02`\x19\x1e[\x09'\xf13\xfa\x19\x81\x22\xfc\xdc>vH~\x8a\xa0\xb9\xb6Y\x1c2\xcf\xadB9\xfe\x1dD\xf6Q\xd8\xc7\xe6\xe8\x87\x86={\xf6XSR\xae,\xca::\x10N\xe2\xe5I\xc3\xc41\x04\xb7>I\xf9,`\x9b]YSM\x03M\xb6\x114\xeb\xfb 1y`\x19\x9d\xc5\xbb\xef\xbe?\xc5\xab\xbe\x83\xf1\x89)LO\xcf\xae\x92\xef\xd7\xbct\x02\x11\x9f\x0f\xbe\x1d\xe3\xb2\x04CO\xb43@\x8b{\x06\xcd=.4\xeb\xec\xa8W\xf6 \x87S\x852^5C\xbc\xb0\xf4\x90\x81\xc1`\x5c&\xbfK|\xe1\x04H\x1c$8A\xfd\xdd\xeas'\xf1\xb9'\x04H\x87\x97\xc1\xd7\xbb \x22U7\xdc7\xa2\xb8N\x88,V>\xccV\xdb:q\x04,\x16k,\xfc\xce\xe7'\x10\x916\x93\x95?F}\xa5\xfe\x12\xc4o\xf4Y1\xb6\x02~\xef Z{\x9c\xe0?0\xa1L(CF\x0e\x1b\xb2\x0e\xf9&\xd2\xf9\xc5e\xcc-,!4\xbf\x88\xbd{\xf7Z\xc9;~\xbam\x02$~C\x90F=5\x13iu\xb3\x80\xd2?\x0f\xcb\xc4\xe2\x9aP\xa1Z\xb4l\xf1Y\xa0\xb6\xa0\xa6]\x8d/\xb2sq\xb7\x9e\xff\x0c1%\x9d\x09\xcdcbj\x06\x83C\x81'\xe4\xdd\xbc-\xd3\xb0;\x92\x033&\xd4S\xb5\xd3\xfbXO\x88\xc5\x03!\x88,CP\xbaF\xd0\xed\x09B\xe5\x9bB\x9bs\xfc\xa9\xcfZ\x1b\xee*t\xc8\xbc\xc9E\x09\xa7l\x93\xcf\x9b\x88'\xa7\x11\x18\x1d\xc3\x80o\x08\xa2\xd6\xd6%\xc2Q\xdb(\x12\x87\xc6\x1f\xaf\x82/b\x94M\x89$\x90\x22\xeaR-\x9aB\xab\xe8\x18y\x04\xa1\xc5\xcf\x10St\xf6\x0d\xa3\xd3\xe1\x87\xd4<\x80\x16\xbd\x03\x0d]\x06\x14\xd5\x0a\x90\x91\x95\x0d/y\xf1\xc6\xaa\xa9\xd4\xb3s\x0bL\xc5\x94\xd8\xdd\xef\x85\xc9b\x05\xb7\xbc\x12\xa5\xe5\x95K\x13\xf3\xcb\xab#\x0f\x017\xd9\x11\xe6\xd9\x15\x84\x97\x15\x13\x06\xcb<\xd0h\xf2\xa3\xdd\xee_'\x96;\x86 \xb3x\xd7}\xe6\x08\xa4\xf8<3\x1b*\x8d6\xaa\xdcS3!\x8c\x8e\x8d3\x15\xd3&\xe47\x09\xf1\xc1\xc5\x8fQs\xaf\x01\xbee`\xfc\x11\xa0#\x13#\xf2\xce\xa1\xbe]\xb9\xb8Q\x01\x83\x81ttM\xa7\x1e\x0ag\x80\xa9\xb8\xdd\xea\x83\xd8\xe8B\x93\xca\xcc\xf8|\xe5\xcb,\x88\xda$Q\x89\xa7g\xe7\x18\x1b\x86\x86G`w8I\x82:$|\xf8!\xae\xb3\x0b\xe1\x99\x5c\x80o\x09\xd0\x90\xde\xe1\x0f,\x81\xab\x1f\xc4}\xef\x04\xdd\x07\x1da\xeb\xff\x9f\xc0\x1d\xb9\x16\x1d\xf6!H\xcc\xfdO}\xee\xd4\x22\x9dU\x84\xaa\x9a\xbaM>G\xe4\x8e\xf8<<\x12\x84\xd3\xdd\x0f\xbd\xc1\x88\xc2\xe2b\x9c~/\x1e=\x03\x01\xf4/\x02\x83\x84\xbc\xc5\xff-\xee:C(Q\x91\xf7\xf6\x05\xf1N\xdc\xbf}\x843i\xe3 \x18\xf43\xab\xe0\xc9Th58\xd1\xd8\xdd\x0b\x9eX\x89\xac\x5c\xf63>G\xaa\x9e\x9c\x9ee\xe4\xee\xf7\x0e\xa2\xd7lAC\x03\x1f'b\xe3 \xe9\xd6\xc0E\xcf\x01R\x90$\xb8\x86\xb2\x9e\x00n\xb4\xdbP\xd1\x1bD\x85\xce\x8bJ~\x0bm\xbe\x9b['\xd1\xa0\x99\xf8\x16e\x22\x05\xee)\xf4(\x13\xc8\x90x5\x0b\x1a\xad>\xaa\xdcc\x13\x93\xf0\x0d\x0d\xc3f\xef\x83\xb4]\x8e\xc4K\x97\x90\xc3\xca\xc3\xd4c\xc0NzI1N\xfa\x89\x94\x7f[;\x84|\x85\x13%j\x1fJ\xd5\x03\xe8\xf20\xa3(\x22\xf8\xf93\x09t\x8f.\xa1\xa8\xbe\x15\xa5|\x09\xb2J*\xf0\xcf\xe3qQ\xe5\xf6\x07F\xd1\xe7\xf2@\xab7 \xfdj\x06\x92\xbfH\x83\xcd7\x02'\xa9\xda@\x1aL\xe0{\x88R\x9d\x1fE\xdd\xfd\x0cqA\x97\x1b\xc5\xdd\x1e\x88\x9cA\xfc\xf9\xcd\xb7]\x84\xebl\xb4C\xd0(\xf7N#\xa7\xfc\x1e\xb2K\xab\xf1Q\xeaWH\xfeo\xea\xfaXQ\xb9G\x82\xe3\xf0\x0c\xf8`4\x99Q\xc9\xab\xc2\xfbg\xcfA\xfe@\x03?\xe9n\xb2\x8d\x19\xb9oi\x06\x19\xd2\x9b*/r\xe5\x0e\xe4u\xf6\xa1\xf0\xbe\x1b\x1c\x95\x1b\xf9\x9c\xca)\xc2S\xb8\xdd)\xdc+v\x04\x90Q\xc8\xc5\x95ky8\x11\x9f\x80\x9b\xb7n3c\x15\x91\xdbjs@\x22m\xc7\x85\x84\x0fPt\xbb\x0c\xf3+\x80\x9f4X\xf7$ \x1c|\x84J\xd3\x188\xfaa\x86\x9cV\xfdU\xb3\x1e\xac\x0e;\xb8:\x1f\xd9!\x1ez/\xe0\x13\xbc\xba]\x02&\xbe\xc1\x83\x94o\xd88\x9f\x9c\x8a\x03\x7f=\x04c\xaf\x99\xe9n*\xb7F\xd7\x83\xa4\xcb\xc9H\xff:\x8b\x8c\xd5\xc7\xd1\xd83\xf881\x09\x86^\x13\x1a\x9b\x04\xf8\xdd\x1b\xfbQO\xd4\xf1\x90\x99\xee\x9a\x00\xaa\xad\x93`+]\x0c9\xf5\xbc\xf0\xbeg\xbd\xea\xcc\x16=JU\x1e\x08m\x01\x94\xd4\xf1C\xe1eS@\xf0\xca\xf7%`+nj\xc7\xa9\x84D\xc4\x1c9\x8a\xdc|6ZZ\xc58\x14\x13\x83/95\xc8\x14j\x98\xe6\xa2\xd5\xd2'\xf5\x9azL\x13\xa1Id\xb7\x99\x90\xdbnF\xb9\xda\x8d\x06\xa5v9,9=\xf9N\x13\xec\xd9r\xd4G\x0d;\xabF\x88c\xff9\x8f\xdf\xee\xfb=\x1a\xf9\x02\x9c\xbf\x90\x80\x93\xf1\x17p\xa3\xad\x07\x19\xc4OJ\x14\xe9n\xbaX\xa8\xef,\xfa\x94\x98P(\xb7@\xe9\x0e<\xf9W\xec)*w-\xc1g\x04\xfb\xb6\xb9\xe4D\x8d\xbe\xcc\xb2Z\xfc\xe3\xe4\x19\x1c<\xf47\xb0r\xf3\xb0\xef\xc0\x1fP \xd1!\x89'e*\xa6K\x85>\xbf!\xd5F\xe4.\x90[!\xb0\x0c\xae\xe5\xdc\xe2\xd2\x11\x13\x13\xe4\x87o<\xaf<\xe7\x96\x155\x9ciE\xe5\xf8\xfb\xb1X\x1c?\x19\x877\xf6\xef\xc7\x8d:\x11\x92\xab\xa4\x0c!\xedp\xea5U!\x8b4[\xc9\x037*4n\xd4I:\x17\xc3rs\x08\x8em\x95\xfb\x87$\xe0Jesp\xe4\xf8)\x1c>|\x98\x8cc.2\x05*\x5c\x22\xd5\xd3]~M\xdc\x0b6\xe9tv\xa7\x1dw\x8c\xe4\x88\xb6\xf9\x9e\x84\xb7\x1a\x95\xfb\x22\xbdI\xfd\x80\x0bm\xf4\x042JxL\x0f\x9cKI\xc3\xb5\xa6.|\xc2me6Y\xf1\x83\x01\x5c\x97\x9a\xc1Q{ \xf3\x04\xd7\xce%&\x056\xc8\xfd\xc7\x9d\xc8\x1d\xd5\x82\xdc\x1a\x01\xce^NE\x81X\x85x\xf6]\x5c\xa9U\x90\xaa\xfb\xc0\x96\xdbP\xadu\xe3\xaeTA/\x10\xca\x0dr\xbf\xba\xd3j\xa3\x05\xb7\xa2Q\xf8\x1d\xafC\x8dO\xb9-\x88\xcb\xe6\xe1\x9aH\x8f\xaa\x1e/\x9a5\xe6\xc7\x7fz\xf3-Wx\xac\xa8\xdc\xaf\xbd\xac\xdc\xd1\xe2\x08\xdd\x05\x5cu\x1f\xde\xcb\xafE\xb9v\x002g`\xf5\xc2\xa7\x97\xa9\xdc\xf7\x08\xd2\xa9\xdc;\xf8\x03\xf3\xc2\xf1\x13\x82\xca\x1c\xee\x9dP\x0b9\x94\xb8\x0d\xc2\xc8\x16\xa3\x17\x87\xc3/\x22\xf7\x0e\xff\xdam\x8a\xdda\x99\xd5\x1b\xb6\xd8k\xbb^2\xbe/\x89\xff\x01f\xb9_\xfc\x11\x80=\xcf\x00\x00\x00\x00IEND\xaeB`\x82" 13 | qt_resource_name = "\x00\x06\x07\x03}\xc3\x00i\x00m\x00a\x00g\x00e\x00s\x00\x07\x04\xcaW\xa7\x00n\x00e\x00w\x00.\x00p\x00n\x00g\x00\x08\x06|Z\x07\x00c\x00o\x00p\x00y\x00.\x00p\x00n\x00g\x00\x07\x0a\xc7W\x87\x00c\x00u\x00t\x00.\x00p\x00n\x00g\x00\x08\x08\xc8Xg\x00s\x00a\x00v\x00e\x00.\x00p\x00n\x00g\x00\x09\x0a\xa8\xbaG\x00p\x00a\x00s\x00t\x00e\x00.\x00p\x00n\x00g\x00\x08\x06\xc1Y\x87\x00o\x00p\x00e\x00n\x00.\x00p\x00n\x00g" 14 | qt_resource_struct = "\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\x00\x00\x01\x00\x00\x03X\x00\x00\x00~\x00\x00\x00\x00\x00\x01\x00\x00\x18\xdd\x00\x00\x00P\x00\x00\x00\x00\x00\x01\x00\x00\x0d\xc5\x00\x00\x00f\x00\x00\x00\x00\x00\x01\x00\x00\x12l\x00\x00\x00<\x00\x00\x00\x00\x00\x01\x00\x00\x08\x96" 15 | def qInitResources(): 16 | QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) 17 | 18 | def qCleanupResources(): 19 | QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) 20 | 21 | qInitResources() 22 | -------------------------------------------------------------------------------- /data/templates/qt_gui_application/images/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/data/templates/qt_gui_application/images/copy.png -------------------------------------------------------------------------------- /data/templates/qt_gui_application/images/cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/data/templates/qt_gui_application/images/cut.png -------------------------------------------------------------------------------- /data/templates/qt_gui_application/images/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/data/templates/qt_gui_application/images/new.png -------------------------------------------------------------------------------- /data/templates/qt_gui_application/images/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/data/templates/qt_gui_application/images/open.png -------------------------------------------------------------------------------- /data/templates/qt_gui_application/images/paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/data/templates/qt_gui_application/images/paste.png -------------------------------------------------------------------------------- /data/templates/qt_gui_application/images/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DamnWidget/SublimePySide/c7db00113aa711a5e4f19a08293868a5bf28e160/data/templates/qt_gui_application/images/save.png -------------------------------------------------------------------------------- /data/templates/qt_quick_application/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | ${PyQT_API_CHECK}from ${QT_LIBRARY}.QtCore import QUrl 4 | from ${QT_LIBRARY}.QtGui import QApplication 5 | from ${QT_LIBRARY}.QtDeclarative import QDeclarativeView 6 | 7 | if __name__ == "__main__": 8 | app = QApplication([]) 9 | view = QDeclarativeView() 10 | 11 | ctxt = view.rootContext() 12 | ctxt.setContextProperty("myModel", "Hello ${APP_NAME}") 13 | 14 | url = QUrl('view.qml') 15 | view.setSource(url) 16 | 17 | view.engine().quit.connect(lambda: app.quit()) 18 | 19 | view.show() 20 | 21 | sys.exit(app.exec_()) 22 | -------------------------------------------------------------------------------- /data/templates/qt_quick_application/view.qml: -------------------------------------------------------------------------------- 1 | import Qt 4.7 2 | 3 | ListView { 4 | width: 360 5 | height: 360 6 | model: myModel 7 | Text { 8 | text: myModel 9 | anchors.centerIn: parent 10 | } 11 | MouseArea { 12 | anchors.fill: parent 13 | onClicked: { 14 | Qt.quit(); 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /data/templates/qt_quick_ui/main.qml: -------------------------------------------------------------------------------- 1 | // import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 2 | import QtQuick 1.1 3 | 4 | Rectangle { 5 | width: 360 6 | height: 360 7 | Text { 8 | anchors.centerIn: parent 9 | text: "Hello ${APP_NAME}" 10 | } 11 | MouseArea { 12 | anchors.fill: parent 13 | onClicked: { 14 | Qt.quit(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/templates/qt_quick_ui/quickui.qmlproject: -------------------------------------------------------------------------------- 1 | /* File generated by SublimePySide */ 2 | 3 | import QmlProject 1.1 4 | 5 | Project { 6 | mainFile: "main.qml" 7 | 8 | 9 | /* Include .qml, .js, and image files from current directory and subdirectories */ 10 | QmlFiles { 11 | directory: "." 12 | } 13 | JavaScriptFiles { 14 | directory: "." 15 | } 16 | ImageFiles { 17 | directory: "." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/templates/qt_unit_test/test_main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import unittest 5 | 6 | ${PyQT_API_CHECK}from ${QT_LIBRARY}.QtGui import * 7 | 8 | 9 | class QAppPresence(unittest.TestCase): 10 | 11 | def testQApp(self): 12 | #QtGui.qApp variable is instance of QApplication 13 | self.assert_(isinstance(qApp, QApplication)) 14 | 15 | 16 | def main(): 17 | app = QApplication([]) 18 | unittest.main() 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /data/templates/template.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "${PATH}" 6 | } 7 | ], 8 | "settings": 9 | { 10 | "rope_autoimport_modules": 11 | [ 12 | "${QT_LIBRARY}.*" 13 | ] 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /data/templates/templates.lst: -------------------------------------------------------------------------------- 1 | Qt Quick Application: Creates a Qt Quick application project that can contain both QML and Python code and includes a QDeclarativeView 2 | Qt Quick UI: Creates a Qt Quick UI project with a single QML file that contains the main view 3 | Qt Gui Application: Creates a Qt application for the desktop. Includes a Qt Designer-based main window 4 | Qt Console Application: Creates a project containing a single main.py file with a stub implementation 5 | Qt Unit Test: Creates a QTestLib-based unit test for a feature or a class. Unit tests allow you to verify that the code is fit for use and that there are no regressions 6 | Git Repository Clone: Clones a Git repository and tries to load the contained project 7 | -------------------------------------------------------------------------------- /sublime_pyside.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | 3 | # Copyright (C) 2012 - Oscar Campos 4 | # This plugin is Free Software see LICENSE file for details 5 | 6 | """ 7 | Sublime PySide adds support for Digia's PySide and Riberbancks PyQt libraries 8 | """ 9 | 10 | import os 11 | import sys 12 | import shutil 13 | import functools 14 | import threading 15 | import subprocess 16 | from glob import glob 17 | 18 | import sublime 19 | import sublime_plugin 20 | 21 | try: 22 | import rope 23 | import ropemate 24 | assert ropemate 25 | from rope.base.exceptions import RopeError, ResourceNotFoundError 26 | ROPE_SUPPORT = True 27 | except ImportError: 28 | ROPE_SUPPORT = False 29 | 30 | 31 | if sys.version_info < (3, 3): 32 | from converter import pyqt2pyside, pyside2pyqt 33 | from converter.base import sip_api_2 34 | SUBLIME_TEXT_3 = False 35 | else: 36 | from PySide.converter import pyqt2pyside, pyside2pyqt 37 | from PySide.converter.base import sip_api_2 38 | SUBLIME_TEXT_3 = True 39 | 40 | 41 | # ============================================================================= 42 | # Sublime Plugin subclasses 43 | # ============================================================================= 44 | class CreateQtProjectCommand(sublime_plugin.WindowCommand): 45 | """ 46 | Creates a new PySide/PyQt4 application from a template 47 | """ 48 | 49 | def __init__(self, window): 50 | """Constructor 51 | """ 52 | 53 | sublime_plugin.WindowCommand.__init__(self, window) 54 | self.window = window 55 | 56 | def run(self): 57 | """WindowCommand entry point 58 | """ 59 | 60 | CreateQtProjectThread(self.window).start() 61 | 62 | 63 | class ConvertPyQt42PySideCommand(sublime_plugin.TextCommand): 64 | """Converts a PyQt4 buffer to PySide syntax 65 | """ 66 | 67 | def __init__(self, *args, **kwargs): 68 | sublime_plugin.TextCommand.__init__(self, *args, **kwargs) 69 | 70 | def run(self, edit): 71 | """Run the command""" 72 | 73 | if SUBLIME_TEXT_3 is False: 74 | PyQt42PySideWorker(self.view).start() 75 | else: 76 | PyQt42PySideWorker(self.view, edit).run() 77 | 78 | def is_enabled(self): 79 | """Determine if this command is enabled 80 | """ 81 | 82 | if 'from PyQt4' in self.view.substr( 83 | sublime.Region(0, self.view.size()) 84 | ): 85 | return True 86 | 87 | return False 88 | 89 | 90 | class ConvertPySide2PyQt4Command(sublime_plugin.TextCommand): 91 | """Converts a PySide buffer to PyQt4 syntax 92 | """ 93 | 94 | def __init__(self, *args, **kwargs): 95 | sublime_plugin.TextCommand.__init__(self, *args, **kwargs) 96 | 97 | def run(self, edit): 98 | """Run the command 99 | """ 100 | 101 | if SUBLIME_TEXT_3 is False: 102 | PySide2PyQt4Worker(self.view).start() 103 | else: 104 | PySide2PyQt4Worker(self.view, edit).run() 105 | 106 | def is_enabled(self): 107 | """Determine if this command is enabled 108 | """ 109 | 110 | if 'from PySide' in self.view.substr( 111 | sublime.Region(0, self.view.size()) 112 | ): 113 | return True 114 | 115 | return False 116 | 117 | 118 | class OpenFileInDesignerCommand(sublime_plugin.WindowCommand): 119 | """Open the actual view buffer in Qt Designer if is a valid ui file 120 | """ 121 | 122 | def run(self): 123 | """Run the command 124 | """ 125 | 126 | command = QtDesignerCommand(self.window) 127 | command.open_file_in_designer() 128 | 129 | def is_enabled(self): 130 | """Determine if this command is enbaled in determinate conditions 131 | """ 132 | 133 | if self.window.active_view() is None: 134 | return False 135 | 136 | file_name = self.window.active_view().file_name() 137 | if file_name is not None: 138 | return self.window.active_view().file_name().endswith('.ui') 139 | 140 | return False 141 | 142 | 143 | class NewDialogCommand(sublime_plugin.WindowCommand): 144 | """Create a new dialog with buttons at bottom for QtDesigner 145 | """ 146 | 147 | def run(self, dirs=[]): 148 | """Run the command 149 | """ 150 | 151 | command = QtDesignerCommand(self.window) 152 | command.new_dialog(dirs, buttons=True, position='right') 153 | 154 | def is_enabled(self): 155 | """Determine if this command is enbaled in determinate conditions 156 | """ 157 | 158 | designer = get_settings('sublimepyside_qt_tools_map').get('designer') 159 | if designer is None: 160 | return False 161 | 162 | return True 163 | 164 | 165 | class OpenQdbusviewerCommand(sublime_plugin.WindowCommand): 166 | """Open the QDbusViewer application 167 | """ 168 | 169 | def run(self): 170 | """Run the command 171 | """ 172 | 173 | QDBusViewerCommand() 174 | 175 | 176 | class OpenLinguistCommand(sublime_plugin.WindowCommand): 177 | """Open the Qt Linguist application 178 | """ 179 | 180 | def run(self): 181 | """Run the command 182 | """ 183 | 184 | LinguistCommand().open_linguist() 185 | 186 | 187 | class OpenInLinguistCommand(sublime_plugin.WindowCommand): 188 | """Open a TS or QM file with Qt Linguist 189 | """ 190 | 191 | def run(self): 192 | """Run the command 193 | """ 194 | 195 | LinguistCommand().open_file_in_linguist(self.window.active_view()) 196 | 197 | def is_enabled(self): 198 | """Determine if this command is enabled or not 199 | """ 200 | 201 | if (self.window.active_view() is not None and ( 202 | self.window.active_view().file_name().endswith('.ts') 203 | or self.window.active_view().file_name().endswith('.qm'))): 204 | return True 205 | 206 | return False 207 | 208 | 209 | class GenerateTranslationsCommand(sublime_plugin.WindowCommand): 210 | """Generate Qt Linguist TS files 211 | """ 212 | 213 | def run(self, files=[], dirs=[]): 214 | """Run the command 215 | """ 216 | 217 | if not files and not dirs: 218 | sublime.error_message( 219 | 'You have to call this function from the side bar context menu' 220 | ) 221 | return 222 | 223 | PySideLupdateCommand(self.window).generate_translations(files, dirs) 224 | 225 | def is_enabled(self, files=[], dirs=[]): 226 | """Determine if the command is enabled 227 | """ 228 | 229 | if files: 230 | for filename in files: 231 | if filename.endswith('.py') or filename.endswith('.pro'): 232 | return True 233 | 234 | for dirname in dirs: 235 | for filename in glob('{}/{}'.format(dirname, '*[.py,.pro]')): 236 | if filename.endswith('.py') or filename.endswith('.pro'): 237 | return True 238 | 239 | return False 240 | 241 | 242 | class CompileCommons: 243 | """Compile commons methods and parameters 244 | """ 245 | 246 | def is_enabled(self, files=[]): 247 | """Determine if a command is enabled 248 | """ 249 | 250 | if not files: 251 | if (not self.window.active_view() or not 252 | self.window.active_view().file_name().endswith(self.ext)): 253 | return False 254 | else: 255 | for filename in files: 256 | if not filename.endswith(self.ext): 257 | return False 258 | 259 | return True 260 | 261 | 262 | class CompileResourceCommand(sublime_plugin.WindowCommand, CompileCommons): 263 | """Compile Qt Resources 264 | """ 265 | 266 | def run(self, files=[]): 267 | """Run the command 268 | """ 269 | 270 | if not files: 271 | if (not self.window.active_view() or not 272 | self.window.active_view().file_name().endswith('.qrc')): 273 | sublime.error_message( 274 | 'This command will process QRC files only.' 275 | ) 276 | else: 277 | RCCCommand(self.window).compile() 278 | else: 279 | for filename in files: 280 | RCCCommand(self.window).compile(filename) 281 | 282 | def is_enabled(self, files=[]): 283 | """Determine if the command is enabled 284 | """ 285 | 286 | self.ext = '.qrc' 287 | return CompileCommons.is_enabled(self, files) 288 | 289 | 290 | class CompileUiCommand(sublime_plugin.WindowCommand, CompileCommons): 291 | """Compile Qt UI files 292 | """ 293 | 294 | def run(self, files=[]): 295 | """Run the command 296 | """ 297 | 298 | if not files: 299 | PyUicCommand(self.window).compile() 300 | else: 301 | for filename in files: 302 | PyUicCommand(self.window).compile(filename) 303 | 304 | def is_enabled(self, files=[]): 305 | """Determine if the command is enabled 306 | """ 307 | 308 | self.ext = '.ui' 309 | return CompileCommons.is_enabled(self, files) 310 | 311 | 312 | class PreviewUiCommand(sublime_plugin.WindowCommand): 313 | """Preview an UI file 314 | """ 315 | 316 | def run(self): 317 | """Run the command 318 | """ 319 | 320 | PyUicCommand(self.window).preview() 321 | 322 | def is_enabled(self): 323 | """Determine if the command is enabled 324 | """ 325 | 326 | if self.window.active_view() is not None: 327 | if self.window.active_view().file_name().endswith('.ui'): 328 | return True 329 | 330 | return False 331 | 332 | 333 | # ============================================================================= 334 | # Thread working classes 335 | # ============================================================================= 336 | class CreateQtProjectThread(threading.Thread): 337 | """ 338 | Worker that creates a new application from a template 339 | """ 340 | def __init__(self, window): 341 | self.window = window 342 | self.tplmanager = TplManager( 343 | sublime.packages_path(), 344 | get_settings('sublimepyside_package'), 345 | get_settings('sublimepyside_data_dir') 346 | ) 347 | 348 | self.folders = self.window.folders() 349 | self.proj_dir = None 350 | self.proj_name = None 351 | self.proj_library = get_settings('sublimepyside_library') 352 | self.library_options = ['Use Digia\'s PySide', 'Use RiverBank PyQt4'] 353 | 354 | threading.Thread.__init__(self) 355 | 356 | def run(self): 357 | """ 358 | Starts the thread 359 | """ 360 | 361 | def show_quick_pane(): 362 | """Just a wrapper to get set_timeout on OSX and Windows""" 363 | if not self.tplmanager.get_template_list(): 364 | sublime.error_message( 365 | "{0}: There are no templates to list.".format(__name__)) 366 | return 367 | 368 | self.window.show_quick_panel( 369 | list(self.tplmanager.get_template_list()), self.tpl_selected) 370 | 371 | sublime.set_timeout(show_quick_pane, 10) 372 | 373 | def tpl_selected(self, picked): 374 | """ 375 | This method is called when user pickup a template from list 376 | """ 377 | if picked == -1: 378 | return 379 | 380 | tpl_list = list(self.tplmanager.get_template_list()) 381 | self.tplmanager.selected = tpl_list[picked].split('::')[0] 382 | 383 | suggest = self.folders[0] if self.folders else os.path.expanduser('~') 384 | self.window.show_input_panel( 385 | 'Project root:', suggest, self.entered_proj_dir, None, None) 386 | 387 | def entered_proj_dir(self, path): 388 | """Called when user select an option in the quick panel""" 389 | if not os.path.exists(path): 390 | if sublime.ok_cancel_dialog( 391 | '{path} dont exists.\nDo you want to create it now?'.format( 392 | path=path)): 393 | os.makedirs(path) 394 | else: 395 | return 396 | 397 | if not os.path.isdir(path): 398 | sublime.error_message( 399 | "{path} is not a directory".format(path=path)) 400 | return 401 | 402 | self.proj_dir = path 403 | 404 | self.window.show_input_panel( 405 | 'Give me a project name :', 'MyProject', self.entered_proj_name, 406 | None, None 407 | ) 408 | 409 | def entered_proj_name(self, name): 410 | """Called when the user enter the project name""" 411 | if not name: 412 | sublime.error_message("You must use a project name") 413 | return 414 | 415 | self.proj_name = name 416 | 417 | if not get_settings('sublimepyside_library_ask', bool): 418 | self.generate_project() 419 | else: 420 | self.window.show_quick_panel( 421 | self.library_options, self.library_selected) 422 | 423 | def library_selected(self, picked): 424 | """Sets the selected library or PySide if none""" 425 | if picked == -1: 426 | self.proj_library = 'PySide' 427 | return 428 | 429 | self.proj_library = 'PyQt4' if picked == 1 else 'PySide' 430 | self.generate_project() 431 | 432 | def generate_project(self): 433 | """Generate the PySide or PyQt project""" 434 | 435 | project_library = ( 436 | PySideProject if self.proj_library == 'PySide' else PyQt4Project 437 | ) 438 | 439 | project = project_library( 440 | self.proj_dir, self.proj_name, self.tplmanager 441 | ) 442 | 443 | if self.tplmanager.is_valid(self.tplmanager.get_selected()): 444 | project.generate_project() 445 | 446 | project.generate_st2_project() 447 | if SUBLIME_TEXT_3 is False: 448 | project.generate_rope_project() 449 | 450 | subprocess.Popen( 451 | [ 452 | sublime_executable_path(), 453 | '--project', 454 | '%s/%s.sublime-project' % (self.proj_dir, self.proj_name) 455 | ] 456 | ) 457 | else: 458 | sublime.error_message( 459 | 'Could not create Qt Project files for template "{0}"'.format( 460 | self.tplmanager.get_selected()) 461 | ) 462 | 463 | 464 | # ============================================================================= 465 | # Sublime Text 2 specific code 466 | # ============================================================================= 467 | if SUBLIME_TEXT_3 is False: 468 | class ConversionWorker(threading.Thread): 469 | """ 470 | Base worker class for PySide <--> PyQt4 converters 471 | 472 | This is only used in Sublime Text 2 473 | """ 474 | def __init__(self, view): 475 | threading.Thread.__init__(self) 476 | self.view = view 477 | 478 | def run(self): 479 | """ 480 | Starts the thread 481 | """ 482 | 483 | def show_conversion_confirmation(): 484 | """Shows a confirmation dialog and proceed if true""" 485 | 486 | if self.__class__.__name__ == 'PyQt42PySideWorker': 487 | library = 'PySide' 488 | else: 489 | library = 'PyQt4' 490 | 491 | if sublime.ok_cancel_dialog( 492 | 'Do you really want to convert this file to %s' % library 493 | ): 494 | self.qt_conversion() 495 | 496 | sublime.set_timeout(show_conversion_confirmation, 10) 497 | 498 | def qt_conversion(self): 499 | """Must be reimplemnted""" 500 | 501 | raise NotImplementedError('qt_conversion not implemented yet') 502 | # ============================================================================= 503 | # Sublime Text 3 specific code 504 | # ============================================================================= 505 | else: 506 | class ConversionWorker(object): 507 | """ 508 | Base worker class for PySide <--> PyQt4 converters 509 | 510 | This is only used in Sublime Text 3 511 | """ 512 | def __init__(self, view): 513 | self.view = view 514 | 515 | def run(self): 516 | """ 517 | Starts the thread 518 | """ 519 | 520 | def show_conversion_confirmation(): 521 | """Shows a confirmation dialog and proceed if true""" 522 | 523 | if self.__class__.__name__ == 'PyQt42PySideWorker': 524 | library = 'PySide' 525 | else: 526 | library = 'PyQt4' 527 | 528 | if sublime.ok_cancel_dialog( 529 | 'Do you really want to convert this file to %s' % library 530 | ): 531 | self.qt_conversion() 532 | 533 | show_conversion_confirmation() 534 | 535 | def qt_conversion(self): 536 | """Must be reimplemnted""" 537 | 538 | raise NotImplementedError('qt_conversion not implemented yet') 539 | 540 | 541 | class PyQt42PySideWorker(ConversionWorker): 542 | """ 543 | Worker class to convert PyQt4 buffer to PySide Syntax. 544 | 545 | Note that there is not automatically conversion from PyQt API 1 546 | to PySide yet so you should remove all the QVariant stuff yourself. 547 | 548 | This class is only used in Sublime Text 2 549 | """ 550 | def __init__(self, view, edit=None): 551 | ConversionWorker.__init__(self, view) 552 | self.edit = edit 553 | 554 | def qt_conversion(self): 555 | """Converts Qt code""" 556 | pyqt2pyside.Converter(self.view).convert(self.edit) 557 | self.remove_api_imports() 558 | 559 | def remove_api_imports(self): 560 | """Remove api conversions for PyQt4 API 2""" 561 | 562 | # line_one = self.view.find('import sip', 0) 563 | line_one = self.view.find('# PyQT4 API 2 SetUp.', 0) 564 | if not line_one: 565 | line_one = self.view.find('from sip import setapi', 0) 566 | 567 | # At this point we already changed PyQt4 occurrences to PySide 568 | line_two = self.view.find('from PySide', 0) 569 | if not line_two: 570 | line_two = self.view.find('import PySide', 0) 571 | 572 | if not line_one or not line_two: 573 | return 574 | 575 | region = sublime.Region(line_one.a, self.view.line(line_two).a) 576 | 577 | edit = self.view.begin_edit() if self.edit is None else self.edit 578 | self.view.erase(edit, region) 579 | # self.view.insert(edit, line_one.a, '\n') 580 | self.view.end_edit(edit) 581 | 582 | 583 | class PySide2PyQt4Worker(ConversionWorker): 584 | """ 585 | Worker class to convert PySide buffer to PyQt4 Syntax. 586 | 587 | The conversion is just to PyQt4 API 2 so if you're running Python 3 588 | just remove the explicit api conversion lines. 589 | 590 | This class is only used in Sublime Text 2 591 | """ 592 | def __init__(self, view, edit=None): 593 | ConversionWorker.__init__(self, view) 594 | self.edit = edit 595 | 596 | def qt_conversion(self): 597 | """Converts Qt code""" 598 | pyside2pyqt.Converter(self.view).convert(self.edit) 599 | self.insert_api_imports() 600 | 601 | def insert_api_imports(self): 602 | """Insert api conversions for PyQt4 API 2""" 603 | 604 | pyqt4import = self.view.find('from PyQt4', 0) 605 | if not pyqt4import: 606 | pyqt4import = self.view.find('import PyQt4', 0) 607 | if not pyqt4import: 608 | return 609 | 610 | prior_lines = self.view.lines(sublime.Region(0, pyqt4import.a)) 611 | insert_import_str = '\n' + sip_api_2 + '\n' 612 | existing_imports_str = self.view.substr( 613 | sublime.Region(prior_lines[0].a, prior_lines[-1].b)) 614 | 615 | if insert_import_str.rstrip() in existing_imports_str: 616 | return 617 | 618 | insert_import_point = prior_lines[-1].a 619 | 620 | edit = self.edit if self.edit is not None else self.view.begin_edit() 621 | self.view.insert(self.edit, insert_import_point, insert_import_str) 622 | self.view.end_edit(edit) 623 | 624 | 625 | # ============================================================================= 626 | # Classes 627 | # ============================================================================= 628 | class Project(object): 629 | """ 630 | Project class for Sublime Text 2 and SublimeRope Projects 631 | """ 632 | 633 | def __init__(self, root, name, tplmanager): 634 | super(Project, self).__init__() 635 | 636 | if sublime.platform() == 'windows': 637 | # os.path.normpath is not working 638 | root = root.replace('\\', '/') 639 | 640 | self.root = root 641 | self.name = name 642 | self.tplmanager = tplmanager 643 | self.ropemanager = RopeManager() 644 | self.lib = None 645 | 646 | def generate_rope_project(self): 647 | """ 648 | Create Rope project structure 649 | """ 650 | 651 | if not self.ropemanager.is_supported(): 652 | return 653 | 654 | self.ropemanager.create_project(self.root) 655 | 656 | def generate_st2_project(self): 657 | """ 658 | Create Sublime Text 2 project file 659 | """ 660 | 661 | file_name = '{0}/{1}.sublime-project'.format(self.root, self.name) 662 | with open(file_name, 'w') as fdescriptor: 663 | template_name = '{0}/template.sublime-project'.format( 664 | self.tplmanager.get_template_dir()) 665 | 666 | with open(template_name, 'r') as fhandler: 667 | file_buffer = fhandler.read().replace( 668 | '${PATH}', self.root).replace('${QT_LIBRARY}', self.lib) 669 | 670 | fdescriptor.write(file_buffer) 671 | 672 | def generate_project(self): 673 | """ 674 | Create the project files 675 | """ 676 | 677 | templates_dir = '{0}/{1}/*'.format( 678 | self.tplmanager.get_template_dir(), 679 | self.tplmanager.get_selected(True) 680 | ) 681 | 682 | for tpl in glob(templates_dir): 683 | path = '{0}/{1}'.format(self.root, os.path.basename(tpl)) 684 | 685 | if os.path.isdir(tpl): 686 | sublime.status_message('Copying {0} tree...'.format(tpl)) 687 | try: 688 | shutil.copytree(tpl, path) 689 | except OSError as error: 690 | if error.errno != 17: 691 | message = '%d: %s' % (error.errno, error.strerror) 692 | sublime.error_message(message) 693 | continue 694 | 695 | with open(tpl, 'r') as fhandler: 696 | app_name = ( 697 | self.name.encode('utf-8') 698 | if SUBLIME_TEXT_3 is False else self.name 699 | ) 700 | 701 | file_buffer = fhandler.read().replace( 702 | '${APP_NAME}', app_name).replace( 703 | '${QT_LIBRARY}', self.lib).replace( 704 | '${PyQT_API_CHECK}', self.pyqt_api_check()) 705 | 706 | with open(path, 'w') as fhandler: 707 | fhandler.write(file_buffer) 708 | sublime.status_message('Copying {0} file...'.format(tpl)) 709 | 710 | def pyqt_api_check(self): 711 | """ 712 | If PyQt4 is used then we add API 2 713 | """ 714 | 715 | if self.lib == 'PyQt4': 716 | return sip_api_2 717 | 718 | return '' 719 | 720 | 721 | class PySideProject(Project): 722 | """ 723 | PySide Qt Project 724 | """ 725 | 726 | def __init__(self, root, name, manager): 727 | super(PySideProject, self).__init__(root, name, manager) 728 | 729 | self.lib = 'PySide' 730 | 731 | 732 | class PyQt4Project(Project): 733 | """ 734 | PyQt4 Qt Project 735 | """ 736 | 737 | def __init__(self, root, name, manager): 738 | super(PyQt4Project, self).__init__(root, name, manager) 739 | 740 | self.lib = 'PyQt4' 741 | 742 | 743 | class TplManager(object): 744 | """ 745 | SublimePySide TemplateManager class 746 | """ 747 | 748 | def __init__(self, packagespath, packagedir=None, datadir=None): 749 | super(TplManager, self).__init__() 750 | 751 | self.packagespath = packagespath 752 | self.packagedir = packagedir 753 | self.datadir = datadir 754 | self.selected = None 755 | 756 | def is_valid(self, template): 757 | """ 758 | Check if the given project template is valid 759 | """ 760 | 761 | tpl_list = list(self.get_template_list()) 762 | if template not in [tpl.split('::')[0] for tpl in tpl_list]: 763 | return False 764 | 765 | return True 766 | 767 | def get_template_dir(self): 768 | """ 769 | Return the templates dir 770 | """ 771 | 772 | return '{0}/{1}/{2}/templates'.format( 773 | self.packagespath, 774 | self.packagedir, 775 | self.datadir 776 | ) 777 | 778 | def get_template_list(self): 779 | """ 780 | Generator for lazy templates list 781 | """ 782 | 783 | file_name = '{0}/templates.lst'.format(self.get_template_dir()) 784 | with open(file_name, 'r') as fhandler: 785 | for tpl in fhandler.read().split('\n'): 786 | if len(tpl): 787 | tpl_split = tpl.split(':') 788 | yield '{0}:: {1}'.format(tpl_split[0], tpl_split[1]) 789 | 790 | def get_selected(self, dir_conversion=False): 791 | """Return the selected template""" 792 | 793 | return (self.selected.replace(' ', '_').lower() 794 | if dir_conversion else self.selected) 795 | 796 | 797 | class RopeManager(object): 798 | """ 799 | Manager for rope/SublimeRope features 800 | """ 801 | 802 | def __init__(self): 803 | super(RopeManager, self).__init__() 804 | self.supported = ROPE_SUPPORT 805 | 806 | def is_supported(self): 807 | """Returns true if rope is supported, otherwise returns false""" 808 | 809 | return self.supported 810 | 811 | def create_project(self, projectroot=None): 812 | """ 813 | Create a new Rope project 814 | """ 815 | 816 | if not projectroot or not self.supported: 817 | return 818 | 819 | try: 820 | rope_project = rope.base.project.Project(projectroot) 821 | rope_project.close() 822 | except (ResourceNotFoundError, RopeError) as error: 823 | msg = 'Could not create rope project folder at {0}\nException: {1}' 824 | sublime.status_message(msg.format(self.root, str(error))) 825 | 826 | 827 | class Command(object): 828 | """Base class for external commands 829 | """ 830 | 831 | def __init__(self, command): 832 | self.command = command 833 | self.proc = None 834 | 835 | def launch(self): 836 | """Launch the external process 837 | """ 838 | 839 | kwargs = { 840 | 'cwd': os.path.dirname(os.path.abspath(__file__)), 841 | 'bufsize': -1 842 | } 843 | 844 | if sublime.platform() == 'windows': 845 | startupinfo = subprocess.STARTUPINFO() 846 | startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 847 | kwargs['startupinfo'] = startupinfo 848 | 849 | sub_args = [self.command] + self.options 850 | self.proc = subprocess.Popen(sub_args, **kwargs) 851 | 852 | 853 | class PyUicCommand(Command): 854 | """PySide-uic 855 | """ 856 | 857 | def __init__(self, window): 858 | self.window = window 859 | self.options = [] 860 | 861 | command = get_settings('sublimepyside_tools_map').get('uic') 862 | if command is None: 863 | self.is_valid = False 864 | sublime.error_message( 865 | 'PySide-uic application path is not configured' 866 | ) 867 | else: 868 | self.is_valid = True 869 | super(PyUicCommand, self).__init__(command) 870 | 871 | def preview(self, filename=None): 872 | """Show a preview of the given filename 873 | """ 874 | 875 | if filename is None: 876 | filename = self.window.active_view().file_name() 877 | 878 | self.options += ['-p', filename] 879 | self.launch() 880 | 881 | def compile(self, filename=None): 882 | """Compile a UI file 883 | """ 884 | 885 | if filename is None: 886 | filename = self.window.active_view().file_name() 887 | 888 | self.options += ['-o', filename.replace('.ui', '_ui.py'), filename] 889 | self.launch() 890 | 891 | 892 | class RCCCommand(Command): 893 | """PySide-rcc 894 | """ 895 | 896 | def __init__(self, window): 897 | self.window = window 898 | self.options = [] 899 | 900 | command = get_settings('sublimepyside_tools_map').get('rcc') 901 | if command is None: 902 | self.is_valid = False 903 | sublime.error_message( 904 | 'PySide-rcc application path is not configured' 905 | ) 906 | else: 907 | self.is_valid = True 908 | super(RCCCommand, self).__init__(command) 909 | 910 | def compile(self, filename=None): 911 | """Compile a file 912 | """ 913 | 914 | if filename is None: 915 | filename = self.window.active_view().file_name() 916 | 917 | if filename.lower().endswith('.qrc'): 918 | rcc_options = get_settings('sublimepyside_rcc_options') 919 | if rcc_options.get('output_file', '') != 'same_rc': 920 | self.window.show_input_panel( 921 | 'Output filename (with no extension):', 922 | filename.replace('.qrc', '_rc'), 923 | lambda name: self.compile_resource_file( 924 | filename, '{0}.py'.format(name.strip()), rcc_options 925 | ), None, None 926 | ) 927 | else: 928 | self.compile_resource_file( 929 | filename, filename.replace('.qrc', '_rc.py'), rcc_options 930 | ) 931 | else: 932 | sublime.error_message('Unknown file extension') 933 | 934 | def compile_resource_file(self, input_file, filename, rcc_options): 935 | """Process a QRC file using PySide-rcc 936 | """ 937 | 938 | self.options += ['-o', filename] 939 | 940 | root_path = rcc_options.get('root_path', '') 941 | no_compress = rcc_options.get('no_compress', False) 942 | compression_level = rcc_options.get('compression_level', -1) 943 | 944 | if compression_level != -1 and not no_compress: 945 | if compression_level >= 0 and compression_level <= 9: 946 | self.options += ['-compress', str(compression_level)] 947 | 948 | if no_compress: 949 | self.options.append('-no-compress') 950 | 951 | if root_path != '' and type(root_path) is str: 952 | self.options += ['-root', root_path] 953 | 954 | self.options.append(input_file) 955 | self.launch() 956 | 957 | 958 | class LinguistCommand(Command): 959 | """Linguist 960 | """ 961 | 962 | def __init__(self): 963 | self.options = [] 964 | 965 | command = get_settings('sublimepyside_qt_tools_map').get('linguist') 966 | if command is None: 967 | self.is_valid = False 968 | sublime.error_message( 969 | 'Qt Linguist application path is not configured' 970 | ) 971 | else: 972 | self.is_valid = True 973 | super(LinguistCommand, self).__init__(command) 974 | 975 | def open_linguist(self): 976 | """Just open Qt Linguist 977 | """ 978 | 979 | self.launch() 980 | 981 | def open_file_in_linguist(self, view): 982 | """Open the buffer file with linguist 983 | """ 984 | 985 | if (view.file_name().lower().endswith('.ts') 986 | or view.file_name().lower().endswith('.qm')): 987 | self.options.append(view.file_name()) 988 | self.launch() 989 | else: 990 | sublime.error_message('Unknown file extension...') 991 | 992 | 993 | class PySideLupdateCommand(Command): 994 | """PySide Lupdate 995 | """ 996 | 997 | def __init__(self, window): 998 | self.window = window 999 | self.options = [] 1000 | 1001 | command = get_settings('sublimepyside_tools_map').get('lupdate') 1002 | if command is None: 1003 | self.is_valid = False 1004 | sublime.error_message( 1005 | 'PySide Lupdate tool path is not configured' 1006 | ) 1007 | else: 1008 | self.is_valid = True 1009 | super(PySideLupdateCommand, self).__init__(command) 1010 | 1011 | def generate_translations(self, files, dirs): 1012 | """Generate TS files using project file or iterating over the directory 1013 | """ 1014 | 1015 | self.handle_files(files) 1016 | self.handle_dirs(dirs) 1017 | 1018 | def handle_files(self, files): 1019 | """Handle files to generate TS 1020 | """ 1021 | 1022 | for filename in files: 1023 | if filename.endswith('.py'): 1024 | self.generate_translation_from_file(filename) 1025 | elif filename.endswith('.pro'): 1026 | self.generate_translation_from_project(filename) 1027 | 1028 | def handle_dirs(self, dirs): 1029 | """Hadnle dirs to generate TS 1030 | """ 1031 | 1032 | for dirname in dirs: 1033 | self.handle_files(glob('{}/{}'.format(dirname, '*[.py,.pro]'))) 1034 | 1035 | def generate_translation_from_file(self, filename): 1036 | """Just a convenience method 1037 | """ 1038 | 1039 | self.options = [] 1040 | self.options += [filename, '-ts', filename.replace('.py', '.ts')] 1041 | self.launch() 1042 | 1043 | def generate_translation_from_project(self, filename): 1044 | """Just a convenience method 1045 | """ 1046 | 1047 | self.options = [] 1048 | self.options.append(filename) 1049 | self.launch() 1050 | 1051 | 1052 | class QDBusViewerCommand(Command): 1053 | """QDBusViewer 1054 | """ 1055 | 1056 | def __init__(self): 1057 | self.options = [] 1058 | 1059 | command = get_settings('sublimepyside_qt_tools_map').get('qdbusviewer') 1060 | if command is None: 1061 | self.is_valid = False 1062 | sublime.error_message( 1063 | 'QDBusViewer application path is not configured' 1064 | ) 1065 | else: 1066 | self.is_valid = True 1067 | super(QDBusViewerCommand, self).__init__(command) 1068 | self.launch() 1069 | 1070 | 1071 | class QtDesignerCommand(Command): 1072 | """Qt Designer 1073 | """ 1074 | 1075 | def __init__(self, window): 1076 | self.window = window 1077 | self.options = [] 1078 | self.dirs = [] 1079 | 1080 | command = get_settings('sublimepyside_qt_tools_map').get('designer') 1081 | if command is None: 1082 | self.is_valid = False 1083 | sublime.error_message( 1084 | 'Designer application path is not configured' 1085 | ) 1086 | else: 1087 | designer_dir = os.path.join( 1088 | os.path.dirname(__file__), 'data', 'designer' 1089 | ) 1090 | 1091 | with open(designer_dir + '/templates.json', 'r') as json_file: 1092 | self.designer_options = sublime.decode_value(json_file.read()) 1093 | 1094 | self.is_valid = True 1095 | super(QtDesignerCommand, self).__init__(command) 1096 | 1097 | def open_file_in_designer(self): 1098 | """Open the view buffer into Qt Designer 1099 | """ 1100 | 1101 | try: 1102 | self.options.append(self.window.active_view().file_name()) 1103 | self.launch() 1104 | except AttributeError: 1105 | sublime.error_message('There is no active view!') 1106 | 1107 | def new_dialog(self, dirs, buttons=True, position='right'): 1108 | """Create a new template for QtDesigner and opens it 1109 | """ 1110 | 1111 | self.dirs = dirs 1112 | self.window.show_quick_panel( 1113 | self.designer_options['templates_list'], self.template_selected 1114 | ) 1115 | 1116 | def template_selected(self, picked): 1117 | """Process the template selected by the user 1118 | """ 1119 | 1120 | if picked == -1: 1121 | return 1122 | 1123 | self.tpl = self.designer_options['templates_list'][picked] 1124 | self.window.show_input_panel( 1125 | 'UI name (don\'t add extension):', 1126 | self.tpl, self._new_designer_template, None, None 1127 | ) 1128 | 1129 | def _new_designer_template(self, name): 1130 | """Create the file and init the subprocess 1131 | """ 1132 | 1133 | tpl = os.path.join( 1134 | os.path.dirname(__file__), 'data', 'designer', 'templates', 1135 | '{}.ui'.format('_'.join(self.tpl.lower().split(' '))) 1136 | ) 1137 | 1138 | self.dirs.append(self.window.folders()[0]) 1139 | 1140 | filename = os.path.join(self.dirs[0], name + '.ui') 1141 | in_file = open(tpl, 'r') 1142 | out_file = open(filename, 'w') 1143 | 1144 | out_file.write(in_file.read()) 1145 | in_file.close() 1146 | out_file.close() 1147 | 1148 | self.options.append(filename) 1149 | self.launch() 1150 | 1151 | sublime.message_dialog('Qt Designer is starting, please wait') 1152 | 1153 | 1154 | # ============================================================================= 1155 | # Global functions 1156 | # ============================================================================= 1157 | def sublime_executable_path(): 1158 | """ 1159 | Return the Sublime Text 2 installation path for each platform 1160 | """ 1161 | platform = sublime.platform() 1162 | error = sublime.set_timeout( 1163 | functools.partial(get_settings, 'osx_st2_path'), 0) 1164 | 1165 | # in Sublime Text 3 we can just use `sublime.executable_path() 1166 | if SUBLIME_TEXT_3 is True: 1167 | return sublime.executable_path() 1168 | 1169 | if platform == 'osx': 1170 | if not error: 1171 | return ('/Applications/Sublime Text 2.app' 1172 | '/Contents/SharedSupport/bin/subl') 1173 | else: 1174 | return error 1175 | 1176 | if platform == 'linux': 1177 | if os.path.exists('/proc/self/cmdline'): 1178 | return open('/proc/self/cmdline').read().split(chr(0))[0] 1179 | 1180 | return sys.executable 1181 | 1182 | 1183 | def get_settings(name, typeof=str): 1184 | """Get settings""" 1185 | settings = sublime.load_settings('SublimePySide.sublime-settings') 1186 | setting = settings.get(name) 1187 | if setting: 1188 | if typeof == str: 1189 | return setting 1190 | elif typeof == bool: 1191 | return setting is True 1192 | elif typeof == int: 1193 | return int(settings.get(name, 500)) 1194 | else: 1195 | if typeof == str: 1196 | return '' 1197 | else: 1198 | return None 1199 | --------------------------------------------------------------------------------