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

Javafx从线程通知事件到控制器类,用于设置标签,基于模型的文本字段

屠晟睿
2023-03-14

在线程中,我运行一些代码,需要更新JsonOverviewController中的JavaFXUI元素。我不想在模型或类控制器中传递ui元素。我不要这个

        jfWatch = new WatchController();
        jfWatch.setPathDirectory(absPathSelDir);
        jfWatch.setMyJsonFilesTable(myJsonFilesTable);
        jfWatch.setMyIdTableColumn(myIdTableColumn);
        ...
        
        jfWatch.setMyStateLabel(myStateLabel);
        
        jfWatch.start();

下面的代码显示了该标签。TextField传递给线程,并在运行方法中更新它们。

package it.einaudi.storejson;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;

import com.fasterxml.jackson.core.JsonParseException;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import it.einaudi.storejson.model.ConnectionConfig;
import it.einaudi.storejson.model.JsonFile;
import it.einaudi.storejson.model.JsonFileManager;

public class JsonOverviewController {
    
    @FXML
    private Button myButton;
    
    @FXML
    private Label myDirWatch;
    
    @FXML
    private Label myCurrentJsonFileId;
    
    @FXML
    private Label myCurrentJsonFileName;
    
    @FXML
    private TextArea myCurrentJsonFileContent;
    
    @FXML
    private Button myImportButton;
    
    @FXML
    private TableView<JsonFile> myJsonFilesTable;
    
    @FXML
    private TableColumn<JsonFile, String> myIdTableColumn;
    
    @FXML
    private TableColumn<JsonFile, String> myNameFileTableColumn;
    
    @FXML
    private TableColumn<JsonFile, String> myStateTableColumn;
    
    @FXML
    private TextField myConnFullurlField;
    
    @FXML
    private TextField myConnUsernameField;
    
    @FXML
    private TextField myConnPasswordField;
    
    @FXML
    private TextField myConnNamedbField;
    
    @FXML
    private Label myStateLabel;
    
    //private JsonWatchService threadJsonWatch;
    private WatchController jfWatch;
    
    private JsonFileManager jfManager;
    
    /**
     * The constructor (is called before the initialize()-method).
     */
    public JsonOverviewController() {       
        //threadJsonWatch = null;
        jfManager = null;
        jfWatch = null;
    }
    
    
    @FXML
    private void handleButtonAction(ActionEvent event) {
        // Button was clicked, do something...
        System.out.println("myButton premuto");
        DirectoryChooser directoryChooser = new DirectoryChooser();
        Node node = (Node) event.getSource();
        File selectedDirectory;
        Stage thisStage = (Stage) node.getScene().getWindow();
        String absPathSelDir;
        
        selectedDirectory = directoryChooser.showDialog(thisStage);
        if(selectedDirectory == null ) return;
        
        absPathSelDir = selectedDirectory.getAbsolutePath();
        
        myDirWatch.setText(absPathSelDir);
            
        if(jfWatch != null) jfWatch.interrupt();
        
        jfWatch = new WatchController();
        jfWatch.setPathDirectory(absPathSelDir);
        jfWatch.setMyJsonFilesTable(myJsonFilesTable);
        jfWatch.setMyIdTableColumn(myIdTableColumn);
        jfWatch.setMyNameFileTableColumn(myNameFileTableColumn);
        jfWatch.setMyStateTableColumn(myStateTableColumn);
        
        jfWatch.setMyCurrentJsonFileId(myCurrentJsonFileId);
        jfWatch.setMyCurrentJsonFileName(myCurrentJsonFileName);
        jfWatch.setMyCurrentJsonFileContent(myCurrentJsonFileContent);
        
        jfWatch.setMyStateLabel(myStateLabel);
        
        jfWatch.start();
        
        myStateLabel.setText("La cartella " + 
                             absPathSelDir + 
                             " è monitorata.");    
    }
    
    /**
     * Initializes the controller class. This method is automatically called
     * after the fxml file has been loaded.
     */
    
    /**
     * Initializes the controller class. This method is automatically called
     * after the fxml file has been loaded.
     */
    @FXML
    private void initialize() {
        // Handle Button event.
        myButton.setOnAction(this::handleButtonAction);
        
        myIdTableColumn.setCellValueFactory(new PropertyValueFactory<JsonFile,String>("idFile"));
        myNameFileTableColumn.setCellValueFactory(new PropertyValueFactory<JsonFile,String>("nameFile"));
        myStateTableColumn.setCellValueFactory(new PropertyValueFactory<JsonFile,String>("stateFile"));
        
    }
    
}

public class WatchController extends Thread {
    
    private String pathDirectory;
    
    private Label myCurrentJsonFileId;
    
    private Label myCurrentJsonFileName;
    
    private TextArea myCurrentJsonFileContent; 
    
    private TableView<JsonFile> myJsonFilesTable;
    
    private TableColumn<JsonFile, String> myIdTableColumn;
    
    private TableColumn<JsonFile, String> myNameFileTableColumn;
    
    private TableColumn<JsonFile, String> myStateTableColumn;
    
    private Label myStateLabel;

    public void run() {
        if(!found) {
            Platform.runLater(() -> myCurrentJsonFileId.setText(""));
            Platform.runLater(() -> myCurrentJsonFileName.setText(""));
            Platform.runLater(() -> myCurrentJsonFileContent.setText("Anteprima file json non disponibile."));  
        }else {
            final JsonFile firstJCopy = firstJ;
            Platform.runLater(() -> myCurrentJsonFileId.setText(firstJCopy.getIdFile()));
            Platform.runLater(() -> myCurrentJsonFileName.setText(firstJCopy.getNameFile()));
            
            fullPathFile = pathDirectory + "\\" + firstJ.getNameFile();
            System.out.println(fullPathFile);
            strJ = readFileAsList(fullPathFile);
            String strJCopy = strJ;
            Platform.runLater(() -> myCurrentJsonFileContent.setText(strJCopy));
        }
    }
}

相反,我希望我的线程将发生的情况通知控制器,并将模型的一些数据发送给控制器。从而在基于数据模型的控制器类中正确设置了标签、文本字段。

共有1个答案

巢承安
2023-03-14

在标准的MVC体系结构中,您的模型应该包含一个侦听器列表,并且应该根据需要通知这些侦听器事件。您的控制器(在带有FXML的JavaFX中更像一个MVP演示器)应该是一个监听器,并且应该向模型注册自己。

因此:

public interface WatchListener {
    public void startedProcessing();
    public void fileProcessed(String data);
    public void notFound();
}
public class WatchController extends Thread {
    
    private String pathDirectory;
    
    private final List<WatchListener> listeners = new ArrayList<>();

    public void addListener(WatchListener listener) {
        listeners.add(listener);
    }

    public void removeListener(WatchListener listener) {
        listeners.remove(listener);
    }

    public void run() {
        if(!found) {
            Platform.runLater(() -> {
                listeners.forEach(WatchListener::notFound);
            }); 
        } else {
            final JsonFile firstJCopy = firstJ;
            Platform.runLater(() -> {
                listeners.forEach(WatchListener::startedProcessing);
            });
            
            fullPathFile = pathDirectory + "\\" + firstJ.getNameFile();
            System.out.println(fullPathFile);
            strJ = readFileAsList(fullPathFile);
            String strJCopy = strJ;
            Platform.runLater(() -> {
                listeners.forEach(listener -> listener.fileProcessed(strJCopy));
            });
        }
    }
}

然后在控制器中:

public class JsonOverviewController implements WatchListener {
    
    // fields and initialize method as before
    
    
    @FXML
    private void handleButtonAction(ActionEvent event) {
        // Button was clicked, do something...
        System.out.println("myButton premuto");
        DirectoryChooser directoryChooser = new DirectoryChooser();
        Node node = (Node) event.getSource();
        File selectedDirectory;
        Stage thisStage = (Stage) node.getScene().getWindow();
        String absPathSelDir;
        
        selectedDirectory = directoryChooser.showDialog(thisStage);
        if(selectedDirectory == null ) return;
        
        absPathSelDir = selectedDirectory.getAbsolutePath();
        
        myDirWatch.setText(absPathSelDir);
            
        if(jfWatch != null) jfWatch.interrupt();
        
        jfWatch = new WatchController();
        watchController.addListener(this);
        jfWatch.setPathDirectory(absPathSelDir);
        
        jfWatch.start();
        
        myStateLabel.setText("La cartella " + 
                             absPathSelDir + 
                             " è monitorata.");    
    }
    
    @Override
    public void startedProcessing() {
        myCurrentJsonFileId.setText("");
        myCurrentJsonFileName.setText("");
        myCurrentJsonFileContent.setText("Anteprima file json non disponibile.");
    }

    @Override
    public void fileProcessed(String data) {
        myCurrentJsonFileContent.setText(data);
    }

    @Override
    public void notFound() {
        myCurrentJsonFileId.setText("");
        myCurrentJsonFileName.setText("");
        myCurrentJsonFileContent.setText("Anteprima file json non disponibile.");  
    }
}

