Я написал простую программу для мониторинга моего пинга. В настоящее время я использую NumberAxis
с автоматическим ранжированием, и после каждого пинга я добавляю новые данные в конец, удаляю первый и увеличиваю totalCount
переменную для оси X.Форматирование меток оси JavaFX
Я хотел ось X метки либо:
- показывает время, прошедшее с момента старта. Так, например, ярлык для 1100-го пинга с интервалами ping 0,25 с будет
4m 35s
- показать время со времени пинга. Это потребует, чтобы метки оставались статическими (не двигались с графиком) и находились в обратном порядке.
Возможно ли реализовать одно из этих двух (предпочтительно 1-го) варианта? Я полагаю, что мне нужно было бы использовать CategoryAxis
, но я не уверен, как создать неограниченное количество категорий и выбрать только полные минуты. Можно ли сохранить NumberAxis
для удобства использования с входящими данными и просто изменить форматирование текста на этикетке? У меня уже есть метод, который преобразует секунды в формат 00h 00m 00s
.
Также, я думаю, что это связано с автоматическим ранжированием, график не обновляется после каждого входа, а только один раз превышает 10% заданного диапазона. Так что для 1000 диапазонов, как на картинке, он будет нарисовать 100 новых пинов, а затем переместить все 100 позиций влево. Могу ли я изменить его как-то, чтобы двигаться после всего лишь одного пинга?
Не уверен, если это необходимо, но я выложу код:
контроллер
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
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
Я попытался с помощью форматера, но я получаю 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);
}
}
}
В методе start()
((ValueAxis<Integer>) chart.getXAxis()).setTickLabelFormatter(new XAxisLabelConverter(
Double.parseDouble(sField.getText()),size));
сходиться к MCVE можно также разместить FXML. –
@UlukBiy добавил FXML. – Asalas77
Вы пытаетесь изменить узкое определение NumberAxis. Объявление - это публичный конечный класс NumberAxis, который расширяет ValueAxis '. Я не эксперт по типам, но я думаю, что лучше просто использовать Number и получить intValue(). –
brian