Update values in Pie Chart

2019-08-25 07:49发布

I'm working on this example with Pie chart. I want to create data dynamically and update Pie chart every time when new custom object is generated:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.PieChart.Data;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tooltip;
import javafx.scene.effect.Glow;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class MainApp extends Application
{

    Stage stage;

    private ObservableList<Data> pieChartdData = FXCollections.observableArrayList();
    private final PieChart chart = new PieChart(pieChartdData);
    Label caption;

    @Override
    public void start(Stage stage)
    {

        this.stage = stage;

        setUserAgentStylesheet(STYLESHEET_MODENA);
        Scene scene = new Scene(new Group());
        stage.setTitle("Imported Fruits");
        stage.setWidth(500);
        stage.setHeight(500);

        chart.setTitle("Imported Fruits");

        // Add some data
        addPieChartData("Grapefruit", 13);
        addPieChartData("Oranges", 25);
        addPieChartData("Plums", 10);
        addPieChartData("Pears", 22);
        addPieChartData("Apples", 30);

        // Some task which updates the Pie Chart
        final Task task;
        task = new Task<Void>()
        {
            @Override
            protected Void call() throws Exception
            {
                int max = 50;
                int l = 0;
                for (int i = 1; i <= max; i++)
                {

                    FSPartitions data1 = FSPartitions.newInstance().FSName("test1").FSType("test1").isReadonly(true).totalSize(3434).usedSize(333).availableSize(3644);
                    FSPartitions data2 = FSPartitions.newInstance().FSName("test2").FSType("test2").isReadonly(true).totalSize(34334).usedSize(343).availableSize(3544);
                    FSPartitions data3 = FSPartitions.newInstance().FSName("test3").FSType("test3").isReadonly(true).totalSize(34543).usedSize(353).availableSize(3446);

                    List<FSPartitions> ss = new ArrayList<>();
                    ss.add(data1);
                    ss.add(data2);
                    ss.add(data3);

                    updatePieChart(ss);

                    Thread.sleep(600);
                }
                return null;
            }
        };

        new Thread(task).start();

        ((Group) scene.getRoot()).getChildren().addAll(chart, caption);
        stage.setScene(scene);
        stage.show();
    }

    public void addPieChartData(String name, double value)
    {
        pieChartdData.add(new Data(name, value));

        caption = new Label();
        caption.setTextFill(Color.DARKORANGE);
        caption.setStyle("-fx-font: 24 arial;");

        for (final Data data : chart.getData())
        {

            Node node = data.getNode();

            node.addEventHandler(MouseEvent.MOUSE_MOVED, new EventHandler<MouseEvent>()
            {
                @Override
                public void handle(MouseEvent e)
                {
                    caption.setTranslateX(e.getSceneX() + 15);
                    caption.setTranslateY(e.getSceneY());
                    caption.setText(String.valueOf(data.getPieValue()) + "%");
                    caption.setVisible(true);
                    node.setEffect(new Glow());
                    //String styleString = "-fx-border-color: white; -fx-border-width: 1; -fx-border-style: dashed;";
                    //node.setStyle(styleString);
                }
            });

            node.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>()
            {
                @Override
                public void handle(MouseEvent e)
                {
                    caption.setVisible(false);
                    node.setEffect(null);
                    //node.setStyle("");
                }
            });

            final MenuItem resizeItem = new MenuItem("Resize");
            resizeItem.setOnAction(new EventHandler<ActionEvent>()
            {
                @Override
                public void handle(ActionEvent event)
                {
                    System.out.println("Resize requested");
                }
            });

            final MenuItem aboutItem = new MenuItem("About");
            aboutItem.setOnAction(new EventHandler<ActionEvent>()
            {
                @Override
                public void handle(ActionEvent event)
                {
                    System.out.println("About requested");
                }
            });

            final MenuItem changeColorItem = new MenuItem("Change Color");
            changeColorItem.setOnAction(new EventHandler<ActionEvent>()
            {
                @Override
                public void handle(ActionEvent event)
                {
                    System.out.println("change Color Item requested");

                }
            });

            final ContextMenu menu = new ContextMenu(resizeItem, aboutItem, changeColorItem);

            node.setOnMouseClicked(new EventHandler<MouseEvent>()
            {
                @Override
                public void handle(MouseEvent event)
                {
                    if (MouseButton.SECONDARY.equals(event.getButton()))
                    {
                        menu.show(stage, event.getScreenX(), event.getScreenY());
                    }
                }
            });

        }
    }

    // updates existing Data-Object if name matches
    public void updatePieChart(List<FSPartitions> obj)
    {
        for (FSPartitions obj1 : obj)
        {
            String fsName = obj1.getFSName();
            double usedSize = obj1.getUsedSize();

            for (Data d : pieChartdData)
            {
                if (d.getName().equals(fsName))
                {
                    d.setPieValue(usedSize);
                    return;
                }
            }
        }

        pieChartdData.forEach(data
            -> data.nameProperty().bind(
                Bindings.concat(
                    data.getName(), " ", data.pieValueProperty(), " Tons"
                )
            )
        );

        for (FSPartitions obj1 : obj)
        {
            String fsName = obj1.getFSName();
            double usedSize = obj1.getUsedSize();

            pieChartdData.add(new Data(fsName, usedSize));
        }

        chart.getData().stream().forEach(data ->
        {
            Tooltip aaas = new Tooltip();
            aaas.setText(data.getPieValue() + "%");
            Tooltip.install(chart, aaas);
            data.pieValueProperty().addListener((observable, oldValue, newValue)
                -> aaas.setText(newValue + "%"));
        });

        chart.setData(pieChartdData);
    }

    public static void main(String[] args)
    {
        launch(args);
    }

}

class FSPartitions implements Serializable
{
    private static final long serialVersionUID = 1L;
    private String FSName;
    private String FSType;
    private boolean isReadonly;
    private long totalSize;
    private long usedSize;
    private long availableSize;

    public static FSPartitions newInstance()
    {
        return new FSPartitions();
    }

    public FSPartitions()
    {
    }

    @Override
    public String toString()
    {
        return "FSName=" + FSName + ", FSType=" + FSType + ", totalSize=" + totalSize + ", usedSize=" + usedSize + ", availableSize=" + availableSize;
    }

    public FSPartitions FSName(String FSName)
    {
        this.FSName = FSName;
        return this;
    }

    public FSPartitions FSType(String FSType)
    {
        this.FSType = FSType;
        return this;
    }

    public FSPartitions isReadonly(boolean isReadonly)
    {
        this.isReadonly = isReadonly;
        return this;
    }

    public FSPartitions totalSize(long totalSize)
    {
        this.totalSize = totalSize;
        return this;
    }

    public FSPartitions usedSize(long usedSize)
    {
        this.usedSize = usedSize;
        return this;
    }

    public FSPartitions availableSize(long availableSize)
    {
        this.availableSize = availableSize;
        return this;
    }

    public String getFSName()
    {
        return FSName;
    }

    public String getFSType()
    {
        return FSType;
    }

    public boolean getIsReadonly()
    {
        return isReadonly;
    }

    public long getTotalSize()
    {
        return totalSize;
    }

    public long getUsedSize()
    {
        return usedSize;
    }

    public long getAvailableSize()
    {
        return availableSize;
    }
}

I also want to add some additional labels for pie chart slice. Looks like I need to totally redesign the code because every time I will have different data and I need to to visualize the data. How I can to this? Can you help me to implement this?

1条回答
放荡不羁爱自由
2楼-- · 2019-08-25 08:24

You can override PieChart#layoutChartChildren to display additional values in the label of the pie chart slices. I modified your code to show an example. For adding values, I used your update thread, but modified the usedSize values with the variable "l" to produce changing values and labels in the animation.

The FSPartitions class is missing in the below example.

public class MainApp extends Application {

  Stage                         stage;

  private ObservableList<Data>  pieChartData    = FXCollections.observableArrayList();
  private final PieChart        chart           = new PieChart(pieChartData) {
        @Override
        protected void layoutChartChildren(double top, double left, double contentWidth, double contentHeight) {
          if (getLabelsVisible()) {
            getData().forEach(d -> {
              Optional<Node> opTextNode = chart.lookupAll(".chart-pie-label").stream().filter(n -> n instanceof Text && ((Text) n).getText().contains(d.getName())).findAny();
              if (opTextNode.isPresent()) {
                ((Text) opTextNode.get()).setText(d.getName() + " " + d.getPieValue());
              }
            });
          }
          super.layoutChartChildren(top, left, contentWidth, contentHeight);
        }};

  Label                         caption         = new Label();

  @Override
  public void start(Stage stage) {

    this.stage = stage;

    setUserAgentStylesheet(STYLESHEET_MODENA);
    Scene scene = new Scene(new Group());
    stage.setTitle("File System");
    stage.setWidth(500);
    stage.setHeight(500);

    chart.setTitle("File System Size");

    // Some task which updates the Pie Chart
    final Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            int max = 50;
            int l = 0;
            for (int i = 1; i <= max; i++) {

                FSPartitions data1 = FSPartitions.newInstance().FSName("test1").FSType("test1").isReadonly(true).totalSize(3434).usedSize(333+l).availableSize(3644);
                FSPartitions data2 = FSPartitions.newInstance().FSName("test2").FSType("test2").isReadonly(true).totalSize(34334).usedSize(343+(2*l)).availableSize(3544);
                FSPartitions data3 = FSPartitions.newInstance().FSName("test3").FSType("test3").isReadonly(true).totalSize(34543).usedSize(353+3*l).availableSize(3446);
                l++;

                List<FSPartitions> ss = new ArrayList<>();
                ss.add(data1);
                ss.add(data2);
                ss.add(data3);

                updatePieChart(ss);

                Thread.sleep(600);
            }
            return null;
        }
    };

    new Thread(task).start();

    ((Group) scene.getRoot()).getChildren().addAll(chart, caption);
    stage.setScene(scene);
    stage.show();
  }

  public void addPieChartData(String name, double value) {
    pieChartData.add(new Data(name, value));

    caption = new Label();
    caption.setTextFill(Color.DARKORANGE);
    caption.setStyle("-fx-font: 24 arial;");

    for (final Data data : chart.getData()) {

        Node node = data.getNode();

        node.addEventHandler(MouseEvent.MOUSE_MOVED, new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent e) {
                caption.setTranslateX(e.getSceneX() + 15);
                caption.setTranslateY(e.getSceneY());
                caption.setText(String.valueOf(data.getPieValue()) + "%");
                caption.setVisible(true);
                node.setEffect(new Glow());
                // String styleString = "-fx-border-color: white;
                // -fx-border-width: 1; -fx-border-style: dashed;";
                // node.setStyle(styleString);
            }
        });

        node.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent e) {
                caption.setVisible(false);
                node.setEffect(null);
                // node.setStyle("");
            }
        });

        final MenuItem resizeItem = new MenuItem("Resize");
        resizeItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("Resize requested");
            }
        });

        final MenuItem aboutItem = new MenuItem("About");
        aboutItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("About requested");
            }
        });

        final MenuItem changeColorItem = new MenuItem("Change Color");
        changeColorItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("change Color Item requested");

            }
        });

        final ContextMenu menu = new ContextMenu(resizeItem, aboutItem, changeColorItem);

        node.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                if (MouseButton.SECONDARY.equals(event.getButton())) {
                    menu.show(stage, event.getScreenX(), event.getScreenY());
                }
            }
        });

    }
  }

  // updates existing Data-Object if name matches
  public void updatePieChart(List<FSPartitions> obj) {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            for (FSPartitions obj1 : obj) {
                String fsName = obj1.getFSName();
                double usedSize = obj1.getUsedSize();

                boolean found = false;
                for (Data d : pieChartData) {
                    if (d.getName().equals(fsName)) {
                        d.setPieValue(usedSize);
                        found = true;
                        break;
                    }
                }

                if (!found) {
                    addPieChartData(fsName, usedSize);
                }
            }
        }
    });
  }

  public static void main(String[] args) {
    launch(args);
  }
}
查看更多
登录 后发表回答