在这里,比使用普通线程更好的方法可能是使用JavaFX并发API,并将WatchController作为任务实现。它支持在FX应用程序线程上执行任务启动、完成等时的代码(它基本上处理状态更改时为您调用的所有Platform.runLater())。这看起来像:

public class WatchController extends Task<String> {

    private final String pathDirectory;

    public WatchController(String pathDirectory) {
        this.pathDirectory = pathDirectory ;
    }

    @Override
    public String call() throws Exception {
        if(!found) {
            throw new FileNotFoundException(pathDirectory + " not found");
        } else {
            final JsonFile firstJCopy = firstJ;
            fullPathFile = pathDirectory + "\\" + firstJ.getNameFile();
            System.out.println(fullPathFile);
            return readFileAsList(fullPathFile);
        }
    }
}

然后在控制器中:

public class JsonOverviewController implements WatchListener {

    // fields and initialize method as before

    private final Executor exec = Executors.newCachedThreadPool();


    @FXML
    private void handleButtonAction(ActionEvent event) {
        // Button was clicked, do something...
        System.out.println("myButton premuto");
        DirectoryChooser directoryChooser = new DirectoryChooser();

        File selectedDirectory = directoryChooser.showDialog(myButton.getScene().getWindow());
        if(selectedDirectory == null ) return;

        String absPathSelDir = selectedDirectory.getAbsolutePath();

        myDirWatch.setText(absPathSelDir);

        if(jfWatch != null) jfWatch.cancel();

        jfWatch = new WatchController(absPathSelDir);
        watchController.setOnRunning(evt -> startedProcessing());
        watchController.setOnFailed(evt -> notFound());
        watchController.setOnSucceeded(evt -> fileProcessed(jfWatch.getValue());

        exec.execute(jfWatch);

        myStateLabel.setText("La cartella " + 
                             absPathSelDir + 
                             " è monitorata.");    
    }

    @Override
    public void startedProcessing() {
        myCurrentJsonFileId.setText("");
        myCurrentJsonFileName.setText("");
        myCurrentJsonFileContent.setText("Anteprima file json non disponibile.");
    }

    @Override
    public void fileProcessed(String data) {
        myCurrentJsonFileContent.setText(data);
    }

    @Override
    public void notFound() {
        myCurrentJsonFileId.setText("");
        myCurrentJsonFileName.setText("");
        myCurrentJsonFileContent.setText("Anteprima file json non disponibile.");  
    }
}

您可能需要在任务实现中做更多的工作,以便以适当的方式完全支持取消任务。有关更多详细信息,请参阅任务文档。

 类似资料:
  • 我对stackoverflow和javafx都是新手,所以请友好一点。 描述我正在做的事情: 我正在做一个简单的问答游戏。第一个窗口就像一个欢迎/开始屏幕,当点击那个按钮时,我们会到达第二个屏幕,所有的类别按钮都在那里,当点击其中一个时,它会随机选择用户选择的类别的问题第三个也是最后一个窗口将显示问题类别作为标签,问题作为文本字段,回答作为文本字段。 问题:每当一个类别被点击时,我需要当前控制器来

  • 在我的javafx应用程序中,我们有两个FXML文件,第一个。FXML和second.FXML,相同的第一个Controller.java和secondController.java现在主要的问题是第一个。FXML包含TextField name和on Button当用户单击那个按钮时second.FXML显示在second.FXML中我有一个组合框和一个按钮当用户单击second.FXML按钮时

  • 我在Excel文件中有数据,我需要使用它来使用SVM执行多标签分类。它有两列,如下所示。“推文”-A、B、C、D、E、F、G和“类别”=X、Y、Z 推文类别 一个X B Y C Z D X,Y E Y,Z F X,Y,Z G X,Z 给出一条推特,我想训练我的模型预测它所属的类别。推文和类别都是文本。我正在尝试使用Weka的LibSVM分类器进行分类,因为我读到它可以进行多标签分类。我将csv文件

  • 假设我在一个页面上有3个文本框,定义如下。 我将把值“Open”作为参数传递给JSoup,JSoup应该返回如下数据(这是中间文本框的详细信息)。 JSoup能做到吗? 谢谢您 -阿努普

  • 我正在尝试自定义Symfony表单标签,通过此Symfony文档为所有必填字段添加星号(*)。但是我的星号

  • 我如何拥有一个被所有其他线程引用的字符串。如何知道所有线程何时执行命令字符串?