通过任务线程将JavaFX ObservableList转换为Tableview [英] JavaFX ObservableList toTableview via Task Thread

查看:325
本文介绍了通过任务线程将JavaFX ObservableList转换为Tableview的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个多屏JavaFX应用程序,其数据已从SQL数据库提取到ObservableLists,并通过Tableview在界面中显示.由于该应用程序具有多屏性质,因此我试图通过控制器初始化从ObservableList到Tableview的数据.通过新线程上的任务将SQL拉到ObservableList.当我在Task方法中包括sqlCSEditTbl.itemsProperty().setValue((ObservableList)csSQLList)时,运行该程序时,TableView中什么也没有显示.如果将代码放在Task方法之外,则不会显示特定屏幕.我不知道我在这里缺少什么才能将数据显示在特定的屏幕上.我已经将SQL调试为ObservableList,并且数据已正确存储在ObservableList中.问题是将其从ObservableList传递到Tableview接口.任何帮助将不胜感激.谢谢!

I am building a multiscreen JavaFX app with data being pulled from a SQL database to ObservableLists and displayed in the interface via Tableview. Because of the multiscreen nature of the app, I am trying to initialize the data from the ObservableList to the Tableview via the controller. The SQL pull to ObservableList is done via a Task on a new thread. When I include the sqlCSEditTbl.itemsProperty().setValue((ObservableList) csSQLList) in the Task method nothing is displayed in the TableView when I run the program. If I place the code outside the Task method,the particular screen will not display. I don't know what I am missing here to be able to get the data to display on the particular screen. I've debugged the SQL to ObservableList and the data is being properly stored in the ObservableList. The problem is getting it from the ObservableList to the Tableview interface. Any help would be greatly appreciated. Thank you!

应用程序编辑屏幕截图

SQLCalcScript数组模型

SQLCalcScript Array Model

package model.calcs;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 * Created by jdsmith on 2/17/2016.
 */
public class SQLCalcScripts {

    private final IntegerProperty calcScriptID;
    private final IntegerProperty calcScriptIndex;
    private final StringProperty calcScriptName;
    private final StringProperty calcScriptServer;
    private final StringProperty calcScriptApp;
    private final StringProperty calcScriptGroup;

    public SQLCalcScripts() {
        this(null, null, null, null, null, null);
    }

    public SQLCalcScripts(Integer calcScriptID, Integer calcScriptIndex, String calcScriptName, String calcScriptServer, String calcScriptApp, String calcScriptGroup) {
        this.calcScriptID = new SimpleIntegerProperty(calcScriptID);
        this.calcScriptIndex = new SimpleIntegerProperty(calcScriptIndex);
        this.calcScriptName = new SimpleStringProperty(calcScriptName);
        this.calcScriptServer = new SimpleStringProperty(calcScriptServer);
        this.calcScriptApp = new SimpleStringProperty(calcScriptApp);
        this.calcScriptGroup = new SimpleStringProperty(calcScriptGroup);

        this.calcScriptID.addListener((e) -> System.out.println("ID changed to " + this.calcScriptID.get()));
        this.calcScriptIndex.addListener((e) -> System.out.println("ID changed to " + this.calcScriptIndex.get()));
        this.calcScriptName.addListener((e) -> System.out.println("ID changed to " + this.calcScriptName.get()));
        this.calcScriptServer.addListener((e) -> System.out.println("ID changed to " + this.calcScriptServer.get()));
        this.calcScriptApp.addListener((e) -> System.out.println("ID changed to " + this.calcScriptApp.get()));
        this.calcScriptGroup.addListener((e) -> System.out.println("ID changed to " + this.calcScriptGroup.get()));
    }

    // Getters and Setters for Calc Script
    public Integer getCalcScriptID() {
        return calcScriptID.get();
    }

    public void setCalcScriptID(Integer calcScriptID) {
        this.calcScriptID.set(calcScriptID);
    }

