JavaFX表更新线程体系结构不匹配

时间:2018-04-22 18:58:20

标签: java javafx

我有一个JavaFX表,最终会在网络线程上收到数据。

使用Platform.runLater()更新视图模型很简单,但它不适合我们的架构。

当前的架构将应用程序分为" view"和"网络/通讯"部分。

  • View会侦听模型并更新相应的组件。不了解网络。
  • 网络侦听网络更新并相应地将数据写入模型。不了解JavaFX。

所以我处于两难境地。

  • 忠于架构和"关注点分离" - 网络阅读器类不应该调用Platform.runLater()

  • 为了简单起见并让网络阅读器类调用Platform.runLater() - 只是工作 - 没有其他代码。

我试图在代码

中说明这一点

简单方法

只需从网络阅读器

调用Platform.runLater()即可
public class SimpleUpdate extends Application {
    private int clock;

    public class Item {
        private IntegerProperty x = new SimpleIntegerProperty(0);

        public final IntegerProperty xProperty() {
            return this.x;
        }

        public final int getX() {
            return this.xProperty().get();
        }

        public final void setX(final int x) {
            this.xProperty().set(x);
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        ObservableList<Item> viewModel = FXCollections.observableArrayList();
        TableView<Item> table = new TableView<Item>(viewModel);
        TableColumn<Item, Integer> colX = new TableColumn<>();
        colX.setCellValueFactory(new PropertyValueFactory<Item, Integer>("x"));
        table.getColumns().add(colX);

        primaryStage.setScene(new Scene(table));
        primaryStage.show();

        new Thread(() -> {
            while (true) {
                Platform.runLater(() -> { // update on JavaFX thread
                    if (clock % 2 == 0) {
                        viewModel.add(new Item());
                        viewModel.add(new Item());
                    } else {
                        viewModel.remove(1);
                    }
                    for (Item each : viewModel) {
                        each.setX(each.getX() + 1);
                    }
                    clock++;
                });
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Network update").start();
    }

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

}

纯粹的方法

  • 网络阅读器线程写入自己的模型
  • 视图侦听器侦听网络模型并使用JavaFX线程同步其自己的视图模型
  • 更复杂

代码

public class PureUpdate extends Application {
    private int clock;

    public class Item {
        private IntegerProperty x = new SimpleIntegerProperty(0);

        public final IntegerProperty xProperty() {
            return this.x;
        }

        public final int getX() {
            return this.xProperty().get();
        }

        public final void setX(final int x) {
            this.xProperty().set(x);
        }
    }

    public class ViewItem extends Item {
        private Item original;

        public ViewItem(Item original) {
            super();
            this.original = original;
            sync();
        }

        public void sync() {
            setX(original.getX());
        }

        public Item getOriginal() {
            return original;
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        ObservableList<ViewItem> viewModel = FXCollections.observableArrayList();
        TableView<ViewItem> table = new TableView<ViewItem>(viewModel);
        TableColumn<ViewItem, Integer> colX = new TableColumn<>();
        colX.setCellValueFactory(new PropertyValueFactory<ViewItem, Integer>("x"));
        table.getColumns().add(colX);

        primaryStage.setScene(new Scene(table));
        primaryStage.show();

        ObservableList<Item> networkModel = FXCollections
                .synchronizedObservableList(FXCollections.observableArrayList());

        networkModel.addListener((Observable obs) -> {
            Platform.runLater(() -> {
                List<Item> alreadyKnown = new ArrayList<>();
                for (Iterator<ViewItem> it = viewModel.iterator(); it.hasNext();) {
                    ViewItem each = it.next();
                    alreadyKnown.add(each.getOriginal());
                    if (networkModel.contains(each.getOriginal())) {
                        each.sync();
                    } else {
                        it.remove();
                    }
                }
                for (Item each : networkModel.toArray(new Item[0])) {
                    if (!alreadyKnown.contains(each)) {
                        viewModel.add(new ViewItem(each));
                    }
                }
            });

        });

        new Thread(() -> {
            while (true) {
                if (clock % 2 == 0) {
                    networkModel.add(new Item());
                    networkModel.add(new Item());
                } else {
                    networkModel.remove(1);
                }
                for (Item each : networkModel) {
                    each.setX(each.getX() + 1);
                }
                clock++;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Network update").start();
    }

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

}

问题。我可以在不编写额外代码的情况下实现纯粹的方法吗?

1 个答案:

答案 0 :(得分:4)

让处理器向Runnable发布通知。或Consumer<T>,或定义自定义@FunctionalInterface

当您设计线程以及对JavaFX或任何其他需要在特定线程上进行同步的框架的依赖时,这将使测试更容易。

使用消费者的示例:

public class NetworkReader {
   private final Consumer<? super Data> consumer;

   public NetworkReader(Consumer<? super Data> consumer) {
       this.consumer = Objects.requireNonNull(consumer);
   }

   public void readStuff() {
       while (...) {
           Data data = ...;
           consumer.accept(data);
       }
   }
}

NetworkReader将由例如new NetworkReader(d -> Platform.runLater(() -> updateModel(d)));构建。 NetworkReader reader = new NetworkReader(d -> this.actuals = d); reader.readStuff(); assertEquals(expecteds, actuals);

当您想要测试时,您可以按如下方式传递:

def compute_variance_grayscale(self, clusters, centroids, gImage, k):
    # compute variance
    variance = 0

    for i in range(len(clusters)): #iterate over the available clusters
        cluster = clusters[i]
        sum = 0;
        centroid = centroids[i]
        for j in cluster: #for each data point in cluster j compute the sum
            sum += (gImage.getImageValueAt(j[0], j[1]) - centroid) ** 2

        variance += sum / (len(cluster) - k); #add the sum to the given variance

    return variance

        def compute_variance_rgb(self, clusters, centroids, cImage, k):
    # compute variance
    variance = 0
    for i in range(len(clusters)):
        cluster = clusters[i]
        sum = 0;
        centroid = centroids[i]
        for j in cluster:
            b,g,r = cImage.getImageValueAt(j[0], j[1])
            b_e = (b - centroid[0]) ** 2
            g_e = (g - centroid[1]) ** 2
            r_e = (r - centroid[2]) ** 2

            sum += (b_e + g_e + r_e)

        variance += sum / (len(cluster) - k);

    return variance

智能消费者可以在实际处理之前进行更新。