diff --git a/aqt/__init__.py b/aqt/__init__.py index e8dadf58d..a54b841c8 100644 --- a/aqt/__init__.py +++ b/aqt/__init__.py @@ -38,8 +38,8 @@ except ImportError as e: from anki.utils import checksum -# Dialog manager - manages modeless windows -##########################################################################emacs +# Dialog manager - manages non-modal windows +########################################################################## class DialogManager: @@ -65,18 +65,32 @@ class DialogManager: self._dialogs[name][1] = instance return instance - def close(self, name): + def markClosed(self, name): self._dialogs[name] = [self._dialogs[name][0], None] - def closeAll(self): - "True if all closed successfully." - for (n, (creator, instance)) in list(self._dialogs.items()): - if instance: - if not instance.canClose(): - return False - instance.forceClose = True - instance.close() - self.close(n) + def allClosed(self): + return not any(x[1] for x in self._dialogs.values()) + + def closeAll(self, onsuccess): + # can we close immediately? + if self.allClosed(): + onsuccess() + return + + # ask all windows to close and await a reply + for (name, (creator, instance)) in self._dialogs.items(): + if not instance: + continue + + def callback(): + if self.allClosed(): + onsuccess() + else: + # still waiting for others to close + pass + + instance.closeWithCallback(callback) + return True dialogs = DialogManager() diff --git a/aqt/about.py b/aqt/about.py index 84d2e4af5..5b4d1b0e2 100644 --- a/aqt/about.py +++ b/aqt/about.py @@ -7,17 +7,18 @@ import aqt.forms from aqt import appVersion class ClosableQDialog(QDialog): - def canClose(self): - return True - def reject(self): - aqt.dialogs.close("About") + aqt.dialogs.markClosed("About") QDialog.reject(self) def accept(self): - aqt.dialogs.close("About") + aqt.dialogs.markClosed("About") QDialog.accept(self) + def closeWithCallback(self, callback): + self.reject() + callback() + def show(mw): dialog = ClosableQDialog(mw) mw.setupDialogGC(dialog) @@ -124,6 +125,8 @@ please get in touch.") abouttext += '
' + _("A big thanks to all the people who have provided \ suggestions, bug reports and donations.") abt.label.stdHtml(abouttext, js=" ") - dialog.adjustSize() - dialog.show() + def resizeAndShow(arg): + dialog.adjustSize() + dialog.show() + abt.label.evalWithCallback("1", resizeAndShow) return dialog diff --git a/aqt/addcards.py b/aqt/addcards.py index c196c1c94..cb62a4ca6 100644 --- a/aqt/addcards.py +++ b/aqt/addcards.py @@ -28,7 +28,6 @@ class AddCards(QDialog): self.setupButtons() self.onReset() self.history = [] - self.forceClose = False restoreGeom(self, "add") addHook('reset', self.onReset) addHook('currentModelChanged', self.onModelChange) @@ -205,22 +204,32 @@ question on all cards."""), help="AddItems") return QDialog.keyPressEvent(self, evt) def reject(self): - if not self.canClose(): - return + self.ifCanClose(self._reject) + + def _reject(self): remHook('reset', self.onReset) remHook('currentModelChanged', self.onModelChange) clearAudioQueue() self.removeTempNote(self.editor.note) - self.editor.setNote(None) + self.editor.cleanup() self.modelChooser.cleanup() self.deckChooser.cleanup() self.mw.maybeReset() saveGeom(self, "add") - aqt.dialogs.close("AddCards") + aqt.dialogs.markClosed("AddCards") QDialog.reject(self) - def canClose(self): - if (self.forceClose or self.editor.fieldsAreBlank() or - askUser(_("Close and lose current input?"))): - return True - return False + def ifCanClose(self, onOk): + def afterSave(): + ok = (self.editor.fieldsAreBlank() or + askUser(_("Close and lose current input?"))) + if ok: + onOk() + + self.editor.saveNow(afterSave) + + def closeWithCallback(self, cb): + def doClose(): + self._reject() + cb() + self.ifCanClose(doClose) diff --git a/aqt/browser.py b/aqt/browser.py index be8179868..d23abc576 100644 --- a/aqt/browser.py +++ b/aqt/browser.py @@ -366,7 +366,6 @@ class Browser(QMainWindow): applyStyles(self) self.mw = mw self.col = self.mw.col - self.forceClose = False self.lastFilter = "" self.focusTo = None self._previewWindow = None @@ -459,26 +458,15 @@ class Browser(QMainWindow): curmax + 6) def closeEvent(self, evt): - if not self._closeEventHasCleanedUp: - if self.editor.note and not self.forceClose: - # ignore event for now to allow us to save - self.editor.saveNow(self._closeEventAfterSave) - evt.ignore() - else: - self._closeEventCleanup() - evt.accept() - self.mw.gcWindow(self) - else: + if self._closeEventHasCleanedUp: evt.accept() - self.mw.gcWindow(self) + return + self.editor.saveNow(self._closeWindow) + evt.ignore() - def _closeEventAfterSave(self): - self._closeEventCleanup() - self.close() - - def _closeEventCleanup(self): + def _closeWindow(self): self._cancelPreviewTimer() - self.editor.setNote(None) + self.editor.cleanup() saveSplitter(self.form.splitter, "editor3") saveGeom(self, "editor") saveState(self, "editor") @@ -487,14 +475,18 @@ class Browser(QMainWindow): self.col.setMod() self.teardownHooks() self.mw.maybeReset() - aqt.dialogs.close("Browser") + aqt.dialogs.markClosed("Browser") self._closeEventHasCleanedUp = True + self.mw.gcWindow(self) + self.close() - def canClose(self): - return True + def closeWithCallback(self, onsuccess): + def callback(): + self._closeWindow() + onsuccess() + self.editor.saveNow(callback) def keyPressEvent(self, evt): - "Show answer on RET or register answer." if evt.key() == Qt.Key_Escape: self.close() else: diff --git a/aqt/editcurrent.py b/aqt/editcurrent.py index 8003e86e8..b07cf97d7 100644 --- a/aqt/editcurrent.py +++ b/aqt/editcurrent.py @@ -12,13 +12,13 @@ class EditCurrent(QDialog): def __init__(self, mw): QDialog.__init__(self, None, Qt.Window) + mw.setupDialogGC(self) self.mw = mw self.form = aqt.forms.editcurrent.Ui_Dialog() self.form.setupUi(self) self.setWindowTitle(_("Edit Current")) self.setMinimumHeight(400) self.setMinimumWidth(500) - self.rejected.connect(self.onSave) self.form.buttonBox.button(QDialogButtonBox.Close).setShortcut( QKeySequence("Ctrl+Return")) self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self) @@ -40,15 +40,18 @@ class EditCurrent(QDialog): remHook("reset", self.onReset) self.editor.setNote(None) self.mw.reset() - aqt.dialogs.close("EditCurrent") + aqt.dialogs.markClosed("EditCurrent") self.close() return self.editor.setNote(n) - def onSave(self): - self.editor.saveNow(self._onSave) + def reject(self): + self.saveAndClose() - def _onSave(self): + def saveAndClose(self): + self.editor.saveNow(self._saveAndClose) + + def _saveAndClose(self): remHook("reset", self.onReset) r = self.mw.reviewer try: @@ -58,9 +61,14 @@ class EditCurrent(QDialog): pass else: self.mw.reviewer.cardQueue.append(self.mw.reviewer.card) + self.editor.cleanup() self.mw.moveToState("review") saveGeom(self, "editcurrent") - aqt.dialogs.close("EditCurrent") + aqt.dialogs.markClosed("EditCurrent") + QDialog.reject(self) - def canClose(self): - return True + def closeWithCallback(self, onsuccess): + def callback(): + self._saveAndClose() + onsuccess() + self.editor.saveNow(callback) diff --git a/aqt/editor.py b/aqt/editor.py index 2c2fc8b94..31ba03373 100644 --- a/aqt/editor.py +++ b/aqt/editor.py @@ -319,6 +319,11 @@ class Editor: return False return True + def cleanup(self): + self.setNote(None) + # prevent any remaining evalWithCallback() events from firing after C++ object deleted + self.web = None + # HTML editing ###################################################################### diff --git a/aqt/main.py b/aqt/main.py index 19048d84f..1dd2c82a0 100644 --- a/aqt/main.py +++ b/aqt/main.py @@ -530,7 +530,8 @@ title="%s" %s>%s''' % ( self.form.centralwidget.setLayout(self.mainLayout) def closeAllCollectionWindows(self): - return aqt.dialogs.closeAll() + aqt.dialogs.closeAll(lambda: 1) + return True # Components ########################################################################## diff --git a/aqt/stats.py b/aqt/stats.py index c8ef80b6d..b0ac989c2 100644 --- a/aqt/stats.py +++ b/aqt/stats.py @@ -42,14 +42,15 @@ class DeckStats(QDialog): self.show() self.activateWindow() - def canClose(self): - return True - def reject(self): saveGeom(self, self.name) - aqt.dialogs.close("DeckStats") + aqt.dialogs.markClosed("DeckStats") QDialog.reject(self) + def closeWithCallback(self, callback): + self.reject() + callback() + def _imagePath(self): name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time()))