mirror of https://gitlab.com/pamhyr/pamhyr2
BC: Edit: Add table display.
parent
568be94e56
commit
d49091a469
|
|
@ -15,6 +15,9 @@ class BoundaryCondition(object):
|
||||||
self._header = []
|
self._header = []
|
||||||
self._types = [int, float]
|
self._types = [int, float]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._name
|
||||||
|
|
@ -46,9 +49,16 @@ class BoundaryCondition(object):
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data.copy()
|
return self._data.copy()
|
||||||
|
|
||||||
|
def get_type_column(self, column):
|
||||||
|
if 0 <= column < 2:
|
||||||
|
return self._types[column]
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
def _default_0(self):
|
def _default_0(self):
|
||||||
return self._types[0](0)
|
return self._types[0](0)
|
||||||
|
|
||||||
|
@property
|
||||||
def _default_1(self):
|
def _default_1(self):
|
||||||
return self._types[1](0.0)
|
return self._types[1](0.0)
|
||||||
|
|
||||||
|
|
@ -56,13 +66,14 @@ class BoundaryCondition(object):
|
||||||
return self._data is not None
|
return self._data is not None
|
||||||
|
|
||||||
def add(self, index:int):
|
def add(self, index:int):
|
||||||
value = (self.default_0, self_default_1)
|
value = (self._default_0, self._default_1)
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
|
return value
|
||||||
|
|
||||||
def insert(self, index:int, value):
|
def insert(self, index:int, value):
|
||||||
self._data.insert(index, value)
|
self._data.insert(index, value)
|
||||||
|
|
||||||
def delete(self, indexes):
|
def delete_i(self, indexes):
|
||||||
self._data = list(
|
self._data = list(
|
||||||
map(
|
map(
|
||||||
lambda e: e[1],
|
lambda e: e[1],
|
||||||
|
|
@ -73,12 +84,30 @@ class BoundaryCondition(object):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def sort(self, _reverse):
|
def delete(self, els):
|
||||||
self._data.sort(reverse=_reverse)
|
self._data = list(
|
||||||
|
filter(
|
||||||
|
lambda e: e not in els,
|
||||||
|
self.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def sort(self, _reverse=False, key=None):
|
||||||
|
if key is None:
|
||||||
|
self._data.sort(reverse=_reverse)
|
||||||
|
else:
|
||||||
|
self._data.sort(reverse=_reverse, key=key)
|
||||||
|
|
||||||
def get_i(self, index):
|
def get_i(self, index):
|
||||||
return self.data[index]
|
return self.data[index]
|
||||||
|
|
||||||
|
def get_range(self, _range):
|
||||||
|
l = []
|
||||||
|
for r in _range:
|
||||||
|
l.append(r)
|
||||||
|
return l
|
||||||
|
|
||||||
|
|
||||||
def _set_i_c_v(self, index, column, value):
|
def _set_i_c_v(self, index, column, value):
|
||||||
v = list(self._data[index])
|
v = list(self._data[index])
|
||||||
v[column] = self._types[column](value)
|
v[column] = self._types[column](value)
|
||||||
|
|
@ -103,3 +132,15 @@ class BoundaryCondition(object):
|
||||||
new._set_i_c_v(ind, j, v[i])
|
new._set_i_c_v(ind, j, v[i])
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
def move_up(self, index):
|
||||||
|
if index < len(self):
|
||||||
|
next = index - 1
|
||||||
|
d = self._data
|
||||||
|
d[index], d[next] = d[next], d[index]
|
||||||
|
|
||||||
|
def move_down(self, index):
|
||||||
|
if index >= 0:
|
||||||
|
prev = index + 1
|
||||||
|
d = self._data
|
||||||
|
d[index], d[prev] = d[prev], d[index]
|
||||||
|
|
|
||||||
|
|
@ -41,5 +41,6 @@ class ZOverDebit(BoundaryCondition):
|
||||||
self._header = ["z", "debit"]
|
self._header = ["z", "debit"]
|
||||||
self._types = [float, float]
|
self._types = [float, float]
|
||||||
|
|
||||||
|
@property
|
||||||
def _default_0(self):
|
def _default_0(self):
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
|
||||||
|
|
@ -174,5 +174,5 @@ class DuplicateCommand(QUndoCommand):
|
||||||
self._lst.delete(self._bc)
|
self._lst.delete(self._bc)
|
||||||
|
|
||||||
def redo(self):
|
def redo(self):
|
||||||
for profile in self._profiles:
|
for bc in self._bcs:
|
||||||
self._lst.insert(self._rows[0], profile)
|
self._lst.insert(self._rows[0], bc)
|
||||||
|
|
|
||||||
|
|
@ -319,6 +319,7 @@ class BoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
|
||||||
def index_selected_rows(self):
|
def index_selected_rows(self):
|
||||||
table = self.find(QTableView, "tableView")
|
table = self.find(QTableView, "tableView")
|
||||||
return list(
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
set(
|
set(
|
||||||
map(
|
map(
|
||||||
lambda i: i.row(),
|
lambda i: i.row(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from View.ASubWindow import ASubMainWindow
|
||||||
|
from View.ListedSubWindow import ListedSubWindow
|
||||||
|
|
||||||
|
from PyQt5.QtCore import (
|
||||||
|
Qt, QVariant, QAbstractTableModel,
|
||||||
|
QCoreApplication, QModelIndex, pyqtSlot,
|
||||||
|
QRect,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QTableView, QAbstractItemView,
|
||||||
|
)
|
||||||
|
|
||||||
|
from View.BoundaryCondition.Edit.UndoCommand import (
|
||||||
|
SetDataCommand, AddCommand, DelCommand,
|
||||||
|
SortCommand, MoveCommand, PasteCommand,
|
||||||
|
DuplicateCommand,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.BoundaryCondition.BoundaryConditionTypes import (
|
||||||
|
NotDefined, PonctualContribution,
|
||||||
|
TimeOverZ, TimeOverDebit, ZOverDebit
|
||||||
|
)
|
||||||
|
|
||||||
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
table_headers = {
|
||||||
|
"time": _translate("BoundaryCondition", "Time"),
|
||||||
|
"debit": _translate("BoundaryCondition", "Debit"),
|
||||||
|
"z": _translate("BoundaryCondition", "Z (m)")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TableModel(QAbstractTableModel):
|
||||||
|
def __init__(self, data=None, undo=None):
|
||||||
|
super(QAbstractTableModel, self).__init__()
|
||||||
|
self._headers = data.header
|
||||||
|
self._data = data
|
||||||
|
self._undo = undo
|
||||||
|
|
||||||
|
def flags(self, index):
|
||||||
|
options = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||||
|
options |= Qt.ItemIsEditable
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
def rowCount(self, parent):
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
def columnCount(self, parent):
|
||||||
|
return len(self._headers)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role != Qt.ItemDataRole.DisplayRole:
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
value = QVariant()
|
||||||
|
|
||||||
|
if 0 <= column < 2:
|
||||||
|
v = self._data.get_i(row)[column]
|
||||||
|
if self._data.get_type_column(column) == float:
|
||||||
|
value = f"{v:.4f}"
|
||||||
|
else:
|
||||||
|
# TODO: Time format
|
||||||
|
value = f"{v}"
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def headerData(self, section, orientation, role):
|
||||||
|
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||||
|
return table_headers[self._headers[section]]
|
||||||
|
|
||||||
|
return QVariant()
|
||||||
|
|
||||||
|
def setData(self, index, value, role=Qt.EditRole):
|
||||||
|
if not index.isValid() or role != Qt.EditRole:
|
||||||
|
return False
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
SetDataCommand(
|
||||||
|
self._data, row, column, value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add(self, row, parent=QModelIndex()):
|
||||||
|
self.beginInsertRows(parent, row, row - 1)
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
AddCommand(
|
||||||
|
self._data, row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endInsertRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def delete(self, rows, parent=QModelIndex()):
|
||||||
|
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
DelCommand(
|
||||||
|
self._data, rows
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endRemoveRows()
|
||||||
|
|
||||||
|
def sort(self, _reverse, parent=QModelIndex()):
|
||||||
|
self.layoutAboutToBeChanged.emit()
|
||||||
|
|
||||||
|
self._undo.push(
|
||||||
|
SortCommand(
|
||||||
|
self._data, False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.layoutAboutToBeChanged.emit()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def move_up(self, row, parent=QModelIndex()):
|
||||||
|
if row <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
target = row + 2
|
||||||
|
|
||||||
|
self.beginMoveRows(parent, row - 1, row - 1, parent, target)
|
||||||
|
|
||||||
|
self._undo_stack.push(
|
||||||
|
MoveCommand(
|
||||||
|
self._data, "up", row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endMoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def move_down(self, index, parent=QModelIndex()):
|
||||||
|
if row > len(self._data):
|
||||||
|
return
|
||||||
|
|
||||||
|
target = row
|
||||||
|
|
||||||
|
self.beginMoveRows(parent, row + 1, row + 1, parent, target)
|
||||||
|
|
||||||
|
self._undo_stack.push(
|
||||||
|
MoveCommand(
|
||||||
|
self._data, "down", row
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.endMoveRows()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._undo.undo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._undo.redo()
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from tools import trace, timer
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QMessageBox, QUndoCommand, QUndoStack,
|
||||||
|
)
|
||||||
|
|
||||||
|
from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition
|
||||||
|
|
||||||
|
class SetDataCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, index, column, new_value):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._index = index
|
||||||
|
self._column = column
|
||||||
|
self._old = self._data.get_i(self._index)[self._column]
|
||||||
|
self._new = new_value
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data._set_i_c_v(self._index, self._column, self._old)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._data._set_i_c_v(self._index, self._column, self._new)
|
||||||
|
|
||||||
|
class AddCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, index):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._index = index
|
||||||
|
self._new = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.delete_i([self._index])
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._new is None:
|
||||||
|
self._new = self._data.add(self._index)
|
||||||
|
else:
|
||||||
|
self._data.insert(self._index, self._new)
|
||||||
|
|
||||||
|
class DelCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, rows):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._rows = rows
|
||||||
|
|
||||||
|
self._bc = []
|
||||||
|
for row in rows:
|
||||||
|
self._bc.append((row, self._data.get_i(row)))
|
||||||
|
self._bc.sort()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
for row, el in self._bc:
|
||||||
|
self._data.insert(row, el)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._data.delete_i(self._rows)
|
||||||
|
|
||||||
|
class SortCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, _reverse):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._reverse = _reverse
|
||||||
|
|
||||||
|
self._old = self._data.data
|
||||||
|
self._indexes = None
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
ll = self._data.data
|
||||||
|
self._data.sort(
|
||||||
|
key=lambda x: self._indexes[ll.index(x)]
|
||||||
|
)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._data.sort(
|
||||||
|
reverse=self._reverse,
|
||||||
|
key=lambda x: x.name
|
||||||
|
)
|
||||||
|
if self._indexes is None:
|
||||||
|
self._indexes = list(
|
||||||
|
map(
|
||||||
|
lambda p: self._old.index(p),
|
||||||
|
self._data.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._old = None
|
||||||
|
|
||||||
|
|
||||||
|
class MoveCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, up, i):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._up = up == "up"
|
||||||
|
self._i = i
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
if self._up:
|
||||||
|
self._data.move_up(self._i)
|
||||||
|
else:
|
||||||
|
self._data.move_down(self._i)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
if self._up:
|
||||||
|
self._data.move_up(self._i)
|
||||||
|
else:
|
||||||
|
self._data.move_down(self._i)
|
||||||
|
|
||||||
|
|
||||||
|
class PasteCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, row, bc):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._row = row
|
||||||
|
self._bc = deepcopy(bc)
|
||||||
|
self._bc.reverse()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.delete(self._bc)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for bc in self._bc:
|
||||||
|
self._data.insert(self._row, bc)
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateCommand(QUndoCommand):
|
||||||
|
def __init__(self, data, rows, bc):
|
||||||
|
QUndoCommand.__init__(self)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
self._rows = rows
|
||||||
|
self._bc = deepcopy(bc)
|
||||||
|
self._bc.reverse()
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._data.delete(self._bc)
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
for bc in self._bcs:
|
||||||
|
self._data.insert(self._rows[0], bc)
|
||||||
|
|
@ -3,6 +3,10 @@
|
||||||
from View.ASubWindow import ASubMainWindow
|
from View.ASubWindow import ASubMainWindow
|
||||||
from View.ListedSubWindow import ListedSubWindow
|
from View.ListedSubWindow import ListedSubWindow
|
||||||
|
|
||||||
|
from PyQt5.QtGui import (
|
||||||
|
QKeySequence,
|
||||||
|
)
|
||||||
|
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
Qt, QVariant, QAbstractTableModel, QCoreApplication,
|
||||||
)
|
)
|
||||||
|
|
@ -10,9 +14,11 @@ from PyQt5.QtCore import (
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialogButtonBox, QPushButton, QLineEdit,
|
QDialogButtonBox, QPushButton, QLineEdit,
|
||||||
QFileDialog, QTableView, QAbstractItemView,
|
QFileDialog, QTableView, QAbstractItemView,
|
||||||
|
QUndoStack, QShortcut, QAction, QItemDelegate,
|
||||||
)
|
)
|
||||||
|
|
||||||
from View.BoundaryCondition.translate import long_types
|
from View.BoundaryCondition.translate import long_types
|
||||||
|
from View.BoundaryCondition.Edit.Table import TableModel
|
||||||
|
|
||||||
_translate = QCoreApplication.translate
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
|
@ -26,6 +32,9 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
|
||||||
self._title = title
|
self._title = title
|
||||||
|
|
||||||
self.setup_window()
|
self.setup_window()
|
||||||
|
self.setup_sc()
|
||||||
|
self.setup_table()
|
||||||
|
self.setup_connections()
|
||||||
|
|
||||||
def setup_window(self):
|
def setup_window(self):
|
||||||
if self._data is not None:
|
if self._data is not None:
|
||||||
|
|
@ -39,3 +48,88 @@ class EditBoundaryConditionWindow(ASubMainWindow, ListedSubWindow):
|
||||||
self.ui.setWindowTitle(title)
|
self.ui.setWindowTitle(title)
|
||||||
else:
|
else:
|
||||||
self.ui.setWindowTitle(_translate("BoundaryCondition", self._title))
|
self.ui.setWindowTitle(_translate("BoundaryCondition", self._title))
|
||||||
|
|
||||||
|
def setup_sc(self):
|
||||||
|
self._undo_stack = QUndoStack()
|
||||||
|
|
||||||
|
self.undo_sc = QShortcut(QKeySequence.Undo, self)
|
||||||
|
self.redo_sc = QShortcut(QKeySequence.Redo, self)
|
||||||
|
self.copy_sc = QShortcut(QKeySequence.Copy, self)
|
||||||
|
self.paste_sc = QShortcut(QKeySequence.Paste, self)
|
||||||
|
|
||||||
|
def setup_table(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
self._table = TableModel(
|
||||||
|
data = self._data,
|
||||||
|
undo = self._undo_stack
|
||||||
|
)
|
||||||
|
table.setModel(self._table)
|
||||||
|
table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
table.setAlternatingRowColors(True)
|
||||||
|
|
||||||
|
def setup_connections(self):
|
||||||
|
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||||
|
self.find(QAction, "action_del").triggered.connect(self.delete)
|
||||||
|
self.find(QAction, "action_sort").triggered.connect(self.sort)
|
||||||
|
|
||||||
|
self.undo_sc.activated.connect(self.undo)
|
||||||
|
self.redo_sc.activated.connect(self.redo)
|
||||||
|
self.copy_sc.activated.connect(self.copy)
|
||||||
|
self.paste_sc.activated.connect(self.paste)
|
||||||
|
|
||||||
|
def index_selected_row(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return table.selectionModel()\
|
||||||
|
.selectedRows()[0]\
|
||||||
|
.row()
|
||||||
|
|
||||||
|
def index_selected_rows(self):
|
||||||
|
table = self.find(QTableView, "tableView")
|
||||||
|
return list(
|
||||||
|
# Delete duplicate
|
||||||
|
set(
|
||||||
|
map(
|
||||||
|
lambda i: i.row(),
|
||||||
|
table.selectedIndexes()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(self._data) == 0 or len(rows) == 0:
|
||||||
|
self._table.add(0)
|
||||||
|
else:
|
||||||
|
self._table.add(rows[0])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
rows = self.index_selected_rows()
|
||||||
|
if len(rows) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._table.delete(rows)
|
||||||
|
|
||||||
|
def sort(self):
|
||||||
|
self._table.sort(False)
|
||||||
|
|
||||||
|
def move_up(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_up(row)
|
||||||
|
|
||||||
|
def move_down(self):
|
||||||
|
row = self.index_selected_row()
|
||||||
|
self._table.move_down(row)
|
||||||
|
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
print("TODO")
|
||||||
|
|
||||||
|
def paste(self):
|
||||||
|
print("TODO")
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
self._table.undo()
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
self._table.redo()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue