mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-26 14:31:02 -04:00
test result dialog data is able to show the data as a graph.
This commit is contained in:
parent
2d25909f9e
commit
4cb80b3918
@ -5,6 +5,8 @@ import de.neemann.digital.draw.graphics.Orientation;
|
||||
import de.neemann.digital.draw.graphics.Style;
|
||||
import de.neemann.digital.draw.graphics.Vector;
|
||||
import de.neemann.digital.draw.shapes.Drawable;
|
||||
import de.neemann.digital.gui.sync.NoSync;
|
||||
import de.neemann.digital.gui.sync.Sync;
|
||||
|
||||
/**
|
||||
* The dataSet stores the collected DataSamples.
|
||||
@ -16,6 +18,7 @@ public class DataPlotter implements Drawable {
|
||||
private final ValueTable data;
|
||||
private final int maxTextLength;
|
||||
private double size = SIZE;
|
||||
private Sync modelSync = NoSync.INST;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
@ -44,7 +47,7 @@ public class DataPlotter implements Drawable {
|
||||
* @param width width of the frame
|
||||
*/
|
||||
public void fitInside(int width) {
|
||||
size = ((double) (width - getTextBorder())) / data.getRows();
|
||||
modelSync.access(() -> size = ((double) (width - getTextBorder())) / data.getRows());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,43 +66,45 @@ public class DataPlotter implements Drawable {
|
||||
|
||||
@Override
|
||||
public void drawTo(Graphic g, Style highLight) {
|
||||
int x = getTextBorder();
|
||||
modelSync.access(() -> {
|
||||
int x = getTextBorder();
|
||||
|
||||
int yOffs = SIZE / 2;
|
||||
int y = BORDER;
|
||||
int signals = data.getColumns();
|
||||
for (int i = 0; i < signals; i++) {
|
||||
String text = data.getColumnName(i);
|
||||
g.drawText(new Vector(x - 2, y + yOffs), new Vector(x + 1, y + yOffs), text, Orientation.RIGHTCENTER, Style.NORMAL);
|
||||
g.drawLine(new Vector(x, y - SEP2), new Vector(x + (int) (size * data.getRows()), y - SEP2), Style.DASH);
|
||||
y += SIZE + SEP;
|
||||
}
|
||||
g.drawLine(new Vector(x, y - SEP2), new Vector(x + (int) (size * data.getRows()), y - SEP2), Style.DASH);
|
||||
|
||||
|
||||
int[] lastRy = new int[signals];
|
||||
boolean first = true;
|
||||
double pos = 0;
|
||||
for (Value[] s : data) {
|
||||
int xx = (int) (pos + x);
|
||||
g.drawLine(new Vector(xx, BORDER - SEP2), new Vector(xx, (SIZE + SEP) * signals + BORDER - SEP2), Style.DASH);
|
||||
y = BORDER;
|
||||
int yOffs = SIZE / 2;
|
||||
int y = BORDER;
|
||||
int signals = data.getColumns();
|
||||
for (int i = 0; i < signals; i++) {
|
||||
|
||||
long width = data.getMax(i);
|
||||
if (width == 0) width = 1;
|
||||
int ry = (int) (SIZE - (SIZE * s[i].getValue()) / width);
|
||||
g.drawLine(new Vector(xx, y + ry), new Vector((int) (xx + size), y + ry), Style.NORMAL);
|
||||
if (!first && ry != lastRy[i])
|
||||
g.drawLine(new Vector(xx, y + lastRy[i]), new Vector(xx, y + ry), Style.NORMAL);
|
||||
|
||||
lastRy[i] = ry;
|
||||
String text = data.getColumnName(i);
|
||||
g.drawText(new Vector(x - 2, y + yOffs), new Vector(x + 1, y + yOffs), text, Orientation.RIGHTCENTER, Style.NORMAL);
|
||||
g.drawLine(new Vector(x, y - SEP2), new Vector(x + (int) (size * data.getRows()), y - SEP2), Style.DASH);
|
||||
y += SIZE + SEP;
|
||||
}
|
||||
first = false;
|
||||
pos += size;
|
||||
}
|
||||
g.drawLine(new Vector(x, BORDER - SEP2), new Vector(x, (SIZE + SEP) * signals + BORDER - SEP2), Style.DASH);
|
||||
g.drawLine(new Vector(x, y - SEP2), new Vector(x + (int) (size * data.getRows()), y - SEP2), Style.DASH);
|
||||
|
||||
|
||||
int[] lastRy = new int[signals];
|
||||
boolean first = true;
|
||||
double pos = 0;
|
||||
for (Value[] s : data) {
|
||||
int xx = (int) (pos + x);
|
||||
g.drawLine(new Vector(xx, BORDER - SEP2), new Vector(xx, (SIZE + SEP) * signals + BORDER - SEP2), Style.DASH);
|
||||
y = BORDER;
|
||||
for (int i = 0; i < signals; i++) {
|
||||
|
||||
long width = data.getMax(i);
|
||||
if (width == 0) width = 1;
|
||||
int ry = (int) (SIZE - (SIZE * s[i].getValue()) / width);
|
||||
g.drawLine(new Vector(xx, y + ry), new Vector((int) (xx + size), y + ry), Style.NORMAL);
|
||||
if (!first && ry != lastRy[i])
|
||||
g.drawLine(new Vector(xx, y + lastRy[i]), new Vector(xx, y + ry), Style.NORMAL);
|
||||
|
||||
lastRy[i] = ry;
|
||||
y += SIZE + SEP;
|
||||
}
|
||||
first = false;
|
||||
pos += size;
|
||||
}
|
||||
g.drawLine(new Vector(x, BORDER - SEP2), new Vector(x, (SIZE + SEP) * signals + BORDER - SEP2), Style.DASH);
|
||||
});
|
||||
}
|
||||
|
||||
private int getTextBorder() {
|
||||
@ -117,7 +122,24 @@ public class DataPlotter implements Drawable {
|
||||
* @return the current width of the graphical representation
|
||||
*/
|
||||
public int getCurrentGraphicWidth() {
|
||||
return getTextBorder() + (int) (data.getRows() * size);
|
||||
return modelSync.access(new Runnable() {
|
||||
private int r;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
r = DataPlotter.this.getTextBorder() + (int) (data.getRows() * size);
|
||||
}
|
||||
}).r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets lock to access the data
|
||||
*
|
||||
* @param modelSync the lock
|
||||
* @return this for chained calls
|
||||
*/
|
||||
public DataPlotter setModelSync(Sync modelSync) {
|
||||
this.modelSync = modelSync;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1003,9 +1003,9 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
|
||||
windowPosManager.register("probe", new ProbeDialog(this, model, updateEvent, ordering, modelSync)).setVisible(true);
|
||||
|
||||
if (settings.get(Keys.SHOW_DATA_GRAPH))
|
||||
windowPosManager.register("dataSet", new DataSetDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering, modelSync)).setVisible(true);
|
||||
windowPosManager.register("dataSet", DataSetDialog.createLiveDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering, modelSync)).setVisible(true);
|
||||
if (settings.get(Keys.SHOW_DATA_GRAPH_MICRO))
|
||||
windowPosManager.register("dataSetMicro", new DataSetDialog(this, model, true, ordering, modelSync)).setVisible(true);
|
||||
windowPosManager.register("dataSetMicro", DataSetDialog.createLiveDialog(this, model, true, ordering, modelSync)).setVisible(true);
|
||||
|
||||
if (modelModifier != null)
|
||||
modelModifier.preInit(model);
|
||||
|
@ -3,6 +3,7 @@ package de.neemann.digital.gui.components.data;
|
||||
import de.neemann.digital.data.DataPlotter;
|
||||
import de.neemann.digital.data.ValueTable;
|
||||
import de.neemann.digital.draw.graphics.GraphicSwing;
|
||||
import de.neemann.digital.gui.sync.Sync;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@ -15,15 +16,20 @@ import java.awt.*;
|
||||
*/
|
||||
public class DataSetComponent extends JComponent {
|
||||
private final DataPlotter plotter;
|
||||
/**
|
||||
* The data stored in the plotter needs to be seen as part of the model.
|
||||
* So a lock is necessary to access the data.
|
||||
*/
|
||||
private JScrollPane scrollPane;
|
||||
|
||||
/**
|
||||
* Creates a new dataSet
|
||||
*
|
||||
* @param dataSet the dataSet to paint
|
||||
* @param dataSet the dataSet to paint
|
||||
* @param modelSync lock to access the model
|
||||
*/
|
||||
public DataSetComponent(ValueTable dataSet) {
|
||||
plotter = new DataPlotter(dataSet);
|
||||
public DataSetComponent(ValueTable dataSet, Sync modelSync) {
|
||||
plotter = new DataPlotter(dataSet).setModelSync(modelSync);
|
||||
addMouseWheelListener(e -> {
|
||||
double f = Math.pow(0.9, e.getWheelRotation());
|
||||
scale(f, e.getX());
|
||||
@ -55,8 +61,8 @@ public class DataSetComponent extends JComponent {
|
||||
public void scale(double f, int xPos) {
|
||||
revalidate();
|
||||
repaint();
|
||||
f=plotter.scale(f);
|
||||
|
||||
f = plotter.scale(f);
|
||||
// keep relative mouse position
|
||||
int x = (int) (xPos * f) - (xPos - (int) scrollPane.getViewport().getViewRect().getX());
|
||||
if (x < 0) x = 0;
|
||||
scrollPane.getViewport().setViewPosition(new Point(x, 0));
|
||||
|
@ -7,6 +7,7 @@ import de.neemann.digital.core.Signal;
|
||||
import de.neemann.digital.data.ValueTable;
|
||||
import de.neemann.digital.gui.SaveAsHelper;
|
||||
import de.neemann.digital.gui.components.OrderMerger;
|
||||
import de.neemann.digital.gui.sync.NoSync;
|
||||
import de.neemann.digital.gui.sync.Sync;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
import de.neemann.gui.IconCreator;
|
||||
@ -32,27 +33,29 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
|
||||
private final DataSetComponent dsc;
|
||||
private final JScrollPane scrollPane;
|
||||
private final Sync modelSync;
|
||||
private ValueTable logData;
|
||||
private DataSetObserver dataSetObserver;
|
||||
|
||||
private static final Icon ICON_EXPAND = IconCreator.create("View-zoom-fit.png");
|
||||
private static final Icon ICON_ZOOM_IN = IconCreator.create("View-zoom-in.png");
|
||||
private static final Icon ICON_ZOOM_OUT = IconCreator.create("View-zoom-out.png");
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* Creates a instance prepared for "live logging"
|
||||
*
|
||||
* @param owner the parent frame
|
||||
* @param model the model used to collect the data
|
||||
* @param microStep true the event type which triggers a new DataSample
|
||||
* @param ordering the ordering of the measurement values
|
||||
* @param modelSync used to access the running model
|
||||
* @param model the model
|
||||
* @param microStep stepping mode
|
||||
* @param ordering the ordering to use
|
||||
* @param modelSync the lock to access the model
|
||||
* @return the created instance
|
||||
*/
|
||||
public DataSetDialog(Frame owner, Model model, boolean microStep, List<String> ordering, Sync modelSync) {
|
||||
super(owner, createTitle(microStep), false);
|
||||
this.modelSync = modelSync;
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setAlwaysOnTop(true);
|
||||
public static DataSetDialog createLiveDialog(Frame owner, Model model, boolean microStep, List<String> ordering, Sync modelSync) {
|
||||
String title;
|
||||
if (microStep)
|
||||
title = Lang.get("win_measures_microstep");
|
||||
else
|
||||
title = Lang.get("win_measures_fullstep");
|
||||
|
||||
ArrayList<Signal> signals = model.getSignalsCopy();
|
||||
new OrderMerger<String, Signal>(ordering) {
|
||||
@ -62,11 +65,40 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
|
||||
}
|
||||
}.order(signals);
|
||||
|
||||
DataSetObserver dataSetObserver = new DataSetObserver(microStep, signals, MAX_SAMPLE_SIZE);
|
||||
ValueTable logData = dataSetObserver.getLogData();
|
||||
|
||||
dataSetObserver = new DataSetObserver(microStep, signals, MAX_SAMPLE_SIZE);
|
||||
logData = dataSetObserver.getLogData();
|
||||
return new DataSetDialog(owner, title, model, logData, dataSetObserver, modelSync);
|
||||
}
|
||||
|
||||
dsc = new DataSetComponent(logData);
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param owner the parent frame
|
||||
* @param title the frame title
|
||||
* @param logData the data to visualize
|
||||
*/
|
||||
public DataSetDialog(Frame owner, String title, ValueTable logData) {
|
||||
this(owner, title, null, logData, null, NoSync.INST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param owner the parent frame
|
||||
* @param title the frame title
|
||||
* @param model the model used to collect the data
|
||||
* @param logData the data to visualize
|
||||
* @param modelSync used to access the running model
|
||||
*/
|
||||
private DataSetDialog(Frame owner, String title, Model model, ValueTable logData, DataSetObserver dataSetObserver, Sync modelSync) {
|
||||
super(owner, title, false);
|
||||
this.dataSetObserver = dataSetObserver;
|
||||
this.modelSync = modelSync;
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setAlwaysOnTop(true);
|
||||
|
||||
dsc = new DataSetComponent(logData, modelSync);
|
||||
scrollPane = new JScrollPane(dsc);
|
||||
getContentPane().add(scrollPane);
|
||||
dsc.setScrollPane(scrollPane);
|
||||
@ -100,17 +132,18 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
|
||||
getContentPane().add(toolBar, BorderLayout.NORTH);
|
||||
pack();
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
modelSync.access(() -> model.addObserver(DataSetDialog.this));
|
||||
}
|
||||
if (model != null)
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
modelSync.access(() -> model.addObserver(DataSetDialog.this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
modelSync.access(() -> model.removeObserver(DataSetDialog.this));
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
modelSync.access(() -> model.removeObserver(DataSetDialog.this));
|
||||
}
|
||||
});
|
||||
|
||||
scrollPane.getViewport().setPreferredSize(dsc.getPreferredSize());
|
||||
|
||||
@ -123,7 +156,7 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
|
||||
JFileChooser fileChooser = new MyFileChooser();
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("Comma Separated Values", "csv"));
|
||||
new SaveAsHelper(DataSetDialog.this, fileChooser, "csv")
|
||||
.checkOverwrite(file -> logData.saveCSV(file));
|
||||
.checkOverwrite(logData::saveCSV);
|
||||
}
|
||||
}.setToolTip(Lang.get("menu_saveData_tt")).createJMenuItem());
|
||||
|
||||
@ -138,14 +171,6 @@ public class DataSetDialog extends JDialog implements ModelStateObserver {
|
||||
setLocationRelativeTo(owner);
|
||||
}
|
||||
|
||||
private static String createTitle(boolean microStep) {
|
||||
if (microStep)
|
||||
return Lang.get("win_measures_microstep");
|
||||
else
|
||||
return Lang.get("win_measures_fullstep");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleEvent(ModelEvent event) {
|
||||
modelSync.access(() -> {
|
||||
|
@ -2,6 +2,7 @@ package de.neemann.digital.gui.components.testing;
|
||||
|
||||
import de.neemann.digital.core.Model;
|
||||
import de.neemann.digital.core.NodeException;
|
||||
import de.neemann.digital.data.ValueTable;
|
||||
import de.neemann.digital.data.ValueTableModel;
|
||||
import de.neemann.digital.data.Value;
|
||||
import de.neemann.digital.draw.elements.Circuit;
|
||||
@ -9,15 +10,18 @@ import de.neemann.digital.draw.elements.PinException;
|
||||
import de.neemann.digital.draw.library.ElementLibrary;
|
||||
import de.neemann.digital.draw.library.ElementNotFoundException;
|
||||
import de.neemann.digital.draw.model.ModelCreator;
|
||||
import de.neemann.digital.gui.components.data.DataSetDialog;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
import de.neemann.digital.testing.*;
|
||||
import de.neemann.gui.ErrorMessage;
|
||||
import de.neemann.gui.IconCreator;
|
||||
import de.neemann.gui.LineBreaker;
|
||||
import de.neemann.gui.ToolTipAction;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
@ -32,6 +36,8 @@ public class TestResultDialog extends JDialog {
|
||||
private static final Icon ICON_FAILED = IconCreator.create("testFailed.png");
|
||||
private static final Icon ICON_PASSED = IconCreator.create("testPassed.png");
|
||||
|
||||
private final ArrayList<ValueTable> resultTableData;
|
||||
|
||||
/**
|
||||
* Creates a new result dialog.
|
||||
*
|
||||
@ -50,6 +56,8 @@ public class TestResultDialog extends JDialog {
|
||||
|
||||
Collections.sort(tsl);
|
||||
|
||||
resultTableData = new ArrayList<>();
|
||||
|
||||
JTabbedPane tp = new JTabbedPane();
|
||||
int i = 0;
|
||||
int errorTabIndex = -1;
|
||||
@ -65,8 +73,9 @@ public class TestResultDialog extends JDialog {
|
||||
table.setDefaultRenderer(Value.class, new ValueRenderer());
|
||||
table.setDefaultRenderer(Integer.class, new NumberRenderer());
|
||||
final Font font = table.getFont();
|
||||
table.getColumnModel().getColumn(0).setMaxWidth(font.getSize()*4);
|
||||
table.getColumnModel().getColumn(0).setMaxWidth(font.getSize() * 4);
|
||||
table.setRowHeight(font.getSize() * 6 / 5);
|
||||
resultTableData.add(testExecuter.getResult());
|
||||
|
||||
String tabName;
|
||||
Icon tabIcon;
|
||||
@ -89,6 +98,21 @@ public class TestResultDialog extends JDialog {
|
||||
if (errorTabIndex >= 0)
|
||||
tp.setSelectedIndex(errorTabIndex);
|
||||
|
||||
|
||||
JMenuBar bar = new JMenuBar();
|
||||
JMenu view = new JMenu(Lang.get("menu_view"));
|
||||
ToolTipAction asGraph = new ToolTipAction(Lang.get("menu_showDataAsGraph")) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
int tab = tp.getSelectedIndex();
|
||||
if (tab < 0) tab = 0;
|
||||
new DataSetDialog(owner, Lang.get("win_testdata_N", tp.getTitleAt(tab)), resultTableData.get(tab)).setVisible(true);
|
||||
}
|
||||
}.setToolTip(Lang.get("menu_showDataAsGraph_tt"));
|
||||
view.add(asGraph.createJMenuItem());
|
||||
bar.add(view);
|
||||
setJMenuBar(bar);
|
||||
|
||||
getContentPane().add(tp);
|
||||
pack();
|
||||
setLocationRelativeTo(owner);
|
||||
|
@ -20,20 +20,22 @@ public class LockSync implements Sync {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void access(Runnable run) {
|
||||
public <A extends Runnable> A access(A run) {
|
||||
lock.lock();
|
||||
try {
|
||||
run.run();
|
||||
return run;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accessNEx(Sync.ModelRun run) throws NodeException {
|
||||
public <A extends Sync.ModelRun> A accessNEx(A run) throws NodeException {
|
||||
lock.lock();
|
||||
try {
|
||||
run.run();
|
||||
return run;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
@ -18,12 +18,14 @@ public final class NoSync implements Sync {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void access(Runnable run) {
|
||||
public <A extends Runnable> A access(A run) {
|
||||
run.run();
|
||||
return run;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accessNEx(Sync.ModelRun run) throws NodeException {
|
||||
public <A extends Sync.ModelRun> A accessNEx(A run) throws NodeException {
|
||||
run.run();
|
||||
return run;
|
||||
}
|
||||
}
|
||||
|
@ -13,16 +13,20 @@ public interface Sync {
|
||||
* Calls the given runnable
|
||||
*
|
||||
* @param run the runnable to execute
|
||||
* @param <A> the type oth the runnable
|
||||
* @return the given runnable. Used for chained calls
|
||||
*/
|
||||
void access(Runnable run);
|
||||
<A extends Runnable> A access(A run);
|
||||
|
||||
/**
|
||||
* Same as access, but catches an exception
|
||||
*
|
||||
* @param run the runnable to execute
|
||||
* @param <A> the type oth the runnable
|
||||
* @return the given runnable. Used for chained calls
|
||||
* @throws NodeException NodeException
|
||||
*/
|
||||
void accessNEx(ModelRun run) throws NodeException;
|
||||
<A extends Sync.ModelRun> A accessNEx(A run) throws NodeException;
|
||||
|
||||
/**
|
||||
* Like runnable but throws an exception
|
||||
|
@ -863,7 +863,7 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
|
||||
<string name="menu_undo_tt">Letzte Aktion rückgängig machen</string>
|
||||
<string name="menu_redo">Wiederherstellen</string>
|
||||
<string name="menu_redo_tt">Letzte rückgängig gemachte Aktion wieder herstellen.</string>
|
||||
<string name="menu_showDataAsGraph">Als Graph anzeigen.</string>
|
||||
<string name="menu_showDataAsGraph">Zeige Graph</string>
|
||||
<string name="menu_showDataAsGraph_tt">Zeigt die Daten als Graph an.</string>
|
||||
|
||||
<string name="message">Digital
|
||||
@ -947,6 +947,7 @@ Die Icons stammen aus dem Tango Desktop Project.</string>
|
||||
<string name="tt_moveItemDown">Eintrag nach unten schieben</string>
|
||||
<string name="tt_moveItemUp">Eintrag nach oben schieben</string>
|
||||
<string name="win_allSolutions">Alle möglichen Lösungen</string>
|
||||
<string name="win_testdata_N">Testdaten {0}</string>
|
||||
<string name="win_confirmExit">Beenden bestätigen!</string>
|
||||
<string name="win_measures">Messwerte</string>
|
||||
<string name="win_measures_fullstep">Messwerte im Vollschrittmodus</string>
|
||||
|
@ -851,6 +851,8 @@ The names of the variables may not be unique.</string>
|
||||
<string name="menu_undo_tt">Revert last modification</string>
|
||||
<string name="menu_redo">Redo</string>
|
||||
<string name="menu_redo_tt">Apply last reverted modification again.</string>
|
||||
<string name="menu_showDataAsGraph">Show graph</string>
|
||||
<string name="menu_showDataAsGraph_tt">Show the data as a Graph.</string>
|
||||
|
||||
|
||||
<string name="message">Digital
|
||||
@ -941,6 +943,7 @@ The icons are taken from the Tango Desktop Project.</string>
|
||||
<string name="win_table">Table</string>
|
||||
<string name="win_table_exportDialog">Export</string>
|
||||
<string name="win_itempicker_title">Select</string>
|
||||
<string name="win_testdata_N">Testdata {0}</string>
|
||||
|
||||
<string name="btn_help">Help</string>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user