Created
February 21, 2026 20:22
-
-
Save eros18123/ff4dd2a42f91b5e87d659adebff1de9f to your computer and use it in GitHub Desktop.
Oclusao de tabela aleatoria
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| import os | |
| import json | |
| import time | |
| from aqt.qt import * | |
| from aqt import gui_hooks | |
| from aqt import mw | |
| # Caminho para salvar as configurações | |
| ADDON_DIR = os.path.dirname(__file__) | |
| CONFIG_FILE = os.path.join(ADDON_DIR, "config_shuffle.json") | |
| class ShuffleTableDialog(QDialog): | |
| def __init__(self, editor): | |
| super().__init__(editor.widget) | |
| self.editor = editor | |
| self.setWindowTitle("Oclusão de Tabela Aleatória") | |
| self.resize(700, 500) | |
| self.cells = [] | |
| # Carrega a última configuração salva | |
| self.config_data = self.load_config() | |
| # Layouts principais | |
| self.main_layout = QVBoxLayout(self) | |
| self.controls_layout = QHBoxLayout() | |
| self.grid_layout = QGridLayout() | |
| self.buttons_layout = QHBoxLayout() | |
| # Controles de Linhas e Colunas | |
| self.rows_spin = QSpinBox() | |
| self.rows_spin.setRange(1, 100) | |
| self.rows_spin.setValue(self.config_data.get("rows", 5)) | |
| self.cols_spin = QSpinBox() | |
| self.cols_spin.setRange(1, 20) | |
| self.cols_spin.setValue(self.config_data.get("cols", 2)) | |
| # Checkbox para decidir se embaralha as colunas também | |
| self.shuffle_cols_cb = QCheckBox("Embaralhar itens na linha") | |
| self.shuffle_cols_cb.setChecked(self.config_data.get("shuffle_cols", True)) | |
| self.controls_layout.addWidget(QLabel("Linhas:")) | |
| self.controls_layout.addWidget(self.rows_spin) | |
| self.controls_layout.addWidget(QLabel("Colunas:")) | |
| self.controls_layout.addWidget(self.cols_spin) | |
| self.controls_layout.addStretch() | |
| # Instrução para o usuário | |
| instruction = QLabel("<i>Dica: Clique no botão <b>⬜</b> ao lado do texto para aplicar oclusão naquele item.</i>") | |
| self.main_layout.addLayout(self.controls_layout) | |
| self.main_layout.addWidget(instruction) | |
| # Área de rolagem para a grade de inputs | |
| self.grid_widget = QWidget() | |
| self.grid_widget.setLayout(self.grid_layout) | |
| self.scroll_area = QScrollArea() | |
| self.scroll_area.setWidgetResizable(True) | |
| self.scroll_area.setWidget(self.grid_widget) | |
| # Modo de oclusão | |
| mode_group = QGroupBox("Modo de Oclusão") | |
| mode_layout = QVBoxLayout(mode_group) | |
| self.mode_interactive = QRadioButton("Interativo — uma tabela, revelar célula por célula (atalho W ou clique)") | |
| self.mode_cards = QRadioButton("Um card por oclusão — gera um card separado para cada célula oculta") | |
| saved_mode = self.config_data.get("occlusion_mode", "interactive") | |
| if saved_mode == "cards": | |
| self.mode_cards.setChecked(True) | |
| else: | |
| self.mode_interactive.setChecked(True) | |
| mode_layout.addWidget(self.mode_interactive) | |
| mode_layout.addWidget(self.mode_cards) | |
| # Botões de ação | |
| self.btn_generate = QPushButton("Inserir Tabela no Card") | |
| self.btn_generate.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 5px;") | |
| self.buttons_layout.addWidget(self.shuffle_cols_cb) | |
| self.buttons_layout.addStretch() | |
| self.buttons_layout.addWidget(self.btn_generate) | |
| # Adicionando ao layout principal | |
| self.main_layout.addWidget(self.scroll_area) | |
| self.main_layout.addWidget(mode_group) | |
| self.main_layout.addLayout(self.buttons_layout) | |
| # Sinais (Eventos) | |
| self.rows_spin.valueChanged.connect(lambda: self.update_grid(initial_load=False)) | |
| self.cols_spin.valueChanged.connect(lambda: self.update_grid(initial_load=False)) | |
| self.btn_generate.clicked.connect(self.generate_and_insert) | |
| # Inicializa a grade com os dados salvos | |
| self.update_grid(initial_load=True) | |
| def load_config(self): | |
| default_config = {"rows": 5, "cols": 2, "shuffle_cols": True, "occlusion_mode": "interactive", "data": []} | |
| if os.path.exists(CONFIG_FILE): | |
| try: | |
| with open(CONFIG_FILE, "r", encoding="utf-8") as f: | |
| loaded = json.load(f) | |
| default_config.update(loaded) | |
| except Exception: | |
| pass | |
| return default_config | |
| def save_config(self, current_data): | |
| config_to_save = { | |
| "rows": self.rows_spin.value(), | |
| "cols": self.cols_spin.value(), | |
| "shuffle_cols": self.shuffle_cols_cb.isChecked(), | |
| "occlusion_mode": "cards" if self.mode_cards.isChecked() else "interactive", | |
| "data": current_data | |
| } | |
| try: | |
| with open(CONFIG_FILE, "w", encoding="utf-8") as f: | |
| json.dump(config_to_save, f, indent=4) | |
| except Exception as e: | |
| pass | |
| def update_grid(self, initial_load=False): | |
| rows = self.rows_spin.value() | |
| cols = self.cols_spin.value() | |
| old_values = [] | |
| if initial_load: | |
| old_values = self.config_data.get("data", []) | |
| else: | |
| for r in range(len(self.cells)): | |
| row_vals = [] | |
| for c in range(len(self.cells[r])): | |
| row_vals.append({ | |
| "text": self.cells[r][c]["le"].text(), | |
| "occluded": self.cells[r][c]["btn"].isChecked() | |
| }) | |
| old_values.append(row_vals) | |
| while self.grid_layout.count(): | |
| child = self.grid_layout.takeAt(0) | |
| if child.widget(): | |
| child.widget().deleteLater() | |
| self.cells = [] | |
| for r in range(rows): | |
| row_widgets = [] | |
| for c in range(cols): | |
| cell_widget = QWidget() | |
| cell_layout = QHBoxLayout(cell_widget) | |
| cell_layout.setContentsMargins(0, 0, 0, 0) | |
| cell_layout.setSpacing(2) | |
| le = QLineEdit() | |
| le.setPlaceholderText(f"Linha {r+1}, Col {c+1}") | |
| btn_occ = QPushButton("⬜") | |
| btn_occ.setCheckable(True) | |
| btn_occ.setToolTip("Marcar para Oclusão") | |
| btn_occ.setFixedWidth(35) | |
| btn_occ.setStyleSheet(""" | |
| QPushButton { background-color: #ddd; border: 1px solid #aaa; border-radius: 3px; } | |
| QPushButton:checked { background-color: #FFD700; border: 1px solid #B8860B; } | |
| """) | |
| if r < len(old_values) and c < len(old_values[r]): | |
| val = old_values[r][c] | |
| if isinstance(val, dict): | |
| le.setText(val.get("text", "")) | |
| btn_occ.setChecked(val.get("occluded", False)) | |
| else: | |
| le.setText(str(val)) | |
| btn_occ.setChecked(False) | |
| cell_layout.addWidget(le) | |
| cell_layout.addWidget(btn_occ) | |
| self.grid_layout.addWidget(cell_widget, r, c) | |
| row_widgets.append({"le": le, "btn": btn_occ}) | |
| self.cells.append(row_widgets) | |
| def generate_and_insert(self): | |
| # 1. Coletar dados | |
| current_data = [] | |
| valid_rows = [] | |
| for r in range(self.rows_spin.value()): | |
| row_data = [] | |
| is_row_valid = False | |
| for c in range(self.cols_spin.value()): | |
| text = self.cells[r][c]["le"].text().strip() | |
| is_occ = self.cells[r][c]["btn"].isChecked() | |
| row_data.append({"text": text, "occluded": is_occ}) | |
| if text: is_row_valid = True | |
| current_data.append(row_data) | |
| if is_row_valid: | |
| valid_rows.append(row_data) | |
| self.save_config(current_data) | |
| if not valid_rows: | |
| self.accept() | |
| return | |
| if self.mode_cards.isChecked(): | |
| self._generate_multiple_cards(valid_rows) | |
| else: | |
| self._generate_interactive(valid_rows) | |
| self.accept() | |
| def _build_table_html(self, valid_rows, highlight_occ_index=None, table_id=None, shuffle_cols="false"): | |
| """ | |
| Constrói o HTML da tabela. | |
| highlight_occ_index: se None, mostra todas as oclusões marcadas com fundo. | |
| se inteiro, oculta APENAS a oclusão de índice highlight_occ_index (0-based entre todas as células occluded), | |
| deixando as demais visíveis normalmente. | |
| """ | |
| if table_id is None: | |
| table_id = f"shuffle-table-{int(time.time() * 1000)}" | |
| html = f'<table id="{table_id}" class="anki-shuffle-table" style="border-collapse: collapse; margin: 15px auto; min-width: 50%; max-width: 80%; border: 2px solid #888;"><tbody>' | |
| occ_counter = 0 | |
| for row_data in valid_rows: | |
| html += '<tr class="anki-shuffle-row">' | |
| for cell in row_data: | |
| text = cell["text"] if cell["text"] else " " | |
| is_occ = cell["occluded"] | |
| if is_occ: | |
| if highlight_occ_index is None: | |
| # Modo interativo: marca todas com fundo amarelo claro | |
| occ_class = " target-occlusion" | |
| bg_style = "background-color: rgba(255, 215, 0, 0.3);" | |
| else: | |
| # Modo cards: só oculta a célula do índice especificado | |
| if occ_counter == highlight_occ_index: | |
| occ_class = " target-occlusion" | |
| bg_style = "background-color: rgba(255, 215, 0, 0.3);" | |
| else: | |
| occ_class = "" | |
| bg_style = "" | |
| occ_counter += 1 | |
| else: | |
| occ_class = "" | |
| bg_style = "" | |
| html += f'<td class="anki-shuffle-cell{occ_class}" style="border: 2px solid #888; padding: 10px; text-align: center; font-size: 1.1em; {bg_style}"><div class="cell-content">{text}</div></td>' | |
| html += '</tr>' | |
| html += '</tbody></table>' | |
| return html, table_id | |
| def _generate_interactive(self, valid_rows): | |
| table_id = f"shuffle-table-{int(time.time() * 1000)}" | |
| shuffle_cols = "true" if self.shuffle_cols_cb.isChecked() else "false" | |
| html = f'<table id="{table_id}" class="anki-shuffle-table" style="border-collapse: collapse; margin: 15px auto; min-width: 50%; max-width: 80%; border: 2px solid #888;"><tbody>' | |
| for row_data in valid_rows: | |
| html += '<tr class="anki-shuffle-row">' | |
| for cell in row_data: | |
| text = cell["text"] if cell["text"] else " " | |
| occ_class = " target-occlusion" if cell["occluded"] else "" | |
| bg_style = "background-color: rgba(255, 215, 0, 0.3);" if cell["occluded"] else "" | |
| html += f'<td class="anki-shuffle-cell{occ_class}" style="border: 2px solid #888; padding: 10px; text-align: center; font-size: 1.1em; {bg_style}"><div class="cell-content">{text}</div></td>' | |
| html += '</tr>' | |
| html += '</tbody></table>' | |
| js_code = f""" | |
| this.style.display='none'; | |
| if (document.body.isContentEditable || document.designMode === 'on') return; | |
| var table = document.getElementById('{table_id}'); | |
| if (table && !table.dataset.shuffled) {{ | |
| table.dataset.shuffled = 'true'; | |
| var tbody = table.querySelector('tbody'); | |
| var rows = Array.from(tbody.querySelectorAll('.anki-shuffle-row')); | |
| var shouldShuffleCols = {shuffle_cols}; | |
| for (var i = rows.length - 1; i > 0; i--) {{ | |
| var j = Math.floor(Math.random() * (i + 1)); | |
| var temp = rows[i]; | |
| rows[i] = rows[j]; | |
| rows[j] = temp; | |
| }} | |
| rows.forEach(function(row) {{ | |
| var cells = Array.from(row.querySelectorAll('.anki-shuffle-cell')); | |
| if (shouldShuffleCols) {{ | |
| for (var i = cells.length - 1; i > 0; i--) {{ | |
| var j = Math.floor(Math.random() * (i + 1)); | |
| var temp = cells[i]; | |
| cells[i] = cells[j]; | |
| cells[j] = temp; | |
| }} | |
| cells.forEach(function(cell) {{ row.appendChild(cell); }}); | |
| }} | |
| var targets = Array.from(row.querySelectorAll('.target-occlusion')); | |
| if (targets.length > 1) {{ | |
| var revealIndex = Math.floor(Math.random() * targets.length); | |
| targets[revealIndex].style.backgroundColor = ''; | |
| targets.splice(revealIndex, 1); | |
| }} | |
| targets.forEach(function(cell) {{ | |
| cell.dataset.occludable = 'true'; | |
| cell.dataset.isOccluded = 'true'; | |
| cell.style.backgroundColor = '#FFD700'; | |
| cell.style.color = 'transparent'; | |
| cell.style.cursor = 'pointer'; | |
| var content = cell.querySelector('.cell-content'); | |
| if(content) content.style.visibility = 'hidden'; | |
| cell.onclick = function() {{ | |
| if (this.dataset.isOccluded === 'true') {{ | |
| this.dataset.isOccluded = 'false'; | |
| this.style.backgroundColor = ''; | |
| this.style.color = ''; | |
| var c = this.querySelector('.cell-content'); | |
| if(c) c.style.visibility = 'visible'; | |
| }} else {{ | |
| this.dataset.isOccluded = 'true'; | |
| this.style.backgroundColor = '#FFD700'; | |
| this.style.color = 'transparent'; | |
| var c = this.querySelector('.cell-content'); | |
| if(c) c.style.visibility = 'hidden'; | |
| }} | |
| }}; | |
| }}); | |
| tbody.appendChild(row); | |
| }}); | |
| if (!window.shuffleTableShortcutAdded) {{ | |
| window.shuffleTableShortcutAdded = true; | |
| document.addEventListener('keydown', function(e) {{ | |
| if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return; | |
| if (e.key.toLowerCase() === 'w') {{ | |
| var allTables = document.querySelectorAll('.anki-shuffle-table'); | |
| var totalOccluded = 0; | |
| var totalOccludable = 0; | |
| for (var t = 0; t < allTables.length; t++) {{ | |
| var allCells = Array.from(allTables[t].querySelectorAll('.anki-shuffle-cell')); | |
| allCells.forEach(function(c) {{ | |
| if (c.dataset.occludable === 'true') {{ | |
| totalOccludable++; | |
| if (c.dataset.isOccluded === 'true') totalOccluded++; | |
| }} | |
| }}); | |
| }} | |
| if (totalOccluded === 0 && totalOccludable > 0) {{ | |
| for (var t = 0; t < allTables.length; t++) {{ | |
| var allCells = Array.from(allTables[t].querySelectorAll('.anki-shuffle-cell')); | |
| allCells.forEach(function(c) {{ | |
| if (c.dataset.occludable === 'true') {{ | |
| c.dataset.isOccluded = 'true'; | |
| c.style.backgroundColor = '#FFD700'; | |
| c.style.color = 'transparent'; | |
| var content = c.querySelector('.cell-content'); | |
| if(content) content.style.visibility = 'hidden'; | |
| }} | |
| }}); | |
| }} | |
| return; | |
| }} | |
| var revealedSomething = false; | |
| for (var t = 0; t < allTables.length; t++) {{ | |
| var tRows = Array.from(allTables[t].querySelectorAll('.anki-shuffle-row')); | |
| for (var r = 0; r < tRows.length; r++) {{ | |
| var cellsInRow = Array.from(tRows[r].querySelectorAll('.anki-shuffle-cell')); | |
| var occludedCells = cellsInRow.filter(function(c) {{ return c.dataset.isOccluded === 'true'; }}); | |
| if (occludedCells.length > 0) {{ | |
| occludedCells.forEach(function(c) {{ | |
| c.dataset.isOccluded = 'false'; | |
| c.style.backgroundColor = ''; | |
| c.style.color = ''; | |
| var content = c.querySelector('.cell-content'); | |
| if(content) content.style.visibility = 'visible'; | |
| }}); | |
| revealedSomething = true; | |
| break; | |
| }} | |
| }} | |
| if (revealedSomething) break; | |
| }} | |
| }} | |
| }}); | |
| }} | |
| }} | |
| """ | |
| js_code_escaped = js_code.replace('\n', ' ').replace(' ', ' ').replace('"', '"') | |
| html += f'<img src="x" style="display:none;" onerror="{js_code_escaped}">' | |
| self.editor.web.eval(f"document.execCommand('insertHTML', false, {json.dumps(html)});") | |
| def _generate_multiple_cards(self, valid_rows): | |
| """ | |
| Gera um card separado para cada célula marcada como oclusão. | |
| Cada card mostra a tabela completa, mas com apenas UMA célula oculta. | |
| """ | |
| from aqt.utils import showInfo | |
| # Coleta todas as células com oclusão | |
| occ_cells = [] | |
| for r, row_data in enumerate(valid_rows): | |
| for c, cell in enumerate(row_data): | |
| if cell["occluded"]: | |
| occ_cells.append((r, c)) | |
| if not occ_cells: | |
| # Nenhuma célula marcada: insere tabela normal sem oclusão | |
| self._generate_interactive(valid_rows) | |
| return | |
| # Obtém o note atual aberto no editor | |
| note = self.editor.note | |
| if note is None: | |
| from aqt.utils import showWarning | |
| showWarning("Nenhum card aberto no editor.") | |
| return | |
| # Descobre o campo atual focado no editor | |
| current_field = self.editor.currentField if self.editor.currentField is not None else 0 | |
| shuffle_cols = "true" if self.shuffle_cols_cb.isChecked() else "false" | |
| def build_card_html(occ_idx): | |
| tid = f"shuffle-table-{int(time.time() * 1000)}-{occ_idx}" | |
| html = f'<table id="{tid}" class="anki-shuffle-table" style="border-collapse: collapse; margin: 15px auto; min-width: 50%; max-width: 80%; border: 2px solid #888;"><tbody>' | |
| global_occ_counter = 0 | |
| for row_data in valid_rows: | |
| html += '<tr class="anki-shuffle-row">' | |
| for cell in row_data: | |
| text = cell["text"] if cell["text"] else " " | |
| is_this_occ = cell["occluded"] | |
| if is_this_occ and global_occ_counter == occ_idx: | |
| # Esta é a célula a ser ocluída neste card | |
| occ_class = " target-occlusion" | |
| bg_style = "background-color: rgba(255, 215, 0, 0.3);" | |
| else: | |
| occ_class = "" | |
| bg_style = "" | |
| if is_this_occ: | |
| global_occ_counter += 1 | |
| html += f'<td class="anki-shuffle-cell{occ_class}" style="border: 2px solid #888; padding: 10px; text-align: center; font-size: 1.1em; {bg_style}"><div class="cell-content">{text}</div></td>' | |
| html += '</tr>' | |
| html += '</tbody></table>' | |
| js_code = f""" | |
| this.style.display='none'; | |
| if (document.body.isContentEditable || document.designMode === 'on') return; | |
| var table = document.getElementById('{tid}'); | |
| if (table && !table.dataset.shuffled) {{ | |
| table.dataset.shuffled = 'true'; | |
| var tbody = table.querySelector('tbody'); | |
| var rows = Array.from(tbody.querySelectorAll('.anki-shuffle-row')); | |
| var shouldShuffleCols = {shuffle_cols}; | |
| for (var i = rows.length - 1; i > 0; i--) {{ | |
| var j = Math.floor(Math.random() * (i + 1)); | |
| var temp = rows[i]; rows[i] = rows[j]; rows[j] = temp; | |
| }} | |
| rows.forEach(function(row) {{ | |
| var cells = Array.from(row.querySelectorAll('.anki-shuffle-cell')); | |
| if (shouldShuffleCols) {{ | |
| for (var i = cells.length - 1; i > 0; i--) {{ | |
| var j = Math.floor(Math.random() * (i + 1)); | |
| var temp = cells[i]; cells[i] = cells[j]; cells[j] = temp; | |
| }} | |
| cells.forEach(function(cell) {{ row.appendChild(cell); }}); | |
| }} | |
| var targets = Array.from(row.querySelectorAll('.target-occlusion')); | |
| targets.forEach(function(cell) {{ | |
| cell.dataset.occludable = 'true'; | |
| cell.dataset.isOccluded = 'true'; | |
| cell.style.backgroundColor = '#FFD700'; | |
| cell.style.color = 'transparent'; | |
| cell.style.cursor = 'pointer'; | |
| var content = cell.querySelector('.cell-content'); | |
| if(content) content.style.visibility = 'hidden'; | |
| cell.onclick = function() {{ | |
| if (this.dataset.isOccluded === 'true') {{ | |
| this.dataset.isOccluded = 'false'; | |
| this.style.backgroundColor = ''; | |
| this.style.color = ''; | |
| var c = this.querySelector('.cell-content'); | |
| if(c) c.style.visibility = 'visible'; | |
| }} else {{ | |
| this.dataset.isOccluded = 'true'; | |
| this.style.backgroundColor = '#FFD700'; | |
| this.style.color = 'transparent'; | |
| var c = this.querySelector('.cell-content'); | |
| if(c) c.style.visibility = 'hidden'; | |
| }} | |
| }}; | |
| }}); | |
| tbody.appendChild(row); | |
| }}); | |
| if (!window.shuffleTableShortcutAdded) {{ | |
| window.shuffleTableShortcutAdded = true; | |
| document.addEventListener('keydown', function(e) {{ | |
| if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return; | |
| if (e.key.toLowerCase() === 'w') {{ | |
| var allTables = document.querySelectorAll('.anki-shuffle-table'); | |
| var totalOccluded = 0; | |
| var totalOccludable = 0; | |
| for (var t = 0; t < allTables.length; t++) {{ | |
| var allCells = Array.from(allTables[t].querySelectorAll('.anki-shuffle-cell')); | |
| allCells.forEach(function(c) {{ | |
| if (c.dataset.occludable === 'true') {{ | |
| totalOccludable++; | |
| if (c.dataset.isOccluded === 'true') totalOccluded++; | |
| }} | |
| }}); | |
| }} | |
| if (totalOccluded === 0 && totalOccludable > 0) {{ | |
| for (var t = 0; t < allTables.length; t++) {{ | |
| var allCells = Array.from(allTables[t].querySelectorAll('.anki-shuffle-cell')); | |
| allCells.forEach(function(c) {{ | |
| if (c.dataset.occludable === 'true') {{ | |
| c.dataset.isOccluded = 'true'; | |
| c.style.backgroundColor = '#FFD700'; | |
| c.style.color = 'transparent'; | |
| var content = c.querySelector('.cell-content'); | |
| if(content) content.style.visibility = 'hidden'; | |
| }} | |
| }}); | |
| }} | |
| return; | |
| }} | |
| var revealedSomething = false; | |
| for (var t = 0; t < allTables.length; t++) {{ | |
| var tRows = Array.from(allTables[t].querySelectorAll('.anki-shuffle-row')); | |
| for (var r = 0; r < tRows.length; r++) {{ | |
| var cellsInRow = Array.from(tRows[r].querySelectorAll('.anki-shuffle-cell')); | |
| var occludedCells = cellsInRow.filter(function(c) {{ return c.dataset.isOccluded === 'true'; }}); | |
| if (occludedCells.length > 0) {{ | |
| occludedCells.forEach(function(c) {{ | |
| c.dataset.isOccluded = 'false'; | |
| c.style.backgroundColor = ''; | |
| c.style.color = ''; | |
| var content = c.querySelector('.cell-content'); | |
| if(content) content.style.visibility = 'visible'; | |
| }}); | |
| revealedSomething = true; | |
| break; | |
| }} | |
| }} | |
| if (revealedSomething) break; | |
| }} | |
| }} | |
| }}); | |
| }} | |
| }} | |
| """ | |
| js_escaped = js_code.replace('\n', ' ').replace(' ', ' ').replace('"', '"') | |
| html += f'<img src="x" style="display:none;" onerror="{js_escaped}">' | |
| return html | |
| # Primeiro card: insere no campo atual do card aberto | |
| first_html = build_card_html(0) | |
| self.editor.web.eval(f"document.execCommand('insertHTML', false, {json.dumps(first_html)});") | |
| if len(occ_cells) == 1: | |
| return | |
| # Cards adicionais: duplica o note e muda o campo | |
| col = mw.col | |
| first_note_id = note.id | |
| for occ_idx in range(1, len(occ_cells)): | |
| # Cria uma cópia do note | |
| new_note = col.new_note(note.note_type()) | |
| # Copia todos os campos | |
| for i in range(len(note.fields)): | |
| new_note.fields[i] = note.fields[i] | |
| # Copia as tags | |
| new_note.tags = list(note.tags) | |
| # Substitui o campo alvo pelo HTML desta variação | |
| card_html = build_card_html(occ_idx) | |
| # Limpa o campo e coloca o novo HTML | |
| new_note.fields[current_field] = card_html | |
| try: | |
| did = note.cards()[0].did | |
| except Exception: | |
| did = col.decks.get_current_id() | |
| col.add_note(new_note, did) | |
| showInfo(f"{len(occ_cells)} cards criados, um para cada célula oculta.") | |
| def setup_shuffle_table_button(buttons, editor): | |
| btn = editor.addButton( | |
| icon=None, | |
| cmd="shuffleTableButton", | |
| func=lambda ed=editor: ShuffleTableDialog(ed).exec(), | |
| tip="Oclusão de Tabela Aleatória", | |
| label="🔀Tab" | |
| ) | |
| buttons.append(btn) | |
| return buttons | |
| gui_hooks.editor_did_init_buttons.append(setup_shuffle_table_button) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
aperte w ou clique para mostrar