2016-09-12 6 views
0

У меня есть приложение, которое после входа в систему обращается к базе данных через класс интерфейса. Процесс входа в систему заставляет приложение не реагировать на период, пока оно попадает в базу данных, и поэтому я просматриваю потоки и ожидающий курсор, чтобы это можно было запустить плавно. Я попытался использовать потоки через многие примеры в Интернете и переполнение стека, но мой метод, похоже, не работает, я получаю java.lang.IllegalStateException: не в потоке приложения FX; currentThread = Thread-4 exception, и я не уверен, как это исходить отсюда. Я пытаюсь изменить курсор на режим WAIT, пока этот фоновый поток запускает метод loginLoadEverything() (хотя я не включил в него код, поскольку он слишком длинный). Вот мой класс контроллера:Как нарисовать фоновое задание, изменяя курсор на WAIT в javaFX?

package main.java.gui; 

import javafx.application.Platform; 
import javafx.concurrent.Service; 
import javafx.concurrent.Task; 
import javafx.event.ActionEvent; 
import javafx.fxml.FXML; 
import javafx.fxml.FXMLLoader; 
import javafx.fxml.Initializable; 
import javafx.scene.Cursor; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.image.Image; 
import javafx.scene.image.ImageView; 
import javafx.stage.Stage; 
import main.java.databaseInterface.BackendInterface; 
import java.net.URL; 
import java.util.ResourceBundle; 
import java.util.concurrent.CountDownLatch; 

public class LoginController implements Initializable { 


    private BackendInterface backendInterface; 
    private DashboardController dashboardController; 
    private StudentsController studentsController; 
    private ConsultationController consultationController; 
    private CreateStudentController createStudentController; 
    private CreateConsultationController createConsultationController; 

    @FXML 
    TextField username; 

    @FXML 
    PasswordField password; 

    @FXML 
    Button loginButton; 

    @FXML 
    Label loginLabel; 

    @FXML 
    public void loginButtonPress(ActionEvent event) { 

     Service<Void> service = new Service<Void>() { 
      @Override 
      protected Task<Void> createTask() { 
       return new Task<Void>() { 
        @Override 
        protected Void call() throws Exception { 

         loginLoadEverything(); 

         final CountDownLatch latch = new CountDownLatch(1); 
         Platform.runLater(new Runnable() { 
          @Override 
          public void run() { 
           try { 

            Scene s1 = loginLabel.getScene(); 
            s1.setCursor(Cursor.WAIT); 

           } finally { 
            latch.countDown(); 
           } 
          } 
         }); 
         latch.await(); 
         return null; 
        } 
       }; 
      } 
     }; 
     service.start(); 

    } 

    public void loginLoadEverything() { 

     //chance to true when complete 
    if (username.getText().isEmpty() == false || password.getText().isEmpty() == false) { 

     loginLabel.setText("Please enter data in the fields below"); 

    } else { 

     username.setText("-----"); 
     password.setText("-----"); 
     //initialises backend interface with username and password 
     backendInterface = new BackendInterface(username.getText(), password.getText().toCharArray()); 

     // Open a connection to the database 

     if (backendInterface.openConnection()) { 

      //return and print response 
      System.out.println(backendInterface.getConnectionResponse()); 

      //directs the user to the dashboard after successful login 
      try { 
       if (backendInterface.getAllStudents() && 
         backendInterface.getAllConsultations() && 
         backendInterface.getCourses() && 
         backendInterface.getConsultationCategories() && 
         backendInterface.getConsultationPriorities()) { 


        FXMLLoader loader1 = new FXMLLoader(); 
        loader1.setLocation(getClass().getResource("/main/res/dashboard.fxml")); 
        loader1.load(); 
        Parent p = loader1.getRoot(); 
        Stage stage = new Stage(); 
        stage.setScene(new Scene(p)); 
        stage.show(); 

        //set instances to the dashboard controller 
        dashboardController = loader1.getController(); 
        dashboardController.setBackendInterface(backendInterface); //pass backendInterface object to controller 
        dashboardController.setDashboardController(loader1.getController()); //pass dashboard as reference 

        //load images 
        Image logoutImage = new Image(getClass().getResourceAsStream("images/logout.png")); 
        Image userImage = new Image(getClass().getResourceAsStream("images/users.png")); 
        Image calendarImage = new Image(getClass().getResourceAsStream("images/calendar.png")); 
        Image leftArrowImage = new Image(getClass().getResourceAsStream("images/leftArrow.png")); 
        Image notepadImage = new Image(getClass().getResourceAsStream("images/notepad.png")); 

        //set images 
        dashboardController.studentLabel.setGraphic(new ImageView(userImage)); 
        dashboardController.logoutLabel.setGraphic(new ImageView(logoutImage)); 
        dashboardController.consultationLabel.setGraphic(new ImageView(notepadImage)); 

    } else { 
      system.out.println(backendInterface.getExceptionMessage); 
    } 


    @Override 
    public void initialize(URL location, ResourceBundle resources) { 



} 
+0

Метод 'call()' - это метод, выполняемый в фоновом потоке. Он должен выполнять работу, которая занимает много времени, и ** не должна ** выполнять какой-либо пользовательский интерфейс. Сейчас кажется, что только пользовательский интерфейс работает. Где выполняется фактическая работа с базой данных, из вашего кода это не ясно. Есть и другие проблемы - например. вы меняете курсор на 'WAIT' * после того, как * все остальное в методе' call() 'завершено, что, безусловно, не то, что вы хотите. Но вам нужно уточнить, где в первую очередь занята фактическая трудоемкая работа. –

+0

Спасибо за отзыв Джеймс, я добавил остальную часть кода для метода loginLoadEverything. Этот метод обеспечивает доступ к базе данных из класса BackendInterface. Как вы можете видеть, методы из класса BackendInterface называются здесь, создавая трудоемкую работу. Затем я пытаюсь вызвать этот метод loginLoadEverything в методе call(). И да, это правильно, мне придется изменить курсор, чтобы подождать более подходящим образом, чтобы отразить трудоемкий процесс, но не уверен, как этого добиться. – Philayyy

+0

См. Ответ. Похоже, вам нужно реструктурировать часть вашего кода, поскольку недостаточно чистого разделения между доступом к базе данных и пользовательским интерфейсом. Вы должны иметь возможность делать все фоновые работы в одном фрагменте кода и инкапсулировать результаты в какой-то объект - неясно, будет ли ваш объект backendInterface работать для этого или нет. Затем задача может выполнять работу с базой данных и возвращать этот объект, а обработчик 'onSucceeded' может отображать пользовательский интерфейс, настраивая его из объекта. Когда вы работаете, курсор тривиален. –

ответ

3

Вы, наверное, не нужно Service здесь: вам просто нужно Task.

Метод call() - это метод, выполняемый на фоновом потоке. Он должен выполнять работу, которая занимает много времени (например, подключение к базе данных и получение данных от нее), и не должен выполнять любой пользовательский интерфейс, так как изменения в пользовательском интерфейсе должны составлять в приложении FX Нить. Причина, по которой вы получаете исключение, заключается в том, что вы создаете и показываете Stage из фонового потока.

Итак, основная идея состоит в том, чтобы задача получала данные из базы данных и возвращала их; затем используйте обработчик onSucceeded для задания отображения пользовательского интерфейса, используя результаты задачи. (Обработчик onSucceeded выполнен на потоке приложения FX, что позволяет безопасно изменять интерфейс пользователя.)

Я не знаю точно, как реализованы ваши классы и т. Д., Но может работать что-то в следующих строках. Важно то, что вы ничего не делаете в фоновом потоке, который взаимодействует с пользовательским интерфейсом.

@FXML 
public void loginButtonPress(ActionEvent event) { 

    if ((! username.getText().isEmpty()) || (! password.getText().isEmpty())) { 

     loginLabel.setText("Please enter data in the fields below"); 

    } else { 

     // I assume you want these values before you set them to "-----", no??? 
     final String uName = username.getText(); 
     final char[] pw = password.getText().toCharArray(); 

     username.setText("-----"); 
     password.setText("-----"); 

     // create task for retrieving data: 

     Task<BackendInterface> loadDataTask = new Task<BackendInterface>() { 

      @Override 
      public BackendInterface call() throws Exception { 

       BackendInterface backendInterface = new BackendInterface(uName, pw); 
       if (backendInterface.openConnection()) { 

        if (backendInterface.getAllStudents() && 
         backendInterface.getAllConsultations() && 
         backendInterface.getCourses() && 
         backendInterface.getConsultationCategories() && 
         backendInterface.getConsultationPriorities()) { 

         return backendInterface ; 
        } 
       } 

       // maybe throw an exception here, depending on your requirements... 
       return null ; 
      } 

     }; 

     // show UI on task completion: 

    loadDataTask.setOnSucceeded(e -> { 

     BackendInterface backendInterface = loadDataTask.getValue(); 

     if (backendInterface == null) { 
      // something went wrong... bail, or probably show error message... 
      return ; 
     } 

     FXMLLoader loader1 = new FXMLLoader(); 
     loader1.setLocation(getClass().getResource("/main/res/dashboard.fxml")); 
     Parent p = loader1.load(); 
     DashboardController controller = loader.getController(); 
     controller.setBackendInterface(backendInterface); 

     Stage stage = new Stage(); 
     stage.setScene(new Scene(p)); 
     stage.show(); 

     // etc etc with your Images, etc (not sure why this isn't done in DashboardController though...) 

     // set cursor back to default: 
     loginLabel.getScene().setCursor(Cursor.DEFAULT); 
    }); 

    loadDataTask.setOnFailed(e -> { 
     // show error message or otherwise handle database exception here 
    }); 

    // set cursor to WAIT: 
    loginLabel.getScene().setCursor(Cursor.WAIT); 

    // and run task in a background thread: 
    Thread t = new Thread(loadDataTask); 
    t.start(); 

} 
+0

James_D, вы легенда, боролись с этим несколько дней. Возвращаемыми значениями для методов базы данных были все логические значения, указывающие, работали ли методы базы данных или была ли ошибка. Поэтому я изменил тип данных в методе Task и call() на Boolean, при этом метод вызова возвратил значение вызова этих методов подключения к базе данных. Затем при загрузке страницы панели мониторинга, если это было правдой. Курсор изменится на WAIT и снова на DEFAULT: D – Philayyy

 Смежные вопросы

  • Нет связанных вопросов^_^