Skip to content

Commit 907da34

Browse files
committed
Rework how progress dialog is used
1 parent 3fadccc commit 907da34

File tree

9 files changed

+170
-249
lines changed

9 files changed

+170
-249
lines changed

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Requirements
44

5-
- Python 3.7
5+
- Python 3.11
66
- Git CLI installed
77
- Python, pip and git are all available as command-line commands (add to the path if needed)
88

gui/mainFrame.py

+60-106
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@
6161
from gui.targetProfileEditor import TargetProfileEditor
6262
from gui.updateDialog import UpdateDialog
6363
from gui.utils.clipboard import fromClipboard
64+
from gui.utils.progressHelper import ProgressHelper
6465
from service.character import Character
6566
from service.esi import Esi
6667
from service.fit import Fit
67-
from service.port import IPortUser, Port
68+
from service.port import Port
6869
from service.price import Price
6970
from service.settings import HTMLExportSettings, SettingsProvider
7071
from service.update import Update
@@ -130,7 +131,6 @@ def stop(self):
130131
self.running = False
131132

132133

133-
# todo: include IPortUser again
134134
class MainFrame(wx.Frame):
135135
__instance = None
136136

@@ -845,14 +845,15 @@ def fileImportDialog(self, event):
845845
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE
846846
) as dlg:
847847
if dlg.ShowModal() == wx.ID_OK:
848-
self.progressDialog = wx.ProgressDialog(
849-
_t("Importing fits"),
850-
" " * 100, # set some arbitrary spacing to create width in window
851-
parent=self,
852-
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL
853-
)
854-
Port.importFitsThreaded(dlg.GetPaths(), self)
855-
self.progressDialog.ShowModal()
848+
# set some arbitrary spacing to create width in window
849+
progress = ProgressHelper(message=" " * 100, callback=self._openAfterImport)
850+
call = (Port.importFitsThreaded, [dlg.GetPaths(), progress], {})
851+
self.handleProgress(
852+
title=_t("Importing fits"),
853+
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
854+
call=call,
855+
progress=progress,
856+
errMsgLbl=_t("Import Error"))
856857

857858
def backupToXml(self, event):
858859
""" Back up all fits to EVE XML file """
@@ -863,32 +864,30 @@ def backupToXml(self, event):
863864
_t("Save Backup As..."),
864865
wildcard=_t("EVE XML fitting file") + " (*.xml)|*.xml",
865866
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
866-
defaultFile=defaultFile,
867-
) as dlg:
868-
if dlg.ShowModal() == wx.ID_OK:
869-
filePath = dlg.GetPath()
867+
defaultFile=defaultFile) as fileDlg:
868+
if fileDlg.ShowModal() == wx.ID_OK:
869+
filePath = fileDlg.GetPath()
870870
if '.' not in os.path.basename(filePath):
871871
filePath += ".xml"
872872

873-
sFit = Fit.getInstance()
874-
max_ = sFit.countAllFits()
875-
876-
self.progressDialog = wx.ProgressDialog(
877-
_t("Backup fits"),
878-
_t("Backing up {} fits to: {}").format(max_, filePath),
879-
maximum=max_,
880-
parent=self,
881-
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL
882-
)
883-
Port.backupFits(filePath, self)
884-
self.progressDialog.ShowModal()
873+
fitAmount = Fit.getInstance().countAllFits()
874+
progress = ProgressHelper(
875+
message=_t("Backing up {} fits to: {}").format(fitAmount, filePath),
876+
maximum=fitAmount + 1)
877+
call = (Port.backupFits, [filePath, progress], {})
878+
self.handleProgress(
879+
title=_t("Backup fits"),
880+
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
881+
call=call,
882+
progress=progress,
883+
errMsgLbl=_t("Export Error"))
885884

886885
def exportHtml(self, event):
887886
from gui.utils.exportHtml import exportHtml
887+
888888
sFit = Fit.getInstance()
889889
settings = HTMLExportSettings.getInstance()
890890

891-
max_ = sFit.countAllFits()
892891
path = settings.getPath()
893892

