java JavaFX 图表轴标签格式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32740005/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 20:41:55  来源:igfitidea点击:

JavaFX Chart axis label formatting

javachartsjavafxlabellinechart

提问by Asalas77

I wrote a simple program to monitor my ping. I'm currently using NumberAxiswith auto ranging, and after each ping, I add the new data at the end, remove the first one and increment totalCountvariable for X axis position.

我写了一个简单的程序来监控我的 ping。我目前正在使用NumberAxis自动范围,在每次 ping 之后,我在最后添加新数据,删除第一个并增加totalCountX 轴位置的变量。

I would like the X axis label to either:

我希望 X 轴标签为:

  • show time elapsed since start. So for example, the label for 1100th ping at 0.25s ping intervals would be 4m 35s
  • show time since the ping. This would require the labels to remain static (not move with the plot) and be in reversed order.
  • 显示自开始以来经过的时间。因此,例如,以 0.25 秒 ping 间隔进行的第 1100 次 ping 的标签将是4m 35s
  • 自ping显示时间。这将要求标签保持静态(不随图移动)并以相反的顺序排列。

Is either of these two (preferably 1st) possible to implement? I suppose I would have to use CategoryAxisfor this, but I'm not sure how to create unlimited number of categories and choose to show only full minutes. Is it possible to keep the NumberAxisfor easier use with incoming data and just change the label text formatting? I already have a method that converts seconds to 00h 00m 00sformat.

这两个(最好是第一个)中的任何一个都可以实施吗?我想我将不得不为此使用CategoryAxis,但我不确定如何创建无限数量的类别并选择仅显示完整分钟。是否可以保留NumberAxis传入数据以便于使用并更改标签文本格式?我已经有了一种将秒转换为00h 00m 00s格式的方法。

Also another thing, I think related to auto ranging, the chart doesn't refresh after every input, but only once it exceeds 10% of given range. So for 1000 range as in the picture, it will draw 100 new pings, and then move everything 100 positions to the left. Can I change it somehow to move after just 1 ping?

还有一件事,我认为与自动范围有关,图表不会在每次输入后刷新,但只会在超过给定范围的 10% 时刷新。因此,对于如图所示的 1000 范围,它将绘制 100 个新 ping,然后将所有内容向左移动 100 个位置。我可以改变它以某种方式在 1 ping 后移动吗?

ping

平

Not sure if relevant, but I'll post the code:

不确定是否相关,但我会发布代码:

Controller

控制器

public class GuiController implements Initializable {

@FXML
Button startButton, stopButton;
@FXML
TextField sField, nField, ipField;
@FXML
LineChart<Integer, Integer> chart;
@FXML
Label timeLabel, pingLabel;

ScheduledService<Integer> scheduler;
ObservableList<Data<Integer, Integer>> data;
public static int totalCount = 0;

private String getTime(double seconds) {
    int h = (int) (seconds / 3600);
    int m = (int) ((seconds % 3600) / 60);
    int s = (int) (seconds % 60);
    return String.format("%dh %dm %ds", h, m, s);
}

public void start() {
    if (sField.getText().isEmpty() || Double.parseDouble(sField.getText()) == 0)
        sField.setText("0.1");
    data = FXCollections.observableArrayList();
    int size = Integer.parseInt(nField.getText());
    stop = false;
    flip();
    XYChart.Series<Integer, Integer> series = new Series<>();
    for (int i = 0; i < size; i++) {
        series.getData().add(new XYChart.Data<Integer, Integer>(totalCount++, 0));
    }
    chart.getData().clear();
    chart.getData().add(series);
    scheduler.setPeriod(Duration.seconds(Double.parseDouble(sField.getText())));
    scheduler.setOnSucceeded(new EventHandler<WorkerStateEvent>() {

        @Override
        public void handle(WorkerStateEvent event) {
            if (series.getData().size() >= size)
                series.getData().remove(0);
            series.getData().add(new XYChart.Data<>(totalCount++, scheduler.getValue()));
            updatePingLabel(scheduler.getValue());
        }
    });
    scheduler.restart();
}

public void stop() {
    scheduler.cancel();
    stop = true;
    flip();
    totalCount = 0;
}

public static boolean isNumeric(String str) {
    return str.matches("?\d+(\.\d+)?");
}

public void flip() {
    ipField.setDisable(!ipField.isDisabled());
    nField.setDisable(!nField.isDisabled());
    sField.setDisable(!sField.isDisabled());
    startButton.setDisable(!startButton.isDisabled());
    stopButton.setDisable(!stopButton.isDisabled());
}

public void updatePingLabel(int ping) {
    pingLabel.setText(ping + "ms");
    if (ping < 80)
        pingLabel.setTextFill(Color.LAWNGREEN);
    if (ping >= 80 && ping < 150)
        pingLabel.setTextFill(Color.GOLD);
    if (ping >=150 && ping < 400)
        pingLabel.setTextFill(Color.ORANGE);
    if (ping >= 400)
        pingLabel.setTextFill(Color.RED);
}


@Override
public void initialize(URL arg0, ResourceBundle arg1) {
    chart.getXAxis().setVisible(false);
    chart.getXAxis().setAutoRanging(true);
    stopButton.setDisable(true);
    chart.getYAxis().setAutoRanging(true);
    sField.textProperty().addListener(new ParamsChangeListener());
    nField.textProperty().addListener(new ParamsChangeListener());
    scheduler = new ScheduledService<Integer>() {
        @Override
        protected Task<Integer> createTask() {
            return new PingTask(ipField.getText());
        }
    };
}

class ParamsChangeListener implements ChangeListener<String> {
    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue,
            String newValue) {
        if (isNumeric(newValue))
            timeLabel.setText(getTime(Double.parseDouble(sField.getText())
                    * Integer.parseInt(nField.getText())));
    }
}
}

Ping Task

Ping 任务

public class PingTask extends Task<Integer> {

int time;

String address;

public PingTask(String text) {
    address = text;
}

@Override
protected Integer call() throws Exception {
    try {
        String cmd = "";
        if (System.getProperty("os.name").startsWith("Windows")) {
            cmd = "ping -n 1 " + address;
        } else {
            cmd = "ping -c 1 " + address;
        }
        Process process = Runtime.getRuntime().exec(cmd);
        process.waitFor();
        BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String inputLine = in.readLine();
        while ((inputLine != null)) {
            if (inputLine.startsWith("Reply from")) {
                String[] parts = inputLine.split("[ =ms]");
                time = Integer.parseInt(parts[9]);
                break;
            }
            inputLine = in.readLine();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return time;
}
}

FXML

文件格式

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.GuiController">
<center>
  <LineChart fx:id="chart" alternativeRowFillVisible="false" animated="false" createSymbols="false" horizontalZeroLineVisible="false" legendVisible="false" maxHeight="1.7976931348623157E308" verticalGridLinesVisible="false" verticalZeroLineVisible="false" BorderPane.alignment="CENTER">
    <xAxis>
      <NumberAxis animated="false" forceZeroInRange="false" minorTickCount="0" minorTickLength="0.0" minorTickVisible="false" side="BOTTOM" tickMarkVisible="false" tickUnit="1.0" upperBound="200.0" />
    </xAxis>
    <yAxis>
      <NumberAxis animated="false" autoRanging="false" forceZeroInRange="true" minorTickCount="0" minorTickLength="0.0" minorTickVisible="false" side="LEFT" tickLabelGap="5.0" tickUnit="20.0" />
    </yAxis>
  </LineChart>
</center>
<left>
  <VBox alignment="TOP_CENTER" spacing="10.0" BorderPane.alignment="CENTER">
     <children>
        <HBox alignment="CENTER" VBox.vgrow="NEVER">
           <children>
              <Label text="IP " />
              <TextField fx:id="ipField" prefWidth="100.0" text="euw.leagueoflegends.com">
                 <opaqueInsets>
                    <Insets />
                 </opaqueInsets>
              </TextField>
           </children>
           <padding>
              <Insets top="5.0" />
           </padding>
        </HBox>
        <HBox alignment="CENTER" VBox.vgrow="NEVER">
           <children>
              <Label text="Ping co " />
              <TextField fx:id="sField" alignment="TOP_RIGHT" prefWidth="60.0" text="0.25" HBox.hgrow="NEVER">
                 <HBox.margin>
                    <Insets right="5.0" />
                 </HBox.margin>
              </TextField>
              <Label text="s" />
           </children>
           <padding>
              <Insets top="5.0" />
           </padding>
        </HBox>
        <HBox alignment="CENTER">
           <children>
              <Label text="Rysuj " />
              <TextField fx:id="nField" alignment="CENTER_RIGHT" prefWidth="60.0" text="1000" HBox.hgrow="NEVER" />
              <Label text=" próbek" />
           </children>
           <padding>
              <Insets top="5.0" />
           </padding>
        </HBox>
        <HBox alignment="CENTER" VBox.vgrow="NEVER">
           <children>
              <Label text="Poka?  " />
              <Label fx:id="timeLabel" text="0h 4m 10s" />
           </children>
        </HBox>
        <HBox alignment="CENTER" spacing="10.0" VBox.vgrow="NEVER">
           <children>
              <Button fx:id="startButton" mnemonicParsing="false" onAction="#start" prefWidth="50.0" text="Start" />
              <Button fx:id="stopButton" mnemonicParsing="false" onAction="#stop" prefWidth="50.0" text="Stop" />
           </children>
        </HBox>
        <Label fx:id="pingLabel" text="0ms" textAlignment="CENTER">
           <font>
              <Font name="System Bold" size="40.0" />
           </font>
        </Label>
     </children>
     <padding>
        <Insets left="5.0" right="5.0" />
     </padding>
  </VBox>
 </left>
 </BorderPane>


EDIT

编辑

I tried using the formatter, but I'm getting ClassCastException: java.lang.Double cannot be cast to java.lang.Integerat application.GuiController$XAxisLabelConverter.toString(GuiController.java:1) and I don't know what to do with it.

我尝试使用格式化程序,但我得到ClassCastException: java.lang.Double cannot be cast to java.lang.Integer了 application.GuiController$XAxisLabelConverter.toString(GuiController.java:1) 并且我不知道如何处理它。

    class XAxisLabelConverter extends StringConverter<Integer> {

    double interval;
    int n;

    public XAxisLabelConverter(double interval, int n) {
        this.interval = interval;
        this.n = n;
    }

    @Override
    public Integer fromString(String arg0) {

        return null;
    }

    @Override
    public String toString(Integer value) {
        if (value < n) {
            return "";
        } else {
            return getTime(value.intValue() * interval);
        }
    }

}

In the start()method

start()方法中

    ((ValueAxis<Integer>) chart.getXAxis()).setTickLabelFormatter(new XAxisLabelConverter(
            Double.parseDouble(sField.getText()),size));

回答by brian

You can add a formatter to a number axis.

您可以向数字轴添加格式化程序

You may just want to remove the x-axis. It doesn't really add any information. If you're just showing the last 100 pings which were .25 secs apart, then you don't really need an axis to know when they happened.

您可能只想删除 x 轴。它并没有真正添加任何信息。如果您只是显示相隔 0.25 秒的最后 100 次 ping,那么您真的不需要轴来知道它们何时发生。

The chart is only moving after 100 pings due to the range of the axis. The interval will depend on the total range. The only way to change this is to shut off auto-ranging and set max, min, size yourself. You could use this constructor.

由于轴的范围,图表仅在 100 次 ping 后移动。间隔将取决于总范围。改变这一点的唯一方法是关闭自动量程并自己设置最大、最小、大小。你可以使用这个构造函数

If you want to format you'll need a specialized converter since you need to modify the number. The Chart<..,Number> needs to convert a Number to a String so use a StringConverter<Number>,eg.

如果你想格式化你需要一个专门的转换器,因为你需要修改数字。Chart<..,Number> 需要将数字转换为字符串,因此请使用StringConverter<Number>,eg。

    xAxis.setTickLabelFormatter(new StringConverter<Number>() {
        @Override
        public String toString(Number object) {
            return (object.intValue() * 0.25) + "s";
        }

        @Override
        public Number fromString(String string) {
            return 0;
        }
    });

ps. I've only used this on windows but you can see if it performs better than a new process.

附:我只在 Windows 上使用过它,但您可以查看它是否比新进程性能更好。

http://docs.oracle.com/javase/7/docs/api/java/net/InetAddress.html#isReachable(int)

http://docs.oracle.com/javase/7/docs/api/java/net/InetAddress.html#isReachable(int)