various redraw fixes

- need to drop cardObjs cache when updating cells
- stop listening on editor_did_* hooks. unfocus_field and typing_timer
are covered by operation_did_execute on note save already, and the
user potentially has editors open in other windows as well
- distinguish between card queue refresh and note text redraw in review
screen again
- update preview window when note updated
- defer setUpdatesEnabled(True) until we receive focus again, as it
causes cells to redraw. We might want to use our own flag to prevent
updating in the model instead of using Qt for this
This commit is contained in:
Damien Elmes 2021-03-16 18:30:54 +10:00
parent 3f87f7bf5c
commit 017005a4f8
3 changed files with 43 additions and 43 deletions

View file

@ -17,7 +17,6 @@ from anki.consts import *
from anki.errors import InvalidInput, NotFoundError from anki.errors import InvalidInput, NotFoundError
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from anki.models import NoteType from anki.models import NoteType
from anki.notes import Note
from anki.stats import CardStats from anki.stats import CardStats
from anki.utils import htmlToTextLine, ids2str, isMac, isWin from anki.utils import htmlToTextLine, ids2str, isMac, isWin
from aqt import AnkiQt, colors, gui_hooks from aqt import AnkiQt, colors, gui_hooks
@ -112,15 +111,6 @@ class DataModel(QAbstractTableModel):
self.cardObjs[id] = card self.cardObjs[id] = card
return self.cardObjs[id] return self.cardObjs[id]
def refreshNote(self, note: Note) -> None:
refresh = False
for c in note.cards():
if c.id in self.cardObjs:
del self.cardObjs[c.id]
refresh = True
if refresh:
self.layoutChanged.emit() # type: ignore
# Model interface # Model interface
###################################################################### ######################################################################
@ -214,8 +204,9 @@ class DataModel(QAbstractTableModel):
if not self.cards: if not self.cards:
return return
top_left = self.index(0, 0) top_left = self.index(0, 0)
bottom_right = self.index(len(self.cards)-1, len(self.activeCols)-1) bottom_right = self.index(len(self.cards) - 1, len(self.activeCols) - 1)
self.dataChanged.emit(top_left, bottom_right) self.cardObjs = {}
self.dataChanged.emit(top_left, bottom_right) # type: ignore
def reset(self) -> None: def reset(self) -> None:
self.beginReset() self.beginReset()
@ -513,16 +504,22 @@ class Browser(QMainWindow):
self.setUpdatesEnabled(False) self.setUpdatesEnabled(False)
def on_operation_did_execute(self, changes: OpChanges) -> None: def on_operation_did_execute(self, changes: OpChanges) -> None:
focused = current_top_level_widget() == self
if focused:
self.setUpdatesEnabled(True) self.setUpdatesEnabled(True)
self.model.op_executed(changes, current_top_level_widget() == self) self.model.op_executed(changes, focused)
if (changes.note or changes.notetype) and not self.editor.is_updating_note(): if changes.note or changes.notetype:
if not self.editor.is_updating_note():
note = self.editor.note note = self.editor.note
if note: if note:
note.load() note.load()
self.editor.set_note(note) self.editor.set_note(note)
self._renderPreview()
def on_focus_change(self, new: Optional[QWidget], old: Optional[QWidget]) -> None: def on_focus_change(self, new: Optional[QWidget], old: Optional[QWidget]) -> None:
if current_top_level_widget() == self: if current_top_level_widget() == self:
self.setUpdatesEnabled(True)
self.model.refresh_if_needed() self.model.refresh_if_needed()
def setupMenus(self) -> None: def setupMenus(self) -> None:
@ -860,13 +857,6 @@ QTableView {{ gridline-color: {grid} }}
self._updateFlagsMenu() self._updateFlagsMenu()
gui_hooks.browser_did_change_row(self) gui_hooks.browser_did_change_row(self)
def refreshCurrentCard(self, note: Note) -> None:
self.model.refreshNote(note)
self._renderPreview()
def onLoadNote(self, editor: Editor) -> None:
self.refreshCurrentCard(editor.note)
def currentRow(self) -> int: def currentRow(self) -> int:
idx = self.form.tableView.selectionModel().currentIndex() idx = self.form.tableView.selectionModel().currentIndex()
return idx.row() return idx.row()
@ -1452,9 +1442,6 @@ where id in %s"""
def setupHooks(self) -> None: def setupHooks(self) -> None:
gui_hooks.undo_state_did_change.append(self.onUndoState) gui_hooks.undo_state_did_change.append(self.onUndoState)
gui_hooks.editor_did_fire_typing_timer.append(self.refreshCurrentCard)
gui_hooks.editor_did_load_note.append(self.onLoadNote)
gui_hooks.editor_did_unfocus_field.append(self.on_unfocus_field)
gui_hooks.sidebar_should_refresh_decks.append(self.on_item_added) gui_hooks.sidebar_should_refresh_decks.append(self.on_item_added)
gui_hooks.sidebar_should_refresh_notetypes.append(self.on_item_added) gui_hooks.sidebar_should_refresh_notetypes.append(self.on_item_added)
gui_hooks.operation_will_execute.append(self.on_operation_will_execute) gui_hooks.operation_will_execute.append(self.on_operation_will_execute)
@ -1463,18 +1450,12 @@ where id in %s"""
def teardownHooks(self) -> None: def teardownHooks(self) -> None:
gui_hooks.undo_state_did_change.remove(self.onUndoState) gui_hooks.undo_state_did_change.remove(self.onUndoState)
gui_hooks.editor_did_fire_typing_timer.remove(self.refreshCurrentCard)
gui_hooks.editor_did_load_note.remove(self.onLoadNote)
gui_hooks.editor_did_unfocus_field.remove(self.on_unfocus_field)
gui_hooks.sidebar_should_refresh_decks.remove(self.on_item_added) gui_hooks.sidebar_should_refresh_decks.remove(self.on_item_added)
gui_hooks.sidebar_should_refresh_notetypes.remove(self.on_item_added) gui_hooks.sidebar_should_refresh_notetypes.remove(self.on_item_added)
gui_hooks.operation_will_execute.remove(self.on_operation_will_execute) gui_hooks.operation_will_execute.remove(self.on_operation_will_execute)
gui_hooks.operation_did_execute.remove(self.on_operation_did_execute) gui_hooks.operation_did_execute.remove(self.on_operation_did_execute)
gui_hooks.focus_did_change.remove(self.on_focus_change) gui_hooks.focus_did_change.remove(self.on_focus_change)
def on_unfocus_field(self, changed: bool, note: Note, field_idx: int) -> None:
self.refreshCurrentCard(note)
# covers the tag, note and deck case # covers the tag, note and deck case
def on_item_added(self, item: Any = None) -> None: def on_item_added(self, item: Any = None) -> None:
self.sidebar.refresh() self.sidebar.refresh()