894893
if not os.path.isdir(os.path.dirname(path)):
@@ -903,82 +902,44 @@ def exportHtml(self, event):
903902
) as dlg:
904903
if dlg.ShowModal() == wx.ID_OK:
905904
return
906-
907-
self.progressDialog = wx.ProgressDialog(
908-
_t("Backup fits"),
909-
_t("Generating HTML file at: {}").format(path),
910-
maximum=max_, parent=self,
911-
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME)
912-
913-
exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback)
914-
self.progressDialog.ShowModal()
915-
916-
def backupCallback(self, info):
917-
if info == -1:
918-
self.closeProgressDialog()
919-
else:
920-
self.progressDialog.Update(info)
921-
922-
def on_port_process_start(self):
923-
# flag for progress dialog.
924-
self.__progress_flag = True
925-
926-
def on_port_processing(self, action, data=None):
927-
# 2017/03/29 NOTE: implementation like interface
928-
wx.CallAfter(
929-
self._on_port_processing, action, data
930-
)
931-
932-
return self.__progress_flag
933-
934-
def _on_port_processing(self, action, data):
935-
"""
936-
While importing fits from file, the logic calls back to this function to
937-
update progress bar to show activity. XML files can contain multiple
938-
ships with multiple fits, whereas EFT cfg files contain many fits of
939-
a single ship. When iterating through the files, we update the message
940-
when we start a new file, and then Pulse the progress bar with every fit
941-
that is processed.
942-
943-
action : a flag that lets us know how to deal with :data
944-
None: Pulse the progress bar
945-
1: Replace message with data
946-
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
947-
"""
948-
_message = None
949-
if action & IPortUser.ID_ERROR:
950-
self.closeProgressDialog()
951-
_message = _t("Import Error") if action & IPortUser.PROCESS_IMPORT else _t("Export Error")
905+
progress = ProgressHelper(
906+
message=_t("Generating HTML file at: {}").format(path),
907+
maximum=sFit.countAllFits() + 1)
908+
call = (exportHtml.getInstance().refreshFittingHtml, [True, progress], {})
909+
self.handleProgress(
910+
title=_t("Backup fits"),
911+
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME,
912+
call=call,
913+
progress=progress)
914+
915+
def handleProgress(self, title, style, call, progress, errMsgLbl=None):
916+
extraArgs = {}
917+
if progress.maximum is not None:
918+
extraArgs['maximum'] = progress.maximum
919+
with wx.ProgressDialog(
920+
parent=self,
921+
title=title,
922+
message=progress.message,
923+
style=style,
924+
**extraArgs
925+
) as dlg:
926+
func, args, kwargs = call
927+
func(*args, **kwargs)
928+
while progress.working:
929+
wx.MilliSleep(250)
930+
wx.Yield()
931+
(progress.dlgWorking, skip) = dlg.Update(progress.current, progress.message)
932+
if progress.error and errMsgLbl:
952933
with wx.MessageDialog(
953934
self,
954935
_t("The following error was generated") +
955-
f"\n\n{data}\n\n" +
936+
f"\n\n{progress.error}\n\n" +
956937
_t("Be aware that already processed fits were not saved"),
957-
_message, wx.OK | wx.ICON_ERROR
938+
errMsgLbl, wx.OK | wx.ICON_ERROR
958939
) as dlg:
959940
dlg.ShowModal()
960-
return
961-
962-
# data is str
963-
if action & IPortUser.PROCESS_IMPORT:
964-
if action & IPortUser.ID_PULSE:
965-
_message = ()
966-
# update message
967-
elif action & IPortUser.ID_UPDATE: # and data != self.progressDialog.message:
968-
_message = data
969-
970-
if _message is not None:
971-
self.__progress_flag, _unuse = self.progressDialog.Pulse(_message)
972-
else:
973-
self.closeProgressDialog()
974-
if action & IPortUser.ID_DONE:
975-
self._openAfterImport(data)
976-
# data is tuple(int, str)
977-
elif action & IPortUser.PROCESS_EXPORT:
978-
if action & IPortUser.ID_DONE:
979-
self.closeProgressDialog()
980-
else:
981-
self.__progress_flag, _unuse = self.progressDialog.Update(data[0], data[1])
941+
elif progress.callback:
942+
progress.callback(*progress.cbArgs)
982943

983944
def _openAfterImport(self, fits):
984945
if len(fits) > 0:
@@ -988,6 +949,8 @@ def _openAfterImport(self, fits):
988949
wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True))
989950
else:
990951
fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name))
952+
# Show 100 fits max
953+
fits = fits[:100]
991954
results = []
992955
for fit in fits:
993956
results.append((
@@ -999,15 +962,6 @@ def _openAfterImport(self, fits):
999962
))
1000963
wx.PostEvent(self.shipBrowser, ImportSelected(fits=results, back=True))
1001964

