├── Resources └── icons │ ├── PieMenuQuickMenu.svg │ ├── PieMenuRemove.svg │ ├── PieMenuReset.svg │ ├── PieMenuRemoveCommand.svg │ ├── PieMenuAdd.svg │ ├── PieMenuAddCommand.svg │ ├── PieMenuUp.svg │ ├── PieMenuDown.svg │ ├── PieMenuClose.svg │ ├── PieMenuCopy.svg │ ├── PieMenuAddSeparator.svg │ ├── PieMenuRename.svg │ ├── PieMenuAddMenu.svg │ ├── PieMenuEditMenu.svg │ └── PieMenu_Logo.svg ├── README.md ├── package.xml ├── PieMenuLocator.py ├── License └── lgpl-2.1.txt └── InitGui.py /Resources/icons/PieMenuQuickMenu.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 20 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PieMenu 2 | PieMenu widget for FreeCAD 3 | 4 | ![image](https://user-images.githubusercontent.com/4140247/45217234-51e7e280-b271-11e8-973a-1a80ce5beedd.png) 5 | 6 | ### Installation 7 | Since FreeCAD v0.16+ PieMenu can be installed via the FreeCAD [Addon Manager](https://github.com/FreeCAD/FreeCAD-addons#1-builtin-addon-manager). 8 | 9 | #### Manual installation: 10 | 11 | Install path for FreeCAD modules depends on the operating system used. 12 | 13 | ##### Examples: 14 | 15 | Linux: 16 | 17 | `/home/user/.FreeCAD/Mod/PieMenu/InitGui.py` 18 | 19 | macOS: 20 | 21 | `/Users/user_name/Library/Preferences/FreeCAD/Mod/PieMenu/InitGui.py` 22 | 23 | Windows: 24 | 25 | `C:\Users\user_name\AppData\Roaming\FreeCAD\Mod\PieMenu\InitGui.py` 26 | 27 | ### Usage 28 | Press the Tab key on the keyboard to invoke PieMenu. 29 | 30 | ### Discussion 31 | FreeCAD forum thread: https://forum.freecadweb.org/viewtopic.php?f=34&t=72205 32 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuRemove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuReset.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | PieMenu 4 | The PieMenu module is a tool to accelerate and simplify your workflow in usage of FreeCAD. 5 | 1.2.7 6 | 2023-3-25 7 | Michael Dingerkus 8 | LGPLv2.1 9 | https://github.com/mdkus/PieMenu 10 | https://github.com/mdkus/PieMenu/issues 11 | 12 | Resources/icons/PieMenu_Logo.svg 13 | 14 | 15 | 16 | PieMenu 17 | ./ 18 | Resources/icons/PieMenu_Logo.svg 19 | pie 20 | menu 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuRemoveCommand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /PieMenuLocator.py: -------------------------------------------------------------------------------- 1 | # Pie menu for FreeCAD 2 | # Copyright (C) 2019 triplus @ FreeCAD 3 | # 4 | # 5 | # This library is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU Lesser General Public 7 | # License as published by the Free Software Foundation; either 8 | # version 2.1 of the License, or (at your option) any later version. 9 | # 10 | # This library is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | # Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public 16 | # License along with this library; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | """Pie menu for FreeCAD - Locator.""" 20 | 21 | 22 | import os 23 | 24 | 25 | def path(): 26 | """Return module path location.""" 27 | return os.path.dirname(__file__) 28 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuAdd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuAddCommand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuUp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuDown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuClose.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 15 | 22 | 28 | 34 | 36 | 38 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuCopy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuAddSeparator.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuRename.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuAddMenu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Resources/icons/PieMenuEditMenu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /License/lgpl-2.1.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 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 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | -------------------------------------------------------------------------------- /Resources/icons/PieMenu_Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 18 | 20 | 24 | 28 | 29 | 31 | 35 | 39 | 40 | 42 | 46 | 50 | 51 | 53 | 57 | 61 | 62 | 64 | 68 | 72 | 76 | 77 | 79 | 83 | 87 | 88 | 90 | 94 | 98 | 99 | 101 | 105 | 109 | 113 | 114 | 124 | 132 | 140 | 148 | 156 | 164 | 173 | 181 | 190 | 192 | 196 | 200 | 201 | 203 | 207 | 211 | 212 | 214 | 218 | 222 | 223 | 231 | 233 | 237 | 241 | 242 | 250 | 252 | 256 | 260 | 261 | 263 | 267 | 271 | 272 | 281 | 283 | 287 | 291 | 292 | 301 | 303 | 307 | 311 | 312 | 314 | 318 | 322 | 323 | 332 | 334 | 338 | 342 | 346 | 347 | 356 | 358 | 362 | 366 | 367 | 377 | 379 | 383 | 387 | 388 | 390 | 394 | 398 | 399 | 401 | 405 | 409 | 410 | 412 | 416 | 420 | 421 | 431 | 441 | 443 | 447 | 451 | 452 | 462 | 472 | 482 | 492 | 502 | 511 | 520 | 530 | 540 | 550 | 560 | 570 | 580 | 588 | 598 | 608 | 618 | 628 | 638 | 648 | 658 | 668 | 677 | 686 | 696 | 706 | 716 | 726 | 736 | 744 | 745 | 747 | 748 | 750 | image/svg+xml 751 | 753 | 754 | 755 | Martin Ruskov 756 | 757 | 758 | http://commons.wikimedia.org/wiki/Tango_icon 759 | 761 | 762 | 763 | 765 | 767 | 769 | 771 | 773 | 775 | 777 | 778 | 779 | 780 | 782 | 786 | 788 | 794 | 800 | 801 | 809 | 817 | 825 | 833 | 841 | 849 | 857 | 858 | 859 | -------------------------------------------------------------------------------- /InitGui.py: -------------------------------------------------------------------------------- 1 | # PieMenu widget for FreeCAD 2 | # 3 | # Copyright (C) 2022, 2023 mdkus @ FreeCAD 4 | # Copyright (C) 2016, 2017 triplus @ FreeCAD 5 | # Copyright (C) 2015,2016 looo @ FreeCAD 6 | # Copyright (C) 2015 microelly 7 | # 8 | # 9 | # This library is free software; you can redistribute it and/or 10 | # modify it under the terms of the GNU Lesser General Public 11 | # License as published by the Free Software Foundation; either 12 | # version 2.1 of the License, or (at your option) any later version. 13 | # 14 | # This library is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | # Lesser General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Lesser General Public 20 | # License along with this library; if not, write to the Free Software 21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 | 23 | # Attribution: 24 | # http://forum.freecadweb.org/ 25 | # http://www.freecadweb.org/wiki/index.php?title=Code_snippets 26 | 27 | global PIE_MENU_VERSION 28 | PIE_MENU_VERSION = "1.2.7" 29 | 30 | def pieMenuStart(): 31 | import math 32 | import operator 33 | import platform 34 | import FreeCAD as App 35 | import FreeCADGui as Gui 36 | from PySide import QtCore 37 | from PySide import QtGui 38 | import PieMenuLocator as locator 39 | 40 | path = locator.path() 41 | respath = path + "/Resources/icons/" 42 | 43 | # global status variables 44 | selectionTriggered = False 45 | contextPhase = False 46 | 47 | def remObsoleteParams(): 48 | """Remove obsolete parameters from older versions.""" 49 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 50 | paramGet.RemBool("ContextPhase") 51 | 52 | def accessoriesMenu(): 53 | """Add pie menu preferences to accessories menu.""" 54 | pref = QtGui.QAction(mw) 55 | pref.setText("Pie menu") 56 | pref.setObjectName("PieMenu") 57 | pref.triggered.connect(onPreferences) 58 | try: 59 | import AccessoriesMenu 60 | AccessoriesMenu.addItem("PieMenu") 61 | except ImportError: 62 | a = mw.findChild(QtGui.QAction, "AccessoriesMenu") 63 | if a: 64 | a.menu().addAction(pref) 65 | else: 66 | mb = mw.menuBar() 67 | action = QtGui.QAction(mw) 68 | action.setObjectName("AccessoriesMenu") 69 | action.setIconText("Accessories") 70 | menu = QtGui.QMenu() 71 | action.setMenu(menu) 72 | menu.addAction(pref) 73 | 74 | def addMenu(): 75 | """Add accessories menu to the menu bar.""" 76 | mb.addAction(action) 77 | action.setVisible(True) 78 | 79 | addMenu() 80 | mw.workbenchActivated.connect(addMenu) 81 | 82 | 83 | def onPreferences(): 84 | """Open the preferences dialog.""" 85 | onControl() 86 | 87 | 88 | styleButton = (""" 89 | QToolButton { 90 | background-color: lightGray; 91 | border: 1px outset silver; 92 | } 93 | 94 | QToolButton:disabled { 95 | background-color: darkGray; 96 | } 97 | 98 | QToolButton:hover { 99 | background-color: lightBlue; 100 | } 101 | 102 | QToolButton:checked { 103 | background-color: lightGreen; 104 | } 105 | 106 | QToolButton::menu-indicator { 107 | subcontrol-origin: padding; 108 | subcontrol-position: center center; 109 | } 110 | 111 | """) 112 | 113 | styleMenuClose = (""" 114 | QToolButton { 115 | background-color: rgba(60,60,60,255); 116 | color: silver; 117 | border: 1px solid #1e1e1e; 118 | } 119 | 120 | QToolButton::menu-indicator { 121 | image: none; 122 | } 123 | 124 | """) 125 | 126 | styleContainer = ("QMenu{background: transparent}") 127 | 128 | styleCombo = (""" 129 | QComboBox { 130 | background: transparent; 131 | border: 1px solid transparent; 132 | } 133 | 134 | """) 135 | 136 | styleQuickMenu = ("padding: 5px 10px 5px 10px") 137 | 138 | styleQuickMenuItem = (""" 139 | QMenu::item { 140 | padding: 5px 20px 5px 20px; 141 | } 142 | 143 | """) 144 | 145 | iconClose = respath + "PieMenuClose.svg" 146 | iconMenu = respath + "PieMenuQuickMenu.svg" 147 | iconUp = respath + "PieMenuUp.svg" 148 | iconDown = respath + "PieMenuDown.svg" 149 | iconAdd = respath + "PieMenuAdd.svg" 150 | iconRemove = respath + "PieMenuRemove.svg" 151 | iconRename = respath + "PieMenuRename.svg" 152 | iconCopy = respath + "PieMenuCopy.svg" 153 | iconRemoveCommand = respath + "PieMenuRemoveCommand.svg" 154 | 155 | 156 | def radiusSize(buttonSize): 157 | 158 | radius = str(math.trunc(buttonSize / 2)) 159 | 160 | return "QToolButton {border-radius: " + radius + "px}" 161 | 162 | 163 | def iconSize(buttonSize): 164 | 165 | icon = buttonSize / 3 * 2 166 | 167 | return icon 168 | 169 | 170 | def closeButton(buttonSize=32): 171 | 172 | icon = iconSize(buttonSize) 173 | radius = radiusSize(buttonSize) 174 | 175 | button = QtGui.QToolButton() 176 | button.setProperty("ButtonX", 0) 177 | button.setProperty("ButtonY", 0) 178 | button.setGeometry(0, 0, buttonSize, buttonSize) 179 | button.setIconSize(QtCore.QSize(icon, icon)) 180 | button.setIcon(QtGui.QIcon(iconClose)) 181 | button.setStyleSheet(styleMenuClose + radius) 182 | button.setAttribute(QtCore.Qt.WA_TranslucentBackground) 183 | 184 | def onButton(): 185 | 186 | PieMenuInstance.hide() 187 | 188 | button.clicked.connect(onButton) 189 | 190 | return button 191 | 192 | 193 | def quickMenu(buttonSize=20): 194 | 195 | icon = iconSize(buttonSize) 196 | radius = radiusSize(buttonSize) 197 | 198 | menu = QtGui.QMenu(mw) 199 | menu.setStyleSheet(styleQuickMenu) 200 | 201 | button = QtGui.QToolButton() 202 | button.setMenu(menu) 203 | button.setProperty("ButtonX", 0) 204 | button.setProperty("ButtonY", 32) 205 | button.setGeometry(0, 0, buttonSize, buttonSize) 206 | button.setStyleSheet(styleMenuClose + radius) 207 | button.setIconSize(QtCore.QSize(icon, icon)) 208 | button.setIcon(QtGui.QIcon(iconMenu)) 209 | button.setAttribute(QtCore.Qt.WA_TranslucentBackground) 210 | button.setPopupMode(QtGui.QToolButton 211 | .ToolButtonPopupMode.InstantPopup) 212 | 213 | menuMode = QtGui.QMenu() 214 | menuMode.setTitle("Trigger") 215 | 216 | modeGroup = QtGui.QActionGroup(menuMode) 217 | modeGroup.setExclusive(True) 218 | 219 | actionPress = QtGui.QAction(modeGroup) 220 | actionPress.setText("Press") 221 | actionPress.setData("Press") 222 | actionPress.setCheckable(True) 223 | 224 | actionHover = QtGui.QAction(modeGroup) 225 | actionHover.setText("Hover") 226 | actionHover.setData("Hover") 227 | actionHover.setCheckable(True) 228 | 229 | menuMode.addAction(actionPress) 230 | menuMode.addAction(actionHover) 231 | 232 | actionContext = QtGui.QAction(menu) 233 | actionContext.setText("Context") 234 | actionContext.setCheckable(True) 235 | 236 | menuPieMenu = QtGui.QMenu() 237 | menuPieMenu.setTitle("PieMenu") 238 | 239 | pieGroup = QtGui.QActionGroup(menu) 240 | pieGroup.setExclusive(True) 241 | 242 | menuToolBar = QtGui.QMenu() 243 | menuToolBar.setTitle("ToolBar") 244 | menuToolBar.setStyleSheet(styleQuickMenuItem) 245 | 246 | toolbarGroup = QtGui.QMenu() 247 | 248 | toolbarGroupOps = QtGui.QActionGroup(toolbarGroup) 249 | toolbarGroupOps.setExclusive(True) 250 | 251 | prefAction = QtGui.QAction(menu) 252 | prefAction.setIconText("Preferences") 253 | 254 | prefButton = QtGui.QToolButton() 255 | prefButton.setDefaultAction(prefAction) 256 | 257 | prefButtonWidgetAction = QtGui.QWidgetAction(menu) 258 | prefButtonWidgetAction.setDefaultWidget(prefButton) 259 | 260 | def setChecked(): 261 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 262 | 263 | if paramGet.GetString("TriggerMode") == "Hover": 264 | actionHover.setChecked(True) 265 | else: 266 | actionPress.setChecked(True) 267 | 268 | if paramGet.GetBool("EnableContext"): 269 | actionContext.setChecked(True) 270 | else: 271 | pass 272 | 273 | setChecked() 274 | 275 | def onModeGroup(): 276 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 277 | text = modeGroup.checkedAction().data() 278 | paramGet.SetString("TriggerMode", text) 279 | 280 | PieMenuInstance.hide() 281 | PieMenuInstance.showAtMouse(notKeyTriggered=True) 282 | 283 | modeGroup.triggered.connect(onModeGroup) 284 | 285 | def onActionContext(): 286 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 287 | 288 | if actionContext.isChecked(): 289 | paramGet.SetBool("EnableContext", True) 290 | contextList() 291 | else: 292 | paramGet.SetBool("EnableContext", False) 293 | 294 | addObserver() 295 | 296 | actionContext.triggered.connect(onActionContext) 297 | 298 | def pieList(): 299 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 300 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 301 | indexList = paramIndexGet.GetString("IndexList") 302 | 303 | menuPieMenu.clear() 304 | 305 | if indexList: 306 | indexList = indexList.split(".,.") 307 | 308 | temp = [] 309 | 310 | for i in indexList: 311 | temp.append(int(i)) 312 | 313 | indexList = temp 314 | else: 315 | indexList = [] 316 | 317 | pieList = [] 318 | 319 | for i in indexList: 320 | a = str(i) 321 | try: 322 | pieName = paramIndexGet.GetString(a).decode("UTF-8") 323 | except AttributeError: 324 | pieName = paramIndexGet.GetString(a) 325 | pieList.append(pieName) 326 | 327 | if not paramGet.GetBool("ToolBar"): 328 | try: 329 | text = paramGet.GetString("CurrentPie").decode("UTF-8") 330 | except AttributeError: 331 | text = paramGet.GetString("CurrentPie") 332 | else: 333 | text = None 334 | 335 | for i in pieList: 336 | action = QtGui.QAction(pieGroup) 337 | action.setText(i) 338 | action.setCheckable(True) 339 | 340 | if i == text: 341 | action.setChecked(True) 342 | else: 343 | pass 344 | 345 | menuPieMenu.addAction(action) 346 | 347 | menuPieMenu.aboutToShow.connect(pieList) 348 | 349 | def onPieGroup(): 350 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 351 | 352 | paramGet.SetBool("ToolBar", False) 353 | paramGet.RemString("ToolBar") 354 | try: 355 | text = pieGroup.checkedAction().text().encode("UTF-8") 356 | paramGet.SetString("CurrentPie", text) 357 | except TypeError: 358 | text = pieGroup.checkedAction().text() 359 | paramGet.SetString("CurrentPie", text) 360 | 361 | PieMenuInstance.hide() 362 | PieMenuInstance.showAtMouse(notKeyTriggered=True) 363 | 364 | pieGroup.triggered.connect(onPieGroup) 365 | 366 | def onMenuToolBar(): 367 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 368 | 369 | menuToolBar.clear() 370 | 371 | if paramGet.GetBool("ToolBar"): 372 | text = paramGet.GetString("ToolBar") 373 | if ": " in text: 374 | toolbar_desc = text.split(": ") 375 | text = toolbar_desc[1] 376 | else: 377 | pass 378 | else: 379 | text = None 380 | 381 | for i in mw.findChildren(QtGui.QToolBar): 382 | 383 | commands = [] 384 | 385 | for a in i.findChildren(QtGui.QToolButton): 386 | try: 387 | if not a.defaultAction().isSeparator(): 388 | commands.append(a.defaultAction()) 389 | else: 390 | pass 391 | except AttributeError: 392 | pass 393 | 394 | if len(commands) != 0: 395 | menu = QtGui.QMenu(i.windowTitle()) 396 | menu.aboutToShow.connect(lambda sender=menu: onMenuToolbarGroup(sender)) 397 | menuToolBar.addMenu(menu) 398 | else: 399 | pass 400 | 401 | menuToolBar.aboutToShow.connect(onMenuToolBar) 402 | 403 | def isActualPie(text): 404 | 405 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 406 | if paramGet.GetBool("ToolBar"): 407 | entry = paramGet.GetString("ToolBar") 408 | if ": " in entry: 409 | toolbar_desc = entry.split(": ") 410 | idMenu = toolbar_desc[1] 411 | else: 412 | idMenu = entry 413 | if idMenu == text: 414 | return True; 415 | 416 | return False 417 | 418 | def onMenuToolbarGroup(sender): 419 | 420 | if not sender.actions(): 421 | action = QtGui.QAction(sender) 422 | action.setText("Show") 423 | action.setData(sender.title()) 424 | action.setCheckable(True) 425 | if isActualPie(sender.title()): 426 | action.setChecked(True) 427 | sender.addAction(action) 428 | 429 | action = QtGui.QAction(sender) 430 | action.setText("Save") 431 | action.setData(sender.title()) 432 | sender.addAction(action) 433 | 434 | sender.triggered.connect(lambda sender: onToolbarGroup(sender)) 435 | 436 | def onToolbarGroup(sender): 437 | 438 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 439 | if sender.text() == "Show": 440 | paramGet.SetBool("ToolBar", True) 441 | elif sender.text() == "Save": 442 | newPieGroup = createPie(sender.data()) 443 | else: 444 | return 445 | 446 | workbenches = [] 447 | commands = [] 448 | 449 | if sender.text() == "Save": 450 | # write persistent commands 451 | getGuiToolButtonData(sender.data(), None, commands, None) 452 | newPieGroup.SetString("ToolList", ".,.".join(commands)) 453 | elif sender.text() == "Show": 454 | # write persistent toolbar and its workbenches 455 | getGuiToolButtonData(sender.data(), None, None, workbenches) 456 | toolbar_desc = ", ".join(workbenches) 457 | toolbar_desc = toolbar_desc + ': ' + sender.data() 458 | paramGet.SetString("ToolBar", toolbar_desc) 459 | PieMenuInstance.hide() 460 | PieMenuInstance.showAtMouse(notKeyTriggered=True) 461 | 462 | toolbarGroup.triggered.connect(onToolbarGroup) 463 | 464 | def onPrefButton(): 465 | 466 | PieMenuInstance.hide() 467 | onControl() 468 | 469 | prefButton.clicked.connect(onPrefButton) 470 | 471 | menu.addMenu(menuMode) 472 | menu.addAction(actionContext) 473 | menu.addSeparator() 474 | menu.addMenu(menuPieMenu) 475 | menu.addMenu(menuToolBar) 476 | menu.addSeparator() 477 | menu.addAction(prefButtonWidgetAction) 478 | 479 | return button 480 | 481 | 482 | class HoverButton(QtGui.QToolButton): 483 | 484 | def __init__(self, parent=None): 485 | super(HoverButton, self).__init__() 486 | 487 | def enterEvent(self, event): 488 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 489 | mode = paramGet.GetString("TriggerMode") 490 | if self.defaultAction().isEnabled() and mode == "Hover": 491 | PieMenuInstance.hide() 492 | self.defaultAction().trigger() 493 | else: 494 | pass 495 | 496 | def mouseReleaseEvent(self, event): 497 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 498 | mode = paramGet.GetString("TriggerMode") 499 | 500 | if self.defaultAction().isEnabled(): 501 | PieMenuInstance.hide() 502 | self.defaultAction().trigger() 503 | else: 504 | pass 505 | 506 | 507 | class PieMenu: 508 | 509 | def __init__(self): 510 | 511 | self.radius = 100 512 | self.buttons = [] 513 | self.buttonSize = 32 514 | self.menu = QtGui.QMenu(mw) 515 | self.menuSize = 0 516 | self.menu.setStyleSheet(styleContainer) 517 | self.menu.setWindowFlags(self.menu.windowFlags() | QtCore.Qt.FramelessWindowHint) 518 | self.menu.setAttribute(QtCore.Qt.WA_TranslucentBackground) 519 | 520 | if compositingManager: 521 | pass 522 | else: 523 | self.menu.setAttribute(QtCore.Qt.WA_PaintOnScreen) 524 | 525 | def add_commands(self, commands, context=False): 526 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 527 | 528 | for i in self.buttons: 529 | i.deleteLater() 530 | 531 | self.buttons = [] 532 | 533 | if context: 534 | group = getGroup(mode=2) 535 | else: 536 | group = getGroup(mode=1) 537 | 538 | if len(commands) == 0: 539 | commandNumber = 1 540 | else: 541 | commandNumber = len(commands) 542 | 543 | valueRadius = group.GetInt("Radius") 544 | valueButton = group.GetInt("Button") 545 | 546 | if paramGet.GetBool("ToolBar"): 547 | valueRadius = 100 548 | valueButton = 32 549 | 550 | if valueRadius: 551 | self.radius = valueRadius 552 | else: 553 | self.radius = 100 554 | 555 | if valueButton: 556 | self.buttonSize = valueButton 557 | else: 558 | self.buttonSize = 32 559 | 560 | if commandNumber == 1: 561 | angle = 0 562 | buttonSize = self.buttonSize 563 | else: 564 | angle = 2 * math.pi / commandNumber 565 | buttonRadius = math.sin(angle / 2) * self.radius 566 | buttonSize = math.trunc(2 * buttonRadius / math.sqrt(2)) 567 | 568 | angleStart = 3 * math.pi / 2 - angle 569 | 570 | if buttonSize > self.buttonSize: 571 | buttonSize = self.buttonSize 572 | else: 573 | pass 574 | 575 | radius = radiusSize(buttonSize) 576 | icon = iconSize(buttonSize) 577 | 578 | if windowShadow: 579 | pass 580 | else: 581 | self.menuSize = valueRadius * 2 + buttonSize + 4 582 | 583 | if self.menuSize < 90: 584 | self.menuSize = 90 585 | else: 586 | pass 587 | 588 | self.menu.setMinimumWidth(self.menuSize) 589 | self.menu.setMinimumHeight(self.menuSize) 590 | 591 | num = 1 592 | 593 | for i in commands: 594 | 595 | button = HoverButton() 596 | button.setParent(self.menu) 597 | button.setAttribute(QtCore.Qt.WA_Hover) 598 | button.setStyleSheet(styleButton + radius) 599 | button.setAttribute(QtCore.Qt.WA_TranslucentBackground) 600 | button.setDefaultAction(commands[commands.index(i)]) 601 | button.setGeometry(0, 0, buttonSize, buttonSize) 602 | button.setIconSize(QtCore.QSize(icon, icon)) 603 | button.setProperty("ButtonX", self.radius * 604 | (math.cos(angle * num + angleStart))) 605 | button.setProperty("ButtonY", self.radius * 606 | (math.sin(angle * num + angleStart))) 607 | 608 | self.buttons.append(button) 609 | 610 | num = num + 1 611 | 612 | buttonQuickMenu = quickMenu() 613 | buttonQuickMenu.setParent(self.menu) 614 | self.buttons.append(buttonQuickMenu) 615 | 616 | buttonClose = closeButton() 617 | buttonClose.setParent(self.menu) 618 | self.buttons.append(buttonClose) 619 | 620 | if compositingManager: 621 | pass 622 | else: 623 | for i in self.buttons: 624 | i.setAttribute(QtCore.Qt.WA_PaintOnScreen) 625 | 626 | def hide(self): 627 | for i in self.buttons: 628 | i.hide() 629 | 630 | self.menu.hide() 631 | 632 | def showAtMouse(self, notKeyTriggered=False): 633 | 634 | nonlocal selectionTriggered 635 | nonlocal contextPhase 636 | global lastPosX 637 | global lastPosY 638 | 639 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 640 | 641 | enableContext = paramGet.GetBool("EnableContext") 642 | 643 | if contextPhase: 644 | sel = Gui.Selection.getSelectionEx() 645 | if not sel: 646 | self.hide() 647 | contextPhase = False 648 | updateCommands() 649 | elif not enableContext: 650 | self.hide() 651 | updateCommands() 652 | else: 653 | updateCommands(context=True) 654 | else: 655 | updateCommands() 656 | 657 | if self.menu.isVisible(): 658 | self.hide() 659 | else: 660 | if windowShadow: 661 | pos = mw.mapFromGlobal(QtGui.QCursor.pos()) 662 | if notKeyTriggered: 663 | if contextPhase: 664 | # special case treatment 665 | if selectionTriggered: 666 | selectionTriggered = False 667 | else: 668 | pos.setX(lastPosX) 669 | pos.setY(lastPosY) 670 | lastPosX = pos.x() 671 | lastPosY = pos.y() 672 | else: 673 | pos.setX(lastPosX) 674 | pos.setY(lastPosY) 675 | else: 676 | lastPosX = pos.x() 677 | lastPosY = pos.y() 678 | 679 | self.menu.popup(QtCore.QPoint(mw.pos())) 680 | self.menu.setGeometry(mw.geometry()) 681 | 682 | for i in self.buttons: 683 | i.move(i.property("ButtonX") + pos.x() - i.width() / 2, 684 | i.property("ButtonY") + pos.y() - i.height() / 2) 685 | 686 | i.setVisible(True) 687 | 688 | for i in self.buttons: 689 | i.repaint() 690 | else: 691 | pos = QtGui.QCursor.pos() 692 | if notKeyTriggered: 693 | if contextPhase: 694 | # special case treatment 695 | if selectionTriggered: 696 | selectionTriggered = False 697 | else: 698 | pos.setX(lastPosX) 699 | pos.setY(lastPosY) 700 | lastPosX = pos.x() 701 | lastPosY = pos.y() 702 | else: 703 | pos.setX(lastPosX) 704 | pos.setY(lastPosY) 705 | else: 706 | lastPosX = pos.x() 707 | lastPosY = pos.y() 708 | 709 | for i in self.buttons: 710 | i.move(i.property("ButtonX") + (self.menuSize - i.size().width()) / 2, 711 | i.property("ButtonY") + (self.menuSize - i.size().height()) / 2) 712 | 713 | i.setVisible(True) 714 | 715 | self.menu.popup(QtCore.QPoint(pos.x() - self.menuSize / 2, pos.y() - self.menuSize / 2)) 716 | 717 | 718 | sign = { 719 | "<": operator.lt, 720 | "<=": operator.le, 721 | "==": operator.eq, 722 | "!=": operator.ne, 723 | ">": operator.gt, 724 | ">=": operator.ge, 725 | } 726 | 727 | 728 | def contextList(): 729 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 730 | indexList = paramIndexGet.GetString("IndexList") 731 | 732 | contextAll.clear() 733 | 734 | if indexList: 735 | indexList = indexList.split(".,.") 736 | 737 | temp = [] 738 | 739 | for i in indexList: 740 | temp.append(int(i)) 741 | 742 | indexList = temp 743 | else: 744 | indexList = [] 745 | 746 | for i in indexList: 747 | a = str(i) 748 | group = paramIndexGet.GetGroup(a) 749 | groupContext = group.GetGroup("Context") 750 | 751 | if groupContext.GetBool("Enabled"): 752 | 753 | current = {} 754 | 755 | current["Index"] = a 756 | 757 | current["VertexSign"] = groupContext.GetString("VertexSign") 758 | current["VertexValue"] = groupContext.GetInt("VertexValue") 759 | 760 | current["EdgeSign"] = groupContext.GetString("EdgeSign") 761 | current["EdgeValue"] = groupContext.GetInt("EdgeValue") 762 | 763 | current["FaceSign"] = groupContext.GetString("FaceSign") 764 | current["FaceValue"] = groupContext.GetInt("FaceValue") 765 | 766 | current["ObjectSign"] = groupContext.GetString("ObjectSign") 767 | current["ObjectValue"] = groupContext.GetInt("ObjectValue") 768 | 769 | contextAll[i] = current 770 | 771 | else: 772 | pass 773 | 774 | 775 | def getContextPie(v, e, f, o): 776 | global globalContextPie 777 | global globalIndexPie 778 | 779 | globalContextPie = False 780 | globalIndexPie = None 781 | 782 | for i in contextAll: 783 | 784 | current = contextAll[i] 785 | 786 | def vertex(): 787 | if sign[current["VertexSign"]](v, current["VertexValue"]): 788 | edge() 789 | else: 790 | pass 791 | 792 | def edge(): 793 | if sign[current["EdgeSign"]](e, current["EdgeValue"]): 794 | face() 795 | else: 796 | pass 797 | 798 | def face(): 799 | if sign[current["FaceSign"]](f, current["FaceValue"]): 800 | obj() 801 | else: 802 | pass 803 | 804 | def obj(): 805 | if sign[current["ObjectSign"]](o, current["ObjectValue"]): 806 | global globalContextPie 807 | global globalIndexPie 808 | 809 | globalContextPie = "True" 810 | globalIndexPie = current["Index"] 811 | else: 812 | pass 813 | 814 | vertex() 815 | 816 | if globalContextPie == "True": 817 | return globalIndexPie 818 | else: 819 | return None 820 | 821 | 822 | def listTopo(): 823 | 824 | nonlocal selectionTriggered 825 | nonlocal contextPhase 826 | 827 | sel = Gui.Selection.getSelectionEx() 828 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 829 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 830 | 831 | vertexes = 0 832 | edges = 0 833 | faces = 0 834 | objects = 0 835 | 836 | allList = [] 837 | 838 | for i in sel: 839 | if not i.SubElementNames: 840 | objects = objects + 1 841 | else: 842 | for a in i.SubElementNames: 843 | allList.append(a) 844 | 845 | for i in allList: 846 | if i.startswith('Vertex'): 847 | vertexes = vertexes + 1 848 | elif i.startswith('Edge'): 849 | edges = edges + 1 850 | elif i.startswith('Face'): 851 | faces = faces + 1 852 | else: 853 | pass 854 | 855 | pieIndex = getContextPie(vertexes, 856 | edges, 857 | faces, 858 | objects) 859 | 860 | if pieIndex: 861 | try: 862 | pieName = paramIndexGet.GetString(pieIndex).decode("UTF-8") 863 | except AttributeError: 864 | pieName = paramIndexGet.GetString(pieIndex) 865 | try: 866 | paramGet.SetString("ContextPie", pieName.encode("UTF-8")) 867 | except TypeError: 868 | paramGet.SetString("ContextPie", pieName) 869 | contextPhase = True 870 | 871 | updateCommands(context=True) 872 | PieMenuInstance.hide() 873 | selectionTriggered = True 874 | PieMenuInstance.showAtMouse(notKeyTriggered=True) 875 | else: 876 | pass 877 | 878 | 879 | class SelObserver: 880 | 881 | def addSelection(self, doc, obj, sub, pnt): 882 | 883 | listTopo() 884 | 885 | def removeSelection(self, doc, obj, sub): 886 | 887 | listTopo() 888 | 889 | 890 | def addObserver(): 891 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 892 | 893 | if paramGet.GetBool("EnableContext"): 894 | Gui.Selection.addObserver(selObserver) 895 | else: 896 | Gui.Selection.removeObserver(selObserver) 897 | 898 | 899 | def getGuiActionMapAll(): 900 | 901 | actions = {} 902 | duplicates = [] 903 | 904 | for i in mw.findChildren(QtGui.QAction): 905 | if i.objectName() is not None: 906 | if i.objectName() != "" and i.icon(): 907 | if i.objectName() in actions: 908 | if i.objectName() not in duplicates: 909 | duplicates.append(i.objectName()) 910 | else: 911 | pass 912 | else: 913 | actions[i.objectName()] = i 914 | else: 915 | pass 916 | else: 917 | pass 918 | 919 | for d in duplicates: 920 | del actions[d] 921 | 922 | return actions 923 | 924 | 925 | def extractWorkbench(command): 926 | 927 | cmd_parts = command.split("_") 928 | if cmd_parts[0] == "": 929 | workbench = "None" 930 | else: 931 | workbench = cmd_parts[0] 932 | 933 | return workbench 934 | 935 | 936 | def getActionData(action, actions, commands, workbenches): 937 | 938 | if not action.icon(): 939 | return 940 | if actions is not None: 941 | if action in actions: 942 | pass 943 | else: 944 | actions.append(action) 945 | if commands is None and workbenches is None: 946 | return 947 | 948 | command = action.objectName() 949 | if len(command) == 0: 950 | workbench = "None" 951 | else: 952 | if commands is not None: 953 | if command in commands: 954 | pass 955 | else: 956 | commands.append(command) 957 | workbench = extractWorkbench(command) 958 | 959 | if workbenches is not None: 960 | if not workbench in workbenches: 961 | workbenches.append(workbench) 962 | 963 | 964 | def getGuiToolButtonData(idToolBar, actions, commands, workbenches): 965 | 966 | actionMapAll = getGuiActionMapAll() 967 | for i in actionMapAll: 968 | action = actionMapAll[i] 969 | for widgets in action.associatedWidgets(): 970 | if widgets.objectName() == idToolBar: 971 | getActionData(action, actions, commands, workbenches) 972 | 973 | 974 | def actualizeWorkbenchActions(actions, toolList, actionMap): 975 | for i in toolList: 976 | # rule out special case: there has to be an entry 977 | if i == "": 978 | pass 979 | elif i in actionMap: 980 | if not actionMap[i] in actions: 981 | actions.append(actionMap[i]) 982 | else: 983 | cmd_parts = i.split("_") 984 | # rule out special case: unknown Std action 985 | if cmd_parts[0] == "Std": 986 | pass 987 | else: 988 | # match special cases 989 | # Fem workbench 990 | if cmd_parts[0] == "FEM": 991 | cmd_parts[0] = "Fem" 992 | # Sheet Metal workbench 993 | if cmd_parts[0][:2] == "SM": 994 | cmd_parts[0] = cmd_parts[0][:2] 995 | 996 | cmdWb = cmd_parts[0] + "Workbench" 997 | # after workbench activation actionMap has to be actualized 998 | Gui.activateWorkbench(cmdWb) 999 | return True 1000 | 1001 | return False 1002 | 1003 | 1004 | def updateCommands(context=False): 1005 | 1006 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 1007 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1008 | indexList = paramIndexGet.GetString("IndexList") 1009 | 1010 | if paramGet.GetBool("ToolBar") and context is False: 1011 | 1012 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 1013 | toolbar = paramGet.GetString("ToolBar") 1014 | 1015 | if ": " in toolbar: 1016 | toolbar_desc = toolbar.split(": ") 1017 | toolbar = toolbar_desc[1] 1018 | workbenches = toolbar_desc[0] 1019 | workbenches = workbenches.split(", ") 1020 | lastWorkbench = Gui.activeWorkbench() 1021 | 1022 | for i in workbenches: 1023 | # rule out special cases 1024 | if i == "None" or i == "Std": 1025 | pass 1026 | else: 1027 | # match special cases 1028 | # Fem workbench 1029 | if i == "FEM": 1030 | i = "Fem" 1031 | # Sheet Metal workbench 1032 | if i[:2] == "SM": 1033 | i = i[:2] 1034 | 1035 | Gui.activateWorkbench(i + "Workbench") 1036 | 1037 | Gui.activateWorkbench(lastWorkbench.__class__.__name__) 1038 | else: 1039 | pass 1040 | 1041 | actions = [] 1042 | getGuiToolButtonData(toolbar, actions, None, None) 1043 | 1044 | else: 1045 | 1046 | if indexList: 1047 | indexList = indexList.split(".,.") 1048 | 1049 | temp = [] 1050 | 1051 | for i in indexList: 1052 | temp.append(int(i)) 1053 | 1054 | indexList = temp 1055 | else: 1056 | indexList = [] 1057 | 1058 | if context: 1059 | try: 1060 | text = paramGet.GetString("ContextPie").decode("UTF-8") 1061 | except AttributeError: 1062 | text = paramGet.GetString("ContextPie") 1063 | else: 1064 | try: 1065 | text = paramGet.GetString("CurrentPie").decode("UTF-8") 1066 | except AttributeError: 1067 | text = paramGet.GetString("CurrentPie") 1068 | 1069 | toolList = None 1070 | 1071 | for i in indexList: 1072 | a = str(i) 1073 | try: 1074 | pie = paramIndexGet.GetString(a).decode("UTF-8") 1075 | except AttributeError: 1076 | pie = paramIndexGet.GetString(a) 1077 | if pie == text: 1078 | group = paramIndexGet.GetGroup(a) 1079 | toolList = group.GetString("ToolList") 1080 | else: 1081 | pass 1082 | 1083 | if toolList: 1084 | toolList = toolList.split(".,.") 1085 | else: 1086 | toolList = [] 1087 | 1088 | actions = [] 1089 | 1090 | actionMapAll = getGuiActionMapAll() 1091 | lastWorkbench = Gui.activeWorkbench() 1092 | 1093 | while actualizeWorkbenchActions(actions, toolList, actionMapAll): 1094 | actionMapAll = getGuiActionMapAll() 1095 | else: 1096 | pass 1097 | 1098 | Gui.activateWorkbench(lastWorkbench.__class__.__name__) 1099 | 1100 | PieMenuInstance.add_commands(actions, context) 1101 | 1102 | 1103 | def getGroup(mode=0): 1104 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 1105 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1106 | indexList = paramIndexGet.GetString("IndexList") 1107 | 1108 | if mode == 2: 1109 | try: 1110 | text = paramGet.GetString("ContextPie").decode("UTF-8") 1111 | except AttributeError: 1112 | text = paramGet.GetString("ContextPie") 1113 | elif mode == 1: 1114 | try: 1115 | text = paramGet.GetString("CurrentPie").decode("UTF-8") 1116 | except AttributeError: 1117 | text = paramGet.GetString("CurrentPie") 1118 | else: 1119 | text = cBox.currentText() 1120 | 1121 | if indexList: 1122 | indexList = indexList.split(".,.") 1123 | 1124 | temp = [] 1125 | 1126 | for i in indexList: 1127 | temp.append(int(i)) 1128 | 1129 | indexList = temp 1130 | else: 1131 | indexList = [] 1132 | 1133 | group = None 1134 | 1135 | for i in indexList: 1136 | a = str(i) 1137 | try: 1138 | pie = paramIndexGet.GetString(a).decode("UTF-8") 1139 | except AttributeError: 1140 | pie = paramIndexGet.GetString(a) 1141 | 1142 | if pie == text: 1143 | group = paramIndexGet.GetGroup(a) 1144 | else: 1145 | pass 1146 | 1147 | if group: 1148 | pass 1149 | else: 1150 | if 0 in indexList: 1151 | group = paramIndexGet.GetGroup("0") 1152 | else: 1153 | setDefaultPie() 1154 | updateCommands() 1155 | group = paramIndexGet.GetGroup("0") 1156 | 1157 | return group 1158 | 1159 | buttonListWidget = QtGui.QListWidget() 1160 | buttonListWidget.setHorizontalScrollBarPolicy(QtCore 1161 | .Qt.ScrollBarAlwaysOff) 1162 | 1163 | 1164 | def buttonList(): 1165 | 1166 | group = getGroup() 1167 | toolList = group.GetString("ToolList") 1168 | 1169 | if toolList: 1170 | toolList = toolList.split(".,.") 1171 | else: 1172 | toolList = [] 1173 | 1174 | actionMapAll = getGuiActionMapAll() 1175 | 1176 | buttonListWidget.blockSignals(True) 1177 | 1178 | buttonListWidget.clear() 1179 | 1180 | workbenches = [] 1181 | lastWorkbench = Gui.activeWorkbench() 1182 | 1183 | for i in toolList: 1184 | if i not in actionMapAll: 1185 | # rule out special case: there has to be an entry 1186 | if i == "": 1187 | pass 1188 | else: 1189 | cmd_parts = i.split("_") 1190 | if cmd_parts[0] not in workbenches: 1191 | # rule out special case: unknown Std action 1192 | if cmd_parts[0] == "Std": 1193 | pass 1194 | else: 1195 | # treatment of special cases 1196 | # Fem workbench 1197 | if cmd_parts[0] == "FEM": 1198 | cmd_parts[0] = "Fem" 1199 | # Sheet Metal workbench 1200 | if cmd_parts[0][:2] == "SM": 1201 | cmd_parts[0] = cmd_parts[0][:2] 1202 | workbenches.append(cmd_parts[0]) 1203 | Gui.activateWorkbench(cmd_parts[0] + "Workbench") 1204 | else: 1205 | pass 1206 | else: 1207 | pass 1208 | 1209 | Gui.activateWorkbench(lastWorkbench.__class__.__name__) 1210 | actionMapAll = getGuiActionMapAll() 1211 | 1212 | for i in toolList: 1213 | if i in actionMapAll: 1214 | item = QtGui.QListWidgetItem(buttonListWidget) 1215 | item.setData(QtCore.Qt.UserRole, i) 1216 | item.setText(actionMapAll[i].text().replace("&", "")) 1217 | item.setIcon(actionMapAll[i].icon()) 1218 | else: 1219 | pass 1220 | 1221 | buttonListWidget.blockSignals(False) 1222 | 1223 | 1224 | cBox = QtGui.QComboBox() 1225 | cBox.setMinimumHeight(30) 1226 | 1227 | 1228 | def cBoxUpdate(): 1229 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 1230 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1231 | indexList = paramIndexGet.GetString("IndexList") 1232 | try: 1233 | currentPie = paramGet.GetString("CurrentPie").decode("UTF-8") 1234 | except AttributeError: 1235 | currentPie = paramGet.GetString("CurrentPie") 1236 | 1237 | if indexList: 1238 | indexList = indexList.split(".,.") 1239 | 1240 | temp = [] 1241 | 1242 | for i in indexList: 1243 | temp.append(int(i)) 1244 | 1245 | indexList = temp 1246 | else: 1247 | indexList = [] 1248 | 1249 | pieList = [] 1250 | 1251 | for i in indexList: 1252 | a = str(i) 1253 | try: 1254 | pieList.append(paramIndexGet.GetString(a).decode("UTF-8")) 1255 | except AttributeError: 1256 | pieList.append(paramIndexGet.GetString(a)) 1257 | 1258 | duplicates = [] 1259 | for i in pieList: 1260 | if i == currentPie: 1261 | pass 1262 | else: 1263 | duplicates.append(i) 1264 | duplicates.append(currentPie) 1265 | pieList = duplicates 1266 | 1267 | pieList.reverse() 1268 | 1269 | cBox.blockSignals(True) 1270 | 1271 | cBox.clear() 1272 | 1273 | for i in pieList: 1274 | cBox.insertItem(0, i) 1275 | 1276 | cBox.blockSignals(False) 1277 | 1278 | onPieChange() 1279 | 1280 | 1281 | def onPieChange(): 1282 | buttonList() 1283 | toolList() 1284 | setDefaults() 1285 | setCheckContext() 1286 | 1287 | cBox.currentIndexChanged.connect(onPieChange) 1288 | 1289 | buttonAddPieMenu = QtGui.QToolButton() 1290 | buttonAddPieMenu.setIcon(QtGui.QIcon(iconAdd)) 1291 | buttonAddPieMenu.setToolTip("Add new pie menu") 1292 | buttonAddPieMenu.setMinimumHeight(30) 1293 | buttonAddPieMenu.setMinimumWidth(30) 1294 | 1295 | 1296 | def inputTextDialog(title): 1297 | 1298 | info1 = "Please insert menu name" 1299 | info2 = "Menu already exists" 1300 | 1301 | d = QtGui.QInputDialog(pieMenuDialog) 1302 | d.setModal(True) 1303 | d.setInputMode(QtGui.QInputDialog.InputMode.TextInput) 1304 | text, ok = QtGui.QInputDialog.getText(pieMenuDialog, 1305 | title, 1306 | info1) 1307 | if not ok: 1308 | return text, ok 1309 | 1310 | while not text: 1311 | text, ok = QtGui.QInputDialog.getText(pieMenuDialog, 1312 | title, 1313 | info1) 1314 | if not ok: 1315 | return text, ok 1316 | else: 1317 | pass 1318 | 1319 | index = cBox.findText(text) 1320 | info = info2 1321 | 1322 | while index != -1: 1323 | d = QtGui.QInputDialog(pieMenuDialog) 1324 | d.setModal(True) 1325 | d.setInputMode(QtGui.QInputDialog.InputMode.TextInput) 1326 | text, ok = QtGui.QInputDialog.getText(pieMenuDialog, 1327 | title, 1328 | info) 1329 | if ok: 1330 | if text: 1331 | index = cBox.findText(text) 1332 | info = info2 1333 | else: 1334 | info = info1 1335 | else: 1336 | return text, ok 1337 | 1338 | return text, ok 1339 | 1340 | 1341 | def splitIndexList(indexList): 1342 | 1343 | if indexList: 1344 | indexList = indexList.split(".,.") 1345 | 1346 | temp = [] 1347 | 1348 | for i in indexList: 1349 | temp.append(int(i)) 1350 | 1351 | indexList = temp 1352 | else: 1353 | indexList = [] 1354 | 1355 | return indexList 1356 | 1357 | 1358 | def createPie(text): 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1365 | indexList = paramIndexGet.GetString("IndexList") 1366 | indexList = splitIndexList(indexList) 1367 | 1368 | pieList = [] 1369 | 1370 | for i in indexList: 1371 | a = str(i) 1372 | pieList.append(paramIndexGet.GetString(a)) 1373 | 1374 | if text.encode('UTF-8') in pieList: 1375 | pass 1376 | elif not text: 1377 | pass 1378 | else: 1379 | 1380 | if text == "restore_default_pie" and text.lower(): 1381 | setDefaultPie(restore=True) 1382 | else: 1383 | x = 1 1384 | 1385 | while x in indexList and x < 999: 1386 | x = x + 1 1387 | else: 1388 | indexNumber = x 1389 | 1390 | indexList.append(indexNumber) 1391 | 1392 | temp = [] 1393 | 1394 | for i in indexList: 1395 | temp.append(str(i)) 1396 | 1397 | indexList = temp 1398 | 1399 | paramIndexGet.SetString("IndexList", ".,.".join(indexList)) 1400 | 1401 | indexNumber = str(indexNumber) 1402 | try: 1403 | paramIndexGet.SetString(indexNumber, text.encode('UTF-8')) 1404 | except TypeError: 1405 | paramIndexGet.SetString(indexNumber, text) 1406 | 1407 | cBoxUpdate() 1408 | 1409 | return paramIndexGet.GetGroup(indexNumber) 1410 | 1411 | 1412 | def onButtonAddPieMenu(): 1413 | 1414 | text, ok = inputTextDialog("New menu") 1415 | if not ok: 1416 | return 1417 | 1418 | createPie(text) 1419 | 1420 | 1421 | buttonAddPieMenu.clicked.connect(onButtonAddPieMenu) 1422 | 1423 | buttonRemovePieMenu = QtGui.QToolButton() 1424 | buttonRemovePieMenu.setIcon(QtGui.QIcon(iconRemove)) 1425 | buttonRemovePieMenu.setToolTip("Remove current pie menu") 1426 | buttonRemovePieMenu.setMinimumHeight(30) 1427 | buttonRemovePieMenu.setMinimumWidth(30) 1428 | 1429 | 1430 | def onButtonRemovePieMenu(): 1431 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 1432 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1433 | indexList = paramIndexGet.GetString("IndexList") 1434 | indexList = splitIndexList(indexList) 1435 | try: 1436 | currentPie = paramGet.GetString("CurrentPie").decode("UTF-8") 1437 | except AttributeError: 1438 | currentPie = paramGet.GetString("CurrentPie") 1439 | try: 1440 | contextPie = paramGet.GetString("ContextPie").decode("UTF-8") 1441 | except AttributeError: 1442 | contextPie = paramGet.GetString("ContextPie") 1443 | 1444 | text = cBox.currentText() 1445 | 1446 | for i in indexList: 1447 | a = str(i) 1448 | try: 1449 | pie = paramIndexGet.GetString(a).decode("UTF-8") 1450 | except AttributeError: 1451 | pie = paramIndexGet.GetString(a) 1452 | if pie == text: 1453 | indexList.remove(i) 1454 | 1455 | temp = [] 1456 | 1457 | for i in indexList: 1458 | temp.append(str(i)) 1459 | 1460 | indexList = temp 1461 | 1462 | paramIndexGet.SetString("IndexList", ".,.".join(indexList)) 1463 | 1464 | paramIndexGet.RemGroup(a) 1465 | paramIndexGet.RemString(a) 1466 | # special case treatment 1467 | if pie == currentPie: 1468 | currentPie = "Default" 1469 | try: 1470 | paramGet.SetString("CurrentPie", currentPie.encode('UTF-8')) 1471 | except TypeError: 1472 | paramGet.SetString("CurrentPie", currentPie) 1473 | if pie == contextPie: 1474 | paramGet.RemString("ContextPie") 1475 | else: 1476 | pass 1477 | 1478 | cBoxUpdate() 1479 | 1480 | if cBox.currentIndex() == -1: 1481 | setDefaultPie() 1482 | cBoxUpdate() 1483 | else: 1484 | pass 1485 | 1486 | buttonRemovePieMenu.clicked.connect(onButtonRemovePieMenu) 1487 | 1488 | buttonRenamePieMenu = QtGui.QToolButton() 1489 | buttonRenamePieMenu.setToolTip("Rename current pie menu") 1490 | buttonRenamePieMenu.setIcon(QtGui.QIcon(iconRename)) 1491 | buttonRenamePieMenu.setMinimumHeight(30) 1492 | buttonRenamePieMenu.setMinimumWidth(30) 1493 | 1494 | 1495 | def onButtonRenamePieMenu(): 1496 | 1497 | text, ok = inputTextDialog("Rename menu") 1498 | if not ok: 1499 | return 1500 | 1501 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 1502 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1503 | indexList = paramIndexGet.GetString("IndexList") 1504 | indexList = splitIndexList(indexList) 1505 | try: 1506 | currentPie = paramGet.GetString("CurrentPie").decode("UTF-8") 1507 | except AttributeError: 1508 | currentPie = paramGet.GetString("CurrentPie") 1509 | currentText = cBox.currentText() 1510 | 1511 | for i in indexList: 1512 | a = str(i) 1513 | try: 1514 | pie = paramIndexGet.GetString(a).decode("UTF-8") 1515 | except AttributeError: 1516 | pie = paramIndexGet.GetString(a) 1517 | if pie == currentText: 1518 | try: 1519 | paramIndexGet.SetString(a, text.encode('UTF-8')) 1520 | except TypeError: 1521 | paramIndexGet.SetString(a, text) 1522 | if pie == currentPie: 1523 | try: 1524 | paramGet.SetString("CurrentPie", text.encode('UTF-8')) 1525 | except TypeError: 1526 | paramGet.SetString("CurrentPie", text) 1527 | else: 1528 | pass 1529 | cBoxUpdate() 1530 | 1531 | buttonRenamePieMenu.clicked.connect(onButtonRenamePieMenu) 1532 | 1533 | buttonCopyPieMenu = QtGui.QToolButton() 1534 | buttonCopyPieMenu.setToolTip("Copy current pie menu") 1535 | buttonCopyPieMenu.setIcon(QtGui.QIcon(iconCopy)) 1536 | buttonCopyPieMenu.setMinimumHeight(30) 1537 | buttonCopyPieMenu.setMinimumWidth(30) 1538 | 1539 | 1540 | def getCurrentMenuIndex(currentMenuName): 1541 | 1542 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1543 | indexList = paramIndexGet.GetString("IndexList") 1544 | indexList = splitIndexList(indexList) 1545 | 1546 | for i in indexList: 1547 | a = str(i) 1548 | indexName = paramIndexGet.GetString(a) 1549 | if indexName == currentMenuName: 1550 | return a; 1551 | 1552 | return "-1" 1553 | 1554 | def copyIndexParams(grpOrg, grpCopy): 1555 | 1556 | valButOrg = grpOrg.GetInt("Button") 1557 | valRadOrg = grpOrg.GetInt("Radius") 1558 | tbOrg = grpOrg.GetString("ToolList") 1559 | 1560 | grpCopy.SetInt("Button", valButOrg) 1561 | grpCopy.SetInt("Radius", valRadOrg) 1562 | grpCopy.SetString("ToolList", tbOrg) 1563 | 1564 | def copyContextParams(grpOrg, grpCopy): 1565 | 1566 | grpCntOrg = grpOrg.GetGroup("Context") 1567 | grpCntCopy = grpCopy.GetGroup("Context") 1568 | 1569 | enabledOrg = grpCntOrg.GetBool("Enabled") 1570 | vtxSgnOrg = grpCntOrg.GetString("VertexSign") 1571 | vtxValOrg = grpCntOrg.GetInt("VertexValue") 1572 | edgSgnOrg = grpCntOrg.GetString("EdgeSign") 1573 | edgValOrg = grpCntOrg.GetInt("EdgeValue") 1574 | fceSgnOrg = grpCntOrg.GetString("FaceSign") 1575 | fceValOrg = grpCntOrg.GetInt("FaceValue") 1576 | objSgnOrg = grpCntOrg.GetString("ObjectSign") 1577 | objValOrg = grpCntOrg.GetInt("ObjectValue") 1578 | 1579 | grpCntCopy.SetBool("Enabled", enabledOrg) 1580 | grpCntCopy.SetString("VertexSign", vtxSgnOrg) 1581 | grpCntCopy.SetInt("VertexValue", vtxValOrg) 1582 | grpCntCopy.SetString("EdgeSign", edgSgnOrg) 1583 | grpCntCopy.SetInt("EdgeValue", edgValOrg) 1584 | grpCntCopy.SetString("FaceSign", fceSgnOrg) 1585 | grpCntCopy.SetInt("FaceValue", fceValOrg) 1586 | grpCntCopy.SetString("ObjectSign", objSgnOrg) 1587 | grpCntCopy.SetInt("ObjectValue", objValOrg) 1588 | 1589 | 1590 | def onButtonCopyPieMenu(): 1591 | 1592 | text, ok = inputTextDialog("Copy menu") 1593 | if not ok: 1594 | return 1595 | 1596 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1597 | indexList = paramIndexGet.GetString("IndexList") 1598 | indexList = splitIndexList(indexList) 1599 | 1600 | currentMenuName = cBox.currentText() 1601 | indexOrg = getCurrentMenuIndex(currentMenuName) 1602 | 1603 | pieList = [] 1604 | 1605 | for i in indexList: 1606 | a = str(i) 1607 | pieList.append(paramIndexGet.GetString(a)) 1608 | 1609 | if text.encode('UTF-8') in pieList: 1610 | pass 1611 | elif not text: 1612 | pass 1613 | else: 1614 | x = 1 1615 | 1616 | while x in indexList and x < 999: 1617 | x = x + 1 1618 | else: 1619 | indexCopy = x 1620 | 1621 | indexList.append(indexCopy) 1622 | 1623 | temp = [] 1624 | 1625 | for i in indexList: 1626 | temp.append(str(i)) 1627 | 1628 | indexList = temp 1629 | 1630 | paramIndexGet.SetString("IndexList", ".,.".join(indexList)) 1631 | 1632 | indexCopy = str(indexCopy) 1633 | grpOrg = paramIndexGet.GetGroup(indexOrg) 1634 | grpCopy = paramIndexGet.GetGroup(indexCopy) 1635 | copyIndexParams(grpOrg, grpCopy) 1636 | copyContextParams(grpOrg, grpCopy) 1637 | 1638 | try: 1639 | paramIndexGet.SetString(indexCopy, text.encode('UTF-8')) 1640 | except TypeError: 1641 | paramIndexGet.SetString(indexCopy, text) 1642 | 1643 | cBoxUpdate() 1644 | 1645 | buttonCopyPieMenu.clicked.connect(onButtonCopyPieMenu) 1646 | 1647 | labelRadius = QtGui.QLabel("Pie size") 1648 | spinRadius = QtGui.QSpinBox() 1649 | spinRadius.setMaximum(9999) 1650 | spinRadius.setMinimumWidth(70) 1651 | 1652 | 1653 | def onSpinRadius(): 1654 | group = getGroup() 1655 | value = spinRadius.value() 1656 | group.SetInt("Radius", value) 1657 | 1658 | spinRadius.valueChanged.connect(onSpinRadius) 1659 | 1660 | labelButton = QtGui.QLabel("Button size") 1661 | spinButton = QtGui.QSpinBox() 1662 | spinButton.setMaximum(999) 1663 | spinButton.setMinimumWidth(70) 1664 | 1665 | 1666 | def onSpinButton(): 1667 | group = getGroup() 1668 | value = spinButton.value() 1669 | group.SetInt("Button", value) 1670 | 1671 | spinButton.valueChanged.connect(onSpinButton) 1672 | 1673 | toolListWidget = QtGui.QListWidget() 1674 | toolListWidget.setSortingEnabled(True) 1675 | toolListWidget.sortItems(QtCore.Qt.AscendingOrder) 1676 | toolListWidget.setHorizontalScrollBarPolicy(QtCore 1677 | .Qt.ScrollBarAlwaysOff) 1678 | 1679 | 1680 | def toolList(): 1681 | 1682 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1683 | indexList = paramIndexGet.GetString("IndexList") 1684 | 1685 | text = cBox.currentText() 1686 | 1687 | actionMapAll = getGuiActionMapAll() 1688 | 1689 | toolListWidget.blockSignals(True) 1690 | toolListWidget.clear() 1691 | 1692 | for i in actionMapAll: 1693 | item = QtGui.QListWidgetItem(toolListWidget) 1694 | item.setText(actionMapAll[i].text().replace("&", "")) 1695 | item.setIcon(actionMapAll[i].icon()) 1696 | item.setCheckState(QtCore.Qt.CheckState(0)) 1697 | item.setData(QtCore.Qt.UserRole, actionMapAll[i].objectName()) 1698 | 1699 | if indexList: 1700 | indexList = indexList.split(".,.") 1701 | 1702 | temp = [] 1703 | 1704 | for i in indexList: 1705 | temp.append(int(i)) 1706 | 1707 | indexList = temp 1708 | else: 1709 | 1710 | indexList = [] 1711 | 1712 | toolListOn = None 1713 | 1714 | for i in indexList: 1715 | a = str(i) 1716 | try: 1717 | pie = paramIndexGet.GetString(a).decode("UTF-8") 1718 | except AttributeError: 1719 | pie = paramIndexGet.GetString(a) 1720 | if pie == text: 1721 | group = paramIndexGet.GetGroup(a) 1722 | toolListOn = group.GetString("ToolList") 1723 | else: 1724 | pass 1725 | 1726 | if toolListOn: 1727 | toolListOn = toolListOn.split(".,.") 1728 | else: 1729 | toolListOn = [] 1730 | 1731 | items = [] 1732 | for index in range(toolListWidget.count()): 1733 | items.append(toolListWidget.item(index)) 1734 | 1735 | for i in items: 1736 | if i.data(QtCore.Qt.UserRole) in toolListOn: 1737 | i.setCheckState(QtCore.Qt.CheckState(2)) 1738 | else: 1739 | pass 1740 | 1741 | toolListWidget.blockSignals(False) 1742 | 1743 | 1744 | def onToolListWidget(): 1745 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 1746 | 1747 | text = cBox.currentText() 1748 | 1749 | items = [] 1750 | for index in range(toolListWidget.count()): 1751 | items.append(toolListWidget.item(index)) 1752 | 1753 | checkListOn = [] 1754 | checkListOff = [] 1755 | for i in items: 1756 | if i.checkState(): 1757 | checkListOn.append(i.data(QtCore.Qt.UserRole)) 1758 | else: 1759 | checkListOff.append(i.data(QtCore.Qt.UserRole)) 1760 | 1761 | indexList = paramIndexGet.GetString("IndexList") 1762 | 1763 | if indexList: 1764 | indexList = indexList.split(".,.") 1765 | 1766 | temp = [] 1767 | 1768 | for i in indexList: 1769 | temp.append(int(i)) 1770 | 1771 | indexList = temp 1772 | else: 1773 | indexList = [] 1774 | 1775 | toolList = None 1776 | 1777 | for i in indexList: 1778 | a = str(i) 1779 | try: 1780 | pie = paramIndexGet.GetString(a).decode("UTF-8") 1781 | except AttributeError: 1782 | pie = paramIndexGet.GetString(a) 1783 | if pie == text: 1784 | group = paramIndexGet.GetGroup(a) 1785 | toolList = group.GetString("ToolList") 1786 | else: 1787 | pass 1788 | 1789 | if toolList: 1790 | toolList = toolList.split(".,.") 1791 | else: 1792 | toolList = [] 1793 | 1794 | for i in checkListOn: 1795 | if i not in toolList: 1796 | toolList.append(i) 1797 | else: 1798 | pass 1799 | 1800 | for i in checkListOff: 1801 | if i in toolList: 1802 | toolList.remove(i) 1803 | else: 1804 | pass 1805 | 1806 | for i in indexList: 1807 | a = str(i) 1808 | try: 1809 | pie = paramIndexGet.GetString(a).decode("UTF-8") 1810 | except AttributeError: 1811 | pie = paramIndexGet.GetString(a) 1812 | if pie == text: 1813 | group = paramIndexGet.GetGroup(a) 1814 | toolList = group.SetString("ToolList", ".,.".join(toolList)) 1815 | else: 1816 | pass 1817 | 1818 | buttonList() 1819 | 1820 | toolListWidget.itemChanged.connect(onToolListWidget) 1821 | 1822 | 1823 | def buttonList2ToolList(buttonListWidget): 1824 | 1825 | items = [] 1826 | for index in range(buttonListWidget.count()): 1827 | items.append(buttonListWidget.item(index)) 1828 | 1829 | toolData = [] 1830 | for i in items: 1831 | toolData.append(i.data(QtCore.Qt.UserRole)) 1832 | 1833 | group = getGroup() 1834 | group.SetString("ToolList", ".,.".join(toolData)) 1835 | 1836 | 1837 | buttonUp = QtGui.QToolButton() 1838 | buttonUp.setIcon(QtGui.QIcon(iconUp)) 1839 | buttonUp.setToolTip("Move selected command up") 1840 | buttonUp.setMinimumHeight(30) 1841 | buttonUp.setMinimumWidth(30) 1842 | 1843 | 1844 | def onButtonUp(): 1845 | currentIndex = buttonListWidget.currentRow() 1846 | 1847 | if currentIndex != 0: 1848 | currentItem = buttonListWidget.takeItem(currentIndex) 1849 | buttonListWidget.insertItem(currentIndex - 1, currentItem) 1850 | buttonListWidget.setCurrentRow(currentIndex - 1) 1851 | buttonList2ToolList(buttonListWidget) 1852 | 1853 | buttonUp.clicked.connect(onButtonUp) 1854 | 1855 | buttonDown = QtGui.QToolButton() 1856 | buttonDown.setIcon(QtGui.QIcon(iconDown)) 1857 | buttonDown.setToolTip("Move selected command down") 1858 | buttonDown.setMinimumHeight(30) 1859 | buttonDown.setMinimumWidth(30) 1860 | 1861 | 1862 | def onButtonDown(): 1863 | currentIndex = buttonListWidget.currentRow() 1864 | 1865 | if currentIndex != buttonListWidget.count() - 1 and currentIndex != -1: 1866 | currentItem = buttonListWidget.takeItem(currentIndex) 1867 | buttonListWidget.insertItem(currentIndex + 1, currentItem) 1868 | buttonListWidget.setCurrentRow(currentIndex + 1) 1869 | buttonList2ToolList(buttonListWidget) 1870 | 1871 | buttonDown.clicked.connect(onButtonDown) 1872 | 1873 | buttonRemoveCommand = QtGui.QPushButton() 1874 | buttonRemoveCommand.setIcon(QtGui.QIcon(iconRemoveCommand)) 1875 | buttonRemoveCommand.setToolTip("Remove selected command") 1876 | buttonRemoveCommand.setMinimumHeight(30) 1877 | buttonRemoveCommand.setMinimumWidth(30) 1878 | 1879 | 1880 | def onButtonRemoveCommand(): 1881 | 1882 | currentIndex = buttonListWidget.currentRow() 1883 | buttonListWidget.takeItem(currentIndex) 1884 | 1885 | if currentIndex != 0: 1886 | buttonListWidget.setCurrentRow(currentIndex - 1) 1887 | buttonListWidget.setFocus() 1888 | buttonList2ToolList(buttonListWidget) 1889 | toolList() 1890 | 1891 | buttonRemoveCommand.clicked.connect(onButtonRemoveCommand) 1892 | 1893 | vertexItem = QtGui.QTableWidgetItem() 1894 | vertexItem.setText("Vertex") 1895 | vertexItem.setToolTip("Set desired operator and vertex number") 1896 | vertexItem.setFlags(QtCore.Qt.ItemIsEnabled) 1897 | 1898 | edgeItem = QtGui.QTableWidgetItem() 1899 | edgeItem.setText("Edge") 1900 | edgeItem.setToolTip("Set desired operator and edge number") 1901 | edgeItem.setFlags(QtCore.Qt.ItemIsEnabled) 1902 | 1903 | faceItem = QtGui.QTableWidgetItem() 1904 | faceItem.setText("Face") 1905 | faceItem.setToolTip("Set desired operator and face number") 1906 | faceItem.setFlags(QtCore.Qt.ItemIsEnabled) 1907 | 1908 | objectItem = QtGui.QTableWidgetItem() 1909 | objectItem.setText("Object") 1910 | objectItem.setToolTip("Set desired operator and object number") 1911 | objectItem.setFlags(QtCore.Qt.ItemIsEnabled) 1912 | 1913 | 1914 | def comboBox(TopoType): 1915 | signList = ["<", "<=", "==", "!=", ">", ">="] 1916 | 1917 | model = QtGui.QStandardItemModel() 1918 | 1919 | for i in signList: 1920 | item = QtGui.QStandardItem() 1921 | item.setText(i) 1922 | item.setData(TopoType, QtCore.Qt.UserRole) 1923 | 1924 | model.setItem(signList.index(i), 0, item) 1925 | 1926 | comboBoxSign = QtGui.QComboBox() 1927 | comboBoxSign.setModel(model) 1928 | comboBoxSign.setStyleSheet(styleCombo) 1929 | 1930 | def onCurrentIndexChanged(): 1931 | group = getGroup() 1932 | 1933 | groupContext = group.GetGroup("Context") 1934 | text = comboBoxSign.currentText() 1935 | topo = comboBoxSign.itemData(comboBoxSign.currentIndex(), 1936 | QtCore.Qt.UserRole) 1937 | groupContext.SetString(topo, text) 1938 | 1939 | contextList() 1940 | 1941 | comboBoxSign.currentIndexChanged.connect(onCurrentIndexChanged) 1942 | 1943 | return comboBoxSign 1944 | 1945 | vertexComboBox = comboBox("VertexSign") 1946 | edgeComboBox = comboBox("EdgeSign") 1947 | faceComboBox = comboBox("FaceSign") 1948 | objectComboBox = comboBox("ObjectSign") 1949 | 1950 | 1951 | def spinBox(TopoValue): 1952 | 1953 | spinBox = QtGui.QSpinBox() 1954 | spinBox.setFrame(False) 1955 | 1956 | def onSpinBox(): 1957 | group = getGroup() 1958 | 1959 | groupContext = group.GetGroup("Context") 1960 | value = spinBox.value() 1961 | groupContext.SetInt(TopoValue, value) 1962 | 1963 | contextList() 1964 | 1965 | spinBox.valueChanged.connect(onSpinBox) 1966 | 1967 | return spinBox 1968 | 1969 | vertexSpin = spinBox("VertexValue") 1970 | edgeSpin = spinBox("EdgeValue") 1971 | faceSpin = spinBox("FaceValue") 1972 | objectSpin = spinBox("ObjectValue") 1973 | 1974 | labelContext = QtGui.QLabel("Enable") 1975 | checkContext = QtGui.QCheckBox() 1976 | 1977 | 1978 | def setCheckContext(): 1979 | 1980 | group = getGroup() 1981 | groupContext = group.GetGroup("Context") 1982 | 1983 | if groupContext.GetBool("Enabled"): 1984 | checkContext.setChecked(True) 1985 | contextTable.setEnabled(True) 1986 | resetButton.setEnabled(True) 1987 | else: 1988 | checkContext.setChecked(False) 1989 | contextTable.setEnabled(False) 1990 | resetButton.setEnabled(False) 1991 | 1992 | contextList() 1993 | 1994 | 1995 | def onCheckContext(): 1996 | 1997 | setDefaults() 1998 | 1999 | group = getGroup() 2000 | groupContext = group.GetGroup("Context") 2001 | 2002 | if checkContext.isChecked(): 2003 | contextTable.setEnabled(True) 2004 | resetButton.setEnabled(True) 2005 | 2006 | groupContext.SetBool("Enabled", 1) 2007 | 2008 | else: 2009 | contextTable.setEnabled(False) 2010 | resetButton.setEnabled(False) 2011 | 2012 | groupContext.SetBool("Enabled", 0) 2013 | 2014 | contextList() 2015 | 2016 | checkContext.stateChanged.connect(onCheckContext) 2017 | 2018 | contextTable = QtGui.QTableWidget(4, 3) 2019 | contextTable.setMaximumHeight(120) 2020 | contextTable.setFrameStyle(QtGui.QFrame.NoFrame) 2021 | contextTable.verticalHeader().setVisible(False) 2022 | contextTable.horizontalHeader().setVisible(False) 2023 | try: 2024 | contextTable.verticalHeader().setResizeMode(QtGui.QHeaderView.Stretch) 2025 | contextTable.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) 2026 | except AttributeError: 2027 | contextTable.verticalHeader().setSectionResizeMode(QtGui.QHeaderView.Stretch) 2028 | contextTable.horizontalHeader().setSectionResizeMode(QtGui.QHeaderView.Stretch) 2029 | 2030 | contextTable.setItem(0, 0, vertexItem) 2031 | contextTable.setCellWidget(0, 1, vertexComboBox) 2032 | contextTable.setCellWidget(0, 2, vertexSpin) 2033 | 2034 | contextTable.setItem(1, 0, edgeItem) 2035 | contextTable.setCellWidget(1, 1, edgeComboBox) 2036 | contextTable.setCellWidget(1, 2, edgeSpin) 2037 | 2038 | contextTable.setItem(2, 0, faceItem) 2039 | contextTable.setCellWidget(2, 1, faceComboBox) 2040 | contextTable.setCellWidget(2, 2, faceSpin) 2041 | 2042 | contextTable.setItem(3, 0, objectItem) 2043 | contextTable.setCellWidget(3, 1, objectComboBox) 2044 | contextTable.setCellWidget(3, 2, objectSpin) 2045 | 2046 | resetButton = QtGui.QToolButton() 2047 | resetButton.setMinimumHeight(30) 2048 | resetButton.setMinimumWidth(30) 2049 | resetButton.setText(u'\u27F3') 2050 | 2051 | resetButton.setEnabled(False) 2052 | 2053 | 2054 | def onResetButton(): 2055 | 2056 | group = getGroup() 2057 | group.RemGroup("Context") 2058 | setDefaults() 2059 | setCheckContext() 2060 | 2061 | resetButton.clicked.connect(onResetButton) 2062 | 2063 | 2064 | def setDefaults(): 2065 | group = getGroup() 2066 | groupContext = group.GetGroup("Context") 2067 | 2068 | vertexSign = groupContext.GetString("VertexSign") 2069 | 2070 | if vertexSign in sign: 2071 | pass 2072 | else: 2073 | groupContext.SetString("VertexSign", "==") 2074 | vertexSign = "==" 2075 | 2076 | for i in range(vertexComboBox.count()): 2077 | if vertexComboBox.itemText(i) == vertexSign: 2078 | vertexComboBox.setCurrentIndex(i) 2079 | else: 2080 | pass 2081 | 2082 | vertexValue = groupContext.GetInt("VertexValue") 2083 | 2084 | if vertexValue: 2085 | pass 2086 | else: 2087 | a = groupContext.GetInt("VertexValue", True) 2088 | b = groupContext.GetInt("VertexValue", False) 2089 | 2090 | if a == b: 2091 | groupContext.SetInt("VertexValue", 0) 2092 | vertexValue = 0 2093 | else: 2094 | groupContext.SetInt("VertexValue", 10) 2095 | vertexValue = 10 2096 | 2097 | vertexSpin.setValue(vertexValue) 2098 | 2099 | edgeSign = groupContext.GetString("EdgeSign") 2100 | 2101 | if edgeSign in sign: 2102 | pass 2103 | else: 2104 | groupContext.SetString("EdgeSign", "==") 2105 | edgeSign = "==" 2106 | 2107 | for i in range(edgeComboBox.count()): 2108 | if edgeComboBox.itemText(i) == edgeSign: 2109 | edgeComboBox.setCurrentIndex(i) 2110 | else: 2111 | pass 2112 | 2113 | edgeValue = groupContext.GetInt("EdgeValue") 2114 | 2115 | if edgeValue: 2116 | pass 2117 | else: 2118 | a = groupContext.GetInt("EdgeValue", True) 2119 | b = groupContext.GetInt("EdgeValue", False) 2120 | 2121 | if a == b: 2122 | groupContext.SetInt("EdgeValue", 0) 2123 | edgeValue = 0 2124 | else: 2125 | groupContext.SetInt("EdgeValue", 10) 2126 | edgeValue = 10 2127 | 2128 | edgeSpin.setValue(edgeValue) 2129 | 2130 | faceSign = groupContext.GetString("FaceSign") 2131 | 2132 | if faceSign in sign: 2133 | pass 2134 | else: 2135 | groupContext.SetString("FaceSign", "==") 2136 | faceSign = "==" 2137 | 2138 | for i in range(faceComboBox.count()): 2139 | if faceComboBox.itemText(i) == faceSign: 2140 | faceComboBox.setCurrentIndex(i) 2141 | else: 2142 | pass 2143 | 2144 | faceValue = groupContext.GetInt("FaceValue") 2145 | 2146 | if faceValue: 2147 | pass 2148 | else: 2149 | a = groupContext.GetInt("FaceValue", True) 2150 | b = groupContext.GetInt("FaceValue", False) 2151 | 2152 | if a == b: 2153 | groupContext.SetInt("FaceValue", 0) 2154 | faceValue = 0 2155 | else: 2156 | groupContext.SetInt("FaceValue", 10) 2157 | faceValue = 10 2158 | 2159 | faceSpin.setValue(faceValue) 2160 | 2161 | objectSign = groupContext.GetString("ObjectSign") 2162 | 2163 | if objectSign in sign: 2164 | pass 2165 | else: 2166 | groupContext.SetString("ObjectSign", "==") 2167 | objectSign = "==" 2168 | 2169 | for i in range(objectComboBox.count()): 2170 | if objectComboBox.itemText(i) == objectSign: 2171 | objectComboBox.setCurrentIndex(i) 2172 | else: 2173 | pass 2174 | 2175 | objectValue = groupContext.GetInt("ObjectValue") 2176 | 2177 | if objectValue: 2178 | pass 2179 | else: 2180 | a = groupContext.GetInt("ObjectValue", True) 2181 | b = groupContext.GetInt("ObjectValue", False) 2182 | 2183 | if a == b: 2184 | groupContext.SetInt("ObjectValue", 0) 2185 | objectValue = 0 2186 | else: 2187 | groupContext.SetInt("ObjectValue", 10) 2188 | objectValue = 10 2189 | 2190 | objectSpin.setValue(objectValue) 2191 | 2192 | valueRadius = group.GetInt("Radius") 2193 | 2194 | if valueRadius: 2195 | pass 2196 | else: 2197 | valueRadius = 100 2198 | group.SetInt("Radius", valueRadius) 2199 | 2200 | spinRadius.setValue(valueRadius) 2201 | 2202 | valueButton = group.GetInt("Button") 2203 | 2204 | if valueButton: 2205 | pass 2206 | else: 2207 | valueButton = 32 2208 | group.SetInt("Button", valueButton) 2209 | 2210 | spinButton.setValue(valueButton) 2211 | 2212 | contextList() 2213 | 2214 | 2215 | def setDefaultPie(restore=False): 2216 | paramGet = App.ParamGet("User parameter:BaseApp/PieMenu") 2217 | paramIndexGet = App.ParamGet("User parameter:BaseApp/PieMenu/Index") 2218 | indexList = paramIndexGet.GetString("IndexList") 2219 | 2220 | defaultTools = ["Std_ViewTop", 2221 | "Std_New", 2222 | "Std_ViewRight", 2223 | "Std_BoxSelection", 2224 | "Std_ViewBottom", 2225 | "Std_ViewIsometric", 2226 | "Std_ViewLeft", 2227 | "Std_ViewScreenShot"] 2228 | 2229 | if indexList: 2230 | indexList = indexList.split(".,.") 2231 | 2232 | temp = [] 2233 | 2234 | for i in indexList: 2235 | temp.append(int(i)) 2236 | 2237 | indexList = temp 2238 | else: 2239 | indexList = [] 2240 | 2241 | if 0 in indexList: 2242 | if restore: 2243 | group = paramIndexGet.GetGroup("0") 2244 | group.SetString("ToolList", ".,.".join(defaultTools)) 2245 | else: 2246 | pass 2247 | else: 2248 | indexList.append(0) 2249 | 2250 | temp = [] 2251 | 2252 | for i in indexList: 2253 | temp.append(str(i)) 2254 | 2255 | indexList = temp 2256 | 2257 | paramIndexGet.SetString("0", "Default") 2258 | paramIndexGet.SetString("IndexList", ".,.".join(indexList)) 2259 | 2260 | group = paramIndexGet.GetGroup("0") 2261 | group.SetString("ToolList", ".,.".join(defaultTools)) 2262 | 2263 | paramGet.SetBool("ToolBar", False) 2264 | paramGet.RemString("ToolBar") 2265 | paramGet.SetString("CurrentPie", "Default") 2266 | 2267 | group = getGroup(mode=1) 2268 | 2269 | group.SetInt("Radius", 100) 2270 | group.SetInt("Button", 32) 2271 | 2272 | def onControl(): 2273 | 2274 | global pieMenuDialog 2275 | 2276 | for i in mw.findChildren(QtGui.QDialog): 2277 | if i.objectName() == "PieMenuPreferences": 2278 | i.deleteLater() 2279 | else: 2280 | pass 2281 | 2282 | tabs = QtGui.QTabWidget() 2283 | 2284 | pieMenuTab = QtGui.QWidget() 2285 | pieMenuTabLayout = QtGui.QVBoxLayout() 2286 | pieMenuTab.setLayout(pieMenuTabLayout) 2287 | 2288 | layoutAddRemove = QtGui.QHBoxLayout() 2289 | layoutAddRemove.addWidget(cBox) 2290 | layoutAddRemove.addWidget(buttonAddPieMenu) 2291 | layoutAddRemove.addWidget(buttonRemovePieMenu) 2292 | layoutAddRemove.addWidget(buttonRenamePieMenu) 2293 | layoutAddRemove.addWidget(buttonCopyPieMenu) 2294 | 2295 | layoutRadius = QtGui.QHBoxLayout() 2296 | layoutRadius.addWidget(labelRadius) 2297 | layoutRadius.addStretch(1) 2298 | layoutRadius.addWidget(spinRadius) 2299 | 2300 | layoutButton = QtGui.QHBoxLayout() 2301 | layoutButton.addWidget(labelButton) 2302 | layoutButton.addStretch(1) 2303 | layoutButton.addWidget(spinButton) 2304 | 2305 | pieMenuTabLayout.insertLayout(0, layoutAddRemove) 2306 | pieMenuTabLayout.insertSpacing(1, 24) 2307 | pieMenuTabLayout.insertLayout(2, layoutRadius) 2308 | pieMenuTabLayout.insertLayout(3, layoutButton) 2309 | pieMenuTabLayout.addStretch(0) 2310 | 2311 | contextTab = QtGui.QWidget() 2312 | contextTabLayout = QtGui.QVBoxLayout() 2313 | contextTab.setLayout(contextTabLayout) 2314 | 2315 | layoutCheckContext = QtGui.QHBoxLayout() 2316 | layoutCheckContext.addWidget(labelContext) 2317 | layoutCheckContext.addStretch(1) 2318 | layoutCheckContext.addWidget(checkContext) 2319 | 2320 | resetLayout = QtGui.QHBoxLayout() 2321 | resetLayout.addStretch(1) 2322 | resetLayout.addWidget(resetButton) 2323 | 2324 | contextTabLayout.insertLayout(0, layoutCheckContext) 2325 | contextTabLayout.addWidget(contextTable) 2326 | contextTabLayout.insertLayout(2, resetLayout) 2327 | contextTabLayout.addStretch(1) 2328 | 2329 | tabs.addTab(pieMenuTab, "PieMenu") 2330 | tabs.addTab(toolListWidget, "Tools") 2331 | tabs.addTab(contextTab, "Context") 2332 | 2333 | pieButtons = QtGui.QWidget() 2334 | pieButtonsLayout = QtGui.QVBoxLayout() 2335 | pieButtons.setLayout(pieButtonsLayout) 2336 | pieButtonsLayout.setContentsMargins(0, 0, 0, 0) 2337 | pieButtonsLayout.addWidget(buttonListWidget) 2338 | 2339 | buttonsLayout = QtGui.QHBoxLayout() 2340 | buttonsLayout.addStretch(1) 2341 | buttonsLayout.addWidget(buttonRemoveCommand) 2342 | buttonsLayout.addWidget(buttonDown) 2343 | buttonsLayout.addWidget(buttonUp) 2344 | 2345 | pieButtonsLayout.insertLayout(1, buttonsLayout) 2346 | 2347 | vSplitter = QtGui.QSplitter() 2348 | vSplitter.insertWidget(0, pieButtons) 2349 | vSplitter.insertWidget(0, tabs) 2350 | 2351 | preferencesWidget = QtGui.QWidget() 2352 | preferencesLayout = QtGui.QHBoxLayout() 2353 | preferencesLayout.setContentsMargins(0, 0, 0, 0) 2354 | preferencesWidget.setLayout(preferencesLayout) 2355 | preferencesLayout.addWidget(vSplitter) 2356 | 2357 | pieMenuDialog = QtGui.QDialog(mw) 2358 | pieMenuDialog.resize(800, 450) 2359 | pieMenuDialog.setObjectName("PieMenuPreferences") 2360 | pieMenuDialog.setWindowTitle("PieMenu " + PIE_MENU_VERSION) 2361 | pieMenuDialogLayout = QtGui.QVBoxLayout() 2362 | pieMenuDialog.setLayout(pieMenuDialogLayout) 2363 | pieMenuDialog.show() 2364 | 2365 | pieMenuDialogLayout.addWidget(preferencesWidget) 2366 | 2367 | cBoxUpdate() 2368 | 2369 | 2370 | def addAccessoriesMenu(): 2371 | 2372 | if mw.property("eventLoop"): 2373 | 2374 | startAM = False 2375 | try: 2376 | mw.mainWindowClosed 2377 | mw.workbenchActivated 2378 | startAM = True 2379 | except AttributeError: 2380 | pass 2381 | if startAM: 2382 | t.stop() 2383 | t.deleteLater() 2384 | accessoriesMenu() 2385 | 2386 | 2387 | mw = Gui.getMainWindow() 2388 | start = True 2389 | 2390 | for action in mw.findChildren(QtGui.QAction): 2391 | if action.objectName() == "PieMenuShortCut": 2392 | start = False 2393 | else: 2394 | pass 2395 | 2396 | if start: 2397 | 2398 | remObsoleteParams() 2399 | compositingManager = True 2400 | if QtCore.qVersion() < "5": 2401 | windowShadow = False 2402 | else: 2403 | windowShadow = True 2404 | 2405 | if platform.system() == "Linux": 2406 | try: 2407 | if QtGui.QX11Info.isCompositingManagerRunning(): 2408 | windowShadow = True 2409 | else: 2410 | compositingManager = False 2411 | except AttributeError: 2412 | windowShadow = True 2413 | else: 2414 | pass 2415 | 2416 | if platform.system() == "Windows": 2417 | windowShadow = True 2418 | else: 2419 | pass 2420 | 2421 | contextAll = {} 2422 | contextList() 2423 | selObserver = SelObserver() 2424 | addObserver() 2425 | 2426 | PieMenuInstance = PieMenu() 2427 | 2428 | actionKey = QtGui.QAction(mw) 2429 | actionKey.setText("Invoke pie menu") 2430 | actionKey.setObjectName("PieMenuShortCut") 2431 | actionKey.setShortcut(QtGui.QKeySequence("TAB")) 2432 | actionKey.triggered.connect(PieMenuInstance.showAtMouse) 2433 | mw.addAction(actionKey) 2434 | 2435 | # let the addition of the accessoriesMenu wait until FC is ready for it 2436 | t = QtCore.QTimer() 2437 | t.timeout.connect(addAccessoriesMenu) 2438 | t.start(500) 2439 | 2440 | else: 2441 | pass 2442 | 2443 | pieMenuStart() --------------------------------------------------------------------------------