View file

@ -90,6 +90,7 @@ _html = """
</div> </div>
""" """
class Editor: class Editor:
"""The screen that embeds an editing widget should listen for changes via """The screen that embeds an editing widget should listen for changes via
the `operation_did_execute` hook, and call set_note() when the editor needs the `operation_did_execute` hook, and call set_note() when the editor needs

View file

@ -7,6 +7,7 @@ import html
import json import json
import re import re
import unicodedata as ucd import unicodedata as ucd
from enum import Enum, auto
from typing import Any, Callable, List, Match, Optional, Sequence, Tuple, Union from typing import Any, Callable, List, Match, Optional, Sequence, Tuple, Union
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
@ -41,6 +42,12 @@ from aqt.utils import (
from aqt.webview import AnkiWebView from aqt.webview import AnkiWebView
class RefreshNeeded(Enum):
NO = auto()
NOTE_TEXT = auto()
QUEUES = auto()
class ReviewerBottomBar: class ReviewerBottomBar:
def __init__(self, reviewer: Reviewer) -> None: def __init__(self, reviewer: Reviewer) -> None:
self.reviewer = reviewer self.reviewer = reviewer
@ -69,7 +76,7 @@ class Reviewer:
self._recordedAudio: Optional[str] = None self._recordedAudio: Optional[str] = None
self.typeCorrect: str = None # web init happens before this is set self.typeCorrect: str = None # web init happens before this is set
self.state: Optional[str] = None self.state: Optional[str] = None
self._refresh_needed = False self._refresh_needed = RefreshNeeded.NO
self.bottom = BottomBar(mw, mw.bottomWeb) self.bottom = BottomBar(mw, mw.bottomWeb)
hooks.card_did_leech.append(self.onLeech) hooks.card_did_leech.append(self.onLeech)
@ -78,7 +85,7 @@ class Reviewer:
self.web.set_bridge_command(self._linkHandler, self) self.web.set_bridge_command(self._linkHandler, self)
self.bottom.web.set_bridge_command(self._linkHandler, ReviewerBottomBar(self)) self.bottom.web.set_bridge_command(self._linkHandler, ReviewerBottomBar(self))
self._reps: int = None self._reps: int = None
self._refresh_needed = True self._refresh_needed = RefreshNeeded.QUEUES
self.refresh_if_needed() self.refresh_if_needed()
def lastCard(self) -> Optional[Card]: def lastCard(self) -> Optional[Card]:
@ -96,11 +103,15 @@ class Reviewer:
self.card = None self.card = None
def refresh_if_needed(self) -> None: def refresh_if_needed(self) -> None:
if self._refresh_needed: if self._refresh_needed is RefreshNeeded.QUEUES:
self.mw.col.reset() self.mw.col.reset()
self.nextCard() self.nextCard()
self._refresh_needed = False
self.mw.fade_in_webview() self.mw.fade_in_webview()
self._refresh_needed = RefreshNeeded.NO
elif self._refresh_needed is RefreshNeeded.NOTE_TEXT:
self._redraw_current_card()
self.mw.fade_in_webview()
self._refresh_needed = RefreshNeeded.NO
def op_executed(self, changes: OpChanges, focused: bool) -> bool: def op_executed(self, changes: OpChanges, focused: bool) -> bool:
if changes.note and changes.kind == OpChanges.UPDATE_NOTE_TAGS: if changes.note and changes.kind == OpChanges.UPDATE_NOTE_TAGS:
@ -111,14 +122,21 @@ class Reviewer:
self.card.load() self.card.load()
self._update_flag_icon() self._update_flag_icon()
elif self.mw.col.op_affects_study_queue(changes): elif self.mw.col.op_affects_study_queue(changes):
self._refresh_needed = True self._refresh_needed = RefreshNeeded.QUEUES
elif changes.note or changes.notetype or changes.tag: elif changes.note or changes.notetype or changes.tag:
self._refresh_needed = True self._refresh_needed = RefreshNeeded.NOTE_TEXT
if focused and self._refresh_needed: if focused and self._refresh_needed is not RefreshNeeded.NO:
self.refresh_if_needed() self.refresh_if_needed()
return self._refresh_needed return self._refresh_needed is not RefreshNeeded.NO
def _redraw_current_card(self) -> None:
self.card.load()
if self.state == "answer":
self._showAnswer()
else:
self._showQuestion()
# Fetching a card # Fetching a card
########################################################################## ##########################################################################