def gotomap(self, obj): """ Run the map service """ #First test if any map service is available if not len(self.mapservicedata): msg = _("No map service is available.") msg2 = _("Check your installation.") ErrorDialog(msg, msg2) return place_handles = self.selected_handles() try: place_handle = self.selected_handles()[0] except IndexError: msg = _("No place selected.") msg2 = _("You need to select a place to be able to view it" " on a map. Some Map Services might support multiple" " selections.") ErrorDialog(msg, msg2) return #TODO: support for descriptions in some cases. For now, pass None #TODO: Later this might be 'Birth of William' .... places = [(x, None) for x in place_handles] #run the mapservice: pmgr = GuiPluginManager.get_instance() serv = self.mapservicedata[self.mapservice] mod = pmgr.load_plugin(serv) if mod: servfunc = eval('mod.' + serv.mapservice) servfunc()(self.dbstate.db, places) else: print 'Failed to load map plugin, see Plugin Manager'
def merge(self, obj): """ Merge the selected citations. """ mlist = self.selected_handles() if len(mlist) != 2: msg = _("Cannot merge citations.") msg2 = _("Exactly two citations must be selected to perform a " "merge. A second citation can be selected by holding " "down the control key while clicking on the desired " "citation.") ErrorDialog(msg, msg2) else: citation1 = self.dbstate.db.get_citation_from_handle(mlist[0]) citation2 = self.dbstate.db.get_citation_from_handle(mlist[1]) if not citation1.get_reference_handle() == \ citation2.get_reference_handle(): msg = _("Cannot merge citations.") msg2 = _("The two selected citations must have the same " "source to perform a merge. If you want to merge " "these two citations, then you must merge the " "sources first.") ErrorDialog(msg, msg2) else: import Merge Merge.MergeCitations(self.dbstate, self.uistate, mlist[0], mlist[1])
def check_in(dbase, filename, callback, cursor_func=None): """ Checks in the specified file into RCS """ init = ["rcs", '-x,v', '-i', '-U', '-q', '-t-"Gramps database"'] ci_cmd = ["ci", '-x,v', "-q", "-f"] archive_name = filename + ",v" glade = Glade(toplevel='comment') top = glade.toplevel text = glade.get_object('description') top.run() comment = text.get_text() top.destroy() if not os.path.isfile(archive_name): cmd = init + [archive_name] proc = subprocess.Popen(cmd, stderr=subprocess.PIPE) status = proc.wait() message = "\n".join(proc.stderr.readlines()) proc.stderr.close() del proc if status != 0: ErrorDialog( _("Archiving failed"), _("An attempt to create the archive failed " "with the following message:\n\n%s") % message) if cursor_func: cursor_func(_("Creating data to be archived...")) plugin_manager = GuiPluginManager.get_instance() for plugin in plugin_manager.get_export_plugins(): if plugin.get_extension() == "gramps": export_function = plugin.get_export_function() export_function(dbase, filename, None, callback) if cursor_func: cursor_func(_("Saving archive...")) cmd = ci_cmd + ['-m%s' % comment, filename, archive_name] proc = subprocess.Popen(cmd, stderr=subprocess.PIPE) status = proc.wait() message = "\n".join(proc.stderr.readlines()) proc.stderr.close() del proc if status != 0: ErrorDialog( _("Archiving failed"), _("An attempt to archive the data failed " "with the following message:\n\n%s") % message)
def save(self, *obj): self.ok_button.set_sensitive(False) if self.object_is_empty(): ErrorDialog( _("Cannot save event"), _("No data exists for this event. Please " "enter data or cancel the edit.")) self.ok_button.set_sensitive(True) return (uses_dupe_id, id) = self._uses_duplicate_id() if uses_dupe_id: prim_object = self.get_from_gramps_id(id) name = prim_object.get_description() msg1 = _("Cannot save event. ID already exists.") msg2 = _("You have attempted to use the existing Gramps ID with " "value %(id)s. This value is already used by '" "%(prim_object)s'. Please enter a different ID or leave " "blank to get the next available ID value.") % { 'id': id, 'prim_object': name } ErrorDialog(msg1, msg2) self.ok_button.set_sensitive(True) return t = self.obj.get_type() if t.is_custom() and str(t) == '': ErrorDialog(_("Cannot save event"), _("The event type cannot be empty")) self.ok_button.set_sensitive(True) return if not self.obj.handle: with DbTxn( _("Add Event (%s)") % self.obj.get_gramps_id(), self.db) as trans: self.db.add_event(self.obj, trans) else: orig = self.get_from_handle(self.obj.handle) if cmp(self.obj.serialize(), orig.serialize()): with DbTxn( _("Edit Event (%s)") % self.obj.get_gramps_id(), self.db) as trans: if not self.obj.get_gramps_id(): self.obj.set_gramps_id( self.db.find_next_event_gramps_id()) self.commit_event(self.obj, trans) if self.callback: self.callback(self.obj) self.close()
def save(self, *obj): """ Save the data. """ self.ok_button.set_sensitive(False) if not self.obj.get_reference_handle(): ErrorDialog( _("No source selected"), _("A source is anything (personal testimony, " "video recording, photograph, newspaper column, " "gravestone...) from which information can be " "derived. To create a citation, first select the " "required source, and then record the location of " "the information referenced within the source in the " "'Volume/Page' field.")) self.ok_button.set_sensitive(True) return (uses_dupe_id, gramps_id) = self._uses_duplicate_id() if uses_dupe_id: prim_object = self.get_from_gramps_id(gramps_id) name = prim_object.get_page() msg1 = _("Cannot save citation. ID already exists.") msg2 = _("You have attempted to use the existing Gramps ID with " "value %(id)s. This value is already used by '" "%(prim_object)s'. Please enter a different ID or leave " "blank to get the next available ID value.") % { 'id': gramps_id, 'prim_object': name } ErrorDialog(msg1, msg2) self.ok_button.set_sensitive(True) return with DbTxn('', self.db) as trans: if not self.obj.get_handle(): self.db.add_citation(self.obj, trans) msg = _("Add Citation (%s)") % self.obj.get_page() else: if not self.obj.get_gramps_id(): self.obj.set_gramps_id( self.db.find_next_citation_gramps_id()) self.db.commit_citation(self.obj, trans) msg = _("Edit Citation (%s)") % self.obj.get_page() trans.set_description(msg) if self.callback: self.callback(self.obj.get_handle()) self.close()
def __startgramps(errors, argparser): """ Main startup function started via gobject.timeout_add First action inside the gtk loop """ from QuestionDialog import ErrorDialog #handle first existing errors in GUI fashion if errors: ErrorDialog(errors[0], errors[1]) gtk.main_quit() sys.exit(1) if argparser.errors: ErrorDialog(argparser.errors[0], argparser.errors[1]) gtk.main_quit() sys.exit(1) # add gui logger from GrampsLogger import RotateHandler, GtkHandler form = logging.Formatter(fmt="%(relativeCreated)d: %(levelname)s: " "%(filename)s: line %(lineno)d: %(message)s") # Create the log handlers rh = RotateHandler(capacity=20) rh.setFormatter(form) # Only error and critical log records should # trigger the GUI handler. gtkh = GtkHandler(rotate_handler=rh) gtkh.setFormatter(form) gtkh.setLevel(logging.ERROR) l = logging.getLogger() l.addHandler(rh) l.addHandler(gtkh) # start GRAMPS, errors stop the gtk loop try: quit_now = False exit_code = 0 if constfunc.has_display(): Gramps(argparser) else: print("Gramps terminated because of no DISPLAY") sys.exit(exit_code) except SystemExit, e: quit_now = True if e.code: exit_code = e.code LOG.error("Gramps terminated with exit code: %d." \ % e.code, exc_info=True)
def cb_merge(self, obj): """ Perform the merge of the families when the merge button is clicked. """ self.uistate.set_busy_cursor(True) use_handle1 = self.get_widget("handle_btn1").get_active() if use_handle1: phoenix = self.fy1 titanic = self.fy2 else: phoenix = self.fy2 titanic = self.fy1 # Add second handle to history so that when merge is complete, # phoenix is the selected row. self.uistate.set_active(phoenix.get_handle(), 'Family') phoenix_fh = phoenix.get_father_handle() phoenix_mh = phoenix.get_mother_handle() if self.get_widget("father_btn1").get_active() ^ use_handle1: phoenix_fh = titanic.get_father_handle() if self.get_widget("mother_btn1").get_active() ^ use_handle1: phoenix_mh = titanic.get_mother_handle() if self.get_widget("rel_btn1").get_active() ^ use_handle1: phoenix.set_relationship(titanic.get_relationship()) if self.get_widget("gramps_btn1").get_active() ^ use_handle1: phoenix.set_gramps_id(titanic.get_gramps_id()) try: query = MergeFamilyQuery(self.database, phoenix, titanic, phoenix_fh, phoenix_mh) query.execute() except MergeError, err: ErrorDialog( _("Cannot merge people"), str(err))
def cb_merge(self, obj): """ Perform the merge of the persons when the merge button is clicked. """ self.uistate.set_busy_cursor(True) use_handle1 = self.get_widget("handle_btn1").get_active() if use_handle1: phoenix = self.pr1 titanic = self.pr2 else: phoenix = self.pr2 titanic = self.pr1 # Add second handle to history so that when merge is complete, # phoenix is the selected row. self.uistate.set_active(phoenix.get_handle(), 'Person') if self.get_widget("name_btn1").get_active() ^ use_handle1: swapname = phoenix.get_primary_name() phoenix.set_primary_name(titanic.get_primary_name()) titanic.set_primary_name(swapname) if self.get_widget("gender_btn1").get_active() ^ use_handle1: phoenix.set_gender(titanic.get_gender()) if self.get_widget("gramps_btn1").get_active() ^ use_handle1: swapid = phoenix.get_gramps_id() phoenix.set_gramps_id(titanic.get_gramps_id()) titanic.set_gramps_id(swapid) try: query = MergePersonQuery(self.database, phoenix, titanic) query.execute() except MergeError, err: ErrorDialog(_("Cannot merge people"), str(err))
def check_out(dbase, rev, path, callback): """ Checks out the revision from rcs, and loads the resulting XML file into the database. """ co_cmd = ["co", "-x,v", "-q%s" % rev ] + [os.path.join(path, ARCHIVE), os.path.join(path, ARCHIVE_V)] proc = subprocess.Popen(co_cmd, stderr=subprocess.PIPE) status = proc.wait() message = "\n".join(proc.stderr.readlines()) proc.stderr.close() del proc if status != 0: ErrorDialog( _("Retrieve failed"), _("An attempt to retrieve the data failed " "with the following message:\n\n%s") % message) return pmgr = GuiPluginManager.get_instance() for plugin in pmgr.get_import_plugins(): if plugin.get_extension() == "gramps": rdr = plugin.get_import_function() xml_file = os.path.join(path, ARCHIVE) rdr(dbase, xml_file, callback) os.unlink(xml_file)
def _update_family_ids(self): # Update each of the families child lists to reflect any # change in ordering due to the new birth date family = self.obj.get_main_parents_family_handle() if (family): f = self.db.get_family_from_handle(family) new_order = self.reorder_child_ref_list(self.obj, f.get_child_ref_list()) f.set_child_ref_list(new_order) for family in self.obj.get_parent_family_handle_list(): f = self.db.get_family_from_handle(family) new_order = self.reorder_child_ref_list(self.obj, f.get_child_ref_list()) f.set_child_ref_list(new_order) error = False original = self.db.get_person_from_handle(self.obj.handle) if original: (female, male, unknown) = _select_gender[self.obj.get_gender()] if male and original.get_gender() != gen.lib.Person.MALE: for tmp_handle in self.obj.get_family_handle_list(): temp_family = self.db.get_family_from_handle(tmp_handle) if self.obj == temp_family.get_mother_handle(): if temp_family.get_father_handle() is not None: error = True else: temp_family.set_mother_handle(None) temp_family.set_father_handle(self.obj) elif female and original != gen.lib.Person.FEMALE: for tmp_handle in self.obj.get_family_handle_list(): temp_family = self.db.get_family_from_handle(tmp_handle) if self.obj == temp_family.get_father_handle(): if temp_family.get_mother_handle() is not None: error = True else: temp_family.set_father_handle(None) temp_family.set_mother_handle(self.obj) elif unknown and original.get_gender() != gen.lib.Person.UNKNOWN: for tmp_handle in self.obj.get_family_handle_list(): temp_family = self.db.get_family_from_handle(tmp_handle) if self.obj == temp_family.get_father_handle(): if temp_family.get_mother_handle() is not None: error = True else: temp_family.set_father_handle(None) temp_family.set_mother_handle(self.obj) if self.obj == temp_family.get_mother_handle(): if temp_family.get_father_handle() is not None: error = True else: temp_family.set_mother_handle(None) temp_family.set_father_handle(self.obj) if error: msg2 = _("Problem changing the gender") msg = _("Changing the gender caused problems " "with marriage information.\nPlease check " "the person's marriages.") ErrorDialog(msg2, msg)
def importData(database, filename, callback=None, cl=0): if cl: LOG.warn("Error: %s could not be opened.\n%s Exiting." \ % (filename, _("The database version is not supported " "by this version of Gramps.\n"\ "Please upgrade to the corresponding version " "or use XML for porting data between different " "database versions."))) else: ErrorDialog(_("%s could not be opened") % filename, _("The Database version is not supported " "by this version of Gramps." "You should use an old copy of Gramps at " "version 3.0.x and import your database into " "that version. You should then export a copy " "of your data to Gramps XML (family tree). " "Then you should upgrade to the latest " "version of Gramps (for example this version), " "create a new empty database and import the " "Gramps XML into that version. " "Please refer to:" "http://www.gramps-project.org/wiki/index.php?" "title=Gramps_3.4_Wiki_Manual_-_Manage_Family_Trees#" "Moving_a_Gramps_2.2_databases_to_Gramps_3.x")) return
def __init__(self, uistate): ManagedWindow.ManagedWindow.__init__(self, uistate, [], self) xml = Glade() window = xml.toplevel self.set_window(window, xml.get_object("title"), _("Tip of the Day"), _("Tip of the Day")) self.tip = xml.get_object("tip") self.use = xml.get_object('usetips') self.use.set_active(config.get('behavior.use-tips')) image = xml.get_object('image') image.set_from_file(os.path.join(const.IMAGE_DIR, 'splash.jpg')) next = xml.get_object('next') next.connect("clicked", self.next_tip_cb) close = xml.get_object('close') close.connect("clicked", self.close_cb) try: tparser = TipParser() except (IOError,ExpatError), e: self.close() ErrorDialog( _("Failed to display tip of the day"), _("Unable to read the tips from external file.\n\n%s")%e) return
def merge(self, obj): """ Merge the selected citations. """ mlist = self.selected_handles() if len(mlist) != 2: msg = _("Cannot merge citations.") msg2 = _("Exactly two citations must be selected to perform a " "merge. A second citation can be selected by holding " "down the control key while clicking on the desired " "citation.") ErrorDialog(msg, msg2) else: source1 = self.dbstate.db.get_source_from_handle(mlist[0]) citation1 = self.dbstate.db.get_citation_from_handle(mlist[0]) if (not source1 and not citation1) or (source1 and citation1): raise ValueError("selection must be either source or citation") source2 = self.dbstate.db.get_source_from_handle(mlist[1]) citation2 = self.dbstate.db.get_citation_from_handle(mlist[1]) if (not source2 and not citation2) or (source2 and citation2): raise ValueError("selection must be either source or citation") if citation1 and citation2: if not citation1.get_reference_handle() == \ citation2.get_reference_handle(): msg = _("Cannot merge citations.") msg2 = _("The two selected citations must have the same " "source to perform a merge. If you want to merge " "these two citations, then you must merge the " "sources first.") ErrorDialog(msg, msg2) else: import Merge Merge.MergeCitations(self.dbstate, self.uistate, mlist[0], mlist[1]) elif source1 and source2: import Merge Merge.MergeSources(self.dbstate, self.uistate, mlist[0], mlist[1]) else: msg = _("Cannot perform merge.") msg2 = _("Both objects must be of the same type, either " "both must be sources, or both must be " "citations.") ErrorDialog(msg, msg2)
def write_book_item(database, report_class, options, user): """Write the report using options set. All user dialog has already been handled and the output file opened.""" try: return report_class(database, options, user) except Errors.ReportError, msg: (m1, m2) = msg.messages() ErrorDialog(m1, m2)
def importData(database, filename, cb=None): global callback try: g = GeneWebParser(database, filename) except IOError, msg: ErrorDialog(_("%s could not be opened\n") % filename, str(msg)) return
def read_csv(self, filehandle): "Read the data from the file and return it as a list." reader = UnicodeReader(filehandle) try: data = [[r.strip() for r in row] for row in reader] except csv.Error, err: ErrorDialog(_('format error: line %(line)d: %(zero)s') % { 'line' : reader.reader.line_num, 'zero' : err } ) return None
def importData(database, filename, cb_progress=None): """Function called by Gramps to import data on persons in VCard format.""" parser = VCardParser(database) try: with OpenFileOrStdin(filename) as filehandle: parser.parse(filehandle) except EnvironmentError, msg: ErrorDialog(_("%s could not be opened\n") % filename, str(msg)) return
def importData(dbase, filename, callback=None): """Function called by Gramps to import data on persons in CSV format.""" parser = CSVParser(dbase, callback) try: with OpenFileOrStdin(filename, 'b') as filehandle: parser.parse(filehandle) except EnvironmentError, err: ErrorDialog(_("%s could not be opened\n") % filename, str(err)) return
def on_ok_clicked(self, obj): """Called with the OK button is clicked; Calls the callback task, then saves the stylesheet.""" if self.callback is not None: self.callback() try: self.sheetlist.save() except IOError, msg: from QuestionDialog import ErrorDialog ErrorDialog(_("Error saving stylesheet"), str(msg))
def notify_error(self, title, error=""): """ Notify the user of an error. @param title: the title of the error @type title: str @param error: the error message @type error: str @returns: none """ ErrorDialog(title, error)
def importData(database, filename, callback=None): """ Try to handle ANSEL encoded files that are not really ANSEL encoded """ if DbMixin not in database.__class__.__bases__: database.__class__.__bases__ = (DbMixin,) + \ database.__class__.__bases__ try: ifile = open(filename, "r") except IOError: return ansel = False gramps = False for index in range(50): line = ifile.readline().split() if len(line) == 0: break if len(line) > 2 and line[1][0:4] == 'CHAR' and line[2] == "ANSEL": ansel = True if len(line) > 2 and line[1][0:4] == 'SOUR' and line[2] == "GRAMPS": gramps = True ifile.close() if not gramps and ansel: top = Glade() code = top.get_object('codeset') code.set_active(0) dialog = top.toplevel dialog.run() enc = ['ANSEL', 'ANSEL', 'ANSI', 'ASCII', 'UTF-8'] code_set = enc[code.get_active()] dialog.destroy() else: code_set = "" assert (isinstance(code_set, basestring)) try: ifile = open(filename, "rU") stage_one = libgedcom.GedcomStageOne(ifile) stage_one.parse() if code_set: stage_one.set_encoding(code_set) ifile.seek(0) gedparse = libgedcom.GedcomParser( database, ifile, filename, callback, stage_one, config.get('preferences.default-source')) except IOError, msg: ErrorDialog(_("%s could not be opened\n") % filename, str(msg)) return
def save(self, *obj): self.ok_button.set_sensitive(False) if self.object_is_empty(): ErrorDialog( _("Cannot save source"), _("No data exists for this source. Please " "enter data or cancel the edit.")) self.ok_button.set_sensitive(True) return (uses_dupe_id, id) = self._uses_duplicate_id() if uses_dupe_id: prim_object = self.get_from_gramps_id(id) name = prim_object.get_title() msg1 = _("Cannot save source. ID already exists.") msg2 = _("You have attempted to use the existing Gramps ID with " "value %(id)s. This value is already used by '" "%(prim_object)s'. Please enter a different ID or leave " "blank to get the next available ID value.") % { 'id': id, 'prim_object': name } ErrorDialog(msg1, msg2) self.ok_button.set_sensitive(True) return with DbTxn('', self.db) as trans: if not self.obj.get_handle(): self.db.add_source(self.obj, trans) msg = _("Add Source (%s)") % self.obj.get_title() else: if not self.obj.get_gramps_id(): self.obj.set_gramps_id( self.db.find_next_source_gramps_id()) self.db.commit_source(self.obj, trans) msg = _("Edit Source (%s)") % self.obj.get_title() trans.set_description(msg) self.close() if self.callback: self.callback(self.obj)
def save(self, *obj): """Save the data.""" self.ok_button.set_sensitive(False) self.update_note() if self.object_is_empty(): ErrorDialog(_("Cannot save note"), _("No data exists for this note. Please " "enter data or cancel the edit.")) self.ok_button.set_sensitive(True) return (uses_dupe_id, id) = self._uses_duplicate_id() if uses_dupe_id: msg1 = _("Cannot save note. ID already exists.") msg2 = _("You have attempted to use the existing Gramps ID with " "value %(id)s. This value is already used. Please " "enter a different ID or leave " "blank to get the next available ID value.") % { 'id' : id } ErrorDialog(msg1, msg2) self.ok_button.set_sensitive(True) return with DbTxn('', self.db) as trans: if not self.obj.get_handle(): self.db.add_note(self.obj, trans) msg = _("Add Note") else: if not self.obj.get_gramps_id(): self.obj.set_gramps_id(self.db.find_next_note_gramps_id()) self.db.commit_note(self.obj, trans) msg = _("Edit Note") trans.set_description(msg) if self.callback: self.callback(self.obj.get_handle()) self.close()
def build_widget(self): self.vbox = gtk.VBox(False, 0) cache_path = config.get('geography.path') if not os.path.isdir(cache_path): try: os.makedirs(cache_path, 0755) # create dir like mkdir -p except: ErrorDialog( _("Can't create tiles cache directory %s") % cache_path) return self.vbox self.change_map(None, config.get("geography.map_service")) return self.vbox
def __init__(self, dbstate, options_class, name): if not dbstate.get_active_person(): from QuestionDialog import ErrorDialog ErrorDialog( _('Active person has not been set'), _('You must select an active person for this ' 'tool to work properly.')) self.fail = True return Tool.__init__(self, dbstate, options_class, name) self.fail = False
def change_map(self, obj, map_type): if obj is not None: self.osm.layer_remove_all() self.osm.image_remove_all() self.vbox.remove(self.osm) self.osm.destroy() tiles_path = os.path.join(config.get('geography.path'), constants.tiles_path[map_type]) if not os.path.isdir(tiles_path): try: os.makedirs(tiles_path, 0755) # create dir like mkdir -p except: ErrorDialog( _("Can't create tiles cache directory for '%s'.") % constants.map_title[map_type]) config.set("geography.map_service", map_type) self.current_map = map_type http_proxy = os.environ.get('http_proxy') if 0: self.osm = DummyMapNoGpsPoint() else: if http_proxy: self.osm = osmgpsmap.GpsMap( tile_cache=tiles_path, proxy_uri=http_proxy, map_source=constants.map_type[map_type]) else: self.osm = osmgpsmap.GpsMap( tile_cache=tiles_path, map_source=constants.map_type[map_type]) current_map = osmgpsmap.GpsMapOsd(show_dpad=False, show_zoom=True) self.end_selection = None self.osm.layer_add(current_map) self.osm.layer_add(DummyLayer()) self.selection_layer = self.add_selection_layer() self.cross_map = osmgpsmap.GpsMapOsd(show_crosshair=False) self.set_crosshair(config.get("geography.show_cross")) self.osm.set_center_and_zoom(config.get("geography.center-lat"), config.get("geography.center-lon"), config.get("geography.zoom")) self.osm.connect('button_release_event', self.map_clicked) self.osm.connect('button_press_event', self.map_clicked) self.osm.connect("motion-notify-event", self.motion_event) self.osm.connect('changed', self.zoom_changed) self.osm.show() self.vbox.pack_start(self.osm) if obj is not None: self._createmap(None)
def merge(self, obj): """ Merge the selected places. """ mlist = self.selected_handles() if len(mlist) != 2: msg = _("Cannot merge places.") msg2 = _("Exactly two places must be selected to perform a merge. " "A second place can be selected by holding down the " "control key while clicking on the desired place.") ErrorDialog(msg, msg2) else: import Merge Merge.MergePlaces(self.dbstate, self.uistate, mlist[0], mlist[1])
def __init__(self, dbstate, uistate, options_class, name): if not uistate.get_active('Person'): # TODO: should we replace this with a callback? from QuestionDialog import ErrorDialog ErrorDialog( _('Active person has not been set'), _('You must select an active person for this ' 'tool to work properly.')) self.fail = True return Tool.__init__(self, dbstate, options_class, name) self.fail = False
def merge(self, obj): """ Merge the selected people. """ mlist = self.selected_handles() if len(mlist) != 2: ErrorDialog( _("Cannot merge people"), _("Exactly two people must be selected to perform a merge. " "A second person can be selected by holding down the " "control key while clicking on the desired person.")) else: import Merge Merge.MergePeople(self.dbstate, self.uistate, mlist[0], mlist[1])
def save(self, *obj): """ Called when the OK button is pressed. Gets data from the form and updates the Attribute data structure. """ t = self.obj.get_type() if t.is_custom() and str(t) == '': from QuestionDialog import ErrorDialog ErrorDialog(_("Cannot save attribute"), _("The attribute type cannot be empty")) return if self.callback: self.callback(self.obj) self.close()