Bugfix/wrong translate implementation (#1520)

* Removes all translate call on the parameter name and moves them into the title instead allowing to decouple visualization from code logic

* Removes all translate calls from the Exporter class property Name and moves the translation logic when setting the QListWidgetItems for the formatList

* Adds missing call to translation function for the export action on GraphicsScene
This commit is contained in:
Sérgio Peixoto 2021-01-28 16:42:18 +00:00 committed by GitHub
parent b094945dbc
commit 3f02b30140
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 79 additions and 73 deletions

View File

@ -116,7 +116,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
self.lastHoverEvent = None
self.minDragTime = 0.5 # drags shorter than 0.5 sec are interpreted as clicks
self.contextMenu = [QtGui.QAction("Export...", self)]
self.contextMenu = [QtGui.QAction(QtCore.QCoreApplication.translate("GraphicsScene", "Export..."), self)]
self.contextMenu[0].triggered.connect(self.showExportDialog)
self.exportDialog = None

View File

@ -1,4 +1,4 @@
from ..Qt import QtCore, QtGui, QT_LIB
from ..Qt import QtCore, QtGui, QtWidgets, QT_LIB
from .. import exporters as exporters
from .. import functions as fn
from ..graphicsItems.ViewBox import ViewBox
@ -9,6 +9,12 @@ ui_template = importlib.import_module(
f'.exportDialogTemplate_{QT_LIB.lower()}', package=__package__)
class FormatExportListWidgetItem(QtWidgets.QListWidgetItem):
def __init__(self, expClass, *args, **kwargs):
QtWidgets.QListWidgetItem.__init__(self, *args, **kwargs)
self.expClass = expClass
class ExportDialog(QtGui.QWidget):
def __init__(self, scene):
QtGui.QWidget.__init__(self)
@ -94,16 +100,14 @@ class ExportDialog(QtGui.QWidget):
def updateFormatList(self):
current = self.ui.formatList.currentItem()
if current is not None:
current = str(current.text())
self.ui.formatList.clear()
self.exporterClasses = {}
gotCurrent = False
for exp in exporters.listExporters():
self.ui.formatList.addItem(exp.Name)
self.exporterClasses[exp.Name] = exp
if exp.Name == current:
self.ui.formatList.setCurrentRow(self.ui.formatList.count()-1)
item = FormatExportListWidgetItem(exp, QtCore.QCoreApplication.translate('Exporter', exp.Name))
self.ui.formatList.addItem(item)
if item == current:
self.ui.formatList.setCurrentRow(self.ui.formatList.count() - 1)
gotCurrent = True
if not gotCurrent:
@ -114,7 +118,7 @@ class ExportDialog(QtGui.QWidget):
self.currentExporter = None
self.ui.paramTree.clear()
return
expClass = self.exporterClasses[str(item.text())]
expClass = item.expClass
exp = expClass(item=self.ui.itemTree.currentItem().gitem)
params = exp.parameters()

View File

@ -11,14 +11,14 @@ __all__ = ['CSVExporter']
class CSVExporter(Exporter):
Name = translate("Exporter", "CSV from plot data")
Name = "CSV from plot data"
windows = []
def __init__(self, item):
Exporter.__init__(self, item)
self.params = Parameter(name='params', type='group', children=[
{'name': translate("Exporter", 'separator'), 'type': 'list', 'value': 'comma', 'values': ['comma', 'tab']},
{'name': translate("Exporter", 'precision'), 'type': 'int', 'value': 10, 'limits': [0, None]},
{'name': translate("Exporter", 'columnMode'), 'type': 'list', 'values': ['(x,y) per plot', '(x,y,y,y) for all plots']}
{'name': 'separator', 'title': translate("Exporter", 'separator'), 'type': 'list', 'value': 'comma', 'values': ['comma', 'tab']},
{'name': 'precision', 'title': translate("Exporter", 'precision'), 'type': 'int', 'value': 10, 'limits': [0, None]},
{'name': 'columnMode', 'title': translate("Exporter", 'columnMode'), 'type': 'list', 'values': ['(x,y) per plot', '(x,y,y,y) for all plots']}
])
def parameters(self):
@ -83,5 +83,3 @@ class CSVExporter(Exporter):
CSVExporter.register()

View File

@ -16,15 +16,16 @@ __all__ = ['HDF5Exporter']
class HDF5Exporter(Exporter):
Name = translate("Exporter", "HDF5 Export: plot (x,y)")
Name = "HDF5 Export: plot (x,y)"
windows = []
allowCopy = False
def __init__(self, item):
Exporter.__init__(self, item)
self.params = Parameter(name='params', type='group', children=[
{'name': translate("Exporter", 'Name'), 'type': 'str', 'value': 'Export',},
{'name': translate("Exporter", 'columnMode'), 'type': 'list', 'values': ['(x,y) per plot', '(x,y,y,y) for all plots']},
{'name': 'Name', 'title': translate("Exporter", 'Name'), 'type': 'str', 'value': 'Export', },
{'name': 'columnMode', 'title': translate("Exporter", 'columnMode'), 'type': 'list',
'values': ['(x,y) per plot', '(x,y,y,y) for all plots']},
])
def parameters(self):
@ -41,11 +42,11 @@ class HDF5Exporter(Exporter):
if fileName is None:
self.fileSaveDialog(filter=["*.h5", "*.hdf", "*.hd5"])
return
dsname = self.params[translate("Exporter", 'Name')]
fd = h5py.File(fileName, 'a') # forces append to file... 'w' doesn't seem to "delete/overwrite"
dsname = self.params['Name']
fd = h5py.File(fileName, 'a') # forces append to file... 'w' doesn't seem to "delete/overwrite"
data = []
appendAllX = self.params[translate("Exporter", 'columnMode')] == '(x,y) per plot'
appendAllX = self.params['columnMode'] == '(x,y) per plot'
# Check if the arrays are ragged
len_first = len(self.item.curves[0].getData()[0]) if self.item.curves[0] else None
ragged = any(len(i.getData()[0]) != len_first for i in self.item.curves)

View File

@ -8,7 +8,7 @@ translate = QtCore.QCoreApplication.translate
__all__ = ['ImageExporter']
class ImageExporter(Exporter):
Name = f"{translate('Exporter', 'Image File')} (PNG, TIF, JPG, ...)"
Name = "Image File (PNG, TIF, JPG, ...)"
allowCopy = True
def __init__(self, item):
@ -22,26 +22,28 @@ class ImageExporter(Exporter):
bg = bgbrush.color()
if bgbrush.style() == QtCore.Qt.NoBrush:
bg.setAlpha(0)
self.params = Parameter(name='params', type='group', children=[
{'name': translate("Exporter", 'width'), 'type': 'int', 'value': int(tr.width()), 'limits': (0, None)},
{'name': translate("Exporter", 'height'), 'type': 'int', 'value': int(tr.height()), 'limits': (0, None)},
{'name': translate("Exporter", 'antialias'), 'type': 'bool', 'value': True},
{'name': translate("Exporter", 'background'), 'type': 'color', 'value': bg},
{'name': translate("Exporter", 'invertValue'), 'type': 'bool', 'value': False}
{'name': 'width', 'title': translate("Exporter", 'width'), 'type': 'int', 'value': int(tr.width()),
'limits': (0, None)},
{'name': 'height', 'title': translate("Exporter", 'height'), 'type': 'int', 'value': int(tr.height()),
'limits': (0, None)},
{'name': 'antialias', 'title': translate("Exporter", 'antialias'), 'type': 'bool', 'value': True},
{'name': 'background', 'title': translate("Exporter", 'background'), 'type': 'color', 'value': bg},
{'name': 'invertValue', 'title': translate("Exporter", 'invertValue'), 'type': 'bool', 'value': False}
])
self.params.param(translate("Exporter", 'width')).sigValueChanged.connect(self.widthChanged)
self.params.param(translate("Exporter", 'height')).sigValueChanged.connect(self.heightChanged)
self.params.param('width').sigValueChanged.connect(self.widthChanged)
self.params.param('height').sigValueChanged.connect(self.heightChanged)
def widthChanged(self):
sr = self.getSourceRect()
ar = float(sr.height()) / sr.width()
self.params.param(translate("Exporter", 'height')).setValue(int(self.params[translate("Exporter", 'width')] * ar), blockSignal=self.heightChanged)
self.params.param('height').setValue(int(self.params['width'] * ar), blockSignal=self.heightChanged)
def heightChanged(self):
sr = self.getSourceRect()
ar = float(sr.width()) / sr.height()
self.params.param(translate("Exporter", 'width')).setValue(int(self.params[translate("Exporter", 'height')] * ar), blockSignal=self.widthChanged)
self.params.param('width').setValue(int(self.params['height'] * ar), blockSignal=self.widthChanged)
def parameters(self):
return self.params
@ -62,8 +64,8 @@ class ImageExporter(Exporter):
self.fileSaveDialog(filter=filter)
return
w = int(self.params[translate("Exporter", 'width')])
h = int(self.params[translate("Exporter", 'height')])
w = int(self.params['width'])
h = int(self.params['height'])
if w == 0 or h == 0:
raise Exception("Cannot export image with size=0 (requested "
"export size is %dx%d)" % (w, h))
@ -72,7 +74,7 @@ class ImageExporter(Exporter):
sourceRect = self.getSourceRect()
bg = np.empty((h, w, 4), dtype=np.ubyte)
color = self.params[translate("Exporter", 'background')]
color = self.params['background']
bg[:,:,0] = color.blue()
bg[:,:,1] = color.green()
bg[:,:,2] = color.red()
@ -91,11 +93,11 @@ class ImageExporter(Exporter):
#dtr = painter.deviceTransform()
try:
self.setExportMode(True, {
'antialias': self.params[translate("Exporter", 'antialias')],
'background': self.params[translate("Exporter", 'background')],
'antialias': self.params['antialias'],
'background': self.params['background'],
'painter': painter,
'resolutionScale': resolutionScale})
painter.setRenderHint(QtGui.QPainter.Antialiasing, self.params[translate("Exporter", 'antialias')])
painter.setRenderHint(QtGui.QPainter.Antialiasing, self.params['antialias'])
self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
finally:
self.setExportMode(False)
@ -114,5 +116,4 @@ class ImageExporter(Exporter):
else:
return self.png.save(fileName)
ImageExporter.register()
ImageExporter.register()

View File

@ -4,8 +4,6 @@ from .Exporter import Exporter
from .. import PlotItem
from .. import functions as fn
translate = QtCore.QCoreApplication.translate
__all__ = ['MatplotlibExporter']
"""
@ -32,7 +30,7 @@ publication. Fonts are not vectorized (outlined), and window colors are white.
"""
class MatplotlibExporter(Exporter):
Name = translate('Exporter', "Matplotlib Window")
Name = "Matplotlib Window"
windows = []
def __init__(self, item):

View File

@ -14,8 +14,10 @@ class PrintExporter(Exporter):
Exporter.__init__(self, item)
tr = self.getTargetRect()
self.params = Parameter(name='params', type='group', children=[
{'name': translate("Exporter", 'width'), 'type': 'float', 'value': 0.1, 'limits': (0, None), 'suffix': 'm', 'siPrefix': True},
{'name': translate("Exporter", 'height'), 'type': 'float', 'value': (0.1 * tr.height()) / tr.width(), 'limits': (0, None), 'suffix': 'm', 'siPrefix': True},
{'name': 'width', 'title': translate("Exporter", 'width'), 'type': 'float', 'value': 0.1,
'limits': (0, None), 'suffix': 'm', 'siPrefix': True},
{'name': 'height', 'title': translate("Exporter", 'height'), 'type': 'float',
'value': (0.1 * tr.height()) / tr.width(), 'limits': (0, None), 'suffix': 'm', 'siPrefix': True},
])
self.params.param('width').sigValueChanged.connect(self.widthChanged)
self.params.param('height').sigValueChanged.connect(self.heightChanged)
@ -23,13 +25,13 @@ class PrintExporter(Exporter):
def widthChanged(self):
sr = self.getSourceRect()
ar = sr.height() / sr.width()
self.params.param(translate("Exporter", 'height')).setValue(self.params[translate("Exporter", 'width')] * ar, blockSignal=self.heightChanged)
self.params.param('height').setValue(self.params['width'] * ar, blockSignal=self.heightChanged)
def heightChanged(self):
sr = self.getSourceRect()
ar = sr.width() / sr.height()
self.params.param(translate("Exporter", 'width')).setValue(self.params[translate("Exporter", 'height')] * ar, blockSignal=self.widthChanged)
self.params.param('width').setValue(self.params['height'] * ar, blockSignal=self.widthChanged)
def parameters(self):
return self.params

View File

@ -13,7 +13,7 @@ translate = QtCore.QCoreApplication.translate
__all__ = ['SVGExporter']
class SVGExporter(Exporter):
Name = translate("Exporter", "Scalable Vector Graphics (SVG)")
Name = "Scalable Vector Graphics (SVG)"
allowCopy=True
def __init__(self, item):
@ -30,26 +30,28 @@ class SVGExporter(Exporter):
bg.setAlpha(0)
self.params = Parameter(name='params', type='group', children=[
{'name': translate("Exporter", 'background'), 'type': 'color', 'value': bg},
{'name': translate("Exporter", 'width'), 'type': 'float', 'value': tr.width(), 'limits': (0, None)},
{'name': translate("Exporter", 'height'), 'type': 'float', 'value': tr.height(), 'limits': (0, None)},
{'name': 'background', 'title': translate("Exporter", 'background'), 'type': 'color', 'value': bg},
{'name': 'width', 'title': translate("Exporter", 'width'), 'type': 'float', 'value': tr.width(),
'limits': (0, None)},
{'name': 'height', 'title': translate("Exporter", 'height'), 'type': 'float', 'value': tr.height(),
'limits': (0, None)},
#{'name': 'viewbox clipping', 'type': 'bool', 'value': True},
#{'name': 'normalize coordinates', 'type': 'bool', 'value': True},
{'name': translate("Exporter", 'scaling stroke'), 'type': 'bool', 'value': False, 'tip': "If False, strokes are non-scaling, "
{'name': 'scaling stroke', 'title': translate("Exporter", 'scaling stroke'), 'type': 'bool', 'value': False, 'tip': "If False, strokes are non-scaling, "
"which means that they appear the same width on screen regardless of how they are scaled or how the view is zoomed."},
])
self.params.param(translate("Exporter", 'width')).sigValueChanged.connect(self.widthChanged)
self.params.param(translate("Exporter", 'height')).sigValueChanged.connect(self.heightChanged)
self.params.param('width').sigValueChanged.connect(self.widthChanged)
self.params.param('height').sigValueChanged.connect(self.heightChanged)
def widthChanged(self):
sr = self.getSourceRect()
ar = sr.height() / sr.width()
self.params.param(translate("Exporter", 'height')).setValue(self.params[translate("Exporter", 'width')] * ar, blockSignal=self.heightChanged)
self.params.param('height').setValue(self.params['width'] * ar, blockSignal=self.heightChanged)
def heightChanged(self):
sr = self.getSourceRect()
ar = sr.width() / sr.height()
self.params.param(translate("Exporter", 'width')).setValue(self.params[translate("Exporter", 'height')] * ar, blockSignal=self.widthChanged)
self.params.param('width').setValue(self.params['height'] * ar, blockSignal=self.widthChanged)
def parameters(self):
return self.params
@ -63,9 +65,9 @@ class SVGExporter(Exporter):
## Instead, we will use Qt to generate SVG for each item independently,
## then manually reconstruct the entire document.
options = {ch.name():ch.value() for ch in self.params.children()}
options['background'] = self.params[translate("Exporter", 'background')]
options['width'] = self.params[translate("Exporter", 'width')]
options['height'] = self.params[translate("Exporter", 'height')]
options['background'] = self.params['background']
options['width'] = self.params['width']
options['height'] = self.params['height']
xml = generateSvg(self.item, options)
if toBytes:
@ -127,9 +129,9 @@ def _generateItemSvg(item, nodes=None, root=None, options={}):
## 1) Qt SVG does not implement clipping paths. This is absurd.
## The solution is to let Qt generate SVG for each item independently,
## then glue them together manually with clipping.
##
##
## The format Qt generates for all items looks like this:
##
##
## <g>
## <g transform="matrix(...)">
## one or more of: <path/> or <polyline/> or <text/>
@ -139,21 +141,21 @@ def _generateItemSvg(item, nodes=None, root=None, options={}):
## </g>
## . . .
## </g>
##
##
## 2) There seems to be wide disagreement over whether path strokes
## should be scaled anisotropically.
## should be scaled anisotropically.
## see: http://web.mit.edu/jonas/www/anisotropy/
## Given that both inkscape and illustrator seem to prefer isotropic
## scaling, we will optimize for those cases.
##
## 3) Qt generates paths using non-scaling-stroke from SVG 1.2, but
## inkscape only supports 1.1.
##
## scaling, we will optimize for those cases.
##
## 3) Qt generates paths using non-scaling-stroke from SVG 1.2, but
## inkscape only supports 1.1.
##
## Both 2 and 3 can be addressed by drawing all items in world coordinates.
profiler = debug.Profiler()
if nodes is None: ## nodes maps all node IDs to their XML element.
if nodes is None: ## nodes maps all node IDs to their XML element.
## this allows us to ensure all elements receive unique names.
nodes = {}
@ -424,7 +426,7 @@ def itemTransform(item, root):
tr.translate(pos.x(), pos.y())
tr = item.transform() * tr
else:
## find next parent that is either the root item or
## find next parent that is either the root item or
## an item that ignores its transformation
nextRoot = item
while True: