mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
Use backend rows in browser.py
This commit is contained in:
parent
c68a6131e0
commit
922fccee58
2 changed files with 116 additions and 245 deletions
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, List, Literal, Optional, Sequence, Tuple, Union
|
from typing import Any, Generator, List, Literal, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
import anki._backend.backend_pb2 as _pb
|
import anki._backend.backend_pb2 as _pb
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ UndoStatus = _pb.UndoStatus
|
||||||
OpChanges = _pb.OpChanges
|
OpChanges = _pb.OpChanges
|
||||||
OpChangesWithCount = _pb.OpChangesWithCount
|
OpChangesWithCount = _pb.OpChangesWithCount
|
||||||
DefaultsForAdding = _pb.DeckAndNotetype
|
DefaultsForAdding = _pb.DeckAndNotetype
|
||||||
|
BrowserRow = _pb.BrowserRow
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
|
@ -682,6 +683,20 @@ class Collection:
|
||||||
else:
|
else:
|
||||||
return SearchNode.Group.Joiner.OR
|
return SearchNode.Group.Joiner.OR
|
||||||
|
|
||||||
|
# Browser rows
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def browser_row_for_card(
|
||||||
|
self, cid: int
|
||||||
|
) -> Tuple[Generator[Tuple[str, bool], None, None], BrowserRow.Color, str, int]:
|
||||||
|
row = self._backend.browser_row_for_card(cid)
|
||||||
|
return (
|
||||||
|
((cell.text, cell.is_rtl) for cell in row.cells),
|
||||||
|
row.color,
|
||||||
|
row.font_name,
|
||||||
|
row.font_size,
|
||||||
|
)
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
|
@ -5,21 +5,32 @@ from __future__ import annotations
|
||||||
|
|
||||||
import html
|
import html
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, cast
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Generator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Tuple,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
import aqt.forms
|
import aqt.forms
|
||||||
from anki.cards import Card
|
from anki.cards import Card
|
||||||
from anki.collection import Collection, Config, OpChanges, SearchNode
|
from anki.collection import BrowserRow, Collection, Config, OpChanges, SearchNode
|
||||||
from anki.consts import *
|
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.stats import CardStats
|
from anki.stats import CardStats
|
||||||
from anki.tags import MARKED_TAG
|
from anki.tags import MARKED_TAG
|
||||||
from anki.utils import htmlToTextLine, ids2str, isMac, isWin
|
from anki.utils import ids2str, isMac, isWin
|
||||||
from aqt import AnkiQt, colors, gui_hooks
|
from aqt import AnkiQt, colors, gui_hooks
|
||||||
from aqt.card_ops import set_card_deck, set_card_flag
|
from aqt.card_ops import set_card_deck, set_card_flag
|
||||||
from aqt.editor import Editor
|
from aqt.editor import Editor
|
||||||
|
@ -91,25 +102,63 @@ class SearchContext:
|
||||||
# Data model
|
# Data model
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
# temporary cache to avoid hitting the database on redraw
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Cell:
|
class Cell:
|
||||||
text: str = ""
|
text: str
|
||||||
font: Optional[Tuple[str, int]] = None
|
is_rtl: bool
|
||||||
is_rtl: bool = False
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class CellRow:
|
class CellRow:
|
||||||
columns: List[Cell]
|
def __init__(
|
||||||
refreshed_at: float = field(default_factory=time.time)
|
self,
|
||||||
card_flag: int = 0
|
cells: Generator[Tuple[str, bool], None, None],
|
||||||
marked: bool = False
|
color: BrowserRow.Color.V,
|
||||||
suspended: bool = False
|
font_name: str,
|
||||||
|
font_size: int,
|
||||||
|
) -> None:
|
||||||
|
self.refreshed_at: float = time.time()
|
||||||
|
self.cells: Tuple[Cell, ...] = tuple(Cell(*cell) for cell in cells)
|
||||||
|
self.color: Optional[Tuple[str, str]] = backend_color_to_aqt_color(color)
|
||||||
|
self.font_name: str = font_name or "arial"
|
||||||
|
self.font_size: int = font_size if font_size > 0 else 12
|
||||||
|
|
||||||
def is_stale(self, threshold: float) -> bool:
|
def is_stale(self, threshold: float) -> bool:
|
||||||
return self.refreshed_at < threshold
|
return self.refreshed_at < threshold
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generic(length: int, cell_text: str) -> CellRow:
|
||||||
|
return CellRow(
|
||||||
|
((cell_text, False) for cell in range(length)),
|
||||||
|
BrowserRow.COLOR_DEFAULT,
|
||||||
|
"arial",
|
||||||
|
12,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def placeholder(length: int) -> CellRow:
|
||||||
|
return CellRow.generic(length, "...")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deleted(length: int) -> CellRow:
|
||||||
|
return CellRow.generic(length, tr(TR.BROWSING_ROW_DELETED))
|
||||||
|
|
||||||
|
|
||||||
|
def backend_color_to_aqt_color(color: BrowserRow.Color.V) -> Optional[Tuple[str, str]]:
|
||||||
|
if color == BrowserRow.COLOR_MARKED:
|
||||||
|
return colors.MARKED_BG
|
||||||
|
if color == BrowserRow.COLOR_SUSPENDED:
|
||||||
|
return colors.SUSPENDED_BG
|
||||||
|
if color == BrowserRow.COLOR_FLAG_RED:
|
||||||
|
return colors.FLAG1_BG
|
||||||
|
if color == BrowserRow.COLOR_FLAG_ORANGE:
|
||||||
|
return colors.FLAG2_BG
|
||||||
|
if color == BrowserRow.COLOR_FLAG_GREEN:
|
||||||
|
return colors.FLAG3_BG
|
||||||
|
if color == BrowserRow.COLOR_FLAG_BLUE:
|
||||||
|
return colors.FLAG4_BG
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class DataModel(QAbstractTableModel):
|
class DataModel(QAbstractTableModel):
|
||||||
def __init__(self, browser: Browser) -> None:
|
def __init__(self, browser: Browser) -> None:
|
||||||
|
@ -121,73 +170,38 @@ class DataModel(QAbstractTableModel):
|
||||||
"activeCols", ["noteFld", "template", "cardDue", "deck"]
|
"activeCols", ["noteFld", "template", "cardDue", "deck"]
|
||||||
)
|
)
|
||||||
self.cards: Sequence[int] = []
|
self.cards: Sequence[int] = []
|
||||||
self.cardObjs: Dict[int, Card] = {}
|
self._rows: Dict[int, CellRow] = {}
|
||||||
self._row_cache: Dict[int, CellRow] = {}
|
|
||||||
self._last_refresh = 0.0
|
self._last_refresh = 0.0
|
||||||
# serve stale content to avoid hitting the DB?
|
# serve stale content to avoid hitting the DB?
|
||||||
self.block_updates = False
|
self.block_updates = False
|
||||||
|
|
||||||
def getCard(self, index: QModelIndex) -> Optional[Card]:
|
def get_id(self, index: QModelIndex) -> int:
|
||||||
return self._get_card_by_row(index.row())
|
return self.cards[index.row()]
|
||||||
|
|
||||||
def _get_card_by_row(self, row: int) -> Optional[Card]:
|
|
||||||
"None if card is not in DB."
|
|
||||||
id = self.cards[row]
|
|
||||||
if not id in self.cardObjs:
|
|
||||||
try:
|
|
||||||
card = self.col.getCard(id)
|
|
||||||
except NotFoundError:
|
|
||||||
# deleted
|
|
||||||
card = None
|
|
||||||
self.cardObjs[id] = card
|
|
||||||
return self.cardObjs[id]
|
|
||||||
|
|
||||||
# Card and cell data cache
|
|
||||||
######################################################################
|
|
||||||
# Stopgap until we can fetch this data a row at a time from Rust.
|
|
||||||
|
|
||||||
def get_cell(self, index: QModelIndex) -> Cell:
|
def get_cell(self, index: QModelIndex) -> Cell:
|
||||||
row = self.get_row(index.row())
|
return self.get_row(index).cells[index.column()]
|
||||||
return row.columns[index.column()]
|
|
||||||
|
|
||||||
def get_row(self, row: int) -> CellRow:
|
def get_row(self, index: QModelIndex) -> CellRow:
|
||||||
if entry := self._row_cache.get(row):
|
cid = self.get_id(index)
|
||||||
if not self.block_updates and entry.is_stale(self._last_refresh):
|
if row := self._rows.get(cid):
|
||||||
|
if not self.block_updates and row.is_stale(self._last_refresh):
|
||||||
# need to refresh
|
# need to refresh
|
||||||
entry = self._build_cell_row(row)
|
self._rows[cid] = self._fetch_row_from_backend(cid)
|
||||||
self._row_cache[row] = entry
|
return self._rows[cid]
|
||||||
return entry
|
# return row, even if it's stale
|
||||||
else:
|
return row
|
||||||
# return entry, even if it's stale
|
if self.block_updates:
|
||||||
return entry
|
# blank row until we unblock
|
||||||
elif self.block_updates:
|
return CellRow.placeholder(len(self.activeCols))
|
||||||
# blank entry until we unblock
|
# missing row, need to build
|
||||||
return CellRow(columns=[Cell(text="...")] * len(self.activeCols))
|
self._rows[cid] = self._fetch_row_from_backend(cid)
|
||||||
else:
|
return self._rows[cid]
|
||||||
# missing entry, need to build
|
|
||||||
entry = self._build_cell_row(row)
|
|
||||||
self._row_cache[row] = entry
|
|
||||||
return entry
|
|
||||||
|
|
||||||
def _build_cell_row(self, row: int) -> CellRow:
|
def _fetch_row_from_backend(self, cid: int) -> CellRow:
|
||||||
if not (card := self._get_card_by_row(row)):
|
try:
|
||||||
cell = Cell(text=tr(TR.BROWSING_ROW_DELETED))
|
return CellRow(*self.col.browser_row_for_card(cid))
|
||||||
return CellRow(columns=[cell] * len(self.activeCols))
|
except:
|
||||||
|
return CellRow.deleted(len(self.activeCols))
|
||||||
return CellRow(
|
|
||||||
columns=[
|
|
||||||
Cell(
|
|
||||||
text=self._column_data(card, column_type),
|
|
||||||
font=self._font(card, column_type),
|
|
||||||
is_rtl=self._is_rtl(card, column_type),
|
|
||||||
)
|
|
||||||
for column_type in self.activeCols
|
|
||||||
],
|
|
||||||
# should probably make these an enum instead?
|
|
||||||
card_flag=card.user_flag(),
|
|
||||||
marked=card.note().has_tag(MARKED_TAG),
|
|
||||||
suspended=card.queue == QUEUE_TYPE_SUSPENDED,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Model interface
|
# Model interface
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -206,13 +220,13 @@ class DataModel(QAbstractTableModel):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return
|
return
|
||||||
if role == Qt.FontRole:
|
if role == Qt.FontRole:
|
||||||
if font := self.get_cell(index).font:
|
if self.activeCols[index.column()] not in ("question", "answer", "noteFld"):
|
||||||
qfont = QFont()
|
return
|
||||||
qfont.setFamily(font[0])
|
qfont = QFont()
|
||||||
qfont.setPixelSize(font[1])
|
row = self.get_row(index)
|
||||||
return qfont
|
qfont.setFamily(row.font_name)
|
||||||
else:
|
qfont.setPixelSize(row.font_size)
|
||||||
return None
|
return qfont
|
||||||
|
|
||||||
elif role == Qt.TextAlignmentRole:
|
elif role == Qt.TextAlignmentRole:
|
||||||
align: Union[Qt.AlignmentFlag, int] = Qt.AlignVCenter
|
align: Union[Qt.AlignmentFlag, int] = Qt.AlignVCenter
|
||||||
|
@ -238,7 +252,7 @@ class DataModel(QAbstractTableModel):
|
||||||
if orientation == Qt.Vertical:
|
if orientation == Qt.Vertical:
|
||||||
return None
|
return None
|
||||||
elif role == Qt.DisplayRole and section < len(self.activeCols):
|
elif role == Qt.DisplayRole and section < len(self.activeCols):
|
||||||
type = self.columnType(section)
|
type = self.activeCols[section]
|
||||||
txt = None
|
txt = None
|
||||||
for stype, name in self.browser.columns:
|
for stype, name in self.browser.columns:
|
||||||
if type == stype:
|
if type == stype:
|
||||||
|
@ -291,8 +305,7 @@ class DataModel(QAbstractTableModel):
|
||||||
self.browser.mw.progress.start()
|
self.browser.mw.progress.start()
|
||||||
self.saveSelection()
|
self.saveSelection()
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
self.cardObjs = {}
|
self._rows = {}
|
||||||
self._row_cache = {}
|
|
||||||
|
|
||||||
def endReset(self) -> None:
|
def endReset(self) -> None:
|
||||||
self.endResetModel()
|
self.endResetModel()
|
||||||
|
@ -366,8 +379,7 @@ class DataModel(QAbstractTableModel):
|
||||||
def op_executed(self, op: OpChanges, focused: bool) -> None:
|
def op_executed(self, op: OpChanges, focused: bool) -> None:
|
||||||
print("op executed")
|
print("op executed")
|
||||||
if op.card or op.note or op.deck or op.notetype:
|
if op.card or op.note or op.deck or op.notetype:
|
||||||
# clear card cache
|
self._rows = {}
|
||||||
self.cardObjs = {}
|
|
||||||
if focused:
|
if focused:
|
||||||
self.redraw_cells()
|
self.redraw_cells()
|
||||||
|
|
||||||
|
@ -378,148 +390,6 @@ class DataModel(QAbstractTableModel):
|
||||||
self.block_updates = False
|
self.block_updates = False
|
||||||
self.redraw_cells()
|
self.redraw_cells()
|
||||||
|
|
||||||
# Column data
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def columnType(self, column: int) -> str:
|
|
||||||
return self.activeCols[column]
|
|
||||||
|
|
||||||
def time_format(self) -> str:
|
|
||||||
return "%Y-%m-%d"
|
|
||||||
|
|
||||||
def _font(self, card: Card, column_type: str) -> Optional[Tuple[str, int]]:
|
|
||||||
if column_type not in ("question", "answer", "noteFld"):
|
|
||||||
return None
|
|
||||||
|
|
||||||
template = card.template()
|
|
||||||
if not template.get("bfont"):
|
|
||||||
return None
|
|
||||||
|
|
||||||
return (
|
|
||||||
cast(str, template.get("bfont", "arial")),
|
|
||||||
cast(int, template.get("bsize", 12)),
|
|
||||||
)
|
|
||||||
|
|
||||||
# legacy
|
|
||||||
def columnData(self, index: QModelIndex) -> str:
|
|
||||||
col = index.column()
|
|
||||||
type = self.columnType(col)
|
|
||||||
c = self.getCard(index)
|
|
||||||
if not c:
|
|
||||||
return tr(TR.BROWSING_ROW_DELETED)
|
|
||||||
else:
|
|
||||||
return self._column_data(c, type)
|
|
||||||
|
|
||||||
def _column_data(self, card: Card, column_type: str) -> str:
|
|
||||||
type = column_type
|
|
||||||
if type == "question":
|
|
||||||
return self.question(card)
|
|
||||||
elif type == "answer":
|
|
||||||
return self.answer(card)
|
|
||||||
elif type == "noteFld":
|
|
||||||
f = card.note()
|
|
||||||
return htmlToTextLine(f.fields[self.col.models.sortIdx(f.model())])
|
|
||||||
elif type == "template":
|
|
||||||
t = card.template()["name"]
|
|
||||||
if card.model()["type"] == MODEL_CLOZE:
|
|
||||||
t = f"{t} {card.ord + 1}"
|
|
||||||
return cast(str, t)
|
|
||||||
elif type == "cardDue":
|
|
||||||
# catch invalid dates
|
|
||||||
try:
|
|
||||||
t = self._next_due(card)
|
|
||||||
except:
|
|
||||||
t = ""
|
|
||||||
if card.queue < 0:
|
|
||||||
t = f"({t})"
|
|
||||||
return t
|
|
||||||
elif type == "noteCrt":
|
|
||||||
return time.strftime(
|
|
||||||
self.time_format(), time.localtime(card.note().id / 1000)
|
|
||||||
)
|
|
||||||
elif type == "noteMod":
|
|
||||||
return time.strftime(self.time_format(), time.localtime(card.note().mod))
|
|
||||||
elif type == "cardMod":
|
|
||||||
return time.strftime(self.time_format(), time.localtime(card.mod))
|
|
||||||
elif type == "cardReps":
|
|
||||||
return str(card.reps)
|
|
||||||
elif type == "cardLapses":
|
|
||||||
return str(card.lapses)
|
|
||||||
elif type == "noteTags":
|
|
||||||
return " ".join(card.note().tags)
|
|
||||||
elif type == "note":
|
|
||||||
return card.model()["name"]
|
|
||||||
elif type == "cardIvl":
|
|
||||||
if card.type == CARD_TYPE_NEW:
|
|
||||||
return tr(TR.BROWSING_NEW)
|
|
||||||
elif card.type == CARD_TYPE_LRN:
|
|
||||||
return tr(TR.BROWSING_LEARNING)
|
|
||||||
return self.col.format_timespan(card.ivl * 86400)
|
|
||||||
elif type == "cardEase":
|
|
||||||
if card.type == CARD_TYPE_NEW:
|
|
||||||
return tr(TR.BROWSING_NEW)
|
|
||||||
return "%d%%" % (card.factor / 10)
|
|
||||||
elif type == "deck":
|
|
||||||
if card.odid:
|
|
||||||
# in a cram deck
|
|
||||||
return "%s (%s)" % (
|
|
||||||
self.browser.mw.col.decks.name(card.did),
|
|
||||||
self.browser.mw.col.decks.name(card.odid),
|
|
||||||
)
|
|
||||||
# normal deck
|
|
||||||
return self.browser.mw.col.decks.name(card.did)
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def question(self, c: Card) -> str:
|
|
||||||
return htmlToTextLine(c.q(browser=True))
|
|
||||||
|
|
||||||
def answer(self, c: Card) -> str:
|
|
||||||
if c.template().get("bafmt"):
|
|
||||||
# they have provided a template, use it verbatim
|
|
||||||
c.q(browser=True)
|
|
||||||
return htmlToTextLine(c.a())
|
|
||||||
# need to strip question from answer
|
|
||||||
q = self.question(c)
|
|
||||||
a = htmlToTextLine(c.a())
|
|
||||||
if a.startswith(q):
|
|
||||||
return a[len(q) :].strip()
|
|
||||||
return a
|
|
||||||
|
|
||||||
# legacy
|
|
||||||
def nextDue(self, c: Card, index: QModelIndex) -> str:
|
|
||||||
return self._next_due(c)
|
|
||||||
|
|
||||||
def _next_due(self, card: Card) -> str:
|
|
||||||
date: float
|
|
||||||
if card.odid:
|
|
||||||
return tr(TR.BROWSING_FILTERED)
|
|
||||||
elif card.queue == QUEUE_TYPE_LRN:
|
|
||||||
date = card.due
|
|
||||||
elif card.queue == QUEUE_TYPE_NEW or card.type == CARD_TYPE_NEW:
|
|
||||||
return tr(TR.STATISTICS_DUE_FOR_NEW_CARD, number=card.due)
|
|
||||||
elif card.queue in (QUEUE_TYPE_REV, QUEUE_TYPE_DAY_LEARN_RELEARN) or (
|
|
||||||
card.type == CARD_TYPE_REV and card.queue < 0
|
|
||||||
):
|
|
||||||
date = time.time() + ((card.due - self.col.sched.today) * 86400)
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
return time.strftime(self.time_format(), time.localtime(date))
|
|
||||||
|
|
||||||
# legacy
|
|
||||||
def isRTL(self, index: QModelIndex) -> bool:
|
|
||||||
col = index.column()
|
|
||||||
type = self.columnType(col)
|
|
||||||
c = self.getCard(index)
|
|
||||||
return self._is_rtl(c, type)
|
|
||||||
|
|
||||||
def _is_rtl(self, card: Card, column_type: str) -> bool:
|
|
||||||
if column_type != "noteFld":
|
|
||||||
return False
|
|
||||||
|
|
||||||
nt = card.note().model()
|
|
||||||
return nt["flds"][self.col.models.sortIdx(nt)]["rtl"]
|
|
||||||
|
|
||||||
|
|
||||||
# Line painter
|
# Line painter
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -534,27 +404,13 @@ class StatusDelegate(QItemDelegate):
|
||||||
def paint(
|
def paint(
|
||||||
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
|
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
|
||||||
) -> None:
|
) -> None:
|
||||||
row = self.model.get_row(index.row())
|
if self.model.get_cell(index).is_rtl:
|
||||||
cell = row.columns[index.column()]
|
|
||||||
|
|
||||||
if cell.is_rtl:
|
|
||||||
option.direction = Qt.RightToLeft
|
option.direction = Qt.RightToLeft
|
||||||
|
if row_color := self.model.get_row(index).color:
|
||||||
if row.card_flag:
|
brush = QBrush(theme_manager.qcolor(row_color))
|
||||||
color = getattr(colors, f"FLAG{row.card_flag}_BG")
|
|
||||||
elif row.marked:
|
|
||||||
color = colors.MARKED_BG
|
|
||||||
elif row.suspended:
|
|
||||||
color = colors.SUSPENDED_BG
|
|
||||||
else:
|
|
||||||
color = None
|
|
||||||
|
|
||||||
if color:
|
|
||||||
brush = QBrush(theme_manager.qcolor(color))
|
|
||||||
painter.save()
|
painter.save()
|
||||||
painter.fillRect(option.rect, brush)
|
painter.fillRect(option.rect, brush)
|
||||||
painter.restore()
|
painter.restore()
|
||||||
|
|
||||||
return QItemDelegate.paint(self, painter, option, index)
|
return QItemDelegate.paint(self, painter, option, index)
|
||||||
|
|
||||||
|
|
||||||
|
@ -962,7 +818,7 @@ QTableView {{ gridline-color: {grid} }}
|
||||||
show = self.model.cards and update == 1
|
show = self.model.cards and update == 1
|
||||||
idx = self.form.tableView.selectionModel().currentIndex()
|
idx = self.form.tableView.selectionModel().currentIndex()
|
||||||
if idx.isValid():
|
if idx.isValid():
|
||||||
self.card = self.model.getCard(idx)
|
self.card = self.col.getCard(self.model.get_id(idx))
|
||||||
show = show and self.card is not None
|
show = show and self.card is not None
|
||||||
self.form.splitter.widget(1).setVisible(bool(show))
|
self.form.splitter.widget(1).setVisible(bool(show))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue