mirror of https://gitlab.com/pamhyr/pamhyr2
geometry: Copy/Paste with system clipboard.
parent
d610dfef7e
commit
012f7ce445
|
|
@ -116,3 +116,44 @@ class FileFormatError(ExeceptionWithMessageBox):
|
|||
_translate("Exception", "format because of") +
|
||||
f" '{self.reason}'"
|
||||
)
|
||||
|
||||
|
||||
class ClipboardFormatError(ExeceptionWithMessageBox):
|
||||
def __init__(self, mime=None, header=None, data=None):
|
||||
super(ClipboardFormatError, self).__init__(
|
||||
title = _translate("Exception", "Clipboard format error")
|
||||
)
|
||||
|
||||
self.mime = mime
|
||||
self.header = header
|
||||
self.data = data
|
||||
|
||||
if self.mime is not None:
|
||||
self.msg = f"Impossible to decode data to mime code '{self.mime}'"
|
||||
else:
|
||||
if len(self.header) == 0:
|
||||
msg = _translate("Exception", "without header")
|
||||
else:
|
||||
msg = (
|
||||
_translate("Exception", "with header") +
|
||||
f": {self.header}"
|
||||
)
|
||||
|
||||
self.msg = (
|
||||
_translate("Exception", "Invalid clipboard data format:") +
|
||||
f" '{self.data}' {msg}"
|
||||
)
|
||||
|
||||
self.alert()
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
def header(self):
|
||||
return _translate("Exception", "Clipboard format error")
|
||||
|
||||
def short_message(self):
|
||||
return _translate("Exception", "Clipboard format unknown")
|
||||
|
||||
def message(self):
|
||||
return self.msg
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ from Model.Geometry.PointXYZ import PointXYZ
|
|||
from Model.Geometry.Vector_1d import Vector1d
|
||||
|
||||
class ProfileXYZ(Profile):
|
||||
def __init__(self, num: int = 0,
|
||||
code1: int = 0, code2: int = 0,
|
||||
def __init__(self,
|
||||
name: str = "",
|
||||
kp: float = 0.,
|
||||
reach = None,
|
||||
nb_point: int = 0,
|
||||
kp: float = 0., name: str = "",
|
||||
reach = None):
|
||||
code1: int = 0, code2: int = 0):
|
||||
"""ProfileXYZ constructor
|
||||
|
||||
Args:
|
||||
|
|
@ -28,7 +29,7 @@ class ProfileXYZ(Profile):
|
|||
Nothing.
|
||||
"""
|
||||
super(ProfileXYZ, self).__init__(
|
||||
num = num,
|
||||
num = 0,
|
||||
name = name,
|
||||
kp = kp,
|
||||
code1 = code1, code2 = code2,
|
||||
|
|
@ -36,6 +37,28 @@ class ProfileXYZ(Profile):
|
|||
reach = reach,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_data(cls, header, data):
|
||||
profile = None
|
||||
try:
|
||||
if len(header) == 0:
|
||||
profile = cls(
|
||||
*data
|
||||
)
|
||||
else:
|
||||
valid_header = {'name', 'reach', 'kp'}
|
||||
d = {}
|
||||
for i, v in enumerate(data):
|
||||
h = header[i].strip().lower().split(' ')[0]
|
||||
if h in valid_header:
|
||||
d[h] = v
|
||||
|
||||
profile = cls(**d)
|
||||
except Exception as e:
|
||||
raise ClipboardFormatError(header, data)
|
||||
|
||||
return profile
|
||||
|
||||
def x(self):
|
||||
return [point.x for point in self._points]
|
||||
|
||||
|
|
|
|||
|
|
@ -24,14 +24,19 @@ class WindowToolKit(object):
|
|||
def __init__(self, parent=None):
|
||||
super(WindowToolKit, self).__init__()
|
||||
|
||||
def copyTableIntoClipboard(self, table):
|
||||
stream = StringIO()
|
||||
csv.writer(stream, delimiter='\t').writerows(table)
|
||||
QApplication.clipboard().setText(stream.getvalue())
|
||||
|
||||
def parseClipboardTable(self):
|
||||
clip = QApplication.clipboard()
|
||||
mime = clip.mimeData()
|
||||
# print(mime.formats())
|
||||
data = mime.data('text/plain').data().decode()
|
||||
if 'text/plain' not in mime.formats():
|
||||
raise ClipboardFormatError(mime='text/plain')
|
||||
|
||||
data = mime.data('text/plain').data().decode()
|
||||
has_header = csv.Sniffer().has_header(data)
|
||||
print(f"header? {has_header}")
|
||||
|
||||
header = []
|
||||
values = []
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import sys
|
|||
import time
|
||||
|
||||
from copy import deepcopy
|
||||
from tools import timer
|
||||
from tools import timer, trace
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5.QtGui import (
|
||||
|
|
@ -372,18 +372,27 @@ class GeometryWindow(QMainWindow, WindowToolKit):
|
|||
.selectionModel()\
|
||||
.selectedRows()
|
||||
|
||||
self._clipboard = []
|
||||
table = []
|
||||
table.append(["name", "kp"])
|
||||
|
||||
for row in rows:
|
||||
self._clipboard.append(
|
||||
deepcopy(
|
||||
self._reach.profile(row.row())
|
||||
)
|
||||
profile = self._reach.profile(row.row())
|
||||
table.append(
|
||||
[profile.name, profile.kp]
|
||||
)
|
||||
|
||||
self.copyTableIntoClipboard(table)
|
||||
|
||||
def paste(self):
|
||||
header, data = self.parseClipboardTable()
|
||||
|
||||
if len(header) != 0:
|
||||
header.append("reach")
|
||||
for row in data:
|
||||
row.append(self._reach)
|
||||
|
||||
row = self.index_selected_row()
|
||||
self._tablemodel.paste(row, self._clipboard)
|
||||
self._tablemodel.paste(row, header, data)
|
||||
self.select_current_profile()
|
||||
|
||||
def undo(self):
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
import time
|
||||
|
||||
from tools import timer, trace
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
QKeySequence, QColor
|
||||
)
|
||||
|
|
@ -16,6 +18,7 @@ from PyQt5.QtWidgets import (
|
|||
)
|
||||
|
||||
from Model.Geometry import Reach
|
||||
from Model.Geometry.ProfileXYZ import ProfileXYZ
|
||||
from View.Geometry.ReachUndoCommand import *
|
||||
|
||||
|
||||
|
|
@ -30,11 +33,16 @@ class TableEditableModel(QAbstractTableModel):
|
|||
self._undo_stack = undo
|
||||
self._reach = reach
|
||||
|
||||
# Hack for qtlinguist
|
||||
_ = _translate("Geometry", "Name")
|
||||
_ = _translate("Geometry", "Kp (m)")
|
||||
_ = _translate("Geometry", "Type")
|
||||
|
||||
if headers is None:
|
||||
self.headers = [
|
||||
_translate("Geometry", "Name"),
|
||||
_translate("Geometry", "Kp (m)"),
|
||||
_translate("Geometry", "Type")
|
||||
"Name",
|
||||
"Kp (m)",
|
||||
"Type"
|
||||
]
|
||||
else:
|
||||
self.headers = headers
|
||||
|
|
@ -79,7 +87,7 @@ class TableEditableModel(QAbstractTableModel):
|
|||
if role == Qt.DisplayRole:
|
||||
if orientation == Qt.Horizontal:
|
||||
if section < len(self.headers):
|
||||
return self.headers[section]
|
||||
return _translate("Geometry", self.headers[section])
|
||||
else:
|
||||
return str(section + 1)
|
||||
|
||||
|
|
@ -204,18 +212,25 @@ class TableEditableModel(QAbstractTableModel):
|
|||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def paste(self, row, profiles):
|
||||
@trace
|
||||
def paste(self, row, header, data):
|
||||
if row > self._reach.number_profiles:
|
||||
return
|
||||
|
||||
if len(profiles) == 0:
|
||||
if len(data) == 0:
|
||||
return
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
self._undo_stack.push(
|
||||
PasteCommand(
|
||||
self._reach, row, profiles
|
||||
self._reach, row,
|
||||
list(
|
||||
map(
|
||||
lambda d: ProfileXYZ.from_data(header, d),
|
||||
data
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue