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
This commit is contained in:
Damien Elmes 2017-12-28 18:31:05 +10:00
parent ab46a4530c
commit 5ad8f67f12
3 changed files with 105 additions and 34 deletions

View file

@ -4,6 +4,7 @@
import time import time
from aqt.qt import * from aqt.qt import *
import aqt.forms
# fixme: if mw->subwindow opens a progress dialog with mw as the parent, mw # 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 # 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.app.processEvents(QEventLoop.ExcludeUserInputEvents)
self.inDB = False self.inDB = False
# DB-safe timers # Safer timers
########################################################################## ##########################################################################
# QTimer may fire in processEvents(). We provide a custom timer which # 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 timer(self, ms, func, repeat):
def handler(): def handler():
if self.inDB: if self.inDB or self._levels:
# retry in 100ms # retry in 100ms
self.timer(100, func, False) self.timer(100, func, False)
else: else:
@ -73,27 +75,31 @@ class ProgressManager:
# Creating progress dialogs # 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): def closeEvent(self, evt):
if self._closingDown:
evt.accept()
else:
self.wantCancel = True
evt.ignore() evt.ignore()
def keyPressEvent(self, evt): def keyPressEvent(self, evt):
if evt.key() == Qt.Key_Escape: if evt.key() == Qt.Key_Escape:
evt.ignore() evt.ignore()
self.wantCancel = True
class ProgressCancellable(QProgressDialog): def start(self, max=0, min=0, label=None, parent=None, immediate=False):
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):
self._levels += 1 self._levels += 1
if self._levels > 1: if self._levels > 1:
return return
@ -103,20 +109,13 @@ class ProgressManager:
parent = self.mw parent = self.mw
label = label or _("Processing...") label = label or _("Processing...")
if cancellable: self._win = self.ProgressDialog(parent)
klass = self.ProgressCancellable self._win.form.progressBar.setMinimum(min)
else: self._win.form.progressBar.setMaximum(max)
klass = self.ProgressNoCancel self._win.form.label.setText(label)
self._win = klass(label, "", min, max, parent)
self._win.setWindowTitle("Anki") self._win.setWindowTitle("Anki")
self._win.setCancelButton(None)
self._win.setAutoClose(False)
self._win.setAutoReset(False)
self._win.setWindowModality(Qt.ApplicationModal) self._win.setWindowModality(Qt.ApplicationModal)
self._win.setMinimumWidth(300) 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: if immediate:
self._showWin() self._showWin()
else: else:
@ -137,10 +136,10 @@ class ProgressManager:
self._maybeShow() self._maybeShow()
elapsed = time.time() - self._lastUpdate elapsed = time.time() - self._lastUpdate
if label: if label:
self._win.setLabelText(label) self._win.form.label.setText(label)
if self._max and self._shown: if self._max and self._shown:
self._counter = value or (self._counter+1) 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: if process and elapsed >= 0.2:
self._updating = True self._updating = True
self.app.processEvents(QEventLoop.ExcludeUserInputEvents) self.app.processEvents(QEventLoop.ExcludeUserInputEvents)
@ -172,6 +171,7 @@ class ProgressManager:
def _showWin(self): def _showWin(self):
self._shown = time.time() self._shown = time.time()
self._win.show() self._win.show()
self._win.adjustSize()
self._setBusy() self._setBusy()
def _closeWin(self): def _closeWin(self):
@ -186,6 +186,7 @@ class ProgressManager:
break break
self.app.processEvents(QEventLoop.ExcludeUserInputEvents) self.app.processEvents(QEventLoop.ExcludeUserInputEvents)
self._win.cancel() self._win.cancel()
self._win = None
self._unsetBusy() self._unsetBusy()
def _setBusy(self): def _setBusy(self):

View file

@ -46,12 +46,12 @@ class SyncManager(QObject):
auth=auth, media=self.pm.profile['syncMedia']) auth=auth, media=self.pm.profile['syncMedia'])
t.event.connect(self.onEvent) t.event.connect(self.onEvent)
self.label = _("Connecting...") 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.sentBytes = self.recvBytes = 0
self._updateLabel() self._updateLabel()
self.thread.start() self.thread.start()
while not self.thread.isFinished(): while not self.thread.isFinished():
if prog.ankiCancel: if prog.wantCancel:
self.thread.flagAbort() self.thread.flagAbort()
# make sure we don't display 'upload success' msg # make sure we don't display 'upload success' msg
self._didFullUp = False self._didFullUp = False

70
designer/progress.ui Normal file
View file

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>310</width>
<height>69</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>6</number>
</property>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>