Rework how progress dialog is used
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.7
|
||||
- Python 3.11
|
||||
- Git CLI installed
|
||||
- Python, pip and git are all available as command-line commands (add to the path if needed)
|
||||
|
||||
|
||||
164
gui/mainFrame.py
164
gui/mainFrame.py
@@ -61,10 +61,11 @@ from gui.statsPane import StatsPane
|
||||
from gui.targetProfileEditor import TargetProfileEditor
|
||||
from gui.updateDialog import UpdateDialog
|
||||
from gui.utils.clipboard import fromClipboard
|
||||
from gui.utils.progressHelper import ProgressHelper
|
||||
from service.character import Character
|
||||
from service.esi import Esi
|
||||
from service.fit import Fit
|
||||
from service.port import IPortUser, Port
|
||||
from service.port import Port
|
||||
from service.price import Price
|
||||
from service.settings import HTMLExportSettings, SettingsProvider
|
||||
from service.update import Update
|
||||
@@ -130,7 +131,6 @@ class OpenFitsThread(threading.Thread):
|
||||
self.running = False
|
||||
|
||||
|
||||
# todo: include IPortUser again
|
||||
class MainFrame(wx.Frame):
|
||||
__instance = None
|
||||
|
||||
@@ -845,14 +845,15 @@ class MainFrame(wx.Frame):
|
||||
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
self.progressDialog = wx.ProgressDialog(
|
||||
_t("Importing fits"),
|
||||
" " * 100, # set some arbitrary spacing to create width in window
|
||||
parent=self,
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL
|
||||
)
|
||||
Port.importFitsThreaded(dlg.GetPaths(), self)
|
||||
self.progressDialog.ShowModal()
|
||||
# set some arbitrary spacing to create width in window
|
||||
progress = ProgressHelper(message=" " * 100, callback=self._openAfterImport)
|
||||
call = (Port.importFitsThreaded, [dlg.GetPaths(), progress], {})
|
||||
self.handleProgress(
|
||||
title=_t("Importing fits"),
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
|
||||
call=call,
|
||||
progress=progress,
|
||||
errMsgLbl=_t("Import Error"))
|
||||
|
||||
def backupToXml(self, event):
|
||||
""" Back up all fits to EVE XML file """
|
||||
@@ -863,32 +864,30 @@ class MainFrame(wx.Frame):
|
||||
_t("Save Backup As..."),
|
||||
wildcard=_t("EVE XML fitting file") + " (*.xml)|*.xml",
|
||||
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
|
||||
defaultFile=defaultFile,
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
filePath = dlg.GetPath()
|
||||
defaultFile=defaultFile) as fileDlg:
|
||||
if fileDlg.ShowModal() == wx.ID_OK:
|
||||
filePath = fileDlg.GetPath()
|
||||
if '.' not in os.path.basename(filePath):
|
||||
filePath += ".xml"
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
max_ = sFit.countAllFits()
|
||||
|
||||
self.progressDialog = wx.ProgressDialog(
|
||||
_t("Backup fits"),
|
||||
_t("Backing up {} fits to: {}").format(max_, filePath),
|
||||
maximum=max_,
|
||||
parent=self,
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL
|
||||
)
|
||||
Port.backupFits(filePath, self)
|
||||
self.progressDialog.ShowModal()
|
||||
fitAmount = Fit.getInstance().countAllFits()
|
||||
progress = ProgressHelper(
|
||||
message=_t("Backing up {} fits to: {}").format(fitAmount, filePath),
|
||||
maximum=fitAmount + 1)
|
||||
call = (Port.backupFits, [filePath, progress], {})
|
||||
self.handleProgress(
|
||||
title=_t("Backup fits"),
|
||||
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
|
||||
call=call,
|
||||
progress=progress,
|
||||
errMsgLbl=_t("Export Error"))
|
||||
|
||||
def exportHtml(self, event):
|
||||
from gui.utils.exportHtml import exportHtml
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
settings = HTMLExportSettings.getInstance()
|
||||
|
||||
max_ = sFit.countAllFits()
|
||||
path = settings.getPath()
|
||||
|
||||
if not os.path.isdir(os.path.dirname(path)):
|
||||
@@ -903,82 +902,44 @@ class MainFrame(wx.Frame):
|
||||
) as dlg:
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
return
|
||||
progress = ProgressHelper(
|
||||
message=_t("Generating HTML file at: {}").format(path),
|
||||
maximum=sFit.countAllFits() + 1)
|
||||
call = (exportHtml.getInstance().refreshFittingHtml, [True, progress], {})
|
||||
self.handleProgress(
|
||||
title=_t("Backup fits"),
|
||||
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME,
|
||||
call=call,
|
||||
progress=progress)
|
||||
|
||||
self.progressDialog = wx.ProgressDialog(
|
||||
_t("Backup fits"),
|
||||
_t("Generating HTML file at: {}").format(path),
|
||||
maximum=max_, parent=self,
|
||||
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME)
|
||||
|
||||
exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback)
|
||||
self.progressDialog.ShowModal()
|
||||
|
||||
def backupCallback(self, info):
|
||||
if info == -1:
|
||||
self.closeProgressDialog()
|
||||
else:
|
||||
self.progressDialog.Update(info)
|
||||
|
||||
def on_port_process_start(self):
|
||||
# flag for progress dialog.
|
||||
self.__progress_flag = True
|
||||
|
||||
def on_port_processing(self, action, data=None):
|
||||
# 2017/03/29 NOTE: implementation like interface
|
||||
wx.CallAfter(
|
||||
self._on_port_processing, action, data
|
||||
)
|
||||
|
||||
return self.__progress_flag
|
||||
|
||||
def _on_port_processing(self, action, data):
|
||||
"""
|
||||
While importing fits from file, the logic calls back to this function to
|
||||
update progress bar to show activity. XML files can contain multiple
|
||||
ships with multiple fits, whereas EFT cfg files contain many fits of
|
||||
a single ship. When iterating through the files, we update the message
|
||||
when we start a new file, and then Pulse the progress bar with every fit
|
||||
that is processed.
|
||||
|
||||
action : a flag that lets us know how to deal with :data
|
||||
None: Pulse the progress bar
|
||||
1: Replace message with data
|
||||
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
|
||||
"""
|
||||
_message = None
|
||||
if action & IPortUser.ID_ERROR:
|
||||
self.closeProgressDialog()
|
||||
_message = _t("Import Error") if action & IPortUser.PROCESS_IMPORT else _t("Export Error")
|
||||
def handleProgress(self, title, style, call, progress, errMsgLbl=None):
|
||||
extraArgs = {}
|
||||
if progress.maximum is not None:
|
||||
extraArgs['maximum'] = progress.maximum
|
||||
with wx.ProgressDialog(
|
||||
parent=self,
|
||||
title=title,
|
||||
message=progress.message,
|
||||
style=style,
|
||||
**extraArgs
|
||||
) as dlg:
|
||||
func, args, kwargs = call
|
||||
func(*args, **kwargs)
|
||||
while progress.working:
|
||||
wx.MilliSleep(250)
|
||||
wx.Yield()
|
||||
(progress.dlgWorking, skip) = dlg.Update(progress.current, progress.message)
|
||||
if progress.error and errMsgLbl:
|
||||
with wx.MessageDialog(
|
||||
self,
|
||||
_t("The following error was generated") +
|
||||
f"\n\n{data}\n\n" +
|
||||
f"\n\n{progress.error}\n\n" +
|
||||
_t("Be aware that already processed fits were not saved"),
|
||||
_message, wx.OK | wx.ICON_ERROR
|
||||
errMsgLbl, wx.OK | wx.ICON_ERROR
|
||||
) as dlg:
|
||||
dlg.ShowModal()
|
||||
return
|
||||
|
||||
# data is str
|
||||
if action & IPortUser.PROCESS_IMPORT:
|
||||
if action & IPortUser.ID_PULSE:
|
||||
_message = ()
|
||||
# update message
|
||||
elif action & IPortUser.ID_UPDATE: # and data != self.progressDialog.message:
|
||||
_message = data
|
||||
|
||||
if _message is not None:
|
||||
self.__progress_flag, _unuse = self.progressDialog.Pulse(_message)
|
||||
else:
|
||||
self.closeProgressDialog()
|
||||
if action & IPortUser.ID_DONE:
|
||||
self._openAfterImport(data)
|
||||
# data is tuple(int, str)
|
||||
elif action & IPortUser.PROCESS_EXPORT:
|
||||
if action & IPortUser.ID_DONE:
|
||||
self.closeProgressDialog()
|
||||
else:
|
||||
self.__progress_flag, _unuse = self.progressDialog.Update(data[0], data[1])
|
||||
elif progress.callback:
|
||||
progress.callback(*progress.cbArgs)
|
||||
|
||||
def _openAfterImport(self, fits):
|
||||
if len(fits) > 0:
|
||||
@@ -988,6 +949,8 @@ class MainFrame(wx.Frame):
|
||||
wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True))
|
||||
else:
|
||||
fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name))
|
||||
# Show 100 fits max
|
||||
fits = fits[:100]
|
||||
results = []
|
||||
for fit in fits:
|
||||
results.append((
|
||||
@@ -999,15 +962,6 @@ class MainFrame(wx.Frame):
|
||||
))
|
||||
wx.PostEvent(self.shipBrowser, ImportSelected(fits=results, back=True))
|
||||
|
||||
def closeProgressDialog(self):
|
||||
# Windows apparently handles ProgressDialogs differently. We can
|
||||
# simply Destroy it here, but for other platforms we must Close it
|
||||
if 'wxMSW' in wx.PlatformInfo:
|
||||
self.progressDialog.Destroy()
|
||||
else:
|
||||
self.progressDialog.EndModal(wx.ID_OK)
|
||||
self.progressDialog.Close()
|
||||
|
||||
def importCharacter(self, event):
|
||||
""" Imports character XML file from EVE API """
|
||||
with wx.FileDialog(
|
||||
|
||||
@@ -26,20 +26,20 @@ class exportHtml:
|
||||
def __init__(self):
|
||||
self.thread = exportHtmlThread()
|
||||
|
||||
def refreshFittingHtml(self, force=False, callback=False):
|
||||
def refreshFittingHtml(self, force=False, progress=None):
|
||||
settings = HTMLExportSettings.getInstance()
|
||||
|
||||
if force or settings.getEnabled():
|
||||
self.thread.stop()
|
||||
self.thread = exportHtmlThread(callback)
|
||||
self.thread = exportHtmlThread(progress)
|
||||
self.thread.start()
|
||||
|
||||
|
||||
class exportHtmlThread(threading.Thread):
|
||||
def __init__(self, callback=False):
|
||||
def __init__(self, progress=False):
|
||||
threading.Thread.__init__(self)
|
||||
self.name = "HTMLExport"
|
||||
self.callback = callback
|
||||
self.progress = progress
|
||||
self.stopRunning = False
|
||||
|
||||
def stop(self):
|
||||
@@ -72,11 +72,13 @@ class exportHtmlThread(threading.Thread):
|
||||
pass
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as ex:
|
||||
pass
|
||||
|
||||
if self.callback:
|
||||
wx.CallAfter(self.callback, -1)
|
||||
except Exception as e:
|
||||
if self.progress:
|
||||
self.progress.error = f'{e}'
|
||||
finally:
|
||||
if self.progress:
|
||||
self.progress.current += 1
|
||||
self.progress.workerWorking = False
|
||||
|
||||
def generateFullHTML(self, sMkt, sFit, dnaUrl):
|
||||
""" Generate the complete HTML with styling and javascript """
|
||||
@@ -234,8 +236,8 @@ class exportHtmlThread(threading.Thread):
|
||||
pyfalog.warning("Failed to export line")
|
||||
continue
|
||||
finally:
|
||||
if self.callback:
|
||||
wx.CallAfter(self.callback, count)
|
||||
if self.progress:
|
||||
self.progress.current = count
|
||||
count += 1
|
||||
HTMLgroup += HTMLship + (' </ul>\n'
|
||||
' </li>\n')
|
||||
@@ -291,7 +293,7 @@ class exportHtmlThread(threading.Thread):
|
||||
pyfalog.error("Failed to export line")
|
||||
continue
|
||||
finally:
|
||||
if self.callback:
|
||||
wx.CallAfter(self.callback, count)
|
||||
if self.progress:
|
||||
self.progress.current = count
|
||||
count += 1
|
||||
return HTML
|
||||
|
||||
19
gui/utils/progressHelper.py
Normal file
19
gui/utils/progressHelper.py
Normal file
@@ -0,0 +1,19 @@
|
||||
class ProgressHelper:
|
||||
|
||||
def __init__(self, message, maximum=None, callback=None):
|
||||
self.message = message
|
||||
self.current = 0
|
||||
self.maximum = maximum
|
||||
self.workerWorking = True
|
||||
self.dlgWorking = True
|
||||
self.error = None
|
||||
self.callback = callback
|
||||
self.cbArgs = []
|
||||
|
||||
@property
|
||||
def working(self):
|
||||
return self.workerWorking and self.dlgWorking and not self.error
|
||||
|
||||
@property
|
||||
def userCancelled(self):
|
||||
return not self.dlgWorking
|
||||
@@ -1,2 +1,2 @@
|
||||
from .efs import EfsPort
|
||||
from .port import Port, IPortUser
|
||||
from .port import Port
|
||||
|
||||
@@ -38,7 +38,7 @@ from service.const import PortEftOptions
|
||||
from service.fit import Fit as svcFit
|
||||
from service.market import Market
|
||||
from service.port.muta import parseMutant, renderMutant
|
||||
from service.port.shared import IPortUser, fetchItem, processing_notify
|
||||
from service.port.shared import fetchItem
|
||||
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
@@ -365,7 +365,7 @@ def importEft(lines):
|
||||
return fit
|
||||
|
||||
|
||||
def importEftCfg(shipname, lines, iportuser):
|
||||
def importEftCfg(shipname, lines, progress):
|
||||
"""Handle import from EFT config store file"""
|
||||
|
||||
# Check if we have such ship in database, bail if we don't
|
||||
@@ -388,6 +388,8 @@ def importEftCfg(shipname, lines, iportuser):
|
||||
fitIndices.append(startPos)
|
||||
|
||||
for i, startPos in enumerate(fitIndices):
|
||||
if progress and progress.userCancelled:
|
||||
return []
|
||||
# End position is last file line if we're trying to get it for last fit,
|
||||
# or start position of next fit minus 1
|
||||
endPos = len(lines) if i == len(fitIndices) - 1 else fitIndices[i + 1]
|
||||
@@ -558,11 +560,8 @@ def importEftCfg(shipname, lines, iportuser):
|
||||
# Append fit to list of fits
|
||||
fits.append(fitobj)
|
||||
|
||||
if iportuser: # NOTE: Send current processing status
|
||||
processing_notify(
|
||||
iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
|
||||
"%s:\n%s" % (fitobj.ship.name, fitobj.name)
|
||||
)
|
||||
if progress:
|
||||
progress.message = "%s:\n%s" % (fitobj.ship.name, fitobj.name)
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
|
||||
@@ -38,7 +38,6 @@ from service.port.eft import (
|
||||
isValidImplantImport, isValidBoosterImport)
|
||||
from service.port.esi import exportESI, importESI
|
||||
from service.port.multibuy import exportMultiBuy
|
||||
from service.port.shared import IPortUser, UserCancelException, processing_notify
|
||||
from service.port.shipstats import exportFitStats
|
||||
from service.port.xml import importXml, exportXml
|
||||
from service.port.muta import parseMutant, parseDynamicItemString, fetchDynamicItem
|
||||
@@ -73,53 +72,48 @@ class Port:
|
||||
return cls.__tag_replace_flag
|
||||
|
||||
@staticmethod
|
||||
def backupFits(path, iportuser):
|
||||
def backupFits(path, progress):
|
||||
pyfalog.debug("Starting backup fits thread.")
|
||||
|
||||
def backupFitsWorkerFunc(path, iportuser):
|
||||
success = True
|
||||
def backupFitsWorkerFunc(path, progress):
|
||||
try:
|
||||
iportuser.on_port_process_start()
|
||||
backedUpFits = Port.exportXml(svcFit.getInstance().getAllFits(), iportuser)
|
||||
backupFile = open(path, "w", encoding="utf-8")
|
||||
backupFile.write(backedUpFits)
|
||||
backupFile.close()
|
||||
except UserCancelException:
|
||||
success = False
|
||||
# Send done signal to GUI
|
||||
# wx.CallAfter(callback, -1, "Done.")
|
||||
flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
|
||||
iportuser.on_port_processing(IPortUser.PROCESS_EXPORT | flag,
|
||||
"User canceled or some error occurrence." if not success else "Done.")
|
||||
backedUpFits = Port.exportXml(svcFit.getInstance().getAllFits(), progress)
|
||||
if backedUpFits:
|
||||
progress.message = f'writing {path}'
|
||||
backupFile = open(path, "w", encoding="utf-8")
|
||||
backupFile.write(backedUpFits)
|
||||
backupFile.close()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as e:
|
||||
progress.error = f'{e}'
|
||||
finally:
|
||||
progress.current += 1
|
||||
progress.workerWorking = False
|
||||
|
||||
threading.Thread(
|
||||
target=backupFitsWorkerFunc,
|
||||
args=(path, iportuser)
|
||||
args=(path, progress)
|
||||
).start()
|
||||
|
||||
@staticmethod
|
||||
def importFitsThreaded(paths, iportuser):
|
||||
# type: (tuple, IPortUser) -> None
|
||||
def importFitsThreaded(paths, progress):
|
||||
"""
|
||||
:param paths: fits data file path list.
|
||||
:param iportuser: IPortUser implemented class.
|
||||
:rtype: None
|
||||
"""
|
||||
pyfalog.debug("Starting import fits thread.")
|
||||
|
||||
def importFitsFromFileWorkerFunc(paths, iportuser):
|
||||
iportuser.on_port_process_start()
|
||||
success, result = Port.importFitFromFiles(paths, iportuser)
|
||||
flag = IPortUser.ID_ERROR if not success else IPortUser.ID_DONE
|
||||
iportuser.on_port_processing(IPortUser.PROCESS_IMPORT | flag, result)
|
||||
def importFitsFromFileWorkerFunc(paths, progress):
|
||||
Port.importFitFromFiles(paths, progress)
|
||||
|
||||
threading.Thread(
|
||||
target=importFitsFromFileWorkerFunc,
|
||||
args=(paths, iportuser)
|
||||
args=(paths, progress)
|
||||
).start()
|
||||
|
||||
@staticmethod
|
||||
def importFitFromFiles(paths, iportuser=None):
|
||||
def importFitFromFiles(paths, progress=None):
|
||||
"""
|
||||
Imports fits from file(s). First processes all provided paths and stores
|
||||
assembled fits into a list. This allows us to call back to the GUI as
|
||||
@@ -132,11 +126,13 @@ class Port:
|
||||
fit_list = []
|
||||
try:
|
||||
for path in paths:
|
||||
if iportuser: # Pulse
|
||||
if progress:
|
||||
if progress and progress.userCancelled:
|
||||
progress.workerWorking = False
|
||||
return False, "Cancelled by user"
|
||||
msg = "Processing file:\n%s" % path
|
||||
progress.message = msg
|
||||
pyfalog.debug(msg)
|
||||
processing_notify(iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE, msg)
|
||||
# wx.CallAfter(callback, 1, msg)
|
||||
|
||||
with open(path, "rb") as file_:
|
||||
srcString = file_.read()
|
||||
@@ -148,15 +144,21 @@ class Port:
|
||||
continue
|
||||
|
||||
try:
|
||||
importType, makesNewFits, fitsImport = Port.importAuto(srcString, path, iportuser=iportuser)
|
||||
importType, makesNewFits, fitsImport = Port.importAuto(srcString, path, progress=progress)
|
||||
fit_list += fitsImport
|
||||
except xml.parsers.expat.ExpatError:
|
||||
pyfalog.warning("Malformed XML in:\n{0}", path)
|
||||
return False, "Malformed XML in %s" % path
|
||||
msg = "Malformed XML in %s" % path
|
||||
if progress:
|
||||
progress.error = msg
|
||||
progress.workerWorking = False
|
||||
return False, msg
|
||||
|
||||
# IDs = [] # NOTE: what use for IDs?
|
||||
numFits = len(fit_list)
|
||||
for idx, fit in enumerate(fit_list):
|
||||
if progress and progress.userCancelled:
|
||||
progress.workerWorking = False
|
||||
return False, "Cancelled by user"
|
||||
# Set some more fit attributes and save
|
||||
fit.character = sFit.character
|
||||
fit.damagePattern = sFit.pattern
|
||||
@@ -168,25 +170,23 @@ class Port:
|
||||
fit.implantLocation = ImplantLocation.CHARACTER if useCharImplants else ImplantLocation.FIT
|
||||
db.save(fit)
|
||||
# IDs.append(fit.ID)
|
||||
if iportuser: # Pulse
|
||||
if progress:
|
||||
pyfalog.debug("Processing complete, saving fits to database: {0}/{1}", idx + 1, numFits)
|
||||
processing_notify(
|
||||
iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
|
||||
"Processing complete, saving fits to database\n(%d/%d) %s" % (idx + 1, numFits, fit.ship.name)
|
||||
)
|
||||
|
||||
except UserCancelException:
|
||||
return False, "Processing has been canceled.\n"
|
||||
progress.message = "Processing complete, saving fits to database\n(%d/%d) %s" % (idx + 1, numFits, fit.ship.name)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except Exception as e:
|
||||
pyfalog.critical("Unknown exception processing: {0}", path)
|
||||
pyfalog.critical("Unknown exception processing: {0}", paths)
|
||||
pyfalog.critical(e)
|
||||
# TypeError: not all arguments converted during string formatting
|
||||
# return False, "Unknown Error while processing {0}" % path
|
||||
if progress:
|
||||
progress.error = f'{e}'
|
||||
progress.workerWorking = False
|
||||
return False, "Unknown error while processing {}\n\n Error: {} {}".format(
|
||||
path, type(e).__name__, getattr(e, 'message', ''))
|
||||
paths, type(e).__name__, getattr(e, 'message', ''))
|
||||
|
||||
if progress:
|
||||
progress.cbArgs.append(fit_list[:])
|
||||
progress.workerWorking = False
|
||||
return True, fit_list
|
||||
|
||||
@staticmethod
|
||||
@@ -211,8 +211,7 @@ class Port:
|
||||
return importType, importData
|
||||
|
||||
@classmethod
|
||||
def importAuto(cls, string, path=None, activeFit=None, iportuser=None):
|
||||
# type: (Port, str, str, object, IPortUser) -> object
|
||||
def importAuto(cls, string, path=None, activeFit=None, progress=None):
|
||||
lines = string.splitlines()
|
||||
# Get first line and strip space symbols of it to avoid possible detection errors
|
||||
firstLine = ''
|
||||
@@ -224,7 +223,7 @@ class Port:
|
||||
|
||||
# If XML-style start of tag encountered, detect as XML
|
||||
if re.search(RE_XML_START, firstLine):
|
||||
return "XML", True, cls.importXml(string, iportuser)
|
||||
return "XML", True, cls.importXml(string, progress)
|
||||
|
||||
# If JSON-style start, parse as CREST/JSON
|
||||
if firstLine[0] == '{':
|
||||
@@ -235,7 +234,7 @@ class Port:
|
||||
if re.match("^\s*\[.*\]", firstLine) and path is not None:
|
||||
filename = os.path.split(path)[1]
|
||||
shipName = filename.rsplit('.')[0]
|
||||
return "EFT Config", True, cls.importEftCfg(shipName, lines, iportuser)
|
||||
return "EFT Config", True, cls.importEftCfg(shipName, lines, progress)
|
||||
|
||||
# If no file is specified and there's comma between brackets,
|
||||
# consider that we have [ship, setup name] and detect like eft export format
|
||||
@@ -297,8 +296,8 @@ class Port:
|
||||
return importEft(lines)
|
||||
|
||||
@staticmethod
|
||||
def importEftCfg(shipname, lines, iportuser=None):
|
||||
return importEftCfg(shipname, lines, iportuser)
|
||||
def importEftCfg(shipname, lines, progress=None):
|
||||
return importEftCfg(shipname, lines, progress)
|
||||
|
||||
@classmethod
|
||||
def exportEft(cls, fit, options, callback=None):
|
||||
@@ -328,12 +327,12 @@ class Port:
|
||||
|
||||
# XML-related methods
|
||||
@staticmethod
|
||||
def importXml(text, iportuser=None):
|
||||
return importXml(text, iportuser)
|
||||
def importXml(text, progress=None):
|
||||
return importXml(text, progress)
|
||||
|
||||
@staticmethod
|
||||
def exportXml(fits, iportuser=None, callback=None):
|
||||
return exportXml(fits, iportuser, callback=callback)
|
||||
def exportXml(fits, progress=None, callback=None):
|
||||
return exportXml(fits, progress, callback=callback)
|
||||
|
||||
# Multibuy-related methods
|
||||
@staticmethod
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
# =============================================================================
|
||||
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
from service.market import Market
|
||||
@@ -28,55 +26,6 @@ from service.market import Market
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
class UserCancelException(Exception):
|
||||
"""when user cancel on port processing."""
|
||||
pass
|
||||
|
||||
|
||||
class IPortUser(metaclass=ABCMeta):
|
||||
|
||||
ID_PULSE = 1
|
||||
# Pulse the progress bar
|
||||
ID_UPDATE = ID_PULSE << 1
|
||||
# Replace message with data: update messate
|
||||
ID_DONE = ID_PULSE << 2
|
||||
# open fits: import process done
|
||||
ID_ERROR = ID_PULSE << 3
|
||||
# display error: raise some error
|
||||
|
||||
PROCESS_IMPORT = ID_PULSE << 4
|
||||
# means import process.
|
||||
PROCESS_EXPORT = ID_PULSE << 5
|
||||
# means import process.
|
||||
|
||||
@abstractmethod
|
||||
def on_port_processing(self, action, data=None):
|
||||
"""
|
||||
While importing fits from file, the logic calls back to this function to
|
||||
update progress bar to show activity. XML files can contain multiple
|
||||
ships with multiple fits, whereas EFT cfg files contain many fits of
|
||||
a single ship. When iterating through the files, we update the message
|
||||
when we start a new file, and then Pulse the progress bar with every fit
|
||||
that is processed.
|
||||
|
||||
action : a flag that lets us know how to deal with :data
|
||||
None: Pulse the progress bar
|
||||
1: Replace message with data
|
||||
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
|
||||
"""
|
||||
|
||||
"""return: True is continue process, False is cancel."""
|
||||
pass
|
||||
|
||||
def on_port_process_start(self):
|
||||
pass
|
||||
|
||||
|
||||
def processing_notify(iportuser, flag, data):
|
||||
if not iportuser.on_port_processing(flag, data):
|
||||
raise UserCancelException
|
||||
|
||||
|
||||
def fetchItem(typeName, eagerCat=False):
|
||||
sMkt = Market.getInstance()
|
||||
eager = 'group.category' if eagerCat else None
|
||||
|
||||
@@ -36,7 +36,7 @@ from gui.fitCommands.helpers import activeStateLimit
|
||||
from service.fit import Fit as svcFit
|
||||
from service.market import Market
|
||||
from service.port.muta import renderMutantAttrs, parseMutantAttrs
|
||||
from service.port.shared import IPortUser, processing_notify, fetchItem
|
||||
from service.port.shared import fetchItem
|
||||
from utils.strfunctions import replace_ltgt, sequential_rep
|
||||
|
||||
|
||||
@@ -154,9 +154,8 @@ def _resolve_module(hardware, sMkt, b_localized):
|
||||
return item, mutaplasmidItem, mutatedAttrs
|
||||
|
||||
|
||||
def importXml(text, iportuser):
|
||||
def importXml(text, progress):
|
||||
from .port import Port
|
||||
# type: (str, IPortUser) -> list[eos.saveddata.fit.Fit]
|
||||
sMkt = Market.getInstance()
|
||||
doc = xml.dom.minidom.parseString(text)
|
||||
# NOTE:
|
||||
@@ -169,6 +168,9 @@ def importXml(text, iportuser):
|
||||
failed = 0
|
||||
|
||||
for fitting in fittings:
|
||||
if progress and progress.userCancelled:
|
||||
return []
|
||||
|
||||
try:
|
||||
fitobj = _resolve_ship(fitting, sMkt, b_localized)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
@@ -272,16 +274,13 @@ def importXml(text, iportuser):
|
||||
fitobj.modules.append(module)
|
||||
|
||||
fit_list.append(fitobj)
|
||||
if iportuser: # NOTE: Send current processing status
|
||||
processing_notify(
|
||||
iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
|
||||
"Processing %s\n%s" % (fitobj.ship.name, fitobj.name)
|
||||
)
|
||||
if progress:
|
||||
progress.message = "Processing %s\n%s" % (fitobj.ship.name, fitobj.name)
|
||||
|
||||
return fit_list
|
||||
|
||||
|
||||
def exportXml(fits, iportuser, callback):
|
||||
def exportXml(fits, progress, callback):
|
||||
doc = xml.dom.minidom.Document()
|
||||
fittings = doc.createElement("fittings")
|
||||
# fit count
|
||||
@@ -295,6 +294,12 @@ def exportXml(fits, iportuser, callback):
|
||||
node.setAttribute("mutated_attrs", renderMutantAttrs(mutant))
|
||||
|
||||
for i, fit in enumerate(fits):
|
||||
if progress:
|
||||
if progress.userCancelled:
|
||||
return None
|
||||
processedFits = i + 1
|
||||
progress.current = processedFits
|
||||
progress.message = "converting to xml (%s/%s) %s" % (processedFits, fit_count, fit.ship.name)
|
||||
try:
|
||||
fitting = doc.createElement("fitting")
|
||||
fitting.setAttribute("name", fit.name)
|
||||
@@ -387,12 +392,6 @@ def exportXml(fits, iportuser, callback):
|
||||
except Exception as e:
|
||||
pyfalog.error("Failed on fitID: %d, message: %s" % e.message)
|
||||
continue
|
||||
finally:
|
||||
if iportuser:
|
||||
processing_notify(
|
||||
iportuser, IPortUser.PROCESS_EXPORT | IPortUser.ID_UPDATE,
|
||||
(i, "convert to xml (%s/%s) %s" % (i + 1, fit_count, fit.ship.name))
|
||||
)
|
||||
text = doc.toprettyxml()
|
||||
|
||||
if callback:
|
||||
|
||||
Reference in New Issue
Block a user