1 2from __future__ import print_function 3 4import itertools, os 5from glob import glob 6 7import pymol 8import pymol._gui 9 10from .pymol_gl_widget import PyMOLGLWidget 11from pymol.Qt import QtGui, QtCore 12from pymol.Qt import QtWidgets 13from pymol.Qt.utils import PopupOnException 14 15Qt = QtCore.Qt 16 17from pymol.wizard import Wizard 18from pymol.parsing import QuietException 19 20from pymol import editor 21from pymol import computing 22 23from chempy import cpv 24 25active_sele = "_builder_active" # object we're working on... 26newest_sele = "_builder_added" # last atom added? 27display_sele = "_build_display" 28 29undocontext = editor.undocontext 30 31def undoablemethod(sele): 32 def decorator(func): 33 def wrapper(self, *args, **kwargs): 34 with undocontext(self.cmd, sele): 35 return func(self, *args, **kwargs) 36 return wrapper 37 return decorator 38 39############################################################# 40# Action-directing Wizards 41 42class ActionWizard(Wizard): 43 44 def __init__(self, _self=pymol.cmd): 45 Wizard.__init__(self,_self) 46 self.actionHash = str(self.__class__) 47 48 def setActionHash(self, action_hash): 49 self.actionHash = action_hash 50 51 def activateOrDismiss(self): 52 activate_flag = 1 53 cur_wiz = self.cmd.get_wizard() 54 if cur_wiz is not None: 55 if cur_wiz.__class__ == self.__class__: 56 if cur_wiz.actionHash == self.actionHash: 57 activate_flag = 0 58 if activate_flag: 59 self.cmd.set_wizard(self,replace=1) 60 self.cmd.refresh_wizard() 61 else: 62 self.actionWizardDone() 63 return activate_flag 64 65 def actionWizardDone(self): 66 self.cmd.delete(active_sele) 67 self.cmd.unpick() 68 self.cmd.set_wizard() 69 self.cmd.refresh_wizard() 70 71 def activeSeleValid(self): 72 if active_sele in self.cmd.get_names("selections"): 73 if self.cmd.select(active_sele, "byobj "+active_sele)<1: 74 self.cmd.delete(active_sele) 75 else: 76 enabled_list = self.cmd.get_names("objects",enabled_only=1) 77 active_obj_list = self.cmd.get_object_list(active_sele) 78 if len(active_obj_list) != 1: 79 self.cmd.delete(active_sele) 80 elif active_obj_list[0] not in enabled_list: 81 self.cmd.delete(active_sele) 82 if "pk1" in self.cmd.get_names("selections"): 83 self.cmd.select(active_sele,"byobj pk1") 84 else: 85 enabled_list = self.cmd.get_names("objects",enabled_only=1) 86 if len(enabled_list)==1: 87 if self.cmd.select(active_sele, enabled_list[0])<1: 88 self.cmd.delete(active_sele) 89 return active_sele in self.cmd.get_names("selections") 90 91 92class CleanWizard(ActionWizard): 93 94 def __init__(self, _self=pymol.cmd): 95 self.clean_obj = None 96 ActionWizard.__init__(self,_self) 97 98 def run_job(self): 99 if active_sele in self.cmd.get_names("selections"): 100 obj_list = self.cmd.get_object_list(active_sele) 101 if len(obj_list)==1: 102 self.cmd.unpick() 103 self.cmd.set_wizard() 104 self.cmd.refresh_wizard() 105 self.cmd.do("_ cmd.clean('%s',message='''Cleaning %s...''',async_=1)"%(active_sele,obj_list[0])) 106 107 def do_pick(self, bondFlag): 108 if active_sele in self.cmd.get_names("selections"): 109 obj_list = self.cmd.get_object_list(active_sele) 110 if len(obj_list)!=1: 111 self.cmd.delete(active_sele) 112 else: 113 self.cmd.select(active_sele, "byobj pk1") 114 self.cmd.unpick() 115 self.cmd.deselect() 116 obj_list = self.cmd.get_object_list(active_sele) 117 if isinstance(obj_list,list) and (len(obj_list)==1): 118 self.run_job() 119 else: 120 print("Error: can only clean one object at a time") 121 122 def toggle(self): 123 if self.activateOrDismiss(): 124 if self.activeSeleValid(): 125 self.run_job() 126 127 def get_prompt(self): 128 return ["Pick object to clean..."] 129 130 def get_panel(self): 131 return [ 132 [ 1, 'Clean', ''], 133 [ 2, 'Done','cmd.set_wizard()'], 134 ] 135 136 137class SculptWizard(ActionWizard): 138 139 def __init__(self, _self=pymol.cmd): 140 ActionWizard.__init__(self,_self) 141 self.sculpt_object = None 142 143 def sculpt_activate(self): 144 if active_sele in self.cmd.get_names("selections"): 145 obj_list = self.cmd.get_object_list(active_sele) 146 if len(obj_list)==1: 147 obj_name = obj_list[0] 148 self.cmd.push_undo(obj_name) 149 self.cmd.sculpt_activate(obj_name) 150 self.cmd.set("sculpting",1) 151 self.sculpt_object = obj_name 152 self.cmd.sculpt_activate(obj_name) 153 if int(self.cmd.get("sculpt_vdw_vis_mode")): 154 self.cmd.show("cgo",obj_name) 155 self.cmd.unpick() 156 self.cmd.refresh_wizard() 157 else: 158 print("Error: cannot sculpt more than one object at a time") 159 160 def sculpt_deactivate(self): 161 if ((self.sculpt_object is not None) and 162 self.sculpt_object in self.cmd.get_names()): 163 self.cmd.set("sculpt_vdw_vis_mode","0",self.sculpt_object) 164 self.cmd.sculpt_iterate(self.sculpt_object,self.cmd.get_state(),0) 165 self.cmd.unset("sculpt_vdw_vis_mode",self.sculpt_object) 166 self.cmd.sculpt_deactivate(self.sculpt_object) 167 self.sculpt_object = None 168 self.cmd.refresh_wizard() 169 170 def do_pick(self, bondFlag): 171 if self.sculpt_object is None: 172 self.cmd.select(active_sele, "byobj pk1") 173 self.sculpt_activate() 174 else: 175 return 0 # already sculpting, so handle like a normal edit 176 177 def toggle(self): 178 if self.activateOrDismiss(): 179 if self.activeSeleValid(): 180 self.sculpt_activate() 181 182 def get_prompt(self): 183 if self.sculpt_object is None: 184 return ["Pick object to sculpt..."] 185 else: 186 return ["Sculpting %s..."%self.sculpt_object] 187 188 def finish_sculpting(self): 189 if self.sculpt_object: 190 self.sculpt_deactivate() 191 self.cmd.set("sculpting",0) 192 self.cmd.delete(active_sele) 193 self.cmd.set_wizard() 194 self.cmd.refresh_wizard() 195 196 def scramble(self,mode): 197 if self.cmd.count_atoms(self.sculpt_object): 198 sc_tmp = "_scramble_tmp" 199 if mode == 0: 200 self.cmd.select(sc_tmp,self.sculpt_object+ 201 " and not (fixed or restrained)") 202 if mode == 1: 203 self.cmd.select(sc_tmp,self.sculpt_object+ 204 " and not (fixed)") 205 extent = self.cmd.get_extent(sc_tmp) 206 center = self.cmd.get_position(sc_tmp) 207 radius = 1.25*cpv.length(cpv.sub(extent[0],extent[1])) 208 self.cmd.alter_state(self.cmd.get_state(), sc_tmp, 209 "(x,y,z)=rsp(pos,rds)", 210 space= { 'rsp' : cpv.random_displacement, 211 'pos' : center, 212 'rds' : radius }) 213 self.cmd.delete(sc_tmp) 214 215 def get_panel(self): 216 return [ 217 [ 1, 'Sculpt', ''], 218 [ 2, 'Undo', 'cmd.undo()'], 219 220 [ 2, 'Switch Object', 'cmd.get_wizard().sculpt_deactivate()'], 221 [ 2, 'Scramble Unrestrained Coords.', 'cmd.get_wizard().scramble(0)'], 222 [ 2, 'Scramble Unfixed Coords.', 'cmd.get_wizard().scramble(1)'], 223 [ 2, 'Done','cmd.get_wizard().finish_sculpting()'], 224 ] 225 226 def cleanup(self): 227 self.sculpt_deactivate() 228 ActionWizard.cleanup(self) 229 230 231class RepeatableActionWizard(ActionWizard): 232 233 def __init__(self, _self=pymol.cmd): 234 ActionWizard.__init__(self,_self) 235 self.repeating = 0 236 237 def repeat(self): 238 self.repeating = 1 239 self.cmd.refresh_wizard() 240 241 def getRepeating(self): 242 return self.repeating 243 244 def activateRepeatOrDismiss(self): 245 activate_flag = 1 246 cur_wiz = self.cmd.get_wizard() 247 if cur_wiz is not None: 248 if cur_wiz.__class__ == self.__class__: 249 if cur_wiz.actionHash == self.actionHash: 250 if cur_wiz.getRepeating(): 251 activate_flag = 0 252 else: 253 self.repeat() 254 elif cur_wiz.getRepeating(): 255 self.repeat() 256 if activate_flag: 257 self.cmd.set_wizard(self,replace=1) 258 self.repeat() # always repeating for now... 259 self.cmd.refresh_wizard() 260 else: 261 self.actionWizardDone() 262 return activate_flag 263 264 def cleanup(self): 265 self.cmd.unpick() 266 ActionWizard.cleanup(self) 267 268 269class ReplaceWizard(RepeatableActionWizard): 270 271 def do_pick(self, bondFlag): 272 self.cmd.select(active_sele, "bymol pk1") 273 self.cmd.replace(self.symbol, self.geometry, self.valence) 274 if not self.getRepeating(): 275 self.actionWizardDone() 276 277 def toggle(self, symbol, geometry, valence, text): 278 self.symbol = symbol 279 self.geometry = geometry 280 self.valence = valence 281 self.text = text 282 self.setActionHash( (symbol,geometry,valence,text) ) 283 self.activateRepeatOrDismiss() 284 285 def get_prompt(self): 286 if self.getRepeating(): 287 return ["Pick atoms to replace with %s..."%self.text] 288 else: 289 return ["Pick atom to replace with %s..."%self.text] 290 291 def get_panel(self): 292 if self.getRepeating(): 293 return [ 294 [ 1, 'Replacing Multiple Atoms',''], 295 [ 2, 'Done','cmd.set_wizard()'], 296 ] 297 else: 298 return [ 299 [ 1, 'Replacing an Atom',''], 300 [ 2, 'Replace Multiple Atoms','cmd.get_wizard().repeat()'], 301 [ 2, 'Done','cmd.set_wizard()'], 302 ] 303 304 305class AttachWizard(RepeatableActionWizard): 306 307 def __init__(self, _self=pymol.cmd): 308 RepeatableActionWizard.__init__(self,_self) 309 self.mode = 0 310 311 def do_pick(self, bondFlag): 312 if self.mode == 0: 313 self.cmd.select(active_sele, "bymol pk1") 314 editor.attach_fragment("pk1", self.fragment, self.position, self.geometry, _self=self.cmd) 315 elif self.mode == 1: 316 self.cmd.select(active_sele, "bymol pk1") 317 editor.combine_fragment("pk1", self.fragment, self.position, 318 self.geometry, _self=self.cmd) 319 self.mode = 0 320 self.cmd.refresh_wizard() 321 self.cmd.unpick() 322 if not self.getRepeating(): 323 self.actionWizardDone() 324 325 def toggle(self, fragment, position, geometry, text): 326 self.fragment = fragment 327 self.position = position 328 self.geometry = geometry 329 self.text = text 330 self.setActionHash( (fragment, position, geometry, text) ) 331 self.activateRepeatOrDismiss() 332 333 def create_new(self): 334 names = self.cmd.get_names("objects") 335 num = 1 336 while 1: 337 name = "obj%02d"%num 338 if name not in names: 339 break 340 num = num + 1 341 self.cmd.fragment(self.fragment, name) 342 if not self.getRepeating(): 343 self.actionWizardDone() 344 345 def get_prompt(self): 346 if self.mode == 0: 347 if self.getRepeating(): 348 return ["Pick locations to attach %s..."%self.text] 349 else: 350 return ["Pick location to attach %s..."%self.text] 351 else: 352 return ["Pick object to combine %s into..."%self.text] 353 354 def combine(self): 355 self.mode = 1 356 self.cmd.refresh_wizard() 357 358 def get_panel(self): 359 if self.getRepeating(): 360 return [ 361 [ 1, 'Attaching Multiple Fragmnts',''], 362 [ 2, 'Create As New Object','cmd.get_wizard().create_new()'], 363 [ 2, 'Combine w/ Existing Object','cmd.get_wizard().combine()'], 364 [ 2, 'Done','cmd.set_wizard()'], 365 ] 366 else: 367 return [ 368 [ 1, 'Attaching One Fragment',''], 369 [ 2, 'Create As New Object','cmd.get_wizard().create_new()'], 370 [ 2, 'Combine w/ Existing Object','cmd.get_wizard().combine()'], 371 [ 2, 'Attach Multiple Fragments','cmd.get_wizard().repeat()'], 372 [ 2, 'Done','cmd.set_wizard()'], 373 ] 374 375 376class AminoAcidWizard(RepeatableActionWizard): 377 378 def __init__(self, _self=pymol.cmd, ss=-1): 379 RepeatableActionWizard.__init__(self,_self) 380 self.mode = 0 381 self.setSecondaryStructure(ss) 382 383 def setSecondaryStructure(self, ss): 384 self._secondary_structure = ss 385 386 def attach_monomer(self, objectname=""): 387 editor.attach_amino_acid("?pk1", self.aminoAcid, object=objectname, 388 ss=self._secondary_structure, 389 _self=self.cmd) 390 391 def do_pick(self, bondFlag): 392 # since this function can change any position of atoms in a related 393 # molecule, bymol is used 394 if self.mode == 0: 395 self.cmd.select(active_sele, "bymol pk1") 396 try: 397 with undocontext(self.cmd, "bymol ?pk1"): 398 self.attach_monomer(self.aminoAcid) 399 except QuietException: 400 fin = -1 401 elif self.mode == 1: 402 self.cmd.select(active_sele, "bymol pk1") 403 editor.combine_fragment("pk1", self.aminoAcid, 0, 1, _self=self.cmd) 404 self.mode = 0 405 self.cmd.refresh_wizard() 406 407 self.cmd.unpick() 408 if not self.getRepeating(): 409 self.actionWizardDone() 410 411 def toggle(self, amino_acid): 412 self.aminoAcid = amino_acid 413 self.setActionHash( (amino_acid,) ) 414 self.activateRepeatOrDismiss() 415 416 def create_new(self): 417 names = self.cmd.get_names("objects") 418 num = 1 419 while 1: 420 name = "obj%02d"%num 421 if name not in names: 422 break 423 num = num + 1 424 self.attach_monomer(self.aminoAcid) 425 426 if not self.getRepeating(): 427 self.actionWizardDone() 428 429 def combine(self): 430 self.mode = 1 431 self.cmd.refresh_wizard() 432 433 def get_prompt(self): 434 if self.mode == 0: 435 if self.getRepeating(): 436 return ["Pick locations to attach %s..."%self.aminoAcid] 437 else: 438 return ["Pick location to attach %s..."%self.aminoAcid] 439 else: 440 return ["Pick object to combine %s into..."%self.aminoAcid] 441 442 def get_panel(self): 443 if self.getRepeating(): 444 return [ 445 [ 1, 'Attaching Multiple Residues',''], 446 [ 2, 'Create As New Object','cmd.get_wizard().create_new()'], 447 [ 2, 'Combine w/ Existing Object','cmd.get_wizard().combine()'], 448 [ 2, 'Done','cmd.set_wizard()'], 449 ] 450 else: 451 return [ 452 [ 1, 'Attaching Amino Acid',''], 453 [ 2, 'Create As New Object','cmd.get_wizard().create_new()'], 454 [ 2, 'Combine w/ Existing Object','cmd.get_wizard().combine()'], 455 [ 2, 'Attach Multiple...','cmd.get_wizard().repeat()'], 456 [ 2, 'Done','cmd.set_wizard()'], 457 ] 458 459 460class ValenceWizard(RepeatableActionWizard): 461 462 def cleanup(self): 463 self.cmd.button('single_left','none','PkAt') 464 self.cmd.button('double_left','none','MovA') 465 466 @undoablemethod("(?pk1 ?pk2) extend 1") 467 def do_pick(self, bondFlag): 468 self.cmd.select(active_sele, "bymol pk1") 469 if bondFlag: 470 if int(self.order)>=0: 471 self.cmd.valence(self.order, "pk1", "pk2") 472 self.cmd.h_fill() 473 else: 474 self.cmd.cycle_valence() 475 else: 476 self.cmd.button('double_left','none','PkBd') 477 self.cmd.button('single_left','none','PkBd') 478 self.cmd.unpick() 479 if not self.getRepeating(): 480 self.actionWizardDone() 481 482 def toggle(self, order, text): 483 self.order = order 484 self.text = text 485 self.setActionHash( (order,text) ) 486 self.activateRepeatOrDismiss() 487 if self.cmd.get_wizard() == self: 488 # get us into bond picking mode... 489 self.cmd.button('double_left','none','PkBd') 490 self.cmd.button('single_left','none','PkBd') 491 492 def get_prompt(self): 493 if self.getRepeating(): 494 return ["Pick bonds to set as %s..."%self.text] 495 else: 496 return ["Pick bond to set as %s..."%self.text] 497 498 def get_panel(self): 499 if self.getRepeating(): 500 return [ 501 [ 1, 'Setting Multiple Valences',''], 502 [ 2, 'Done','cmd.set_wizard()'], 503 ] 504 else: 505 return [ 506 [ 1, 'Set a Bond Valence',''], 507 [ 2, 'Set Multiple Valences','cmd.get_wizard().repeat()'], 508 [ 2, 'Done','cmd.set_wizard()'], 509 ] 510 511 512class ChargeWizard(RepeatableActionWizard): 513 514 @undoablemethod("bymol ?pk1") 515 def do_pick(self, bondFlag): 516 self.cmd.select(active_sele, "bymol pk1") 517 self.cmd.alter("pk1","formal_charge=%s" % self.charge) 518 self.cmd.h_fill() 519 if abs(float(self.charge))>0.0001: 520 self.cmd.label("pk1","'''"+self.text+"'''") 521 else: 522 self.cmd.label("pk1") 523 self.cmd.unpick() 524 if not self.getRepeating(): 525 self.actionWizardDone() 526 527 def toggle(self, charge, text): 528 self.charge = charge 529 self.text = text 530 self.setActionHash( (charge,text) ) 531 self.activateRepeatOrDismiss() 532 533 def get_prompt(self): 534 if self.getRepeating(): 535 return ["Pick atoms to set charge = %s..."%self.text] 536 else: 537 return ["Pick atom to set charge = %s..."%self.text] 538 539 def get_panel(self): 540 if self.getRepeating(): 541 return [ 542 [ 1, 'Setting Multiple Charges',''], 543 [ 2, 'Done','cmd.set_wizard()'], 544 ] 545 else: 546 return [ 547 [ 1, 'Setting Atom Charge',''], 548 [ 2, 'Modify Multiple Atoms','cmd.get_wizard().repeat()'], 549 [ 2, 'Done','cmd.set_wizard()'], 550 ] 551 552 553class InvertWizard(RepeatableActionWizard): 554 555 @PopupOnException.decorator 556 def do_pick(self, bondFlag): 557 self.cmd.select(active_sele, "bymol pk1") 558 picked = collectPicked(self.cmd) 559 if picked == ["pk1","pk2","pk3"]: 560 self.cmd.invert() 561 self.cmd.unpick() 562 if not self.getRepeating(): 563 self.actionWizardDone() 564 self.cmd.refresh_wizard() 565 566 def toggle(self): 567 self.activateRepeatOrDismiss() 568 569 def get_prompt(self): 570 if "pk1" in self.cmd.get_names("selections"): 571 if "pk2" in self.cmd.get_names("selections"): 572 return ["Pick the second stationary atom..."] 573 else: 574 return ["Pick the first stationary atom..."] 575 else: 576 return ["Pick origin atom for inversion..."] 577 578 def get_panel(self): 579 if self.getRepeating(): 580 return [ 581 [ 1, 'Inverting Multiple',''], 582 [ 2, 'Done','cmd.set_wizard()'], 583 ] 584 else: 585 return [ 586 [ 1, 'Inverting Stereocenter',''], 587 [ 2, 'Invert Multiple','cmd.get_wizard().repeat()'], 588 [ 2, 'Done','cmd.set_wizard()'], 589 ] 590 591 592class BondWizard(RepeatableActionWizard): 593 594 @staticmethod 595 def staticaction(cmd): 596 picked = collectPicked(cmd) 597 if picked != ["pk1","pk2"]: 598 return False 599 600 cmd.select(active_sele, "bymol ?pk1") 601 602 if ( cmd.count_atoms("?pk1&hydro") and 603 cmd.count_atoms("?pk2&hydro") and 604 cmd.count_atoms("(?pk1 extend 1)&!hydro") and 605 cmd.count_atoms("(?pk2 extend 1)&!hydro")): 606 # two hydrogens picked -> pick their heavy neighbors instead 607 cmd.select("pk1","(pk1 extend 1) and not hydro") 608 cmd.select("pk2","(pk2 extend 1) and not hydro") 609 610 with undocontext(cmd, "(?pk1 ?pk2) extend 1"): 611 cmd.bond("pk1", "pk2") 612 cmd.h_fill() 613 614 cmd.unpick() 615 return True 616 617 def do_pick(self, bondFlag): 618 if self.staticaction(self.cmd): 619 if not self.getRepeating(): 620 self.actionWizardDone() 621 self.cmd.refresh_wizard() 622 623 def toggle(self): 624 self.activateRepeatOrDismiss() 625 626 def get_prompt(self): 627 if "pk1" in self.cmd.get_names("selections"): 628 return ["Pick second atom for bond..."] 629 else: 630 return ["Pick first atom for bond..."] 631 632 def get_panel(self): 633 if self.getRepeating(): 634 return [ 635 [ 1, 'Creating Multiple Bonds',''], 636 [ 2, 'Done','cmd.set_wizard()'], 637 ] 638 else: 639 return [ 640 [ 1, 'Creating Bond',''], 641 [ 2, 'Create Multiple Bonds','cmd.get_wizard().repeat()'], 642 [ 2, 'Done','cmd.set_wizard()'], 643 ] 644 645 646class UnbondWizard(RepeatableActionWizard): 647 648 def cleanup(self): 649 self.cmd.button('single_left','none','PkAt') 650 651 @undoablemethod("(?pk1 ?pk2) extend 1") 652 def do_pick(self, bondFlag): 653 self.cmd.select(active_sele, "bymol pk1") 654 if bondFlag: 655 self.cmd.unbond("pk1", "pk2") 656 self.cmd.h_fill() 657 self.cmd.unpick() 658 else: 659 self.cmd.button('single_left','none','PkBd') 660 self.cmd.unpick() 661 if not self.getRepeating(): 662 self.actionWizardDone() 663 664 def toggle(self): 665 self.activateRepeatOrDismiss() 666 if self.cmd.get_wizard() == self: 667 self.cmd.button('single_left','none','PkBd') # get us into bond picking mode... 668 669 def get_prompt(self): 670 if self.getRepeating(): 671 return ["Pick bonds to delete..."] 672 else: 673 return ["Pick bond to delete..."] 674 675 def get_panel(self): 676 if self.getRepeating(): 677 return [ 678 [ 1, 'Deleting Multiple Bonds',''], 679 [ 2, 'Done','cmd.set_wizard()'], 680 ] 681 else: 682 return [ 683 [ 1, 'Deleting a Bond',''], 684 [ 2, 'Delete Multiple Bonds','cmd.get_wizard().repeat()'], 685 [ 2, 'Done','cmd.set_wizard()'], 686 ] 687 688 689class HydrogenWizard(RepeatableActionWizard): 690 691 def run_add(self): 692 if self.mode == 'add': 693 self.cmd.h_add(active_sele) 694 self.cmd.delete(active_sele) 695 696 def do_pick(self, bondFlag): 697 self.cmd.select(active_sele, "bymol pk1") 698 if self.mode == 'fix': 699 self.cmd.h_fill() 700 self.cmd.unpick() 701 elif self.mode == 'add': 702 self.cmd.unpick() 703 self.run_add() 704 if not self.getRepeating(): 705 self.actionWizardDone() 706 707 def toggle(self,mode): 708 self.mode = mode 709 self.setActionHash( (mode,) ) 710 711 if self.mode == 'add': 712 if self.activateOrDismiss(): 713 if self.activeSeleValid(): 714 self.run_add() 715 else: 716 self.activateRepeatOrDismiss() 717 718 def get_prompt(self): 719 if self.mode == 'fix': 720 if self.getRepeating(): 721 return ["Pick atom upon which to fix hydrogens..."] 722 else: 723 return ["Pick atoms upon which to fix hydrogens..."] 724 else: 725 return ["Pick molecule upon which to add hydrogens..."] 726 727 def get_panel(self): 728 if self.getRepeating(): 729 if self.mode == 'fix': 730 return [ 731 [ 1, 'Fixing Hydrogens',''], 732 [ 2, 'Done','cmd.set_wizard()'], 733 ] 734 else: 735 return [ 736 [ 1, 'Adding Hydrogens',''], 737 [ 2, 'Done','cmd.set_wizard()'], 738 ] 739 else: 740 if self.mode == 'fix': 741 return [ 742 [ 1, 'Fixing Hydrogens',''], 743 [ 2, 'Fix Multiple Atoms','cmd.get_wizard().repeat()'], 744 [ 2, 'Done','cmd.set_wizard()'], 745 ] 746 else: 747 return [ 748 [ 1, 'Adding Hydrogens',''], 749 [ 2, 'Add To Multiple...','cmd.get_wizard().repeat()'], 750 [ 2, 'Done','cmd.set_wizard()'], 751 ] 752 753 754class RemoveWizard(RepeatableActionWizard): 755 756 @undoablemethod("?pk1 extend 1") 757 def do_pick(self, bondFlag): 758 cnt = self.cmd.select(active_sele, 759 "((pk1 and not hydro) extend 1) and not hydro") 760 self.cmd.remove_picked() 761 if cnt: 762 self.cmd.fix_chemistry(active_sele) 763 self.cmd.h_add(active_sele) 764 self.cmd.delete(active_sele) 765 if not self.getRepeating(): 766 self.actionWizardDone() 767 768 def toggle(self): 769 self.activateRepeatOrDismiss() 770 771 def get_prompt(self): 772 if self.getRepeating(): 773 return ["Pick atoms to delete..."] 774 else: 775 return ["Pick atom to delete..."] 776 777 def get_panel(self): 778 if self.getRepeating(): 779 return [ 780 [ 1, 'Deleting Atoms',''], 781 [ 2, 'Done','cmd.set_wizard()'], 782 ] 783 else: 784 return [ 785 [ 1, 'Deleting an Atom',''], 786 [ 2, 'Delete Multiple Atoms','cmd.get_wizard().repeat()'], 787 [ 2, 'Done','cmd.set_wizard()'], 788 ] 789 790class AtomFlagWizard(ActionWizard): 791 792 def update_display(self): 793 if active_sele in self.cmd.get_names("selections"): 794 self.cmd.select(display_sele,active_sele+ 795 " and flag %d"%self.flag) 796 self.cmd.enable(display_sele) 797 else: 798 self.cmd.delete(display_sele) 799 self.cmd.refresh_wizard() 800 801 def do_pick(self, bondFlag): 802 if not(active_sele not in self.cmd.get_names("selections")): 803 if self.cmd.count_atoms("pk1 and flag %d"%self.flag): 804 self.cmd.flag(self.flag,"pk1","clear") 805 else: 806 self.cmd.flag(self.flag,"pk1","set") 807 self.cmd.select(active_sele, "byobj pk1") 808 self.cmd.unpick() 809 self.update_display() 810 811 def do_select(self,selection): 812 if selection == display_sele: 813 self.cmd.flag(self.flag,active_sele+" and "+display_sele,"set") 814 self.cmd.flag(self.flag,active_sele+" and not "+display_sele,"clear") 815 self.cmd.refresh_wizard() 816 self.update_display() 817 818 def get_prompt(self): 819 if active_sele not in self.cmd.get_names("selections"): 820 return ["Pick object to operate on..."] 821 else: 822 self.cmd.reference("validate",active_sele) # overbroad 823 if self.flag == 2: 824 return ["Toggle restrained atoms..."] 825 elif self.flag ==3: 826 return ["Toggle fixed atoms..."] 827 else: 828 return ["Toggle unknown atom flag..."] 829 830 def toggle(self,flag=0): 831 self.flag = flag 832 if self.activateOrDismiss(): 833 if self.activeSeleValid(): 834 self.update_display() 835 else: 836 self.cmd.deselect() 837 self.cmd.unpick() 838 839 def do_all(self): 840 if active_sele in self.cmd.get_names("selections"): 841 self.cmd.flag(self.flag,active_sele,"set") 842 self.update_display() 843 844 def do_less(self,mode): 845 if active_sele in self.cmd.get_names("selections"): 846 if mode == 0: 847 self.cmd.flag(self.flag,"(( byobj " + active_sele + 848 " ) and not flag %d) extend 1"%self.flag,"clear") 849 elif mode == 1: 850 self.cmd.flag(self.flag,"byres ((( byobj " + active_sele + 851 " ) and not flag %d) extend 1)"%self.flag,"clear") 852 self.update_display() 853 854 def do_cas(self,mode): 855 if active_sele in self.cmd.get_names("selections"): 856 if mode == 1: 857 self.cmd.flag(self.flag,active_sele,"clear") 858 self.cmd.flag(self.flag,active_sele+" and polymer and name ca","set") 859 elif mode == 0: 860 self.cmd.flag(self.flag,active_sele+ 861 " and flag %d and polymer and name ca"%self.flag,"set") 862 self.cmd.flag(self.flag,active_sele+ 863 " and not (polymer and name ca)","clear") 864 self.update_display() 865 866 def do_more(self,mode): 867 if active_sele in self.cmd.get_names("selections"): 868 if mode == 0: 869 self.cmd.flag(self.flag,active_sele + 870 " and (flag %d extend 1)"%self.flag,"set") 871 elif mode == 1: 872 self.cmd.flag(self.flag,"byres ("+ active_sele + 873 " and (byres flag %d) extend 1)"%self.flag,"set") 874 elif mode == 2: 875 self.cmd.flag(self.flag,"byres ("+ active_sele + 876 " and flag %d )"%self.flag,"set") 877 self.update_display() 878 879 def do_none(self): 880 if active_sele in self.cmd.get_names("selections"): 881 self.cmd.flag(self.flag,active_sele,"clear") 882 self.update_display() 883 884 def do_store(self): 885 if active_sele in self.cmd.get_names("selections"): 886 self.cmd.reference("store",active_sele) 887 888 def do_recall(self): 889 if active_sele in self.cmd.get_names("selections"): 890 self.cmd.reference("recall",active_sele) 891 892 def do_swap(self): 893 if active_sele in self.cmd.get_names("selections"): 894 self.cmd.reference("swap",active_sele) 895 896 def get_panel(self): 897 title = {2:"Restrained Atoms", 898 3:"Fixed Atoms"}.get(self.flag) 899 verb = {2:"Restrain", 3:"Fix"}.get(self.flag) 900 901 result = [ 902 [ 1, title, ''], 903 [ 2, "All",'cmd.get_wizard().do_all()'], 904 [ 2, "All C-alphas",'cmd.get_wizard().do_cas(1)'], 905 [ 2, "More (byres)",'cmd.get_wizard().do_more(1)'], 906 [ 2, "More",'cmd.get_wizard().do_more(0)'], 907 [ 2, "Byresidue",'cmd.get_wizard().do_more(2)'], 908 [ 2, "Less", 'cmd.get_wizard().do_less(0)'], 909 [ 2, "Less (by residue)", 'cmd.get_wizard().do_less(1)'], 910 [ 2, "Only C-alphas",'cmd.get_wizard().do_cas(0)'], 911 [ 2, "None", 'cmd.get_wizard().do_none()'], 912 [ 2, 'Done','cmd.set_wizard()'], 913 ] 914 915 if self.flag == 2: 916 result[-1:-1] = [ 917 [ 2, "Store Reference Coords.", 'cmd.get_wizard().do_store()'], 918 [ 2, "Recall Reference Coords.", 'cmd.get_wizard().do_recall()'], 919 [ 2, "Swap Reference Coords.", 'cmd.get_wizard().do_swap()']] 920 921 return result 922 923 def cleanup(self): 924 self.cmd.delete(display_sele) 925 Wizard.cleanup(self) 926 927 928class FixAtomWizard(AtomFlagWizard): 929 pass 930 931 932class RestAtomWizard(AtomFlagWizard): 933 pass 934 935 936############################################################# 937### Helper functions 938 939def getSeleDict(self_cmd): 940 result = {} 941 for sele in self_cmd.get_names("selections"): 942 result[sele] = 1 943 return result 944 945 946def collectPicked(self_cmd): 947 result = [] 948 sele_dict = getSeleDict(self_cmd) 949 for sele in ["pk1","pk2","pk3","pk4"]: 950 if sele in sele_dict: 951 result.append(sele) 952 return result 953 954 955############################################################# 956### Actual GUI 957 958 959def makeFragmentButton(): 960 btn = QtWidgets.QPushButton() 961 btn.setAttribute(Qt.WA_LayoutUsesWidgetRect) # OS X workaround 962 btn.setSizePolicy( 963 QtWidgets.QSizePolicy.Minimum, 964 QtWidgets.QSizePolicy.MinimumExpanding) 965 btn.setAutoDefault(False) 966 return btn 967 968 969class _BuilderPanel(QtWidgets.QWidget): 970 971 def __init__(self, parent=None, app=None): 972 super(_BuilderPanel, self).__init__(parent) 973 974 self.setWindowTitle("Builder") 975 self.setObjectName("builder") 976 self.cmd = app.pymol.cmd 977 978 self.layout = QtWidgets.QVBoxLayout() 979 self.setLayout(self.layout) 980 self.buttons_layout = QtWidgets.QVBoxLayout() 981 982 self.tabs = QtWidgets.QTabWidget(self) 983 self.layout.setContentsMargins(5, 5, 5, 5); 984 self.layout.setSpacing(5); 985 self.layout.addWidget(self.tabs) 986 self.layout.addLayout(self.buttons_layout) 987 self.layout.addStretch() 988 989 self.fragments_layout = QtWidgets.QGridLayout() 990 self.fragments_layout.setContentsMargins(5, 5, 5, 5); 991 self.fragments_layout.setSpacing(5); 992 self.fragments_tab = QtWidgets.QWidget() 993 self.fragments_tab.setLayout(self.fragments_layout) 994 self.protein_layout = QtWidgets.QGridLayout() 995 self.protein_layout.setContentsMargins(5, 5, 5, 5); 996 self.protein_layout.setSpacing(5); 997 self.protein_tab = QtWidgets.QWidget() 998 self.protein_tab.setLayout(self.protein_layout) 999 1000 self.tabs.addTab(self.fragments_tab, "Chemical") 1001 self.tabs.addTab(self.protein_tab, "Protein") 1002 1003 self.getIcons() 1004 1005 buttons = [ 1006 [ ("H", "Hydrogen", lambda: self.replace("H", 1, 1, "Hydrogen")), 1007 ("C", "Carbon", lambda: self.replace("C", 4, 4, "Carbon")), 1008 ("N", "Nitrogen", lambda: self.replace("N", 4, 3, "Nitrogen")), 1009 ("O", "Oxygen", lambda: self.replace("O", 4, 2, "Oxygen")), 1010 ("P", "Phosphorus", lambda: self.replace("P",4,3, "Phosphorous")), 1011 ("S", "Sulfur", lambda: self.replace("S",2,2, "Sulfur")), 1012 ("F", "Fluorine", lambda: self.replace("F",1,1, "Fluorine")), 1013 ("Cl", "Chlorrine", lambda: self.replace("Cl",1,1, "Chlorine")), 1014 ("Br", "Bromine", lambda: self.replace("Br",1,1, "Bromine")), 1015 ("I", "Iodine", lambda: self.replace("I",1,1, "Iodine")), 1016 ("-CF3", "Trifluoromethane", lambda: self.grow("trifluoromethane",4,0, "trifluoro")), 1017 ("-OMe", "Methanol", lambda: self.grow("methanol",5,0, "methoxy")), 1018 ], 1019 [ ("CH4", "Methyl", lambda: self.grow("methane",1,0,"methyl")), 1020 ("C=C", "Ethylene", lambda: self.grow("ethylene",4,0,"vinyl")), 1021 ("C#C", "Acetylene", lambda: self.grow("acetylene",2,0,"alkynl")), 1022 ("C#N", "Cyanide", lambda: self.grow("cyanide",2,0,"cyano")), 1023 ("C=O", "Aldehyde", lambda: self.grow("formaldehyde",2,0,"carbonyl",)), 1024 ("C=OO", "Formic Acid", lambda: self.grow("formic",4,0,"carboxyl")), 1025 ("C=ON", "C->N amide", lambda: self.grow("formamide",5,0,"C->N amide")), 1026 ("NC=O", "N->C amide", lambda: self.grow("formamide",3,1,"N->C amide")), 1027 ("S=O2", "Sulfone", lambda: self.grow("sulfone",3,1,"sulfonyl")), 1028 ("P=O3", "Phosphite", lambda: self.grow("phosphite",4,0,"phosphoryl")), 1029 ("N=O2", "Nitro", lambda: self.grow("nitro",3,0,"nitro")), 1030 ], 1031 [ 1032 ("#cyc3", "Cyclopropane", lambda: self.grow("cyclopropane",4,0,"cyclopropyl")), 1033 ("#cyc4", "Cyclobutane", lambda: self.grow("cyclobutane",4,0,"cyclobutyl")), 1034 ("#cyc5", "Cyclopentane", lambda: self.grow("cyclopentane",5,0,"cyclopentyl")), 1035 ("#cyc6", "Cyclohexane", lambda: self.grow("cyclohexane",7,0,"cyclohexyl")), 1036 ("#cyc7", "Cycloheptane", lambda: self.grow("cycloheptane",8,0,"cycloheptyl")), 1037 ("#aro5", "Cyclopentadiene", lambda: self.grow("cyclopentadiene",5,0,"cyclopentadienyl")), 1038 ("#aro6", "Benzene", lambda: self.grow("benzene",6,0,"phenyl")), 1039 ("#aro65", "Indane", lambda: self.grow("indane",12,0,"indanyl")), 1040 ("#aro66", "Napthylene", lambda: self.grow("napthylene",13,0,"napthyl")), 1041 ("#aro67", "Benzocycloheptane", lambda: self.grow("benzocycloheptane",13,0, "benzocycloheptyl")), 1042 ] 1043 ] 1044 1045 self.btn_icons = {} 1046 1047 requestsize = QtCore.QSize(48, 48) 1048 for row, btn_row in enumerate(buttons): 1049 for col, bb in enumerate(btn_row): 1050 btn_label, btn_tooltip, btn_command = bb 1051 btn = makeFragmentButton() 1052 if btn_label.startswith('#'): 1053 icons = self.icons[btn_label[1:]] 1054 btn.setIcon(icons[0]) 1055 btn.setIconSize(icons[1].actualSize(requestsize)) 1056 self.btn_icons[btn] = icons 1057 else: 1058 btn.setText(btn_label) 1059 btn.setToolTip(btn_tooltip) 1060 btn.clicked.connect(btn_command) 1061 self.fragments_layout.addWidget(btn, row, col) 1062 1063 buttons = [ 1064 [ 'Ace', 'Ala', 'Arg', 'Asn', 'Asp', 'Cys', 'Gln', 'Glu', 'Gly', 'His', 'Ile', 'Leu' ], 1065 [ 'Lys', 'Met', 'Phe', 'Pro', 'Ser', 'Thr', 'Trp', 'Tyr', 'Val', 'NMe', 'NHH' ] 1066 ] 1067 for row, btn_row in enumerate(buttons): 1068 for col, btn_label in enumerate(btn_row): 1069 btn = makeFragmentButton() 1070 btn.setText(btn_label) 1071 btn.setToolTip("Build %s residue" % btn_label) 1072 res = btn_label.lower() 1073 slot = lambda val=None, s=self,r=res: s.attach(r) 1074 btn.clicked.connect(slot) 1075 self.protein_layout.addWidget(btn, row, col) 1076 1077 lab = QtWidgets.QLabel('Secondary Structure:') 1078 lab_cols = 3 1079 self.ss_cbox = QtWidgets.QComboBox() 1080 self.ss_cbox.addItem("Alpha Helix") 1081 self.ss_cbox.addItem("Beta Sheet (Anti-Parallel)") 1082 self.ss_cbox.addItem("Beta Sheet (Parallel)") 1083 self.protein_layout.addWidget(lab, 2, 0, 1, lab_cols) 1084 self.protein_layout.addWidget(self.ss_cbox, 2, lab_cols, 1, 4) 1085 self.ss_cbox.currentIndexChanged[int].connect(self.ssIndexChanged) 1086 1087 buttons = [ 1088 [ 1089 ( "@Atoms:", None, None), 1090 ( "Fix H", "Fix hydrogens on picked atoms", self.fixH), 1091 ( "Add H", "Add hydrogens to entire molecule", self.addH), 1092 ( "Invert", "Invert stereochemistry around pk1 (pk2 and pk3 will remain fixed)", self.invert), 1093 ( "Delete", "Remove atoms", self.removeAtom), 1094 ( "Clear", "Delete everything", self.clear), 1095 ( "@ Charge:", None, None), 1096 ( " +1 ", "Positive Charge", lambda: self.setCharge(1,"+1")), 1097 ( " 0 ", "Neutral Charge", lambda: self.setCharge(0,"neutral")), 1098 ( " -1 ", "Negative Charge", lambda: self.setCharge(-1,"-1")), 1099 ], 1100 [ 1101 ( "@Bonds:", None, None), 1102 ( "Create", "Create bond between pk1 and pk2", self.createBond), 1103 ( "Delete", "Delete bond between pk1 and pk2", self.deleteBond), 1104 ( "Cycle", "Cycle bond valence", self.cycleBond), 1105 ( " | ", "Create single bond", lambda: self.setOrder("1", "single")), 1106 ( " || ", "Create double bond", lambda: self.setOrder("2", "double")), 1107 ( " ||| ", "Create triple bond", lambda: self.setOrder("3", "triple")), 1108 ( "Arom", "Create aromatic bond", lambda: self.setOrder("4", "aromatic")), 1109 ( "@ Model:", None, None), 1110 ( "Clean", "Cleanup structure", self.clean), 1111 ( "Sculpt", "Molecular sculpting", self.sculpt), 1112 ( "Fix", "Fix atom positions", self.fix), 1113 ( "Rest", "Restrain atom positions", self.rest), 1114 ], 1115 [ 1116 ( "$El-stat", "Electrostatics term for 'Clean' action", "clean_electro_mode"), 1117 ( "@ ", None, None), 1118 ( "$Bumps", "Show VDW contacts during sculpting", "sculpt_vdw_vis_mode"), 1119 ( "@ ", None, None), 1120 ( "#Undo Enabled", "", "suspend_undo"), 1121 ( "Undo", "Undo last change", self.undo), 1122 ( "Redo", "Redo last change", self.redo), 1123 ] 1124 ] 1125 1126 for row, btn_row in enumerate(buttons): 1127 btn_row_layout = QtWidgets.QHBoxLayout() 1128 self.buttons_layout.addLayout(btn_row_layout) 1129 for col, bb in enumerate(btn_row): 1130 btn_label, btn_tooltip, btn_command = bb 1131 if btn_label[0] == '@': 1132 btn = QtWidgets.QLabel(btn_label[1:]) 1133 elif btn_label[0] in ('#', '$'): 1134 btn = QtWidgets.QCheckBox(btn_label[1:]) 1135 setting = btn_command 1136 value = self.cmd.get_setting_int(setting) 1137 if btn_label[0] == '$': 1138 btn.setChecked(bool(value)) 1139 @btn.toggled.connect 1140 def _(checked, n=setting): 1141 self.cmd.set(n, checked, quiet=0) 1142 else: 1143 btn.setChecked(not value) 1144 @btn.toggled.connect 1145 def _(checked, n=setting): 1146 self.cmd.set(n, not checked, quiet=0) 1147 else: 1148 btn = makeFragmentButton() 1149 btn.setText(btn_label) 1150 btn.clicked.connect(btn_command) 1151 if btn_tooltip: 1152 btn.setToolTip(btn_tooltip) 1153 btn_row_layout.addWidget(btn) 1154 btn_row_layout.addStretch() 1155 1156 def getIcons(self): 1157 self.icons = {} 1158 # use old Tk icons 1159 imgDir = os.path.join(os.environ['PYMOL_DATA'], "pmg_tk/bitmaps/builder") 1160 imgList = glob("%s/aro*.gif" % imgDir) + glob("%s/cyc*.gif" % imgDir) 1161 for imgFile in imgList: 1162 imgName = os.path.splitext(os.path.split(imgFile)[1])[0] 1163 if imgName not in list(self.icons.keys()): 1164 image = QtGui.QImage(imgFile) 1165 pixmap = QtGui.QPixmap.fromImage(image) 1166 image.invertPixels() 1167 inv_pixmap = QtGui.QPixmap.fromImage(image) 1168 self.icons[imgName] = (QtGui.QIcon(pixmap), QtGui.QIcon(inv_pixmap)) 1169 1170 def showEvent(self, event): 1171 self.cmd.set("editor_auto_measure", 0) 1172 self.cmd.set("auto_overlay") 1173 self.cmd.set("valence") 1174 self.cmd.edit_mode(1) 1175 1176 def grow(self, name, pos, geom, text): 1177 if "pk1" in self.cmd.get_names("selections"): 1178 self.cmd.select(active_sele,"byobj pk1") 1179 editor.attach_fragment("pk1", name, pos, geom, _self=self.cmd) 1180 self.doAutoPick() 1181 else: 1182 self.cmd.unpick() 1183 AttachWizard(self.cmd).toggle(name, pos, geom, text) 1184 1185 def replace(self, atom, geometry, valence, text): 1186 picked = collectPicked(self.cmd) 1187 if len(picked): 1188 self.cmd.select(active_sele,"byobj "+picked[0]) 1189 self.cmd.replace(atom, geometry, valence) 1190 self.doAutoPick() 1191 else: 1192 ReplaceWizard(_self=self.cmd).toggle(atom,geometry,valence,text) 1193 1194 def attach(self, aa): 1195 ss = self.ss_cbox.currentIndex() + 1 1196 picked = collectPicked(self.cmd) 1197 if len(picked)==1: 1198 try: 1199 with undocontext(self.cmd, "bymol %s" % picked[0]): 1200 editor.attach_amino_acid(picked[0], aa, 1201 ss=ss, _self=self.cmd) 1202 except: 1203 fin = -1 1204 self.doZoom() 1205 else: 1206 self.cmd.unpick() 1207 AminoAcidWizard(_self=self.cmd, ss=ss).toggle(aa) 1208 1209 def ssIndexChanged(self, index): 1210 w = self.cmd.get_wizard() 1211 if isinstance(w, AminoAcidWizard): 1212 w.setSecondaryStructure(index + 1) 1213 1214 def doAutoPick(self, old_atoms=None): 1215 self.cmd.unpick() 1216 if self.cmd.select(newest_sele,"(byobj "+active_sele+") and not "+active_sele)==0: 1217 self.cmd.select(newest_sele, active_sele) 1218 new_list = self.cmd.index(newest_sele+" and hydro") 1219 if len(new_list)==0: 1220 new_list = self.cmd.index(newest_sele) 1221 if new_list: 1222 index = new_list.pop() 1223 try: 1224 self.cmd.edit("%s`%d" % index) 1225 if self.cmd.get_wizard() is not None: 1226 self.cmd.do("_ cmd.get_wizard().do_pick(0)") 1227 except pymol.CmdException: 1228 print(" doAutoPick-Error: exception") 1229 self.doZoom() 1230 1231 def doZoom(self, *ignore): 1232 if "pk1" in self.cmd.get_names("selections"): 1233 self.cmd.zoom("((neighbor pk1) extend 4)", 4.0, animate=-1) 1234 1235 def setCharge(self, charge, text): 1236 picked = collectPicked(self.cmd) 1237 if len(picked)>0: 1238 sele = "?pk1 ?pk2 ?pk3 ?pk4" 1239 with undocontext(self.cmd, sele): 1240 self.cmd.alter(sele,"formal_charge=%s" % charge) 1241 self.cmd.h_fill() 1242 self.cmd.label(sele,'"%+d" % formal_charge if formal_charge else ""') 1243 self.cmd.unpick() 1244 else: 1245 ChargeWizard(self.cmd).toggle(charge, text) 1246 1247 def createBond(self): 1248 if not BondWizard.staticaction(self.cmd): 1249 BondWizard(self.cmd).toggle() 1250 1251 def deleteBond(self): 1252 picked = collectPicked(self.cmd) 1253 if picked == ["pk1","pk2"]: 1254 with undocontext(self.cmd, "(?pk1 ?pk2) extend 1"): 1255 self.cmd.unbond("pk1", "pk2") 1256 self.cmd.h_fill() 1257 self.cmd.unpick() 1258 else: 1259 self.cmd.unpick() 1260 UnbondWizard(self.cmd).toggle() 1261 1262 def cycleBond(self): 1263 picked = collectPicked(self.cmd) 1264 if picked == ["pk1","pk2"]: 1265 with undocontext(self.cmd, "(?pk1 ?pk2) extend 1"): 1266 self.cmd.cycle_valence() 1267 self.cmd.unpick() 1268 else: 1269 ValenceWizard(_self=self.cmd).toggle(-1,"Cycle bond") 1270 1271 @undoablemethod("(?pk1 ?pk2) extend 1") 1272 def setOrder(self, order, text): 1273 picked = collectPicked(self.cmd) 1274 if picked == ["pk1","pk2"]: 1275 self.cmd.unbond("pk1", "pk2") 1276 self.cmd.bond("pk1", "pk2", order) 1277 self.cmd.h_fill() 1278 self.cmd.unpick() 1279 else: 1280 self.cmd.unpick() 1281 ValenceWizard(_self=self.cmd).toggle(order,text) 1282 1283 def fixH(self): 1284 picked = collectPicked(self.cmd) 1285 if len(picked): 1286 self.cmd.h_fill() 1287 self.cmd.unpick() 1288 else: 1289 HydrogenWizard(_self=self.cmd).toggle('fix') 1290 1291 def addH(self): 1292 picked = collectPicked(self.cmd) 1293 if len(picked): 1294 self.cmd.h_add("pkmol") 1295 self.cmd.unpick() 1296 else: 1297 HydrogenWizard(_self=self.cmd).toggle('add') 1298 1299 @PopupOnException.decorator 1300 def invert(self, _=None): 1301 picked = collectPicked(self.cmd) 1302 if picked == ["pk1","pk2","pk3"]: 1303 self.cmd.invert() 1304 self.cmd.unpick() 1305 else: 1306 self.cmd.unpick() 1307 InvertWizard(self.cmd).toggle() 1308 1309 def center(self): 1310 if "pk1" in self.cmd.get_names("selections"): 1311 self.cmd.zoom("pk1", 5.0, animate=-1) 1312 else: 1313 self.cmd.zoom("all", 3.0, animate=-1) 1314 1315 def removeAtom(self): 1316 picked = collectPicked(self.cmd) 1317 if len(picked): 1318 if self.cmd.count_atoms("?pkbond"): 1319 self.cmd.edit("(pk1)","(pk2)",pkbond=0) 1320 cnt = self.cmd.select(active_sele, 1321 "(((?pkset or ?pk1) and not hydro) extend 1) and not hydro") 1322 with undocontext(self.cmd, "(?pkset ?pk1) extend 1"): 1323 self.cmd.remove_picked() 1324 if cnt: 1325 self.cmd.fix_chemistry(active_sele) 1326 self.cmd.h_add(active_sele) 1327 self.cmd.delete(active_sele) 1328 self.cmd.unpick() 1329 else: 1330 RemoveWizard(self.cmd).toggle() 1331 1332 def reset(self): 1333 self.cmd.unpick() 1334 1335 def clear(self): 1336 QMB = QtWidgets.QMessageBox 1337 check = QMB.question(None, "Confirm", 1338 "Really delete everything?", QMB.Yes | QMB.No) 1339 if check == QMB.Yes: 1340 self.cmd.delete("all") 1341 self.cmd.refresh_wizard() 1342 1343 def sculpt(self): 1344 picked = collectPicked(self.cmd) 1345 if len(picked): 1346 self.cmd.select(active_sele, ' or '.join(picked)) 1347 SculptWizard(_self=self.cmd).toggle() 1348 1349 def clean(self): 1350 picked = collectPicked(self.cmd) 1351 if len(picked): 1352 self.cmd.select(active_sele, "pkmol") 1353 self.cmd.unpick() 1354 CleanWizard(_self=self.cmd).toggle() 1355 1356 def undo(self): 1357 self.cmd.undo() 1358 1359 def redo(self): 1360 self.cmd.redo() 1361 1362 def fix(self): 1363 picked = collectPicked(self.cmd) 1364 if len(picked): 1365 self.cmd.select(active_sele,"pk1") 1366 self.cmd.deselect() 1367 else: 1368 self.cmd.delete(active_sele) 1369 FixAtomWizard(_self=self.cmd).toggle(3) 1370 1371 def rest(self): 1372 picked = collectPicked(self.cmd) 1373 if len(picked): 1374 self.cmd.select(active_sele,"byobj ("+' or '.join(picked)+")") 1375 self.cmd.deselect() 1376 else: 1377 self.cmd.delete(active_sele) 1378 RestAtomWizard(_self=self.cmd).toggle(2) 1379 1380 1381def BuilderPanelDocked(parent, *args, **kwargs): 1382 widget = _BuilderPanel(parent, *args, **kwargs) 1383 window = QtWidgets.QDockWidget(parent) 1384 window.setWindowTitle("Builder") 1385 window.setWidget(widget) 1386 window.setFloating(True) 1387 return window 1388