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, parent=self.uistate.window) 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, parent=self.uistate.window) 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, self.uistate) else: print('Failed to load map plugin, see Plugin Manager')
def check_file_path_and_name(self): """Check if file path exists and file name is alphanumeric.""" path = self.opt["path"] name = self.opt["name"] txt = _("Invalid filename.") txt2 = _("Path does not exist.") if os.path.exists(path): if name.isalnum(): self.filename = path + "/" + name + ".html" return True chars = [x for x in name if not x.isalnum()] for char in chars: if char not in ["_", "-", " "]: ErrorDialog(_("INFO"), txt, parent=self.user.uistate.window) return False if name != "" and name[0].isalnum() and name[-1].isalnum(): self.filename = path + "/" + name + ".html" return True ErrorDialog(_("INFO"), txt, parent=self.user.uistate.window) return False ErrorDialog(_("INFO"), txt2, parent=self.user.uistate.window) return False
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, parent=self.uistate.window) 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, parent=self.uistate.window) else: MergeCitation(self.dbstate, self.uistate, [], mlist[0], mlist[1])
def build_widget(self): """ create the vbox """ self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) cache_path = config.get('geography.path') if not os.path.isdir(cache_path): try: os.makedirs(cache_path, 0o755) # create dir like mkdir -p except: ErrorDialog(_("Can't create " "tiles cache directory %s") % cache_path, parent=self.uistate.window) gini = os.path.join(VERSION_DIR, 'gramps.ini') ErrorDialog(_("You must verify and change the tiles cache" "\n..." "\n[geography]" "\n..." "\npath='bad/path'" "\n..." "\nin the gramps.ini file :\n%s" "\n\nBefore to change the gramps.ini file, " "you need to close gramps" "\n\nThe next errors will be normal") % gini, parent=self.uistate.window) return None self.change_map(None, config.get("geography.map_service")) return self.vbox
def get_geo_data(self, geo_url): """ Get GeoNames data from web with error checking """ print(geo_url) try: with urlopen(geo_url, timeout=20) as response: data = response.read() except URLError as err: try: txt = err.read().decode('utf-8') except: txt = '' ErrorDialog(_('Problem getting data from web'), msg2=str(err) + '\n' + txt, parent=self.uistate.window) return None except socket.timeout: ErrorDialog(_('Problem getting data from web'), msg2=_('Web request Timeout, you can try again...'), parent=self.uistate.window) return None dom = parseString(data) status = dom.getElementsByTagName('status') if status: err = status[0].getAttribute("message") ErrorDialog(_('Problem getting data from GeoNames'), msg2=err, parent=self.uistate.window) return None return dom
def ReadXML(self, entry): """ Read the .gramps """ self.text.set_text('Reading the file...') use_gzip = 1 try: test = gzip.open(entry, "r") test.read(1) test.close() except IOError: use_gzip = 0 # lazy ... if os.name != 'posix' and os.name != 'nt': # GtkTextView self.text.set_text(_('Sorry, no support for your OS yet!')) return filename = os.path.join(USER_PLUGINS, 'lxml', 'etree.xml') if use_gzip == 1: try: os.system('gunzip < %s > %s' % (entry, filename)) except: ErrorDialog(_('Is it a compressed .gramps?'), _('Cannot uncompress "%s"') % entry) return sys.stdout.write( _('From:\n "%(file1)s"\n to:\n "%(file2)s".\n') % { 'file1': entry, 'file2': filename }) else: try: copy(entry, filename) except: ErrorDialog('Is it a .gramps ?', _('Cannot copy "%s"') % entry) return sys.stdout.write( _('From:\n "%(file1)s"\n to:\n "%(file2)s".\n') % { 'file1': entry, 'file2': filename }) tree = ElementTree.parse(filename) self.ParseXML(tree, filename)
def export_data(self): """ main export processing """ name_map = {} id_map = {} id_name = {} for key in self.plist: self.update() pnam = self.db.get_person_from_handle(key).get_primary_name() snam = pnam.get_surname() items = pnam.get_first_name().split() nam = ("%s %s" % (items[0], snam)) if items else snam count = -1 if nam in name_map: count = 0 while 1: nam_num = "%s%d" % (nam, count) if nam_num not in name_map: break count += 1 name_map[nam_num] = key id_map[key] = nam_num else: name_map[nam] = key id_map[key] = nam id_name[key] = get_name(pnam, snam, count) try: with open(self.filename, "w", encoding='utf_8') as file: return self._export_data(file, id_name, id_map) except IOError as msg: msg2 = _("Could not create %s") % self.filename ErrorDialog(msg2, str(msg), parent=self.option_box.window) return False
def change_new_map(self, name, map_source): """ Change the current map with a new provider This map is not supported by osm-gps-map name : the name of the provider map_source : the url to search for tiles """ try: self.osm.layer_remove_all() self.osm.image_remove_all() self.vbox.remove(self.osm) self.osm.destroy() except: pass tiles_path = os.path.join(config.get('geography.path'), name) if not os.path.isdir(tiles_path): try: os.makedirs(tiles_path, 0o755) # create dir like mkdir -p except: ErrorDialog(_("Can't create " "tiles cache directory for '%s'.") % constants.MAP_TITLE[self.current_map], parent=self.uistate.window) http_proxy = get_env_var('http_proxy') if 0: self.osm = DummyMapNoGpsPoint() else: if http_proxy: self.osm = osmgpsmap.Map(tile_cache=tiles_path, proxy_uri=http_proxy, repo_uri=map_source) else: self.osm = osmgpsmap.Map(tile_cache=tiles_path, repo_uri=map_source) self.osm.props.tile_cache = osmgpsmap.MAP_CACHE_AUTO current_map = osmgpsmap.MapOsd(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.kml_layer = self.add_kml_layer() self.lifeway_layer = self.add_lifeway_layer() self.marker_layer = self.add_marker_layer() self.date_layer = self.add_date_layer() self.message_layer = self.add_message_layer() self.cross_map = osmgpsmap.MapOsd(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.update_shortcuts(True) self.osm.show() self.vbox.pack_start(self.osm, True, True, 0) self.goto_handle(handle=None)
def change_map(self, obj, map_type): """ Change the current map """ 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, 0o755) # create dir like mkdir -p except: ErrorDialog( _("Can't create " # parent-OK "tiles cache directory for '%s'.") % constants.MAP_TITLE[map_type], parent=self.uistate.window) config.set("geography.map_service", map_type) self.current_map = map_type http_proxy = get_env_var('http_proxy') if 0: self.osm = DummyMapNoGpsPoint() else: if http_proxy: self.osm = osmgpsmap.Map( tile_cache=tiles_path, proxy_uri=http_proxy, map_source=constants.MAP_TYPE[map_type]) else: self.osm = osmgpsmap.Map( tile_cache=tiles_path, map_source=constants.MAP_TYPE[map_type]) self.osm.props.tile_cache = osmgpsmap.MAP_CACHE_AUTO current_map = osmgpsmap.MapOsd(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.kml_layer = self.add_kml_layer() self.lifeway_layer = self.add_lifeway_layer() self.marker_layer = self.add_marker_layer() self.date_layer = self.add_date_layer() self.message_layer = self.add_message_layer() self.cross_map = osmgpsmap.MapOsd(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.update_shortcuts(True) self.osm.show() self.vbox.pack_start(self.osm, True, True, 0) self.goto_handle(handle=None)
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, parent=self.uistate.window) # parent-OK 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, # parent-OK parent=self.uistate.window) else: MergeCitation(self.dbstate, self.uistate, mlist[0], mlist[1]) elif source1 and source2: MergeSource(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, parent=self.uistate.window) # parent-OK
def openSQL(self): sql = None try: sql = Database(self.filename) except IOError as msg: errmsg = _("%s could not be opened\n") % self.filename ErrorDialog(errmsg, msg) return None return sql
def run(self, obj): """ Method that is run when you click the Run button. The date is retrieved from the entry box, parsed as a date, and then handed to the quick report. """ user = User(callback=self.myprogress) message = _("Import done") buffer = self.import_text.get_buffer() start = buffer.get_start_iter() end = buffer.get_end_iter() text = buffer.get_text(start, end, True).strip() if not text: return print(text) self.uistate.set_busy_cursor(1) self.uistate.progress.show() self.uistate.push_message(self.dbstate, _("Importing Text...")) database = self.dbstate.db if text.startswith("0 HEAD"): raise NotImplementedError elif text.startswith("BEGIN:VCARD"): parser = AtomicVCardParser(database) ifile = StringIO(text) parser.parse(ifile) elif text.find("""<!DOCTYPE database PUBLIC "-//Gramps//""") > 0: ifile = BytesIO(text.encode('utf-8')) ofile = StringIO(text) person_count = 0 line_count = 0 for line in ofile: line_count += 1 if PERSON_RE.match(line): person_count += 1 change = int(time.time()) parser = AtomicGrampsParser(database, user, change) try: info = parser.parse(ifile, line_count, person_count) print(info.info_text()) except: import traceback traceback.print_exc() else: try: parser = AtomicCSVParser(database) ifile = StringIO(text) parser.parse(ifile) except: ErrorDialog(_("Can't determine type of import")) message = _("Import failed") # FIXME: allow file:/// imports as well self.uistate.set_busy_cursor(0) self.uistate.progress.hide() self.uistate.push_message(self.dbstate, message)
def doit(self, dialog, uistate): from gi.repository import Gtk response = dialog.window.run() if response == Gtk.ResponseType.OK: dialog.close() try: from gramps.gui.user import User user = User(uistate=uistate) my_report = TAMexportReport(dialog.db, dialog.options, user) my_report.doc.init() my_report.begin_report() my_report.write_report() my_report.end_report() except FilterError as msg: from gramps.gui.dialog import ErrorDialog (msg1, msg2) = msg.messages() ErrorDialog(msg1, msg2, parent=uistate.window) except IOError as msg: from gramps.gui.dialog import ErrorDialog ErrorDialog(_("Report could not be created"), str(msg), parent=uistate.window) except ReportError as msg: from gramps.gui.dialog import ErrorDialog (msg1, msg2) = msg.messages() ErrorDialog(msg1, msg2, parent=uistate.window) except DatabaseError as msg: from gramps.gui.dialog import ErrorDialog ErrorDialog(_("Report could not be created"), str(msg), parent=uistate.window) raise except: log.error("Failed to run report.", exc_info=True) return True elif response == Gtk.ResponseType.CANCEL: dialog.close() return True elif response == Gtk.ResponseType.DELETE_EVENT: #just stop, in ManagedWindow, delete-event is already coupled to #correct action. return True
def run(self, obj): """ Method that is run when you click the Run button. """ entry = self.entry.get_text() if ' ' in entry: ErrorDialog(_('Space character on filename'), _('Please fix space on "%s"') % entry) return self.ReadXML(entry)
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, parent=self.uistate.window) else: source1, citation1 = self.get_source_or_citation(mlist[0]) source2, citation2 = self.get_source_or_citation(mlist[1]) 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, parent=self.uistate.window) else: MergeCitation(self.dbstate, self.uistate, [], mlist[0], mlist[1]) elif source1 and source2: MergeSource(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, parent=self.uistate.window) self.object_build()
def openSQL(self): try: from gramps.gui.dialog import ErrorDialog except: ErrorDialog = print sql = None try: sql = Database(self.filename) except IOError as msg: errmsg = _("%s could not be opened\n") % self.filename ErrorDialog(errmsg, msg) return None return sql
def merge(self, obj): """ Merge the selected sources. """ mlist = self.selected_handles() if len(mlist) != 2: msg = _("Cannot merge sources.") msg2 = _("Exactly two sources must be selected to perform a merge. " "A second source can be selected by holding down the " "control key while clicking on the desired source.") ErrorDialog(msg, msg2, parent=self.uistate.window) else: MergeSource(self.dbstate, self.uistate, [], mlist[0], mlist[1])
def merge(self, *obj): """ Merge the selected objects. """ mlist = self.selected_handles() if len(mlist) != 2: msg = _("Cannot merge media objects.") msg2 = _("Exactly two media objects must be selected to perform a " "merge. A second object can be selected by holding down the " "control key while clicking on the desired object.") ErrorDialog(msg, msg2, parent=self.uistate.window) else: MergeMedia(self.dbstate, self.uistate, [], mlist[0], mlist[1])
def merge(self, obj): """ Merge the selected events. """ mlist = self.selected_handles() if len(mlist) != 2: msg = _("Cannot merge event objects.") msg2 = _("Exactly two events must be selected to perform a merge. " "A second object can be selected by holding down the " "control key while clicking on the desired event.") ErrorDialog(msg, msg2) else: MergeEvent(self.dbstate, self.uistate, mlist[0], mlist[1])
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: MergePerson(self.dbstate, self.uistate, mlist[0], mlist[1])
def merge(self, *obj): """ Merge the selected repositories. """ mlist = self.selected_handles() if len(mlist) != 2: msg = _("Cannot merge repositories.") msg2 = _("Exactly two repositories must be selected to perform a " "merge. A second repository can be selected by holding " "down the control key while clicking on the desired " "repository.") ErrorDialog(msg, msg2, parent=self.uistate.window) else: MergeRepository(self.dbstate, self.uistate, [], mlist[0], mlist[1])
def build_widget(self): """ create the vbox """ self.vbox = Gtk.VBox(homogeneous=False, spacing=0) cache_path = config.get('geography.path') if not os.path.isdir(cache_path): try: os.makedirs(cache_path, 0o755) # 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 build_widget(self): """ create the vbox """ self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) cache_path = config.get('geography.path') if not os.path.isdir(cache_path): try: os.makedirs(cache_path, 0o755) # 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 parse(self, ifile, linecount=0, personcount=0): """ Parse the xml file :param ifile: must be a file handle that is already open, with position at the start of the file """ with DbTxn(_("Gramps XML import"), self.db, batch=False) as self.trans: self.set_total(linecount) self.p = ParserCreate() self.p.StartElementHandler = self.startElement self.p.EndElementHandler = self.endElement self.p.CharacterDataHandler = self.characters self.p.ParseFile(ifile) if len(self.name_formats) > 0: # add new name formats to the existing table self.db.name_formats += self.name_formats # Register new formats name_displayer.set_name_format(self.db.name_formats) self.db.set_researcher(self.owner) if self.home is not None: person = self.db.get_person_from_handle(self.home) self.db.set_default_person_handle(person.handle) #set media path, this should really do some parsing to convert eg # windows path to unix ? if self.mediapath: oldpath = self.db.get_mediapath() if not oldpath: self.db.set_mediapath(self.mediapath) elif not oldpath == self.mediapath: ErrorDialog(_("Could not change media path"), _("The opened file has media path %s, which conflicts " "with the media path of the family tree you import " "into. The original media path has been retained. " "Copy the files to a correct directory or change the " "media path in the Preferences." ) % self.mediapath ) for key in self.func_map.keys(): del self.func_map[key] del self.func_map del self.func_list del self.p return self.info
def execute_clicked(self, _widget): class User2: """ Helper class to provide "can_cancel" functionality to the progress indicator used by gramps.gen.filters._genericfilter.GenericFilter.apply(). Replaces the gramps.gui.user.User class for this case. Code copied from gramps/gui/user.py. """ def __init__(self, user): self.parent = user.parent self.uistate = user.uistate self.parent = user.parent def begin_progress(self, title, message, steps): # Parameter "can_cancel" added to ProgressMeter creation. from gramps.gui.utils import ProgressMeter self._progress = ProgressMeter(title, parent=self.parent, can_cancel=True) if steps > 0: self._progress.set_pass(message, steps, ProgressMeter.MODE_FRACTION) else: self._progress.set_pass(message, mode=ProgressMeter.MODE_ACTIVITY) def step_progress(self): res = self._progress.step() if res: self.end_progress() raise StopIteration def end_progress(self): self._progress.close() self._progress = None user = User2(self.user) # code copied from gramps/gui/editors/filtereditor.py (test_clicked) try: self.update_params() filter = self.getfilter(self.current_category, self.current_filtername) handle_list = filter.apply(self.db, self.get_all_handles(self.current_category), user=user) except StopIteration: return except FilterError as msg: (msg1, msg2) = msg.messages() ErrorDialog(msg1, msg2, parent=self.window) return ShowResults(self.dbstate, self.uistate, self.track, handle_list, self.current_filtername, self.current_category)
def on_select_clicked(self, *dummy): """ If the selected place is mergable, merge it, otherwise Open completion screen """ model, _iter = self.res_selection.get_selected() if not _iter: return (index, ) = model.get(_iter, 0) place = self.places[index] if not isinstance(place, Place): # we have a geoname_id if place[4]: return # check if we might already have it in db t_place = self.dbstate.db.get_place_from_gramps_id(place[0]) if not t_place or t_place.handle == self.place.handle: # need to process the GeoNames ID for result self.gui.get_child().remove(self.mainwin) self.gui.get_child().add(self.results_win) if not self.geoparse(*place): return self.res_gui() return else: # turns out we already have this place, under different name! place = t_place # we have a Gramps Place, need to merge if place.handle == self.place.handle: # found self, nothing to do. return if (located_in(self.dbstate.db, place.handle, self.place.handle) or located_in(self.dbstate.db, self.place.handle, place.handle)): # attempting to create a place loop, not good! ErrorDialog(_('Place cycle detected'), msg2=_("One of the places you are merging encloses " "the other!\n" "Please choose another place."), parent=self.uistate.window) return # lets clean up the place name self.place.name.value = self.place.name.value.split(',')[0].strip() place_merge = MergePlaceQuery(self.dbstate, place, self.place) place_merge.execute() # after merge we should select merged result self.set_active('Place', place.handle)
def write_report(self): """ This method is called from "outside" to trigger the plugins functionality """ # Root person details self.pid_list = { self.root_pid: { "children":[], "level" : 0, "parent" : [], "siblings": [], # not relevant "subtree" : [self.root_pid] } } self.get_person_details(self.root_pid, True) # Collect all of the root persons descendants siblings = self.get_person_list_recursive(self.root_pid, 0) for sibling in siblings: self.pid_list[sibling]["siblings"] = siblings # Calculate x-Coordinate of all persons self.calculate_x() if self.opt: self.optimize() # Optimization of x coordinates # Collect all mother-father pairs self.get_pairs() # HTML output html = self.out_intro() html = self.out_lines_html(html) for pid in self.pid_list: if len(self.pid_list[pid]["parent"]) > 0 or pid == self.root_pid: html = self.out_person_html(pid, html) html = self.out_extro(html) try: with io.open(self.dest_html, 'w', encoding='UTF-8') as file: file.write(html) # Generate HTML File except IOError as msg: ErrorDialog(_("Failed writing " + self.dest_html + ": " + str(msg)), parent=self._user.uistate.window)
def clone(self, obj): """ Clones the selected event. """ event_list = self.selected_handles() if len(event_list) != 1: msg = _("Cannot clone event object.") msg2 = _("Exactly one event must be selected to perform a clone.") ErrorDialog(msg, msg2, parent=self.uistate.window) else: event = Event() event = copy.deepcopy( self.dbstate.db.get_event_from_handle(event_list[0])) event.gramps_id = None try: self.uistate.action = 'event-clone' EditEvent(self.dbstate, self.uistate, [], event) except WindowActiveError: pass