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

View file

@ -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

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>