    public IntegerProperty calcScriptIDProperty() {
        return calcScriptID;
    }


    public Integer getCalcScriptIndex() {
        return calcScriptIndex.get();
    }

    public void setCalcScriptIndex(Integer calcScriptIndex) {
        this.calcScriptIndex.set(calcScriptIndex);
    }

    public IntegerProperty calcScriptIndexProperty() {
        return calcScriptIndex;
    }


    public String getCalcScriptName() {
        return calcScriptName.get();
    }

    public void setCalcScriptName(String calcScriptName) {
        this.calcScriptName.set(calcScriptName);
    }

    public StringProperty calcScriptNameProperty() {
        return calcScriptName;
    }


    public String getCalcScriptServer() {
        return calcScriptServer.get();
    }

    public void setCalcScriptServer(String calcScriptServer) {
        this.calcScriptServer.set(calcScriptServer);
    }

    public StringProperty calcScriptServerProperty() {
        return calcScriptServer;
    }


    public String getCalcScriptApp() {
        return calcScriptApp.get();
    }

    public void setCalcScriptApp(String calcScriptApp) {
        this.calcScriptApp.set(calcScriptApp);
    }

    public StringProperty calcScriptAppProperty() {
        return calcScriptApp;
    }


    public String getCalcScriptGroup() {
        return calcScriptGroup.get();
    }

    public void setCalcScriptGroup(String calcScriptGroup) {
        this.calcScriptGroup.set(calcScriptGroup);
    }

    public StringProperty calcScriptGroupProperty() {
        return calcScriptGroup;
    }
}

csEditInterface.fxml

csEditInterface.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="essapp.csEditController">
   <children>
      <VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <children>
            <Label alignment="CENTER" contentDisplay="CENTER" text="Calculation Script Editor">
               <font>
                  <Font name="System Bold" size="36.0" />
               </font>
            </Label>
            <ScrollPane fitToHeight="true" fitToWidth="true" pannable="true" prefHeight="800.0" prefWidth="717.0">
               <content>
                  <AnchorPane prefHeight="200.0" prefWidth="717.0">
                     <children>
                        <TableView editable="true" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                          <columns>
                            <TableColumn fx:id="csEditID" editable="false" prefWidth="100.0" sortable="false" text="ID" />
                            <TableColumn fx:id="csEditIndex" prefWidth="100.0" text="Index" />
                              <TableColumn fx:id="csEditName" prefWidth="250.0" sortable="false" text="Name" />
                              <TableColumn fx:id="csEditServer" editable="false" prefWidth="300.0" sortable="false" text="Server" />
                              <TableColumn fx:id="csEditApp" editable="false" prefWidth="250.0" sortable="false" text="Application" />
                              <TableColumn fx:id="csEditGroup" prefWidth="400.0" sortable="false" text="Calc Group" />
                          </columns>
                        </TableView>
                     </children>
                  </AnchorPane>
               </content>
               <VBox.margin>
                  <Insets left="50.0" right="50.0" top="50.0" />
               </VBox.margin>
            </ScrollPane>
            <Button fx:id="csEditOkBtn" defaultButton="true" mnemonicParsing="false" onAction="#createTbl" prefHeight="25.0" prefWidth="151.0" text="Commit Changes">
               <VBox.margin>
                  <Insets top="50.0" />
               </VBox.margin>
            </Button>
            <Button fx:id="csEditExitBtn" cancelButton="true" mnemonicParsing="false" onAction="#goToCSInt" prefHeight="25.0" prefWidth="150.0" text="Cancel">
               <VBox.margin>
                  <Insets top="25.0" />
               </VBox.margin>
            </Button>
         </children>
      </VBox>
   </children>
</AnchorPane>

csEditController代码:

csEditController Code:

package essapp;

import com.sun.javafx.tk.Toolkit;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
import model.calcs.*;

import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;

import static javafx.scene.input.KeyCode.T;