1002-
def closeProgressDialog(self):
1003-
# Windows apparently handles ProgressDialogs differently. We can
1004-
# simply Destroy it here, but for other platforms we must Close it
1005-
if 'wxMSW' in wx.PlatformInfo:
1006-
self.progressDialog.Destroy()
1007-
else:
1008-
self.progressDialog.EndModal(wx.ID_OK)
1009-
self.progressDialog.Close()
1010-
1011965
def importCharacter(self, event):
1012966
""" Imports character XML file from EVE API """
1013967
with wx.FileDialog(

gui/utils/exportHtml.py

+15-13
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ def getInstance(cls):
2626
def __init__(self):
2727
self.thread = exportHtmlThread()
2828

29-
def refreshFittingHtml(self, force=False, callback=False):
29+
def refreshFittingHtml(self, force=False, progress=None):
3030
settings = HTMLExportSettings.getInstance()
3131

3232
if force or settings.getEnabled():
3333
self.thread.stop()
34-
self.thread = exportHtmlThread(callback)
34+
self.thread = exportHtmlThread(progress)
3535
self.thread.start()
3636

3737

3838
class exportHtmlThread(threading.Thread):
39-
def __init__(self, callback=False):
39+
def __init__(self, progress=False):
4040
threading.Thread.__init__(self)
4141
self.name = "HTMLExport"
42-
self.callback = callback
42+
self.progress = progress
4343
self.stopRunning = False
4444

4545
def stop(self):
@@ -72,11 +72,13 @@ def run(self):
7272
pass
7373
except (KeyboardInterrupt, SystemExit):
7474
raise
75-
except Exception as ex:
76-
pass
77-
78-
if self.callback:
79-
wx.CallAfter(self.callback, -1)
75+
except Exception as e:
76+
if self.progress:
77+
self.progress.error = f'{e}'
78+
finally:
79+
if self.progress:
80+
self.progress.current += 1
81+
self.progress.workerWorking = False
8082

8183
def generateFullHTML(self, sMkt, sFit, dnaUrl):
8284
""" Generate the complete HTML with styling and javascript """
@@ -234,8 +236,8 @@ def generateFullHTML(self, sMkt, sFit, dnaUrl):
234236
pyfalog.warning("Failed to export line")
235237
continue
236238
finally:
237-
if self.callback:
238-
wx.CallAfter(self.callback, count)
239+
if self.progress:
240+
self.progress.current = count
239241
count += 1
240242
HTMLgroup += HTMLship + (' </ul>\n'
241243
' </li>\n')
@@ -291,7 +293,7 @@ def generateMinimalHTML(self, sMkt, sFit, dnaUrl):
291293
pyfalog.error("Failed to export line")
292294
continue
293295
finally:
294-
if self.callback:
295-
wx.CallAfter(self.callback, count)
296+
if self.progress:
297+
self.progress.current = count
296298
count += 1
297299
return HTML

gui/utils/progressHelper.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class ProgressHelper:
2+
3+
def __init__(self, message, maximum=None, callback=None):
4+
self.message = message
5+
self.current = 0
6+
self.maximum = maximum
7+
self.workerWorking = True
8+
self.dlgWorking = True
9+
self.error = None
10+
self.callback = callback
11+
self.cbArgs = []
12+
13+
@property
14+
def working(self):
15+
return self.workerWorking and self.dlgWorking and not self.error
16+
17+
@property
18+
def userCancelled(self):
19+
return not self.dlgWorking

service/port/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
from .efs import EfsPort
2-
from .port import Port, IPortUser
2+
from .port import Port

service/port/eft.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from service.fit import Fit as svcFit
3939
from service.market import Market
4040
from service.port.muta import parseMutant, renderMutant
41-
from service.port.shared import IPortUser, fetchItem, processing_notify
41+
from service.port.shared import fetchItem
4242

4343

4444
pyfalog = Logger(__name__)
@@ -365,7 +365,7 @@ def importEft(lines):
365365
return fit
366366

367367

368-
def importEftCfg(shipname, lines, iportuser):
368+
def importEftCfg(shipname, lines, progress):
369369
"""Handle import from EFT config store file"""
370370

371371
# Check if we have such ship in database, bail if we don't
@@ -388,6 +388,8 @@ def importEftCfg(shipname, lines, iportuser):
388388
fitIndices.append(startPos)
389389

390390
for i, startPos in enumerate(fitIndices):
391+
if progress and progress.userCancelled:
392+
return []
391393
# End position is last file line if we're trying to get it for last fit,
392394
# or start position of next fit minus 1
393395
endPos = len(lines) if i == len(fitIndices) - 1 else fitIndices[i + 1]
@@ -558,11 +560,8 @@ def importEftCfg(shipname, lines, iportuser):
558560
# Append fit to list of fits
559561
fits.append(fitobj)
560562

561-
if iportuser: # NOTE: Send current processing status
562-
processing_notify(
563-
iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE,
564-
"%s:\n%s" % (fitobj.ship.name, fitobj.name)
565-
)
563+
if progress:
564+
progress.message = "%s:\n%s" % (fitobj.ship.name, fitobj.name)
566565

567566
except (KeyboardInterrupt, SystemExit):
568567
raise

0 commit comments

Comments
 (0)