def delete_items(self, items, redraw=1, delete_single_atom=1): """deletes items and also makes cleaning of orphan bonds and atoms""" if not items: return items, [] # quick way to avoid costly evaluation deleted = copy.copy(items) for o in items: if o.object_type == 'atom': self.delete_atom(o) else: self.delete_bond(o) if self.atoms: # delete bonds that are not in connect anymore bonds_in_connect = set() for a in self.atoms: for (e, v) in a.get_neighbor_edge_pairs(): if v in self.atoms: bonds_in_connect.add(e) deleted += [ self.delete_bond(o) for o in (self.bonds - bonds_in_connect) ] # delete also orphan atoms if delete_single_atom: atms = [o for o in self.atoms if len(o.neighbors) == 0] deleted += [self.delete_atom(o) for o in atms] # recalculation of second line of double bond position, optimized to do it only when realy # necessary, because its pretty expensive # check_integrity should be called before redrawing, because it moves atoms and bonds # to new molecules when the molecule is spit and avoids working on non-connected graph offspring = self.check_integrity() if redraw: bonds_to_redraw = [] for b in deleted: if b.object_type == 'bond': for a in b.atoms: if a in self.atoms: bonds_to_redraw.extend(a.neighbor_edges) [ o.redraw(recalc_side=1) for o in misc.filter_unique(bonds_to_redraw) if o.order == 2 and o.item ] [o.decide_pos() for o in self.atoms if isinstance(o, atom)] [o.redraw() for o in self.atoms] else: offspring = self.check_integrity() deleted += map(self.delete_bond, copy.copy(self.bonds)) return deleted, offspring
def handle_overlap(self): "deletes one of overlaping atoms and updates the bonds" to_delete = [] bonds_to_check = set( ) # this can speedup the following for b in bonds_to_check by factor of 10 for big mols for i in range(len(self.atoms)): for j in range(i + 1, len(self.atoms)): a = self.atoms[i] b = self.atoms[j] if (abs(a.x - b.x) < 4) and (abs(a.y - b.y) < 4): if a not in to_delete: for e, v in b.get_neighbor_edge_pairs(): e.change_atoms(b, a) a.add_neighbor(v, e) v.add_neighbor(a, e) bonds_to_check.add(e) to_delete.append(b) deleted = misc.filter_unique(to_delete) [self.delete_atom(o) for o in deleted] # after all is done, find and delete orphan bonds and update the others to_redraw = [] bonds = set(self.bonds) for b in bonds_to_check: if not b in self.bonds: #print b, "not in self.bonds" continue recent_b = None for recent_b in self.gen_bonds_between(b.atom1, b.atom2): if recent_b != b: break if recent_b and recent_b != b: self.delete_bond(b) deleted.append(b) to_redraw.append(recent_b) # we redraw the ones that remained elif not recent_b: b.atom1.add_neighbor(b.atom2, b) b.atom2.add_neighbor(b.atom1, b) to_redraw.append(b) # we redraw this also for b in to_redraw: b.redraw() return deleted
def handle_overlap( self): "deletes one of overlaping atoms and updates the bonds" to_delete = [] bonds_to_check = set() # this can speedup the following for b in bonds_to_check by factor of 10 for big mols for i in range( len( self.atoms)): for j in range( i+1, len( self.atoms)): a = self.atoms[i] b = self.atoms[j] if (abs( a.x-b.x) < 4) and (abs( a.y-b.y) <4): if a not in to_delete: for e,v in b.get_neighbor_edge_pairs(): e.change_atoms( b, a) a.add_neighbor( v, e) v.add_neighbor( a, e) bonds_to_check.add( e) to_delete.append( b) deleted = misc.filter_unique( to_delete) [self.delete_atom( o) for o in deleted] # after all is done, find and delete orphan bonds and update the others to_redraw = [] bonds = set( self.bonds) for b in bonds_to_check: if not b in self.bonds: #print(b, "not in self.bonds") continue recent_b = None for recent_b in self.gen_bonds_between( b.atom1, b.atom2): if recent_b != b: break if recent_b and recent_b != b: self.delete_bond( b) deleted.append( b) to_redraw.append( recent_b) # we redraw the ones that remained elif not recent_b: b.atom1.add_neighbor( b.atom2, b) b.atom2.add_neighbor( b.atom1, b) to_redraw.append( b) # we redraw this also for b in to_redraw: b.redraw() return deleted
def delete_items( self, items, redraw=1, delete_single_atom=1): """deletes items and also makes cleaning of orphan bonds and atoms""" if not items: return items, [] # quick way to avoid costly evaluation deleted = copy.copy( items) for o in items: if o.object_type == 'atom': self.delete_atom( o) else: self.delete_bond( o) if self.atoms: # delete bonds that are not in connect anymore bonds_in_connect = set() for a in self.atoms: for (e,v) in a.get_neighbor_edge_pairs(): if v in self.atoms: bonds_in_connect.add( e) deleted += [self.delete_bond( o) for o in (self.bonds - bonds_in_connect)] # delete also orphan atoms if delete_single_atom: atms = [o for o in self.atoms if len(o.neighbors) == 0] deleted += [self.delete_atom( o) for o in atms] # recalculation of second line of double bond position, optimized to do it only when realy # necessary, because its pretty expensive # check_integrity should be called before redrawing, because it moves atoms and bonds # to new molecules when the molecule is spit and avoids working on non-connected graph offspring = self.check_integrity() if redraw: bonds_to_redraw = [] for b in deleted: if b.object_type == 'bond': for a in b.atoms: if a in self.atoms: bonds_to_redraw.extend( a.neighbor_edges) [o.redraw( recalc_side=1) for o in misc.filter_unique( bonds_to_redraw) if o.order == 2 and o.item] [o.decide_pos() for o in self.atoms if isinstance( o, atom)] [o.redraw() for o in self.atoms] else: offspring = self.check_integrity() deleted += map( self.delete_bond, copy.copy( self.bonds)) return deleted, offspring
def __init__(self, parent, items): self.items = items self.changes_made = 0 self.parent = parent self.dialog = Pmw.Dialog(parent, buttons=(_('OK'), _('Cancel')), defaultbutton=_('OK'), title=_('Configuration'), command=self.done, master='parent') #parent.bind_all( "<Button-1>", self.raise_me, add='+') self.pages = Pmw.NoteBook(self.dialog.interior()) self.pages.pack(anchor='w', pady=0, padx=0, fill='both', expand=1) # create pages for different item types self.atom_page = None self.bond_page = None self.arrow_page = None self.text_page = None self.plus_page = None self.font_page = None self.common_page = None arrows = [] for o in items: if o.object_type == 'point': items.remove(o) if o.arrow not in arrows: arrows.append(o.arrow) items += arrows types = misc.filter_unique([o.object_type for o in items]) if 'atom' in types: self.atom_page = self.pages.add(_('Atom')) # charge charges = misc.filter_unique( [o.charge for o in items if hasattr(o, 'charge')]) if len(charges) == 1: charge = charges[0] else: charge = '' self.atom_charge = Pmw.Counter(self.atom_page, labelpos='w', label_text=_('Charge'), entryfield_value=charge, entryfield_validate={ 'validator': 'integer', 'min': -4, 'max': 4 }, entry_width=3, increment=1, datatype='integer') self.atom_charge.pack(anchor='nw', padx=10, pady=5) # show? shows = misc.filter_unique( [o.show for o in items if hasattr(o, 'show')]) if len(shows) == 1: show = int(shows[0]) else: show = 2 # means the show should be preserved as is self.atom_show = Pmw.OptionMenu(self.atom_page, labelpos='nw', label_text=_('Atom name'), items=(_("don't show"), _("show"), u""), initialitem=show) self.atom_show.pack(anchor='nw') # positioning poss = misc.filter_unique( [o.pos for o in items if o.object_type == 'atom']) if not poss: pos = None elif len(poss) == 1 and poss[0]: pos = ['center-first', 'center-last'].index(poss[0]) else: pos = 2 # means the centering should be preserved as is if pos == None: self.atom_pos = None else: self.atom_pos = Pmw.OptionMenu( self.atom_page, labelpos='nw', label_text=_('Atom positioning'), items=(_("center first letter"), _("center last letter"), u""), initialitem=pos) self.atom_pos.pack(anchor='nw') # show hydrogens shows = misc.filter_unique( [o.show_hydrogens for o in items if o.object_type == 'atom']) if len(shows) == 1: show = shows[0] else: show = 2 # means the show should be preserved as is self.atom_show_h = Pmw.OptionMenu(self.atom_page, labelpos='nw', label_text=_('Hydrogens'), items=(_("off"), _("on"), u""), initialitem=show) self.atom_show_h.pack(anchor='nw') # marks #self.marks = widgets.GraphicalAngleChooser( self.atom_page, 270) #self.marks.pack() # BOND if 'bond' in types: self.bond_page = self.pages.add(_('Bond')) # bond_widths (former distances) dists = misc.filter_unique( map(abs, [o.bond_width for o in items if o.object_type == 'bond'])) if len(dists) == 1: dist = dists[0] else: dist = '' if not misc.split_number_and_unit(dist)[1]: dist = str(dist) + 'px' self.bond_dist = widgets.WidthChooser(self.bond_page, dist, label=_('Bond width')) self.bond_dist.pack(anchor='ne', padx=10, pady=5) # wedge_widths dists = misc.filter_unique( map(abs, [o.wedge_width for o in items if o.object_type == 'bond'])) if len(dists) == 1: dist = dists[0] else: dist = '' if not misc.split_number_and_unit(dist)[1]: dist = str(dist) + 'px' self.wedge_width = widgets.WidthChooser( self.bond_page, dist, label=_('Wedge/Hatch width')) self.wedge_width.pack(anchor='ne', padx=10, pady=5) # double bond length ratio ratios = misc.filter_unique([ o.double_length_ratio for o in items if o.object_type == 'bond' ]) if len(ratios) == 1: ratio = ratios[0] else: ratio = '' self.double_length_ratio = widgets.RatioCounter( self.bond_page, ratio, label=_('Double-bond length ratio')) self.double_length_ratio.pack(anchor='nw', padx=10, pady=5) # ARROW if 'arrow' in types: self.arrow_page = self.pages.add(_('Arrow')) self.arrow_end_changed = 0 self.arrow_start_changed = 0 arrow_items = [o for o in items if o.object_type == 'arrow'] # arrow start pins arrow_starts = misc.filter_unique( [o.get_pins()[0] for o in arrow_items]) self.arrow_start = Tkinter.IntVar() if len(arrow_starts) == 1: self.arrow_start.set(arrow_starts[0]) else: self.arrow_start.set(0) self.arrow_start_entry = Tkinter.Checkbutton( self.arrow_page, text=_('Arrow-head on start'), variable=self.arrow_start, command=self._arrow_start_changed) self.arrow_start_entry.pack(anchor='w') # arrow end pins arrow_ends = misc.filter_unique( [o.get_pins()[1] for o in arrow_items]) self.arrow_end = Tkinter.IntVar() if len(arrow_ends) == 1: self.arrow_end.set(arrow_ends[0]) else: self.arrow_end.set(0) self.arrow_end_entry = Tkinter.Checkbutton( self.arrow_page, text=_('Arrow-head on end'), variable=self.arrow_end, command=self._arrow_end_changed) self.arrow_end_entry.pack(anchor='w') # spline? splines = misc.filter_unique([o.spline for o in arrow_items]) self.spline = Tkinter.IntVar() if len(splines) == 1: self.spline.set(splines[0]) else: self.spline.set(0) self.spline_entry = Tkinter.Checkbutton( self.arrow_page, text=_('Spline arrow'), variable=self.spline, command=self._spline_changed) self.spline_changed = 0 self.spline_entry.pack(anchor='w') # TEXTS # PLUS # FONT font_items = filter(lambda x: hasattr(x, 'font_family'), items) if font_items: self.font_page = self.pages.add(_('Font')) sizes = misc.filter_unique([o.font_size for o in font_items]) if len(sizes) == 1: size = sizes[0] else: size = '' self.font_size = widgets.FontSizeChooser(self.font_page, size) self.font_size.pack(anchor='nw') used_families = misc.filter_unique( [o.font_family for o in font_items]) if len(used_families) == 1: self.used_family = used_families[0] else: self.used_family = '' self.font_family = widgets.FontFamilyChooser( self.font_page, self.used_family) self.font_family.pack(anchor="nw", side='bottom') # COMMON self.common_page = self.pages.add(_('Common')) line_items = filter(lambda x: hasattr(x, 'line_width'), items) if line_items: widths = misc.filter_unique([o.line_width for o in line_items]) if len(widths) == 1: width = widths[0] else: width = '' if not misc.split_number_and_unit(width)[1]: width = str(width) + 'px' self.line_width = widgets.WidthChooser(self.common_page, width, label=_('Line width')) self.line_width.pack(anchor='nw', padx=10, pady=5) line_color_items = filter(lambda x: hasattr(x, 'line_color'), items) if line_color_items: lines = misc.filter_unique( [o.line_color for o in line_color_items]) if len(lines) == 1: line = lines[0] else: line = None self.line_color = widgets.ColorButtonWithTransparencyChecker( self.common_page, color=line, text=_("Line color")) self.line_color.pack(anchor='nw', padx=10, pady=10) area_color_items = filter(lambda x: hasattr(x, 'area_color'), items) if area_color_items: areas = misc.filter_unique( [o.area_color for o in area_color_items]) if len(areas) == 1: area = areas[0] else: area = None self.area_color = widgets.ColorButtonWithTransparencyChecker( self.common_page, color=area, text=_("Area color")) self.area_color.pack(anchor='nw', padx=10, pady=5) # RUN IT ALL self.pages.setnaturalsize() self.dialog.activate(globalMode=0)
def __init__( self, parent, items): self.items = items self.changes_made = 0 self.parent = parent self.dialog = Pmw.Dialog( parent, buttons=(_('OK'), _('Cancel')), defaultbutton=_('OK'), title=_('Configuration'), command=self.done, master='parent') #parent.bind_all( "<Button-1>", self.raise_me, add='+') self.pages = Pmw.NoteBook( self.dialog.interior()) self.pages.pack( anchor='w', pady=0, padx=0, fill='both', expand=1) # create pages for different item types self.atom_page = None self.bond_page = None self.arrow_page = None self.text_page = None self.plus_page = None self.font_page = None self.common_page = None arrows = [] for o in items: if o.object_type == 'point': items.remove( o) if o.arrow not in arrows: arrows.append( o.arrow) items += arrows types = misc.filter_unique( [o.object_type for o in items]) if 'atom' in types: self.atom_page = self.pages.add(_('Atom')) # charge charges = misc.filter_unique( [o.charge for o in items if hasattr( o, 'charge')]) if len( charges) == 1: charge = charges[0] else: charge = '' self.atom_charge = Pmw.Counter( self.atom_page, labelpos = 'w', label_text = _('Charge'), entryfield_value = charge, entryfield_validate={ 'validator':'integer', 'min':-4, 'max':4}, entry_width = 3, increment = 1, datatype = 'integer') self.atom_charge.pack( anchor='nw', padx=10, pady=5) # show? shows = misc.filter_unique( [o.show for o in items if hasattr( o, 'show')]) if len( shows) == 1: show = int( shows[0]) else: show = 2 # means the show should be preserved as is self.atom_show = Pmw.OptionMenu( self.atom_page, labelpos = 'nw', label_text = _('Atom name'), items = (_("don't show"), _("show"), ""), initialitem = show) self.atom_show.pack( anchor = 'nw') # positioning poss = misc.filter_unique( [o.pos for o in items if o.object_type == 'atom']) if not poss: pos = None elif len( poss) == 1 and poss[0]: pos = ['center-first', 'center-last'].index( poss[0]) else: pos = 2 # means the centering should be preserved as is if pos == None: self.atom_pos = None else: self.atom_pos = Pmw.OptionMenu( self.atom_page, labelpos = 'nw', label_text = _('Atom positioning'), items = (_("center first letter"), _("center last letter"), ""), initialitem = pos) self.atom_pos.pack( anchor = 'nw') # show hydrogens shows = misc.filter_unique( [o.show_hydrogens for o in items if o.object_type == 'atom']) if len( shows) == 1: show = shows[0] else: show = 2 # means the show should be preserved as is self.atom_show_h = Pmw.OptionMenu( self.atom_page, labelpos = 'nw', label_text = _('Hydrogens'), items = (_("off"), _("on"), ""), initialitem = show) self.atom_show_h.pack( anchor = 'nw') # marks #self.marks = widgets.GraphicalAngleChooser( self.atom_page, 270) #self.marks.pack() # BOND if 'bond' in types: self.bond_page = self.pages.add(_('Bond')) # bond_widths (former distances) dists = misc.filter_unique( map( abs, [o.bond_width for o in items if o.object_type == 'bond'])) if len( dists) == 1: dist = dists[0] else: dist = '' if not misc.split_number_and_unit( dist)[1]: dist = str( dist) + 'px' self.bond_dist = widgets.WidthChooser( self.bond_page, dist, label=_('Bond width')) self.bond_dist.pack( anchor='ne', padx=10, pady=5) # wedge_widths dists = misc.filter_unique( map( abs, [o.wedge_width for o in items if o.object_type == 'bond'])) if len( dists) == 1: dist = dists[0] else: dist = '' if not misc.split_number_and_unit( dist)[1]: dist = str( dist) + 'px' self.wedge_width = widgets.WidthChooser( self.bond_page, dist, label=_('Wedge/Hatch width')) self.wedge_width.pack( anchor='ne', padx=10, pady=5) # double bond length ratio ratios = misc.filter_unique( [o.double_length_ratio for o in items if o.object_type == 'bond']) if len( ratios) == 1: ratio = ratios[0] else: ratio = '' self.double_length_ratio = widgets.RatioCounter( self.bond_page, ratio, label=_('Double-bond length ratio')) self.double_length_ratio.pack( anchor='nw', padx=10, pady=5) # ARROW if 'arrow' in types: self.arrow_page = self.pages.add(_('Arrow')) self.arrow_end_changed = 0 self.arrow_start_changed = 0 arrow_items = [o for o in items if o.object_type == 'arrow'] # arrow start pins arrow_starts = misc.filter_unique( [o.get_pins()[0] for o in arrow_items]) self.arrow_start = Tkinter.IntVar() if len( arrow_starts) == 1: self.arrow_start.set( arrow_starts[0]) else: self.arrow_start.set( 0) self.arrow_start_entry = Tkinter.Checkbutton( self.arrow_page, text=_('Arrow-head on start'), variable = self.arrow_start, command = self._arrow_start_changed) self.arrow_start_entry.pack( anchor='w') # arrow end pins arrow_ends = misc.filter_unique( [o.get_pins()[1] for o in arrow_items]) self.arrow_end = Tkinter.IntVar() if len( arrow_ends) == 1: self.arrow_end.set( arrow_ends[0]) else: self.arrow_end.set( 0) self.arrow_end_entry = Tkinter.Checkbutton( self.arrow_page, text=_('Arrow-head on end'), variable = self.arrow_end, command = self._arrow_end_changed) self.arrow_end_entry.pack( anchor='w') # spline? splines = misc.filter_unique( [o.spline for o in arrow_items]) self.spline = Tkinter.IntVar() if len( splines) == 1: self.spline.set( splines[0]) else: self.spline.set( 0) self.spline_entry = Tkinter.Checkbutton( self.arrow_page, text=_('Spline arrow'), variable = self.spline, command = self._spline_changed) self.spline_changed = 0 self.spline_entry.pack( anchor='w') # TEXTS # PLUS # FONT font_items = filter( lambda x: hasattr( x, 'font_family'), items) if font_items: self.font_page = self.pages.add(_('Font')) sizes = misc.filter_unique( [o.font_size for o in font_items]) if len( sizes) == 1: size = sizes[0] else: size = '' self.font_size = widgets.FontSizeChooser( self.font_page, size) self.font_size.pack( anchor = 'nw') used_families = misc.filter_unique( [o.font_family for o in font_items]) if len( used_families) == 1: self.used_family = used_families[0] else: self.used_family = '' self.font_family = widgets.FontFamilyChooser( self.font_page, self.used_family) self.font_family.pack( anchor="nw", side = 'bottom') # COMMON self.common_page = self.pages.add(_('Common')) line_items = filter( lambda x: hasattr( x, 'line_width'), items) if line_items: widths = misc.filter_unique( [o.line_width for o in line_items]) if len( widths) == 1: width = widths[0] else: width = '' if not misc.split_number_and_unit( width)[1]: width = str( width) + 'px' self.line_width = widgets.WidthChooser( self.common_page, width, label=_('Line width')) self.line_width.pack( anchor='nw', padx=10, pady=5) line_color_items = filter( lambda x: hasattr( x, 'line_color'), items) if line_color_items: lines = misc.filter_unique( [o.line_color for o in line_color_items]) if len( lines) == 1: line = lines[0] else: line = None self.line_color = widgets.ColorButtonWithTransparencyChecker( self.common_page, color=line, text=_("Line color")) self.line_color.pack( anchor='nw', padx=10, pady=10) area_color_items = filter( lambda x: hasattr( x, 'area_color'), items) if area_color_items: areas = misc.filter_unique( [o.area_color for o in area_color_items]) if len( areas) == 1: area = areas[0] else: area = None self.area_color = widgets.ColorButtonWithTransparencyChecker( self.common_page, color=area, text=_("Area color")) self.area_color.pack( anchor='nw', padx=10, pady=5) # RUN IT ALL self.pages.setnaturalsize() self.dialog.activate( globalMode=0)