Skip to content

Commit

Permalink
Rework how progress dialog is used
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkFenX committed Feb 26, 2024
1 parent 3fadccc commit 907da34
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 249 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
166 changes: 60 additions & 106 deletions gui/mainFrame.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@
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
Expand Down Expand Up @@ -130,7 +131,6 @@ def stop(self):
self.running = False


# todo: include IPortUser again
class MainFrame(wx.Frame):
__instance = None

Expand Down Expand Up @@ -845,14 +845,15 @@ def fileImportDialog(self, event):
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 """
Expand All @@ -863,32 +864,30 @@ def backupToXml(self, event):
_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)):
Expand All @@ -903,82 +902,44 @@ def exportHtml(self, event):
) as dlg:
if dlg.ShowModal() == wx.ID_OK:
return

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")
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)

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:
Expand All @@ -988,6 +949,8 @@ def _openAfterImport(self, fits):
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((
Expand All @@ -999,15 +962,6 @@ def _openAfterImport(self, fits):
))
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(
Expand Down
28 changes: 15 additions & 13 deletions gui/utils/exportHtml.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@ def getInstance(cls):
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):
Expand Down Expand Up @@ -72,11 +72,13 @@ def run(self):
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 """
Expand Down Expand Up @@ -234,8 +236,8 @@ def generateFullHTML(self, sMkt, sFit, dnaUrl):
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')
Expand Down Expand Up @@ -291,7 +293,7 @@ def generateMinimalHTML(self, sMkt, sFit, dnaUrl):
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 changes: 19 additions & 0 deletions gui/utils/progressHelper.py
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion service/port/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .efs import EfsPort
from .port import Port, IPortUser
from .port import Port
13 changes: 6 additions & 7 deletions service/port/eft.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
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__)
Expand Down Expand Up @@ -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
Expand All @@ -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]
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 907da34

Please sign in to comment.