@SuppressWarnings("Duplicates")
public class csEditController implements Initializable, ControlledScreen {

    ScreensController myController;
    ObservableList<SQLCalcScripts> csEditCSList = FXCollections.observableArrayList();
    ObservableList<CalcScripts> csEditEssSQL = FXCollections.observableArrayList();
    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {

        //Add SQL data to TableView
        Task task = new Task<Void>() {
            @Override
            public Void call() throws Exception {

                SQL2CalcScripts csSQLList = new SQL2CalcScripts();
                csSQLList.sqlCalc("http://TEST-HYPRPT01:13080/aps/JAPI","GNLESB",csEditCSList);
                sqlCSEditTbl.itemsProperty().setValue((ObservableList<SQLCalcScripts>) csSQLList);

                return null;
            }
        };
//        sqlCSEditTbl.itemsProperty().bind(task.valueProperty());
//        sqlCSEditTbl.setItems(csEditCSList);
        new Thread(task).start();

        // Initialize table with columns
        csEditID.setCellValueFactory(cellData -> cellData.getValue().calcScriptIDProperty().asObject());
        csEditIndex.setCellValueFactory(cellData -> cellData.getValue().calcScriptIndexProperty().asObject());
        csEditName.setCellValueFactory(cellData -> cellData.getValue().calcScriptNameProperty());
        csEditServer.setCellValueFactory(cellData -> cellData.getValue().calcScriptServerProperty());
        csEditApp.setCellValueFactory(cellData -> cellData.getValue().calcScriptAppProperty());
        csEditGroup.setCellValueFactory(cellData -> cellData.getValue().calcScriptGroupProperty());

        // TableView Calc Group ComboBox
        ObservableList<String> csGroupList = FXCollections.observableArrayList("Supplement", "Wrapper", "Board Book");
        csEditGroup.setCellFactory(ComboBoxTableCell.forTableColumn(new DefaultStringConverter(), csGroupList));
        csEditGroup.setOnEditCommit(
                (TableColumn.CellEditEvent<SQLCalcScripts,String> cb) -> {
                    ((SQLCalcScripts) cb.getTableView().getItems().get(
                            cb.getTablePosition().getRow()
                    )).setCalcScriptGroup(cb.getNewValue());
                }
        );
    }

    public void setScreenParent(ScreensController screenParent){
        myController = screenParent;
    }

    @FXML // ResourceBundle that was given to the FXMLLoader
    private ResourceBundle resources;

    @FXML // URL location of the FXML file that was given to the FXMLLoader
    private URL location;

    @FXML // fx:id="sqlCSEditTbl"
    private TableView<SQLCalcScripts> sqlCSEditTbl; // Value injected by FXMLLoader

    @FXML // fx:id="csEditGroup"
    private TableColumn<SQLCalcScripts, String> csEditGroup; // Value injected by FXMLLoader

    @FXML // fx:id="csEditExitBtn"
    private Button csEditExitBtn; // Value injected by FXMLLoader

    @FXML // fx:id="csEditServer"
    private TableColumn<SQLCalcScripts, String> csEditServer; // Value injected by FXMLLoader

    @FXML // fx:id="csEditID"
    private TableColumn<SQLCalcScripts, Integer> csEditID; // Value injected by FXMLLoader

    @FXML // fx:id="csEditIndex"
    private TableColumn<SQLCalcScripts, Integer> csEditIndex; // Value injected by FXMLLoader

    @FXML // fx:id="csEditOkBtn"
    private Button csEditOkBtn; // Value injected by FXMLLoader

    @FXML // fx:id="csEditApp"
    private TableColumn<SQLCalcScripts, String> csEditApp; // Value injected by FXMLLoader

    @FXML // fx:id="csEditName"
    private TableColumn<SQLCalcScripts, String> csEditName; // Value injected by FXMLLoader

    @FXML
    private void goToCSInt(ActionEvent event){
        myController.setScreen(ScreensFramework.calcScriptInterfaceID);
    }

//    @FXML
//    private void createTbl(ActionEvent event) {
//        sqlCSEditTbl.setItems(csEditCSList);
//    }
}

ControlledScreen类代码:

ControlledScreen class Code:

package sample;

/**
 * Created by jdsmith on 4/21/2016.
 */
public interface ControlledScreen {
    //This method will allow the injection of the Parent ScreenPane
    public void setScreenParent(ScreensController screenPage);
}

ScreensController类代码:

ScreensController class Code:

package essapp;

import java.util.HashMap;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;

/**
 * Created by jdsmith on 1/7/2016.
 */
public class ScreensController  extends StackPane {
    //Holds the screens to be displayed

    private HashMap<String, Node> screens = new HashMap<>();

    public ScreensController() {
        super();
    }

    //Add the screen to the collection
    public void addScreen(String name, Node screen) {
        screens.put(name, screen);
    }

    //Returns the Node with the appropriate name
    public Node getScreen(String name) {
        return screens.get(name);
    }

    //Loads the fxml file, add the screen to the screens collection and
    //finally injects the screenPane to the controller.
    public boolean loadScreen(String name, String resource) {
        try {
            FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
            Parent loadScreen = (Parent) myLoader.load();
            ControlledScreen myScreenControler = ((ControlledScreen) myLoader.getController());
            myScreenControler.setScreenParent(this);
            addScreen(name, loadScreen);
            return true;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return false;
        }
    }

    //This method tries to displayed the screen with a predefined name.
    //First it makes sure the screen has been already loaded.  Then if there is more than
    //one screen the new screen is been added second, and then the current screen is removed.
    // If there isn't any screen being displayed, the new screen is just added to the root.
    public boolean setScreen(final String name) {
        if (screens.get(name) != null) {   //screen loaded
            final DoubleProperty opacity = opacityProperty();

            if (!getChildren().isEmpty()) {    //if there is more than one screen
                Timeline fade = new Timeline(
                        new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1.0)),
                        new KeyFrame(new Duration(1000), new EventHandler<ActionEvent>() {
                            @Override
                            public void handle(ActionEvent t) {
                                getChildren().remove(0);                    //remove the displayed screen
                                getChildren().add(0, screens.get(name));     //add the screen
                                Timeline fadeIn = new Timeline(
                                        new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
                                        new KeyFrame(new Duration(800), new KeyValue(opacity, 1.0)));
                                fadeIn.play();
                            }
                        }, new KeyValue(opacity, 0.0)));
                fade.play();

            } else {
                setOpacity(0.0);
                getChildren().add(screens.get(name));       //no one else been displayed, then just show
                Timeline fadeIn = new Timeline(
                        new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
                        new KeyFrame(new Duration(2500), new KeyValue(opacity, 1.0)));
                fadeIn.play();
            }
            return true;
        } else {
            System.out.println("screen hasn't been loaded!!! \n");
            return false;
        }

        /*Node screenToRemove;
         if(screens.get(name) != null){   //screen loaded
         if(!getChildren().isEmpty()){    //if there is more than one screen
         getChildren().add(0, screens.get(name));     //add the screen
         screenToRemove = getChildren().get(1);
         getChildren().remove(1);                    //remove the displayed screen
         }else{
         getChildren().add(screens.get(name));       //no one else been displayed, then just show
         }
         return true;
         }else {
         System.out.println("screen hasn't been loaded!!! \n");
         return false;
         }*/
    }

    //This method will remove the screen with the given name from the collection of screens
    public boolean unloadScreen(String name) {
        if (screens.remove(name) == null) {
            System.out.println("Screen didn't exist");
            return false;
        } else {
            return true;
        }
    }
}

ScreensFramework主要应用代码:

ScreensFramework Main App Code:

package essapp;

import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class ScreensFramework extends Application {

    public static String mainInterfaceID = "main";
    public static String mainInterfaceFile = "mainInterface.fxml";

    public static String msaInterfaceID = "msa";
    public static String msaInterfaceFile = "msaInterface.fxml";

    public static String creditRatingInterfaceID = "credit";
    public static String creditRatingInterfaceFile = "creditRatingInterface.fxml";

    public static String calcScriptEditID = "csEdit";
    public static String calcScriptEditFile = "csEditInterface.fxml";

    public static String calcScriptInterfaceID = "calc";
    public static String calcScriptInterfaceFile = "calcScriptInterface.fxml";

    public static String subVarInterfaceID = "subvar";
    public static String subVarInterfaceFile = "subVarInterface.fxml";

    @Override
    public void start(Stage primaryStage) {

        ScreensController mainContainer = new ScreensController();
        mainContainer.loadScreen(ScreensFramework.mainInterfaceID, ScreensFramework.mainInterfaceFile);
        mainContainer.loadScreen(ScreensFramework.calcScriptInterfaceID, ScreensFramework.calcScriptInterfaceFile);
        mainContainer.loadScreen(ScreensFramework.calcScriptEditID, ScreensFramework.calcScriptEditFile);
        mainContainer.loadScreen(ScreensFramework.subVarInterfaceID, ScreensFramework.subVarInterfaceFile);
        mainContainer.loadScreen(ScreensFramework.msaInterfaceID, ScreensFramework.msaInterfaceFile);
        mainContainer.loadScreen(ScreensFramework.creditRatingInterfaceID, ScreensFramework.creditRatingInterfaceFile);

        mainContainer.setScreen(ScreensFramework.mainInterfaceID);

        mainContainer.prefHeightProperty().bind(primaryStage.heightProperty());
        mainContainer.prefWidthProperty().bind(primaryStage.widthProperty());
        mainContainer.setCenterShape(true);
        mainContainer.setScaleShape(true);

        Group root = new Group();
        root.getChildren().addAll(mainContainer);
        Scene scene = new Scene(root);

        Screen screen = Screen.getPrimary();
        Rectangle2D bounds = screen.getVisualBounds();

        primaryStage.setWidth(bounds.getWidth());
        primaryStage.setHeight(bounds.getHeight());

        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

推荐答案

致电

sqlCSEditTbl.itemsProperty().setValue(...);

您正在更新UI(通过更新表中显示的项目).像(几乎?)所有UI工具包一样,JavaFX是单线程的:对UI的更新只能在FX Application Thread上进行.在正在后台线程上执行的Task中执行此操作,将抛出IllegalStateException.因此您的调用方法永远不会完成,并且永远不会看到表的更新.

you are updating the UI (by updating the items displayed in the table). Like (almost?) all UI toolkits, JavaFX is single-threaded: updates to the UI can only happen on the FX Application Thread. Doing this in the Task, which is being executed on a background thread, will throw a IllegalStateException; so your call method never completes and you never see the update to the table.

通常的方法是从call方法返回数据:

The usual way to do this is to return the data from your call method:

    Task<List<SQLCalcScripts>> task = new Task<List<SQLCalcScripts>>() {
        @Override
        public List<SQLCalcScripts> call() throws Exception {

           List<SQLCalcScripts> data = /* get data.... */ ;

           return data;
        }
    };

任务完成时,将value属性设置为call方法返回的值,因此您现在可以执行以下操作:

when the task completes, the value property is set to the value returned from the call method, so you can now do:

task.setOnSucceeded(e -> sqlCSEditTbl.getItems().setAll(task.getValue()));

记录发生的任何异常可能是个好主意

It's probably a good idea to log any exceptions that occur with

task.setOnFailed(e -> task.getException().printStackTrace());

然后像以前一样使用

new Thread(task).start();

这篇关于通过任务线程将JavaFX ObservableList转换为Tableview的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