New export assistant
svn: r8857
This commit is contained in:
parent
980f5a3a51
commit
167e4288a1
@ -1,3 +1,11 @@
|
||||
2007-08-23 Benny Malengier <bm@cage.ugent.be>
|
||||
* src/ExportAssistant.py: finished, new export assistant
|
||||
* src/ViewManager.py : delete temporary hook new export assistant
|
||||
* src/Makefile.am : delete Exporter.py
|
||||
* po/POTFILES.in : delete Exporter.py
|
||||
* src/Assistant.py : comment to use gtk.Assistant - deprecated
|
||||
* src/Exporter.py : delete
|
||||
|
||||
2007-08-23 Stephane Charette <stephanecharette@gmail.com>
|
||||
* src/plugins/NarrativeWeb.py: moved the source reference code from
|
||||
IndividualPage into BasePage so it can also be used by media objects
|
||||
|
@ -20,7 +20,6 @@ src/DdTargets.py
|
||||
src/DisplayState.py
|
||||
src/Errors.py
|
||||
src/ExportAssistant.py
|
||||
src/Exporter.py
|
||||
src/FontScale.py
|
||||
src/GrampsCfg.py
|
||||
src/GrampsDisplay.py
|
||||
|
@ -67,7 +67,10 @@ _format = '<span weight="bold" size="xx-large">%s</span>'
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class Assistant(gtk.Object,ManagedWindow.ManagedWindow):
|
||||
""" A tabbed dialog box used to implement Assistant interfaces."""
|
||||
""" A tabbed dialog box used to implement Assistant interfaces.
|
||||
Deprecated. Please use gtk.Assistant class from now on.
|
||||
See eg. example use in ExportAssistant
|
||||
"""
|
||||
|
||||
__gproperties__ = {}
|
||||
|
||||
|
@ -103,7 +103,6 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) :
|
||||
|
||||
#set up Assisant
|
||||
gtk.Assistant.__init__(self)
|
||||
self.set_title('test')
|
||||
|
||||
#set up ManagedWindow
|
||||
self.top_title = _("Export Assistant")
|
||||
@ -135,6 +134,9 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) :
|
||||
#no progress page, use uistate progressbar
|
||||
self.create_page_summary()
|
||||
|
||||
#we need our own forward function as options page must not always be shown
|
||||
self.set_forward_page_func(self.forward_func, None)
|
||||
|
||||
#ManagedWindow show method
|
||||
ManagedWindow.ManagedWindow.show(self)
|
||||
|
||||
@ -198,11 +200,8 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) :
|
||||
self.append_page(page)
|
||||
self.set_page_header_image(page, self.logo)
|
||||
self.set_page_title(page, _('Choose the format you want to export to'))
|
||||
#there is always one radio button selected
|
||||
self.set_page_complete(page, True)
|
||||
|
||||
self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT)
|
||||
#After selecting an export type, we need to decide to show options or not
|
||||
self.set_forward_page_func(self.forward_func, None)
|
||||
|
||||
|
||||
def create_page_options(self):
|
||||
@ -261,9 +260,8 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) :
|
||||
self.chooser.set_local_only(False)
|
||||
self.chooser.set_do_overwrite_confirmation(True)
|
||||
|
||||
folder, name = self.suggest_filename()
|
||||
self.chooser.set_current_folder(folder)
|
||||
self.chooser.set_current_name(name)
|
||||
#created, folder and name not set
|
||||
self.folder_is_set = False
|
||||
|
||||
#connect changes in filechooser with check to mark page complete
|
||||
self.chooser.connect("selection-changed", self.check_fileselect)
|
||||
@ -274,7 +272,6 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) :
|
||||
# do not release button, click forward. We expect user not to do this
|
||||
# In case he does, recheck on confirmation page!
|
||||
|
||||
|
||||
self.chooser.show_all()
|
||||
page = self.chooser
|
||||
|
||||
@ -360,38 +357,55 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) :
|
||||
else :
|
||||
back = False
|
||||
|
||||
#remember previous page for next time
|
||||
self.__previous_page = page_number
|
||||
|
||||
if back :
|
||||
#when moving backward, show page as it was
|
||||
pass
|
||||
#when moving backward, show page as it was,
|
||||
#page we come from is set incomplete so as to disallow user jumping
|
||||
# to last page after backward move
|
||||
self.set_page_complete(self.get_nth_page(self.__previous_page),
|
||||
False)
|
||||
|
||||
elif page_number == _ExportAssistant_pages['options'] :
|
||||
self.create_options()
|
||||
self.set_page_complete(page, True)
|
||||
elif page == self.chooser :
|
||||
# next page is the file chooser, reset filename, keep folder where user was
|
||||
folder, name = self.suggest_filename()
|
||||
page.set_current_name(name)
|
||||
if self.folder_is_set :
|
||||
page.set_current_name(name)
|
||||
else :
|
||||
page.set_current_name(name)
|
||||
page.set_current_folder(folder)
|
||||
self.folder_is_set = True
|
||||
# see if page is complete with above
|
||||
self.check_fileselect(page)
|
||||
|
||||
elif self.get_page_type(page) == gtk.ASSISTANT_PAGE_CONFIRM :
|
||||
# The confirm page with apply button
|
||||
# Present user with what will happen
|
||||
filename = unicode(self.chooser.get_filename(),
|
||||
sys.getfilesystemencoding())
|
||||
name = os.path.split(filename)[1]
|
||||
folder = os.path.split(filename)[0]
|
||||
ix = self.get_selected_format_index()
|
||||
format = self.exportformats[ix][1].replace('_','')
|
||||
|
||||
confirm_text = _(
|
||||
#Allow for exotic error: file is still not correct
|
||||
self.check_fileselect(self.chooser)
|
||||
if self.get_page_complete(self.chooser) :
|
||||
filename = unicode(self.chooser.get_filename(),
|
||||
sys.getfilesystemencoding())
|
||||
name = os.path.split(filename)[1]
|
||||
folder = os.path.split(filename)[0]
|
||||
confirm_text = _(
|
||||
'The data will be saved as follows:\n\n'
|
||||
'Format:\t%s\nName:\t%s\nFolder:\t%s\n\n'
|
||||
'Press Apply to proceed, Back to revisit '
|
||||
'your options, or Cancel to abort') % (format, name, folder)
|
||||
|
||||
self.set_page_complete(page, True)
|
||||
else :
|
||||
confirm_text = _(
|
||||
'The selected file and folder to save to '
|
||||
'cannot be created or found.\n\n'
|
||||
'Press Back to return and select a valid filename.'
|
||||
)
|
||||
self.set_page_complete(page, False)
|
||||
|
||||
page.set_label(confirm_text)
|
||||
|
||||
elif self.get_page_type(page) == gtk.ASSISTANT_PAGE_SUMMARY :
|
||||
@ -428,6 +442,13 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) :
|
||||
'a copy of your data that failed to save.')
|
||||
page.set_label(conclusion_text)
|
||||
self.set_page_title(page, conclusion_title)
|
||||
self.set_page_complete(page, True)
|
||||
else :
|
||||
#whatever other page, if we show it, it is complete to
|
||||
self.set_page_complete(page, True)
|
||||
|
||||
#remember previous page for next time
|
||||
self.__previous_page = page_number
|
||||
|
||||
def close(self, *obj) :
|
||||
#clean up ManagedWindow menu, then destroy window, bring forward parent
|
||||
|
394
src/Exporter.py
394
src/Exporter.py
@ -1,394 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2004-2006 Donald N. Allingham
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
# $Id$
|
||||
|
||||
# Written by Alex Roitman
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
import sys
|
||||
from gettext import gettext as _
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# set up logging
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
log = logging.getLogger(".Exporter")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gnome modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import const
|
||||
import Utils
|
||||
from PluginUtils import export_list
|
||||
import QuestionDialog
|
||||
import Config
|
||||
import GrampsDisplay
|
||||
import Assistant
|
||||
import Errors
|
||||
from GrampsDbUtils import gramps_db_writer_factory
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Exporter
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class Exporter:
|
||||
"""
|
||||
This class creates Gnome Druid to guide the user through the various
|
||||
Save as/Export options. The overall goal is to keep things simple by
|
||||
presenting few choice options on each druid page.
|
||||
|
||||
The export formats and options are obtained from the plugins, with the
|
||||
exception of a native save. Native save as just copies file to another
|
||||
name.
|
||||
"""
|
||||
|
||||
def __init__(self,dbstate,uistate):
|
||||
"""
|
||||
Set up the window, the druid, and build all the druid's pages.
|
||||
Some page elements are left empty, since their contents depends
|
||||
on the user choices and on the success of the attempted save.
|
||||
"""
|
||||
self.dbstate = dbstate
|
||||
self.uistate = uistate
|
||||
self.callback = self.uistate.pulse_progressbar
|
||||
if self.dbstate.active:
|
||||
self.person = self.dbstate.get_active_person()
|
||||
else:
|
||||
self.person = self.dbstate.db.find_initial_person()
|
||||
|
||||
self.build_exports()
|
||||
self.format_option = None
|
||||
|
||||
try:
|
||||
self.w = Assistant.Assistant(uistate,self.__class__,self.complete,
|
||||
_("Export Assistant"))
|
||||
except Errors.WindowActiveError:
|
||||
return
|
||||
|
||||
self.w.add_text_page(_('Saving your data'),self.get_intro_text())
|
||||
|
||||
self.format_page = self.w.add_page(_('Choosing the format to save'),
|
||||
self.build_format_page())
|
||||
|
||||
self.file_sel_page = self.w.add_page(_('Selecting the file name'),
|
||||
self.build_file_sel_page())
|
||||
|
||||
self.confirm_page = self.w.add_text_page('','')
|
||||
self.conclusion_page = self.w.add_text_page('','')
|
||||
|
||||
self.w.connect('before-page-next',self.on_before_page_next)
|
||||
self.w.connect('after-page-next' ,self.on_after_page_next)
|
||||
|
||||
self.w.show()
|
||||
|
||||
def complete(self):
|
||||
pass
|
||||
|
||||
def on_before_page_next(self,obj,page,data=None):
|
||||
if page == self.format_page:
|
||||
self.build_options()
|
||||
self.suggest_filename()
|
||||
elif page == self.file_sel_page:
|
||||
#we might have a bad file if user deleted without selecting the file
|
||||
if self.check_valid_file(self.chooser) :
|
||||
self.build_confirmation(True)
|
||||
else :
|
||||
#we need to prevent going to next page
|
||||
QuestionDialog.ErrorDialog(_('No valid filename given'))
|
||||
self.build_confirmation(False)
|
||||
|
||||
elif page == self.confirm_page:
|
||||
success = self.save()
|
||||
self.build_conclusion(success)
|
||||
|
||||
def on_after_page_next(self,obj,page,data=None):
|
||||
if page == self.confirm_page :
|
||||
#we have a file from the file chooser, make sure it is usable
|
||||
self.check_valid_file(self.chooser)
|
||||
|
||||
def help(self,obj):
|
||||
"""
|
||||
Help handler.
|
||||
"""
|
||||
GrampsDisplay.help('export-data')
|
||||
|
||||
def get_intro_text(self):
|
||||
return _('Under normal circumstances, GRAMPS does not require you '
|
||||
'to directly save your changes. All changes you make are '
|
||||
'immediately saved to the database.\n\n'
|
||||
'This process will help you save a copy of your data '
|
||||
'in any of the several formats supported by GRAMPS. '
|
||||
'This can be used to make a copy of your data, backup '
|
||||
'your data, or convert it to a format that will allow '
|
||||
'you to transfer it to a different program.\n\n'
|
||||
'If you change your mind during this process, you '
|
||||
'can safely press the Cancel button at any time and your '
|
||||
'present database will still be intact.')
|
||||
|
||||
def build_confirmation(self, valid):
|
||||
"""
|
||||
Build the text of the confirmation label. This should query
|
||||
the selected options (format, filename) and present the summary
|
||||
of the proposed action.
|
||||
"""
|
||||
if valid :
|
||||
filename = unicode(self.chooser.get_filename(),
|
||||
sys.getfilesystemencoding())
|
||||
name = os.path.split(filename)[1]
|
||||
folder = os.path.split(filename)[0]
|
||||
ix = self.get_selected_format_index()
|
||||
format = self.exports[ix][1].replace('_','')
|
||||
|
||||
confirm_text = _(
|
||||
'The data will be saved as follows:\n\n'
|
||||
'Format:\t%s\nName:\t%s\nFolder:\t%s\n\n'
|
||||
'Press OK to proceed, Cancel to abort, or Back to '
|
||||
'revisit your options.') % (format, name, folder)
|
||||
else :
|
||||
confirm_text = _(
|
||||
'Return and select valid file' )
|
||||
self.w.remove_page(self.confirm_page)
|
||||
self.confirm_page = self.w.insert_text_page(_('Final confirmation'),
|
||||
confirm_text,
|
||||
self.confirm_page)
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Perform the actual Save As/Export operation.
|
||||
Depending on the success status, set the text for the final page.
|
||||
"""
|
||||
filename = unicode(self.chooser.get_filename(),
|
||||
sys.getfilesystemencoding())
|
||||
Config.set(Config.RECENT_EXPORT_DIR,os.path.split(filename)[0])
|
||||
ix = self.get_selected_format_index()
|
||||
self.pre_save()
|
||||
if self.exports[ix][3]:
|
||||
success = self.exports[ix][0](self.dbstate.db,
|
||||
filename,self.person,
|
||||
self.option_box_instance,
|
||||
self.callback)
|
||||
else:
|
||||
success = self.exports[ix][0](self.dbstate.db,
|
||||
filename,self.person,
|
||||
self.callback)
|
||||
self.post_save()
|
||||
return success
|
||||
|
||||
def pre_save(self):
|
||||
self.uistate.set_busy_cursor(1)
|
||||
self.w.set_busy_cursor(1)
|
||||
self.uistate.progress.show()
|
||||
|
||||
def post_save(self):
|
||||
self.uistate.set_busy_cursor(0)
|
||||
self.w.set_busy_cursor(0)
|
||||
self.uistate.progress.hide()
|
||||
|
||||
def build_conclusion(self,success):
|
||||
if success:
|
||||
conclusion_title = _('Your data has been saved')
|
||||
conclusion_text = _(
|
||||
'The copy of your data has been '
|
||||
'successfully saved. You may press OK button '
|
||||
'now to continue.\n\n'
|
||||
'Note: the database currently opened in your GRAMPS '
|
||||
'window is NOT the file you have just saved. '
|
||||
'Future editing of the currently opened database will '
|
||||
'not alter the copy you have just made. ')
|
||||
else:
|
||||
conclusion_title = _('Saving failed'),
|
||||
conclusion_text = _(
|
||||
'There was an error while saving your data. '
|
||||
'You may try starting the export again.\n\n'
|
||||
'Note: your currently opened database is safe. '
|
||||
'It was only '
|
||||
'a copy of your data that failed to save.')
|
||||
self.w.remove_page(self.conclusion_page)
|
||||
self.conclusion_page = self.w.insert_text_page(conclusion_title,
|
||||
conclusion_text,
|
||||
self.conclusion_page)
|
||||
|
||||
def build_format_page(self):
|
||||
"""
|
||||
Build a page with the table of format radio buttons and
|
||||
their descriptions.
|
||||
"""
|
||||
self.format_buttons = []
|
||||
|
||||
box = gtk.VBox()
|
||||
box.set_spacing(12)
|
||||
|
||||
table = gtk.Table(2*len(self.exports),2)
|
||||
table.set_row_spacings(6)
|
||||
table.set_col_spacings(6)
|
||||
|
||||
tip = gtk.Tooltips()
|
||||
|
||||
group = None
|
||||
for ix in range(len(self.exports)):
|
||||
title = self.exports[ix][1]
|
||||
description= self.exports[ix][2]
|
||||
|
||||
button = gtk.RadioButton(group,title)
|
||||
if not group:
|
||||
group = button
|
||||
self.format_buttons.append(button)
|
||||
table.attach(button,0,2,2*ix,2*ix+1)
|
||||
tip.set_tip(button,description)
|
||||
|
||||
box.add(table)
|
||||
return box
|
||||
|
||||
def build_options(self):
|
||||
"""
|
||||
Build an extra page with the options specific for the chosen format.
|
||||
If there's already an entry for this format in self.extra_pages then
|
||||
do nothing, otherwise add a page.
|
||||
|
||||
If the chosen format does not have options then remove all
|
||||
extra pages that are already there (from previous user passes
|
||||
through the assistant).
|
||||
"""
|
||||
ix = self.get_selected_format_index()
|
||||
if self.exports[ix][3]:
|
||||
if ix == self.format_option:
|
||||
return
|
||||
elif self.format_option:
|
||||
self.w.remove_page(self.option_page)
|
||||
self.format_option = None
|
||||
title = self.exports[ix][3][0]
|
||||
option_box_class = self.exports[ix][3][1]
|
||||
self.option_box_instance = option_box_class(self.person)
|
||||
box = self.option_box_instance.get_option_box()
|
||||
self.option_page = self.w.insert_page(title,box,
|
||||
self.format_page+1)
|
||||
self.file_sel_page += 1
|
||||
self.confirm_page += 1
|
||||
self.conclusion_page += 1
|
||||
self.format_option = ix
|
||||
box.show_all()
|
||||
elif self.format_option:
|
||||
self.w.remove_page(self.option_page)
|
||||
self.format_option = None
|
||||
|
||||
def build_file_sel_page(self):
|
||||
"""
|
||||
Build a druid page embedding the FileChooserWidget.
|
||||
"""
|
||||
|
||||
self.chooser = gtk.FileChooserWidget(gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
self.chooser.set_local_only(False)
|
||||
|
||||
box = gtk.VBox()
|
||||
box.set_spacing(12)
|
||||
box.add(self.chooser)
|
||||
|
||||
# Dirty hack to enable proper EXPAND/FILL properties of the chooser
|
||||
box.set_child_packing(self.chooser,1,1,0,gtk.PACK_START)
|
||||
|
||||
return box
|
||||
|
||||
def suggest_filename(self):
|
||||
"""
|
||||
Prepare suggested filename and set it in the file chooser.
|
||||
"""
|
||||
ix = self.get_selected_format_index()
|
||||
ext = self.exports[ix][4]
|
||||
|
||||
# Suggested folder: try last export, then last import, then home.
|
||||
default_dir = Config.get(Config.RECENT_EXPORT_DIR)
|
||||
if len(default_dir)<=1:
|
||||
default_dir = Config.get(Config.RECENT_IMPORT_DIR)
|
||||
if len(default_dir)<=1:
|
||||
default_dir = const.user_home
|
||||
|
||||
if ext == 'gramps':
|
||||
new_filename = os.path.join(default_dir,'data.gramps')
|
||||
elif ext == 'burn':
|
||||
new_filename = os.path.basename(self.dbstate.db.get_save_path())
|
||||
else:
|
||||
new_filename = Utils.get_new_filename(ext,default_dir)
|
||||
self.chooser.set_current_folder(default_dir)
|
||||
self.chooser.set_current_name(os.path.split(new_filename)[1])
|
||||
|
||||
def check_valid_file(self, filechooser):
|
||||
filename = filechooser.get_filename()
|
||||
folder = filechooser.get_current_folder()
|
||||
if filename and filename.strip and Utils.find_folder(filename) == '' \
|
||||
and folder and Utils.find_folder(folder):
|
||||
return True
|
||||
else :
|
||||
self.w.ok.set_sensitive(False)
|
||||
return False
|
||||
|
||||
def get_selected_format_index(self):
|
||||
"""
|
||||
Query the format radiobuttons and return the index number
|
||||
of the selected one.
|
||||
"""
|
||||
for ix in range(len(self.format_buttons)):
|
||||
button = self.format_buttons[ix]
|
||||
if button.get_active():
|
||||
return ix
|
||||
else:
|
||||
return 0
|
||||
|
||||
def native_export(self,database,filename,person,callback=None):
|
||||
"""
|
||||
Native database export.
|
||||
|
||||
In the future, filter and other options may be added.
|
||||
"""
|
||||
try:
|
||||
gramps_db_writer_factory(const.app_gramps)(database,
|
||||
filename,
|
||||
person,
|
||||
callback)
|
||||
return 1
|
||||
except IOError, msg:
|
||||
QuestionDialog.ErrorDialog(
|
||||
_("Could not write file: %s") % filename,
|
||||
_('System message was: %s') % msg )
|
||||
return 0
|
||||
|
||||
def build_exports(self):
|
||||
"""
|
||||
This method builds its own list of available exports.
|
||||
The list is built from the PluginMgr.export_list list
|
||||
and from the locally defined exports (i.e. native export defined here).
|
||||
"""
|
||||
self.exports = [item for item in export_list]
|
@ -49,7 +49,6 @@ gdir_PYTHON = \
|
||||
DisplayState.py\
|
||||
Errors.py\
|
||||
ExportAssistant.py\
|
||||
Exporter.py\
|
||||
FontScale.py\
|
||||
GrampsCfg.py\
|
||||
GrampsDisplay.py\
|
||||
|
@ -110,7 +110,6 @@ UIDEFAULT = '''<ui>
|
||||
<separator/>
|
||||
<menuitem action="Import"/>
|
||||
<menuitem action="Export"/>
|
||||
<menuitem action="ExportNew"/>
|
||||
<placeholder name="LocalExport"/>
|
||||
<separator/>
|
||||
<menuitem action="Abandon"/>
|
||||
@ -363,8 +362,6 @@ class ViewManager:
|
||||
None, self.save_as_activate),
|
||||
('Export', 'gramps-export', _('_Export'), "<control>e", None,
|
||||
self.export_data),
|
||||
('ExportNew', 'gramps-export', _('_ExportNew'), None, None,
|
||||
self.exportnew_data),
|
||||
('Abandon', gtk.STOCK_REVERT_TO_SAVED,
|
||||
_('_Abandon changes and quit'), None, None, self.abort),
|
||||
('Reports', 'gramps-reports', _('_Reports'), None,
|
||||
@ -1186,11 +1183,6 @@ class ViewManager:
|
||||
pass
|
||||
|
||||
def export_data(self, obj):
|
||||
if self.state.db.db_is_open:
|
||||
import Exporter
|
||||
Exporter.Exporter(self.state, self.uistate)
|
||||
|
||||
def exportnew_data(self, obj):
|
||||
if self.state.db.db_is_open:
|
||||
import ExportAssistant
|
||||
try:
|
||||
|
Loading…
Reference in New Issue
Block a user