From 5ad8f67f126490cd4b065b223bd9976752cf64e9 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Thu, 28 Dec 2017 18:31:05 +1000 Subject: [PATCH] move away from qprogressdialog qprogressdialog has been the source of a number of problems in the past, and the most recent issue is that it's showing the progress dialog early, regardless of what the minimum duration is set to. since we're already using our own logic for deciding when to show the dialog, it's easier to move to a normal dialog box also prevent timers from firing while a progress dialog is visible, or if the refresh timer fires we end up with the same issue. https://anki.tenderapp.com/discussions/beta-testing/949-anki-stops-when-field-is-added --- aqt/progress.py | 65 ++++++++++++++++++++-------------------- aqt/sync.py | 4 +-- designer/progress.ui | 70 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 34 deletions(-) create mode 100644 designer/progress.ui diff --git a/aqt/progress.py b/aqt/progress.py index 07d137700..1e43994d6 100644 --- a/aqt/progress.py +++ b/aqt/progress.py @@ -4,6 +4,7 @@ import time from aqt.qt import * +import aqt.forms # fixme: if mw->subwindow opens a progress dialog with mw as the parent, mw # gets raised on finish on compiz. perhaps we should be using the progress @@ -51,14 +52,15 @@ class ProgressManager: self.app.processEvents(QEventLoop.ExcludeUserInputEvents) self.inDB = False - # DB-safe timers + # Safer timers ########################################################################## # QTimer may fire in processEvents(). We provide a custom timer which - # automatically defers until the DB is not busy. + # automatically defers until the DB is not busy, and avoids running + # while a progress window is visible. def timer(self, ms, func, repeat): def handler(): - if self.inDB: + if self.inDB or self._levels: # retry in 100ms self.timer(100, func, False) else: @@ -73,27 +75,31 @@ class ProgressManager: # Creating progress dialogs ########################################################################## - class ProgressNoCancel(QProgressDialog): + class ProgressDialog(QDialog): + def __init__(self, parent): + QDialog.__init__(self, parent) + self.form = aqt.forms.progress.Ui_Dialog() + self.form.setupUi(self) + self._closingDown = False + self.wantCancel = False + + def cancel(self): + self._closingDown = True + self.close() + def closeEvent(self, evt): - evt.ignore() + if self._closingDown: + evt.accept() + else: + self.wantCancel = True + evt.ignore() + def keyPressEvent(self, evt): if evt.key() == Qt.Key_Escape: evt.ignore() + self.wantCancel = True - class ProgressCancellable(QProgressDialog): - def __init__(self, *args, **kwargs): - QProgressDialog.__init__(self, *args, **kwargs) - self.ankiCancel = False - def closeEvent(self, evt): - # avoid standard Qt flag as we don't want to close until we're ready - self.ankiCancel = True - evt.ignore() - def keyPressEvent(self, evt): - if evt.key() == Qt.Key_Escape: - evt.ignore() - self.ankiCancel = True - - def start(self, max=0, min=0, label=None, parent=None, immediate=False, cancellable=False): + def start(self, max=0, min=0, label=None, parent=None, immediate=False): self._levels += 1 if self._levels > 1: return @@ -103,20 +109,13 @@ class ProgressManager: parent = self.mw label = label or _("Processing...") - if cancellable: - klass = self.ProgressCancellable - else: - klass = self.ProgressNoCancel - self._win = klass(label, "", min, max, parent) + self._win = self.ProgressDialog(parent) + self._win.form.progressBar.setMinimum(min) + self._win.form.progressBar.setMaximum(max) + self._win.form.label.setText(label) self._win.setWindowTitle("Anki") - self._win.setCancelButton(None) - self._win.setAutoClose(False) - self._win.setAutoReset(False) self._win.setWindowModality(Qt.ApplicationModal) self._win.setMinimumWidth(300) - # we need to manually manage minimum time to show, as qt gets confused - # by the db handler - self._win.setMinimumDuration(100000) if immediate: self._showWin() else: @@ -137,10 +136,10 @@ class ProgressManager: self._maybeShow() elapsed = time.time() - self._lastUpdate if label: - self._win.setLabelText(label) + self._win.form.label.setText(label) if self._max and self._shown: self._counter = value or (self._counter+1) - self._win.setValue(self._counter) + self._win.form.progressBar.setValue(self._counter) if process and elapsed >= 0.2: self._updating = True self.app.processEvents(QEventLoop.ExcludeUserInputEvents) @@ -172,6 +171,7 @@ class ProgressManager: def _showWin(self): self._shown = time.time() self._win.show() + self._win.adjustSize() self._setBusy() def _closeWin(self): @@ -186,6 +186,7 @@ class ProgressManager: break self.app.processEvents(QEventLoop.ExcludeUserInputEvents) self._win.cancel() + self._win = None self._unsetBusy() def _setBusy(self): diff --git a/aqt/sync.py b/aqt/sync.py index 5b00d720d..78139473c 100644 --- a/aqt/sync.py +++ b/aqt/sync.py @@ -46,12 +46,12 @@ class SyncManager(QObject): auth=auth, media=self.pm.profile['syncMedia']) t.event.connect(self.onEvent) self.label = _("Connecting...") - prog = self.mw.progress.start(immediate=True, label=self.label, cancellable=True) + prog = self.mw.progress.start(immediate=True, label=self.label) self.sentBytes = self.recvBytes = 0 self._updateLabel() self.thread.start() while not self.thread.isFinished(): - if prog.ankiCancel: + if prog.wantCancel: self.thread.flagAbort() # make sure we don't display 'upload success' msg self._didFullUp = False diff --git a/designer/progress.ui b/designer/progress.ui new file mode 100644 index 000000000..5dbfdbbc7 --- /dev/null +++ b/designer/progress.ui @@ -0,0 +1,70 @@ + + + Dialog + + + + 0 + 0 + 310 + 69 + + + + Dialog + + + + 6 + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + Qt::AlignCenter + + + + + + + 24 + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + +