Remove table tool
This commit is contained in:
parent
81cf878ffd
commit
9a82831e87
11 changed files with 3 additions and 798 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
* Adjust History icon and reorder toolbar items
|
* Adjust History icon and reorder toolbar items
|
||||||
* Try to address checkbox/bullet size issues (again)
|
* Try to address checkbox/bullet size issues (again)
|
||||||
* Fix HTML export of markdown (with newlines, tables and other styling preserved)
|
* Fix HTML export of markdown (with newlines, tables and other styling preserved)
|
||||||
|
* Remove table tool
|
||||||
|
|
||||||
# 0.5.1
|
# 0.5.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@
|
||||||
"backup_failed": "Backup failed",
|
"backup_failed": "Backup failed",
|
||||||
"quit": "Quit",
|
"quit": "Quit",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"ok": "OK",
|
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
|
|
@ -266,8 +265,6 @@
|
||||||
"cut": "Cut",
|
"cut": "Cut",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"edit_table": "Edit table",
|
|
||||||
"toolbar_insert_table": "Insert table",
|
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
"pause": "Pause",
|
"pause": "Pause",
|
||||||
"resume": "Resume",
|
"resume": "Resume",
|
||||||
|
|
@ -292,10 +289,5 @@
|
||||||
"friday": "Friday",
|
"friday": "Friday",
|
||||||
"saturday": "Saturday",
|
"saturday": "Saturday",
|
||||||
"sunday": "Sunday",
|
"sunday": "Sunday",
|
||||||
"day": "Day",
|
"day": "Day"
|
||||||
"add_row": "Add row",
|
|
||||||
"add_column": "Add column",
|
|
||||||
"delete_row": "Delete row",
|
|
||||||
"delete_column": "Delete column",
|
|
||||||
"column": "Column"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@
|
||||||
"backup_failed": "Échec de la sauvegarde",
|
"backup_failed": "Échec de la sauvegarde",
|
||||||
"quit": "Quitter",
|
"quit": "Quitter",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
"ok": "OK",
|
|
||||||
"save": "Enregistrer",
|
"save": "Enregistrer",
|
||||||
"help": "Aide",
|
"help": "Aide",
|
||||||
"saved": "Enregistré",
|
"saved": "Enregistré",
|
||||||
|
|
@ -265,8 +264,6 @@
|
||||||
"cut": "Couper",
|
"cut": "Couper",
|
||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"paste": "Coller",
|
"paste": "Coller",
|
||||||
"edit_table": "Modifier le tableau",
|
|
||||||
"toolbar_insert_table": "Insérer un tableau",
|
|
||||||
"start": "Démarrer",
|
"start": "Démarrer",
|
||||||
"pause": "Pause",
|
"pause": "Pause",
|
||||||
"resume": "Reprendre",
|
"resume": "Reprendre",
|
||||||
|
|
@ -289,10 +286,5 @@
|
||||||
"friday": "Vendredi",
|
"friday": "Vendredi",
|
||||||
"saturday": "Samedi",
|
"saturday": "Samedi",
|
||||||
"sunday": "Dimanche",
|
"sunday": "Dimanche",
|
||||||
"day": "Jour",
|
"day": "Jour"
|
||||||
"add_row": "Ajouter une ligne",
|
|
||||||
"add_column": "Ajouter une colonne",
|
|
||||||
"delete_row": "Supprimer la ligne",
|
|
||||||
"delete_column": "Supprimer la colonne",
|
|
||||||
"column": "Colonne"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@
|
||||||
"today": "Oggi",
|
"today": "Oggi",
|
||||||
"show": "Mostra",
|
"show": "Mostra",
|
||||||
"history": "Cronologia",
|
"history": "Cronologia",
|
||||||
"view_history": "Visualizza cronologia",
|
|
||||||
"export_accessible_flag": "&Esporta",
|
"export_accessible_flag": "&Esporta",
|
||||||
"export_entries": "Esporta voci",
|
"export_entries": "Esporta voci",
|
||||||
"export_complete": "Esportazione completata",
|
"export_complete": "Esportazione completata",
|
||||||
|
|
@ -128,7 +127,6 @@
|
||||||
"check_for_updates": "Controlla aggiornamenti",
|
"check_for_updates": "Controlla aggiornamenti",
|
||||||
"close": "Chiudi",
|
"close": "Chiudi",
|
||||||
"send": "Invia",
|
"send": "Invia",
|
||||||
"yes": "Sì",
|
|
||||||
"time_log": "Registro Attività",
|
"time_log": "Registro Attività",
|
||||||
"time_log_no_entries": "Nessuna Attività",
|
"time_log_no_entries": "Nessuna Attività",
|
||||||
"close_tab": "Chiudi scheda",
|
"close_tab": "Chiudi scheda",
|
||||||
|
|
|
||||||
|
|
@ -1103,7 +1103,6 @@ class MainWindow(QMainWindow):
|
||||||
self._tb_checkboxes = lambda: self._call_editor("toggle_checkboxes")
|
self._tb_checkboxes = lambda: self._call_editor("toggle_checkboxes")
|
||||||
self._tb_alarm = self._on_alarm_requested
|
self._tb_alarm = self._on_alarm_requested
|
||||||
self._tb_timer = self._on_timer_requested
|
self._tb_timer = self._on_timer_requested
|
||||||
self._tb_table = self._on_table_requested
|
|
||||||
self._tb_font_larger = self._on_font_larger_requested
|
self._tb_font_larger = self._on_font_larger_requested
|
||||||
self._tb_font_smaller = self._on_font_smaller_requested
|
self._tb_font_smaller = self._on_font_smaller_requested
|
||||||
|
|
||||||
|
|
@ -1117,7 +1116,6 @@ class MainWindow(QMainWindow):
|
||||||
tb.checkboxesRequested.connect(self._tb_checkboxes)
|
tb.checkboxesRequested.connect(self._tb_checkboxes)
|
||||||
tb.alarmRequested.connect(self._tb_alarm)
|
tb.alarmRequested.connect(self._tb_alarm)
|
||||||
tb.timerRequested.connect(self._tb_timer)
|
tb.timerRequested.connect(self._tb_timer)
|
||||||
tb.tableRequested.connect(self._tb_table)
|
|
||||||
tb.insertImageRequested.connect(self._on_insert_image)
|
tb.insertImageRequested.connect(self._on_insert_image)
|
||||||
tb.historyRequested.connect(self._open_history)
|
tb.historyRequested.connect(self._open_history)
|
||||||
tb.fontSizeLargerRequested.connect(self._tb_font_larger)
|
tb.fontSizeLargerRequested.connect(self._tb_font_larger)
|
||||||
|
|
@ -1333,36 +1331,6 @@ class MainWindow(QMainWindow):
|
||||||
timer.start(msecs)
|
timer.start(msecs)
|
||||||
self._reminder_timers.append(timer)
|
self._reminder_timers.append(timer)
|
||||||
|
|
||||||
# ----------- Table handler ------------#
|
|
||||||
def _on_table_requested(self):
|
|
||||||
"""Insert a basic markdown table template."""
|
|
||||||
editor = getattr(self, "editor", None)
|
|
||||||
if editor is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Basic 3x3 table template
|
|
||||||
table_template = """| Column 1 | Column 2 | Column 3 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Cell 1 | Cell 2 | Cell 3 |
|
|
||||||
| Cell 4 | Cell 5 | Cell 6 |
|
|
||||||
"""
|
|
||||||
|
|
||||||
cursor = editor.textCursor()
|
|
||||||
cursor.insertText(table_template)
|
|
||||||
|
|
||||||
# Move cursor to first cell for easy editing
|
|
||||||
# Find the start of "Column 1" text
|
|
||||||
cursor.movePosition(
|
|
||||||
QTextCursor.Left, QTextCursor.MoveAnchor, len(table_template)
|
|
||||||
)
|
|
||||||
cursor.movePosition(QTextCursor.Right, QTextCursor.MoveAnchor, 2) # After "| "
|
|
||||||
cursor.movePosition(
|
|
||||||
QTextCursor.Right, QTextCursor.KeepAnchor, 8
|
|
||||||
) # Select "Column 1"
|
|
||||||
editor.setTextCursor(cursor)
|
|
||||||
|
|
||||||
editor.setFocus()
|
|
||||||
|
|
||||||
# ----------- History handler ------------#
|
# ----------- History handler ------------#
|
||||||
def _open_history(self):
|
def _open_history(self):
|
||||||
if hasattr(self.editor, "current_date"):
|
if hasattr(self.editor, "current_date"):
|
||||||
|
|
|
||||||
|
|
@ -1250,23 +1250,6 @@ class MarkdownEditor(QTextEdit):
|
||||||
menu = QMenu(self)
|
menu = QMenu(self)
|
||||||
cursor = self.cursorForPosition(event.pos())
|
cursor = self.cursorForPosition(event.pos())
|
||||||
|
|
||||||
# Check if we're in a table
|
|
||||||
text = self.toPlainText()
|
|
||||||
cursor_pos = cursor.position()
|
|
||||||
|
|
||||||
from .table_editor import find_table_at_cursor
|
|
||||||
|
|
||||||
table_info = find_table_at_cursor(text, cursor_pos)
|
|
||||||
|
|
||||||
if table_info:
|
|
||||||
# Add table editing action
|
|
||||||
edit_table_action = QAction(strings._("edit_table"), self)
|
|
||||||
edit_table_action.triggered.connect(
|
|
||||||
lambda: self._edit_table_at_cursor(cursor_pos)
|
|
||||||
)
|
|
||||||
menu.addAction(edit_table_action)
|
|
||||||
menu.addSeparator()
|
|
||||||
|
|
||||||
# Check if we're in a code block
|
# Check if we're in a code block
|
||||||
block = cursor.block()
|
block = cursor.block()
|
||||||
if self._is_inside_code_block(block):
|
if self._is_inside_code_block(block):
|
||||||
|
|
@ -1302,30 +1285,6 @@ class MarkdownEditor(QTextEdit):
|
||||||
|
|
||||||
menu.exec(event.globalPos())
|
menu.exec(event.globalPos())
|
||||||
|
|
||||||
def _edit_table_at_cursor(self, cursor_pos: int):
|
|
||||||
"""Open table editor dialog for the table at cursor position."""
|
|
||||||
from .table_editor import find_table_at_cursor, TableEditorDialog
|
|
||||||
from PySide6.QtWidgets import QDialog
|
|
||||||
|
|
||||||
text = self.toPlainText()
|
|
||||||
table_info = find_table_at_cursor(text, cursor_pos)
|
|
||||||
|
|
||||||
if not table_info:
|
|
||||||
return
|
|
||||||
|
|
||||||
start_pos, end_pos, table_text = table_info
|
|
||||||
|
|
||||||
# Open table editor
|
|
||||||
dlg = TableEditorDialog(table_text, self)
|
|
||||||
if dlg.exec() == QDialog.Accepted:
|
|
||||||
# Replace the table with edited version
|
|
||||||
new_table = dlg.get_markdown_table()
|
|
||||||
|
|
||||||
cursor = QTextCursor(self.document())
|
|
||||||
cursor.setPosition(start_pos)
|
|
||||||
cursor.setPosition(end_pos, QTextCursor.KeepAnchor)
|
|
||||||
cursor.insertText(new_table)
|
|
||||||
|
|
||||||
def _set_code_block_language(self, block, language: str):
|
def _set_code_block_language(self, block, language: str):
|
||||||
"""Set the language for a code block and store metadata."""
|
"""Set the language for a code block and store metadata."""
|
||||||
if not hasattr(self, "_code_metadata"):
|
if not hasattr(self, "_code_metadata"):
|
||||||
|
|
|
||||||
|
|
@ -1,255 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import re
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from PySide6.QtCore import Slot
|
|
||||||
from PySide6.QtWidgets import (
|
|
||||||
QDialog,
|
|
||||||
QVBoxLayout,
|
|
||||||
QHBoxLayout,
|
|
||||||
QTableWidget,
|
|
||||||
QTableWidgetItem,
|
|
||||||
QPushButton,
|
|
||||||
QHeaderView,
|
|
||||||
QWidget,
|
|
||||||
)
|
|
||||||
|
|
||||||
from . import strings
|
|
||||||
|
|
||||||
|
|
||||||
class TableEditorDialog(QDialog):
|
|
||||||
"""Dialog for editing markdown tables visually."""
|
|
||||||
|
|
||||||
def __init__(self, table_text: str, parent: Optional[QWidget] = None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.setWindowTitle(strings._("edit_table"))
|
|
||||||
self.setMinimumSize(600, 400)
|
|
||||||
|
|
||||||
layout = QVBoxLayout(self)
|
|
||||||
|
|
||||||
# Parse the table
|
|
||||||
self.table_widget = QTableWidget()
|
|
||||||
self._parse_table(table_text)
|
|
||||||
|
|
||||||
# Allow editing
|
|
||||||
self.table_widget.horizontalHeader().setSectionResizeMode(
|
|
||||||
QHeaderView.Interactive
|
|
||||||
)
|
|
||||||
layout.addWidget(self.table_widget)
|
|
||||||
|
|
||||||
# Buttons for table operations
|
|
||||||
btn_layout = QHBoxLayout()
|
|
||||||
|
|
||||||
add_row_btn = QPushButton(strings._("add_row"))
|
|
||||||
add_row_btn.clicked.connect(self._add_row)
|
|
||||||
btn_layout.addWidget(add_row_btn)
|
|
||||||
|
|
||||||
add_col_btn = QPushButton(strings._("add_column"))
|
|
||||||
add_col_btn.clicked.connect(self._add_column)
|
|
||||||
btn_layout.addWidget(add_col_btn)
|
|
||||||
|
|
||||||
del_row_btn = QPushButton(strings._("delete_row"))
|
|
||||||
del_row_btn.clicked.connect(self._delete_row)
|
|
||||||
btn_layout.addWidget(del_row_btn)
|
|
||||||
|
|
||||||
del_col_btn = QPushButton(strings._("delete_column"))
|
|
||||||
del_col_btn.clicked.connect(self._delete_column)
|
|
||||||
btn_layout.addWidget(del_col_btn)
|
|
||||||
|
|
||||||
layout.addLayout(btn_layout)
|
|
||||||
|
|
||||||
# OK/Cancel buttons
|
|
||||||
btn_layout2 = QHBoxLayout()
|
|
||||||
btn_layout2.addStretch()
|
|
||||||
|
|
||||||
ok_btn = QPushButton(strings._("ok"))
|
|
||||||
ok_btn.clicked.connect(self.accept)
|
|
||||||
ok_btn.setDefault(True)
|
|
||||||
btn_layout2.addWidget(ok_btn)
|
|
||||||
|
|
||||||
cancel_btn = QPushButton(strings._("cancel"))
|
|
||||||
cancel_btn.clicked.connect(self.reject)
|
|
||||||
btn_layout2.addWidget(cancel_btn)
|
|
||||||
|
|
||||||
layout.addLayout(btn_layout2)
|
|
||||||
|
|
||||||
def _parse_table(self, text: str):
|
|
||||||
"""Parse markdown table into QTableWidget."""
|
|
||||||
lines = [line.strip() for line in text.split("\n") if line.strip()]
|
|
||||||
|
|
||||||
if len(lines) < 1:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Parse header
|
|
||||||
header_line = lines[0]
|
|
||||||
# Split by | and remove first/last empty strings from leading/trailing pipes
|
|
||||||
header_parts = header_line.split("|")
|
|
||||||
if len(header_parts) > 0 and not header_parts[0].strip():
|
|
||||||
header_parts = header_parts[1:]
|
|
||||||
if len(header_parts) > 0 and not header_parts[-1].strip():
|
|
||||||
header_parts = header_parts[:-1]
|
|
||||||
headers = [cell.strip() for cell in header_parts]
|
|
||||||
|
|
||||||
# Check if line[1] is a separator line (contains ---)
|
|
||||||
# If not, treat all lines after header as data
|
|
||||||
start_data_idx = 1
|
|
||||||
if len(lines) > 1:
|
|
||||||
separator_check = lines[1]
|
|
||||||
# Split by | and remove first/last empty strings
|
|
||||||
sep_parts = separator_check.split("|")
|
|
||||||
if len(sep_parts) > 0 and not sep_parts[0].strip():
|
|
||||||
sep_parts = sep_parts[1:]
|
|
||||||
if len(sep_parts) > 0 and not sep_parts[-1].strip():
|
|
||||||
sep_parts = sep_parts[:-1]
|
|
||||||
cells = [cell.strip() for cell in sep_parts]
|
|
||||||
# Check if this looks like a separator (contains --- or :--: etc)
|
|
||||||
if cells and all(re.match(r"^:?-+:?$", cell) for cell in cells):
|
|
||||||
start_data_idx = 2 # Skip separator line
|
|
||||||
|
|
||||||
# Parse data rows
|
|
||||||
data_rows = []
|
|
||||||
for line in lines[start_data_idx:]:
|
|
||||||
# Split by | and remove first/last empty strings from leading/trailing pipes
|
|
||||||
parts = line.split("|")
|
|
||||||
if len(parts) > 0 and not parts[0].strip():
|
|
||||||
parts = parts[1:]
|
|
||||||
if len(parts) > 0 and not parts[-1].strip():
|
|
||||||
parts = parts[:-1]
|
|
||||||
cells = [cell.strip() for cell in parts]
|
|
||||||
data_rows.append(cells)
|
|
||||||
|
|
||||||
# Set up table
|
|
||||||
self.table_widget.setColumnCount(len(headers))
|
|
||||||
self.table_widget.setHorizontalHeaderLabels(headers)
|
|
||||||
self.table_widget.setRowCount(len(data_rows))
|
|
||||||
|
|
||||||
# Populate cells
|
|
||||||
for row_idx, row_data in enumerate(data_rows):
|
|
||||||
for col_idx, cell_text in enumerate(row_data):
|
|
||||||
if col_idx < len(headers):
|
|
||||||
item = QTableWidgetItem(cell_text)
|
|
||||||
self.table_widget.setItem(row_idx, col_idx, item)
|
|
||||||
|
|
||||||
@Slot()
|
|
||||||
def _add_row(self):
|
|
||||||
"""Add a new row to the table."""
|
|
||||||
row_count = self.table_widget.rowCount()
|
|
||||||
self.table_widget.insertRow(row_count)
|
|
||||||
|
|
||||||
# Add empty items
|
|
||||||
for col in range(self.table_widget.columnCount()):
|
|
||||||
self.table_widget.setItem(row_count, col, QTableWidgetItem(""))
|
|
||||||
|
|
||||||
@Slot()
|
|
||||||
def _add_column(self):
|
|
||||||
"""Add a new column to the table."""
|
|
||||||
col_count = self.table_widget.columnCount()
|
|
||||||
self.table_widget.insertColumn(col_count)
|
|
||||||
self.table_widget.setHorizontalHeaderItem(
|
|
||||||
col_count, QTableWidgetItem(strings._("column") + f"{col_count + 1}")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add empty items
|
|
||||||
for row in range(self.table_widget.rowCount()):
|
|
||||||
self.table_widget.setItem(row, col_count, QTableWidgetItem(""))
|
|
||||||
|
|
||||||
@Slot()
|
|
||||||
def _delete_row(self):
|
|
||||||
"""Delete the currently selected row."""
|
|
||||||
current_row = self.table_widget.currentRow()
|
|
||||||
if current_row >= 0:
|
|
||||||
self.table_widget.removeRow(current_row)
|
|
||||||
|
|
||||||
@Slot()
|
|
||||||
def _delete_column(self):
|
|
||||||
"""Delete the currently selected column."""
|
|
||||||
current_col = self.table_widget.currentColumn()
|
|
||||||
if current_col >= 0:
|
|
||||||
self.table_widget.removeColumn(current_col)
|
|
||||||
|
|
||||||
def get_markdown_table(self) -> str:
|
|
||||||
"""Convert the table back to markdown format."""
|
|
||||||
if self.table_widget.rowCount() == 0 or self.table_widget.columnCount() == 0:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
lines = []
|
|
||||||
|
|
||||||
# Header
|
|
||||||
headers = []
|
|
||||||
for col in range(self.table_widget.columnCount()):
|
|
||||||
header_item = self.table_widget.horizontalHeaderItem(col)
|
|
||||||
headers.append(
|
|
||||||
header_item.text()
|
|
||||||
if header_item
|
|
||||||
else strings._("column") + f"{col + 1}"
|
|
||||||
)
|
|
||||||
lines.append("| " + " | ".join(headers) + " |")
|
|
||||||
|
|
||||||
# Separator
|
|
||||||
lines.append("| " + " | ".join(["---"] * len(headers)) + " |")
|
|
||||||
|
|
||||||
# Data rows
|
|
||||||
for row in range(self.table_widget.rowCount()):
|
|
||||||
cells = []
|
|
||||||
for col in range(self.table_widget.columnCount()):
|
|
||||||
item = self.table_widget.item(row, col)
|
|
||||||
cells.append(item.text() if item else "")
|
|
||||||
lines.append("| " + " | ".join(cells) + " |")
|
|
||||||
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
def find_table_at_cursor(text: str, cursor_pos: int) -> Optional[tuple[int, int, str]]:
|
|
||||||
"""
|
|
||||||
Find a markdown table containing the cursor position.
|
|
||||||
Returns (start_pos, end_pos, table_text) or None.
|
|
||||||
"""
|
|
||||||
lines = text.split("\n")
|
|
||||||
|
|
||||||
# Find which line the cursor is on
|
|
||||||
current_pos = 0
|
|
||||||
cursor_line_idx = 0
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if current_pos + len(line) >= cursor_pos:
|
|
||||||
cursor_line_idx = i
|
|
||||||
break
|
|
||||||
current_pos += len(line) + 1 # +1 for newline
|
|
||||||
|
|
||||||
# Check if cursor line is part of a table
|
|
||||||
if not _is_table_line(lines[cursor_line_idx]):
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Find table start
|
|
||||||
start_idx = cursor_line_idx
|
|
||||||
while start_idx > 0 and _is_table_line(lines[start_idx - 1]):
|
|
||||||
start_idx -= 1
|
|
||||||
|
|
||||||
# Find table end
|
|
||||||
end_idx = cursor_line_idx
|
|
||||||
while end_idx < len(lines) - 1 and _is_table_line(lines[end_idx + 1]):
|
|
||||||
end_idx += 1
|
|
||||||
|
|
||||||
# Extract table text
|
|
||||||
table_lines = lines[start_idx : end_idx + 1]
|
|
||||||
table_text = "\n".join(table_lines)
|
|
||||||
|
|
||||||
# Calculate character positions
|
|
||||||
start_pos = sum(len(lines[i]) + 1 for i in range(start_idx))
|
|
||||||
end_pos = start_pos + len(table_text)
|
|
||||||
|
|
||||||
return (start_pos, end_pos, table_text)
|
|
||||||
|
|
||||||
|
|
||||||
def _is_table_line(line: str) -> bool:
|
|
||||||
"""Check if a line is part of a markdown table."""
|
|
||||||
stripped = line.strip()
|
|
||||||
if not stripped:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Table lines start and end with |
|
|
||||||
if not (stripped.startswith("|") and stripped.endswith("|")):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Must have at least one | in between
|
|
||||||
return stripped.count("|") >= 2
|
|
||||||
|
|
@ -20,7 +20,6 @@ class ToolBar(QToolBar):
|
||||||
insertImageRequested = Signal()
|
insertImageRequested = Signal()
|
||||||
alarmRequested = Signal()
|
alarmRequested = Signal()
|
||||||
timerRequested = Signal()
|
timerRequested = Signal()
|
||||||
tableRequested = Signal()
|
|
||||||
fontSizeLargerRequested = Signal()
|
fontSizeLargerRequested = Signal()
|
||||||
fontSizeSmallerRequested = Signal()
|
fontSizeSmallerRequested = Signal()
|
||||||
|
|
||||||
|
|
@ -121,11 +120,6 @@ class ToolBar(QToolBar):
|
||||||
self.actTimer.setToolTip(strings._("toolbar_pomodoro_timer"))
|
self.actTimer.setToolTip(strings._("toolbar_pomodoro_timer"))
|
||||||
self.actTimer.triggered.connect(self.timerRequested)
|
self.actTimer.triggered.connect(self.timerRequested)
|
||||||
|
|
||||||
# Table
|
|
||||||
self.actTable = QAction("⊞", self)
|
|
||||||
self.actTable.setToolTip(strings._("toolbar_insert_table"))
|
|
||||||
self.actTable.triggered.connect(self.tableRequested)
|
|
||||||
|
|
||||||
# Set exclusive buttons in QActionGroups
|
# Set exclusive buttons in QActionGroups
|
||||||
self.grpHeadings = QActionGroup(self)
|
self.grpHeadings = QActionGroup(self)
|
||||||
self.grpHeadings.setExclusive(True)
|
self.grpHeadings.setExclusive(True)
|
||||||
|
|
@ -162,7 +156,6 @@ class ToolBar(QToolBar):
|
||||||
self.actBullets,
|
self.actBullets,
|
||||||
self.actNumbers,
|
self.actNumbers,
|
||||||
self.actCheckboxes,
|
self.actCheckboxes,
|
||||||
self.actTable,
|
|
||||||
self.actInsertImg,
|
self.actInsertImg,
|
||||||
self.actAlarm,
|
self.actAlarm,
|
||||||
self.actTimer,
|
self.actTimer,
|
||||||
|
|
@ -192,7 +185,6 @@ class ToolBar(QToolBar):
|
||||||
self._style_letter_button(self.actCheckboxes, "☐")
|
self._style_letter_button(self.actCheckboxes, "☐")
|
||||||
self._style_letter_button(self.actAlarm, "⏰")
|
self._style_letter_button(self.actAlarm, "⏰")
|
||||||
self._style_letter_button(self.actTimer, "⌛")
|
self._style_letter_button(self.actTimer, "⌛")
|
||||||
self._style_letter_button(self.actTable, "⊞")
|
|
||||||
|
|
||||||
# History
|
# History
|
||||||
self._style_letter_button(self.actHistory, "🔁")
|
self._style_letter_button(self.actHistory, "🔁")
|
||||||
|
|
|
||||||
|
|
@ -2244,44 +2244,6 @@ def test_close_current_tab(qtbot, app, tmp_db_cfg, fresh_db):
|
||||||
assert window.tab_widget.count() == initial_count - 1
|
assert window.tab_widget.count() == initial_count - 1
|
||||||
|
|
||||||
|
|
||||||
def test_table_insertion(qtbot, app, tmp_db_cfg, fresh_db):
|
|
||||||
"""Test inserting a table template."""
|
|
||||||
s = get_settings()
|
|
||||||
s.setValue("db/default_db", str(tmp_db_cfg.path))
|
|
||||||
s.setValue("db/key", tmp_db_cfg.key)
|
|
||||||
s.setValue("ui/idle_minutes", 0)
|
|
||||||
s.setValue("ui/theme", "light")
|
|
||||||
s.setValue("ui/move_todos", True)
|
|
||||||
s.setValue("ui/tags", True)
|
|
||||||
s.setValue("ui/time_log", True)
|
|
||||||
s.setValue("ui/reminders", True)
|
|
||||||
s.setValue("ui/locale", "en")
|
|
||||||
s.setValue("ui/font_size", 11)
|
|
||||||
|
|
||||||
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
|
|
||||||
window = MainWindow(themes=themes)
|
|
||||||
qtbot.addWidget(window)
|
|
||||||
window.show()
|
|
||||||
|
|
||||||
# Open a date
|
|
||||||
today = date.today().isoformat()
|
|
||||||
window._open_date_in_tab(QDate.fromString(today, "yyyy-MM-dd"))
|
|
||||||
|
|
||||||
# Ensure we have an editor
|
|
||||||
editor = window.editor
|
|
||||||
assert editor is not None
|
|
||||||
|
|
||||||
# Insert table
|
|
||||||
window._on_table_requested()
|
|
||||||
|
|
||||||
# Verify table was inserted
|
|
||||||
text = editor.toPlainText()
|
|
||||||
assert "Column 1" in text
|
|
||||||
assert "Column 2" in text
|
|
||||||
assert "Column 3" in text
|
|
||||||
assert "---" in text
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_today_alarms(qtbot, app, tmp_db_cfg, fresh_db):
|
def test_parse_today_alarms(qtbot, app, tmp_db_cfg, fresh_db):
|
||||||
"""Test parsing inline alarms from markdown (⏰ HH:MM format)."""
|
"""Test parsing inline alarms from markdown (⏰ HH:MM format)."""
|
||||||
from PySide6.QtCore import QTime
|
from PySide6.QtCore import QTime
|
||||||
|
|
|
||||||
|
|
@ -1802,26 +1802,6 @@ def test_render_images_with_corrupted_data(qtbot, app):
|
||||||
assert len(text) >= 0
|
assert len(text) >= 0
|
||||||
|
|
||||||
|
|
||||||
def test_editor_with_tables(qtbot, app):
|
|
||||||
"""Test editor with markdown tables"""
|
|
||||||
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
|
|
||||||
editor = MarkdownEditor(theme_manager=themes)
|
|
||||||
qtbot.addWidget(editor)
|
|
||||||
editor.show()
|
|
||||||
|
|
||||||
table_markdown = """
|
|
||||||
| Header 1 | Header 2 |
|
|
||||||
|----------|----------|
|
|
||||||
| Cell 1 | Cell 2 |
|
|
||||||
| Cell 3 | Cell 4 |
|
|
||||||
"""
|
|
||||||
editor.from_markdown(table_markdown)
|
|
||||||
qtbot.wait(50)
|
|
||||||
|
|
||||||
result = editor.to_markdown()
|
|
||||||
assert "Header 1" in result or "|" in result
|
|
||||||
|
|
||||||
|
|
||||||
def test_editor_with_code_blocks(qtbot, app):
|
def test_editor_with_code_blocks(qtbot, app):
|
||||||
"""Test editor with code blocks"""
|
"""Test editor with code blocks"""
|
||||||
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
|
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
|
||||||
|
|
|
||||||
|
|
@ -1,384 +0,0 @@
|
||||||
from bouquin.table_editor import TableEditorDialog, find_table_at_cursor, _is_table_line
|
|
||||||
|
|
||||||
|
|
||||||
def test_table_editor_init_simple_table(qtbot, app):
|
|
||||||
"""Test initialization with a simple markdown table."""
|
|
||||||
table_text = """| Header1 | Header2 | Header3 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Cell1 | Cell2 | Cell3 |
|
|
||||||
| Cell4 | Cell5 | Cell6 |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
assert dialog.table_widget.columnCount() == 3
|
|
||||||
assert dialog.table_widget.rowCount() == 2
|
|
||||||
assert dialog.table_widget.horizontalHeaderItem(0).text() == "Header1"
|
|
||||||
assert dialog.table_widget.horizontalHeaderItem(1).text() == "Header2"
|
|
||||||
assert dialog.table_widget.item(0, 0).text() == "Cell1"
|
|
||||||
assert dialog.table_widget.item(1, 2).text() == "Cell6"
|
|
||||||
|
|
||||||
|
|
||||||
def test_table_editor_no_separator_line(qtbot, app):
|
|
||||||
"""Test parsing table without separator line."""
|
|
||||||
table_text = """| Header1 | Header2 |
|
|
||||||
| Cell1 | Cell2 |
|
|
||||||
| Cell3 | Cell4 |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
assert dialog.table_widget.columnCount() == 2
|
|
||||||
assert dialog.table_widget.rowCount() == 2
|
|
||||||
assert dialog.table_widget.item(0, 0).text() == "Cell1"
|
|
||||||
|
|
||||||
|
|
||||||
def test_table_editor_empty_table(qtbot, app):
|
|
||||||
"""Test initialization with empty table text."""
|
|
||||||
dialog = TableEditorDialog("")
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
# Should have no columns/rows
|
|
||||||
assert dialog.table_widget.columnCount() == 0 or dialog.table_widget.rowCount() == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_table_editor_single_header_line(qtbot, app):
|
|
||||||
"""Test table with only header line."""
|
|
||||||
table_text = "| Header1 | Header2 | Header3 |"
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
assert dialog.table_widget.columnCount() == 3
|
|
||||||
assert dialog.table_widget.rowCount() == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_add_row(qtbot, app):
|
|
||||||
"""Test adding a row to the table."""
|
|
||||||
table_text = """| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
initial_rows = dialog.table_widget.rowCount()
|
|
||||||
dialog._add_row()
|
|
||||||
|
|
||||||
assert dialog.table_widget.rowCount() == initial_rows + 1
|
|
||||||
# New row should have empty items
|
|
||||||
assert dialog.table_widget.item(initial_rows, 0).text() == ""
|
|
||||||
assert dialog.table_widget.item(initial_rows, 1).text() == ""
|
|
||||||
|
|
||||||
|
|
||||||
def test_add_column(qtbot, app):
|
|
||||||
"""Test adding a column to the table."""
|
|
||||||
table_text = """| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
initial_cols = dialog.table_widget.columnCount()
|
|
||||||
dialog._add_column()
|
|
||||||
|
|
||||||
assert dialog.table_widget.columnCount() == initial_cols + 1
|
|
||||||
# New column should have header and empty items
|
|
||||||
assert "Column" in dialog.table_widget.horizontalHeaderItem(initial_cols).text()
|
|
||||||
assert dialog.table_widget.item(0, initial_cols).text() == ""
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete_row(qtbot, app):
|
|
||||||
"""Test deleting a row from the table."""
|
|
||||||
table_text = """| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |
|
|
||||||
| C | D |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
initial_rows = dialog.table_widget.rowCount()
|
|
||||||
dialog.table_widget.setCurrentCell(0, 0)
|
|
||||||
dialog._delete_row()
|
|
||||||
|
|
||||||
assert dialog.table_widget.rowCount() == initial_rows - 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete_row_no_selection(qtbot, app):
|
|
||||||
"""Test deleting a row when nothing is selected."""
|
|
||||||
table_text = """| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
initial_rows = dialog.table_widget.rowCount()
|
|
||||||
dialog.table_widget.setCurrentCell(-1, -1) # No selection
|
|
||||||
dialog._delete_row()
|
|
||||||
|
|
||||||
# Row count should remain the same
|
|
||||||
assert dialog.table_widget.rowCount() == initial_rows
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete_column(qtbot, app):
|
|
||||||
"""Test deleting a column from the table."""
|
|
||||||
table_text = """| H1 | H2 | H3 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| A | B | C |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
initial_cols = dialog.table_widget.columnCount()
|
|
||||||
dialog.table_widget.setCurrentCell(0, 1)
|
|
||||||
dialog._delete_column()
|
|
||||||
|
|
||||||
assert dialog.table_widget.columnCount() == initial_cols - 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete_column_no_selection(qtbot, app):
|
|
||||||
"""Test deleting a column when nothing is selected."""
|
|
||||||
table_text = """| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
initial_cols = dialog.table_widget.columnCount()
|
|
||||||
dialog.table_widget.setCurrentCell(-1, -1) # No selection
|
|
||||||
dialog._delete_column()
|
|
||||||
|
|
||||||
# Column count should remain the same
|
|
||||||
assert dialog.table_widget.columnCount() == initial_cols
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_markdown_table(qtbot, app):
|
|
||||||
"""Test converting table back to markdown."""
|
|
||||||
table_text = """| Name | Age | City |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Alice | 30 | NYC |
|
|
||||||
| Bob | 25 | LA |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
result = dialog.get_markdown_table()
|
|
||||||
|
|
||||||
assert "| Name | Age | City |" in result
|
|
||||||
assert "| --- | --- | --- |" in result
|
|
||||||
assert "| Alice | 30 | NYC |" in result
|
|
||||||
assert "| Bob | 25 | LA |" in result
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_markdown_table_empty(qtbot, app):
|
|
||||||
"""Test getting markdown from empty table."""
|
|
||||||
dialog = TableEditorDialog("")
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
result = dialog.get_markdown_table()
|
|
||||||
assert result == ""
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_markdown_table_with_modifications(qtbot, app):
|
|
||||||
"""Test getting markdown after modifying table."""
|
|
||||||
table_text = """| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
# Modify a cell
|
|
||||||
dialog.table_widget.item(0, 0).setText("Modified")
|
|
||||||
|
|
||||||
result = dialog.get_markdown_table()
|
|
||||||
assert "Modified" in result
|
|
||||||
|
|
||||||
|
|
||||||
def test_find_table_at_cursor_middle_of_table(qtbot, app):
|
|
||||||
"""Test finding table when cursor is in the middle."""
|
|
||||||
text = """Some text before
|
|
||||||
|
|
||||||
| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |
|
|
||||||
| C | D |
|
|
||||||
|
|
||||||
Some text after"""
|
|
||||||
|
|
||||||
# Cursor position in the middle of the table
|
|
||||||
cursor_pos = text.find("| A |") + 2
|
|
||||||
result = find_table_at_cursor(text, cursor_pos)
|
|
||||||
|
|
||||||
assert result is not None
|
|
||||||
start, end, table_text = result
|
|
||||||
assert "| H1 | H2 |" in table_text
|
|
||||||
assert "| A | B |" in table_text
|
|
||||||
assert "Some text before" not in table_text
|
|
||||||
assert "Some text after" not in table_text
|
|
||||||
|
|
||||||
|
|
||||||
def test_find_table_at_cursor_first_line(qtbot, app):
|
|
||||||
"""Test finding table when cursor is on the first line."""
|
|
||||||
text = """| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |"""
|
|
||||||
|
|
||||||
cursor_pos = 5 # In the first line
|
|
||||||
result = find_table_at_cursor(text, cursor_pos)
|
|
||||||
|
|
||||||
assert result is not None
|
|
||||||
start, end, table_text = result
|
|
||||||
assert "| H1 | H2 |" in table_text
|
|
||||||
|
|
||||||
|
|
||||||
def test_find_table_at_cursor_not_in_table(qtbot, app):
|
|
||||||
"""Test finding table when cursor is not in a table."""
|
|
||||||
text = """Just some regular text
|
|
||||||
No tables here
|
|
||||||
|
|
||||||
| H1 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| A | B |"""
|
|
||||||
|
|
||||||
cursor_pos = 10 # In "Just some regular text"
|
|
||||||
result = find_table_at_cursor(text, cursor_pos)
|
|
||||||
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_find_table_at_cursor_empty_text(qtbot, app):
|
|
||||||
"""Test finding table in empty text."""
|
|
||||||
result = find_table_at_cursor("", 0)
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_find_table_at_cursor_multiple_tables(qtbot, app):
|
|
||||||
"""Test finding correct table when there are multiple tables."""
|
|
||||||
text = """| Table1 | H1 |
|
|
||||||
| --- | --- |
|
|
||||||
|
|
||||||
Some text
|
|
||||||
|
|
||||||
| Table2 | H2 |
|
|
||||||
| --- | --- |
|
|
||||||
| Data | Here |"""
|
|
||||||
|
|
||||||
# Cursor in second table
|
|
||||||
cursor_pos = text.find("| Data |")
|
|
||||||
result = find_table_at_cursor(text, cursor_pos)
|
|
||||||
|
|
||||||
assert result is not None
|
|
||||||
start, end, table_text = result
|
|
||||||
assert "Table2" in table_text
|
|
||||||
assert "Table1" not in table_text
|
|
||||||
|
|
||||||
|
|
||||||
def test_is_table_line_valid(qtbot, app):
|
|
||||||
"""Test identifying valid table lines."""
|
|
||||||
assert _is_table_line("| Header | Header2 |") is True
|
|
||||||
assert _is_table_line("| --- | --- |") is True
|
|
||||||
assert _is_table_line("| Cell | Cell2 | Cell3 |") is True
|
|
||||||
|
|
||||||
|
|
||||||
def test_is_table_line_invalid(qtbot, app):
|
|
||||||
"""Test identifying invalid table lines."""
|
|
||||||
assert _is_table_line("Just regular text") is False
|
|
||||||
assert _is_table_line("") is False
|
|
||||||
assert _is_table_line(" ") is False
|
|
||||||
assert _is_table_line("| Only one pipe") is False
|
|
||||||
assert _is_table_line("Only one pipe |") is False
|
|
||||||
assert _is_table_line("No pipes at all") is False
|
|
||||||
|
|
||||||
|
|
||||||
def test_is_table_line_edge_cases(qtbot, app):
|
|
||||||
"""Test edge cases for table line detection."""
|
|
||||||
assert _is_table_line("| | |") is True # Minimal valid table
|
|
||||||
assert (
|
|
||||||
_is_table_line(" | Header | Data | ") is True
|
|
||||||
) # With leading/trailing spaces
|
|
||||||
|
|
||||||
|
|
||||||
def test_table_with_alignment_indicators(qtbot, app):
|
|
||||||
"""Test parsing table with alignment indicators."""
|
|
||||||
table_text = """| Left | Center | Right |
|
|
||||||
| :--- | :---: | ---: |
|
|
||||||
| L1 | C1 | R1 |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
assert dialog.table_widget.columnCount() == 3
|
|
||||||
assert dialog.table_widget.rowCount() == 1
|
|
||||||
assert dialog.table_widget.item(0, 0).text() == "L1"
|
|
||||||
|
|
||||||
|
|
||||||
def test_accept_dialog(qtbot, app):
|
|
||||||
"""Test accepting the dialog."""
|
|
||||||
table_text = "| H1 | H2 |\n| --- | --- |\n| A | B |"
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
# Find and click the OK button
|
|
||||||
for child in dialog.findChildren(type(dialog.findChild(type(None)))):
|
|
||||||
if hasattr(child, "text") and callable(child.text):
|
|
||||||
try:
|
|
||||||
if "ok" in child.text().lower() or "OK" in child.text():
|
|
||||||
child.click()
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def test_reject_dialog(qtbot, app):
|
|
||||||
"""Test rejecting the dialog."""
|
|
||||||
table_text = "| H1 | H2 |\n| --- | --- |\n| A | B |"
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
# Find and click the Cancel button
|
|
||||||
for child in dialog.findChildren(type(dialog.findChild(type(None)))):
|
|
||||||
if hasattr(child, "text") and callable(child.text):
|
|
||||||
try:
|
|
||||||
if "cancel" in child.text().lower():
|
|
||||||
child.click()
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def test_table_with_uneven_columns(qtbot, app):
|
|
||||||
"""Test parsing table with uneven number of columns in rows."""
|
|
||||||
table_text = """| H1 | H2 | H3 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| A | B |
|
|
||||||
| C | D | E | F |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
# Should handle gracefully
|
|
||||||
assert dialog.table_widget.columnCount() == 3
|
|
||||||
assert dialog.table_widget.rowCount() == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_table_with_empty_cells(qtbot, app):
|
|
||||||
"""Test parsing table with empty cells."""
|
|
||||||
table_text = """| H1 | H2 | H3 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| | B | |
|
|
||||||
| C | | E |"""
|
|
||||||
|
|
||||||
dialog = TableEditorDialog(table_text)
|
|
||||||
qtbot.addWidget(dialog)
|
|
||||||
|
|
||||||
assert dialog.table_widget.item(0, 0).text() == ""
|
|
||||||
assert dialog.table_widget.item(0, 1).text() == "B"
|
|
||||||
assert dialog.table_widget.item(0, 2).text() == ""
|
|
||||||
assert dialog.table_widget.item(1, 0).text() == "C"
|
|
||||||
assert dialog.table_widget.item(1, 1).text() == ""
|
|
||||||
assert dialog.table_widget.item(1, 2).text() == "E"
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue