当前位置: 首页 > 知识库问答 >
问题:

JavaFX通过任务线程观察到表视图

夏侯鹏
2023-03-14

我正在构建一个多屏幕JavaFX应用程序,数据从SQL数据库拉到ObservableList,并通过Tableview显示在界面上。由于应用程序的多屏幕特性,我试图通过控制器将数据从ObservableList初始化到Tableview。SQL拉入ObservableList是通过新线程上的任务完成的。当我包含sqlCSEditTbl时。itemsProperty()。setValue((ObservableList)csSQLList)在任务方法中,当我运行程序时,TableView中不会显示任何内容。如果我将代码放在任务方法之外,则不会显示特定屏幕。我不知道我在这里遗漏了什么,以便能够获得在特定屏幕上显示的数据。我已经将SQL调试到ObservableList,数据正在ObservableList中正确存储。问题是如何将其从ObservableList获取到Tableview接口。任何帮助都将不胜感激。非常感谢。

应用编辑屏幕截图

SQLCalcScript数组模型

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

<?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代码:

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);
//    }
}

受控屏幕类别代码:

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);
}

屏幕控制器类别代码:

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;
        }
    }
}

屏幕框架主应用程序代码:

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);
    }
}

共有1个答案

呼延源
2023-03-14

当你打电话的时候

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

您正在更新UI(通过更新表中显示的项目)。就像(几乎?)所有UI工具包,JavaFX都是单线程的:对UI的更新只能在FX应用程序线程上进行。在后台线程上执行的任务中执行此操作将引发非法状态异常;因此,您的调用方法永远不会完成,也永远看不到表的更新。

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

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

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

           return data;
        }
    };

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

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

最好将发生的任何异常记录在

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

然后像以前一样启动任务

new Thread(task).start();
 类似资料:
  • 我正在用一个库编程,我不知道代码,只知道方法,我不能修改它。我试着制作一个“航班”的表格视图,但我不知道如何为每个航班命名(或ID)。有人能帮我吗?谢谢此处有一些代码:

  • 我一直在学习JavaFX的任务,并使用这些任务通过或任务的方法等与应用程序线程进行通信。但是,我的需要知道用户何时按下 GUI 上的按钮,因为这可能会更改任务的 方法需要返回的值。我该怎么做?我知道如何响应单线程应用程序上的按钮按下事件,但不确定如何以线程安全的方式处理它。 到目前为止,这是我所拥有的,这是实现按钮事件的明智方式吗?

  • 我今天才开始学习JavaFX,我试图通过制作一个Snake克隆来了解更多,但是我在线程方面遇到了麻烦。我想创建一个线程来更新蛇在屏幕上的位置,但是不能以正常的Runnable线程方式使用它,因为我在线程中使用JavaFX来更新绘制到屏幕上的矩形的位置(我知道你不能这样做,必须使用任务,服务,Platform.run稍后,等等?)我创建线程的类扩展了JavaFX。scene.layout.窗格,我试

  • 我正在尝试用来自可观察列表的数据填充TableView。我以前做过这个,但由于某种原因我现在无法让它工作。我没有得到任何异常或任何东西,但它根本不会向表添加任何东西。 这个问题很相似,但我发现了JPA引起的另一个问题,这使得提到的的构造函数永远不会执行。相反,JPA会神奇地分配值。 这是我的代码-我剪下了与问题无关的代码: FXML 主要的Java语言 修改Java语言 任何帮助解决这个问题将不胜

  • 我正在编写一个JavaFX应用程序,我的对象扩展任务提供了JavaFXGUI线程之外的并发性。 我的主要课程是这样的: 我的GUI控制器示例如下(略作抽象): 目前,我的任务只是进行睡眠并打印数字1到10: 我遇到的问题是,一旦任务完成,就好像启动任务的线程继续运行一样。因此,当我按下右上角的“X”退出JavaFX应用程序时,JVM继续运行,我的应用程序不会终止。如果你看一下我的主课,我已经把系统

  • 我正在使用版本10中的angular应用程序。下面是学习使用硬编码json对象(无http调用)的可观察对象的要求。例如,我有一个接口 下面是我声明一个可观察的代码 在Oninit方法中,我创建如下对象 下面是html代码