JavaFx - Progress Bar does not show up - javafx

When I call my LoadingBar class from another class, the stage pops up but the actual bar does not.. what am I doing wrong? I also added where I call the class as well.
When I call my LoadingBar class from another class, the stage pops up but the actual bar does not.. what am I doing wrong? I also added where I call the class as well.
public class LoadingBar extends Stage {
private ProgressBar progressBar;
public LoadingBar() {
progressBar = new ProgressBar(-1.0);
setScene(new Scene(progressBar));
setResizable(false);
initStyle(StageStyle.TRANSPARENT);
show();
}
}
public class JarParser {
public enum JarTreeLevel {JAR, PACKAGE, CLASS, METHOD};
private BottomUp bottomUp;
private TreeItem<String> rootImport, rootPackage, rootClass, classMethod;
private final Map<TreeItem<String>, JarTreeLevel> levelMap = new HashMap<TreeItem<String>, JarTreeLevel>();
private String currentPackage, lastPackage, currentClass, currentClassFull, currentMethod;
public JarParser(BottomUp bottomUp) {
this.bottomUp = bottomUp;
rootImport = new TreeItem<String>();
levelMap.put(rootImport, JarTreeLevel.JAR);
}
public TreeItem<String> populateJarTree(JarFile jarFile) throws Exception {
LoadingBar loadingBar = new LoadingBar();
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
if (entryName.endsWith(".class")) {
ClassNode classNode = new ClassNode();
InputStream classFileInputStream = jarFile.getInputStream(entry);
try {
ClassReader classReader = new ClassReader(classFileInputStream);
classReader.accept(classNode, 0);
} finally {
classFileInputStream.close();
}
describeClass(classNode);
}
}
loadingBar.close();
return rootImport;
}
}

You set the stage invisible?
public class LoadingBar extends Stage {
private ProgressBar progressBar;
public LoadingBar() {
progressBar = new ProgressBar(-1.0);
setScene(new Scene(progressBar));
setResizable(false);
initStyle(StageStyle.TRANSPARENT);//?! delete this!
show();
}

Try adding your progress bar to a group and then adding the group to the scene:
Group group = new Group();
group.getChildren().add(progressBar);
setScene(new Scene(group));
...

Related

How to attach event handlers to controller in MVC | JavaFX

I am having trouble understanding how to apply the mvc pattern with JavaFX.
Here are my questions with respect to the code below, since I need to follow the pattern given in the code:
a) How can I attach an event handler of the button which is present in my ViewA to the code in my ControllerA (specifically, attachEventHandlers() method). For example, I want my button to populate the comboBox in ViewA with the results of getModelItems() method from controller.
Note that the method getModelItems() is private.
b) I would have multiple buttons and event handlers in my view. How will I bind each one of them uniquely to the controller?
c) I want to invoke setName(String name) on my model in the controller, and the parameter I want to pass is the selected value on the comboBox in viewA. How can I achieve this?
Thank you so much for any help!
Below is the code referred in the description.
Controller:
import model.ModelA;
import view.ViewA;
public class ControllerA {
private ViewA view;
private ModelA model;
public ControllerA(ViewA view, ModelA model) {
//initialise model and view fields
this.model = model;
this.view = view;
//populate combobox in ViewB, e.g. if viewB represented your ViewB you could invoke the line below
//viewB.populateComboBoxWithCourses(setupAndRetrieveCourses());
this.attachEventHandlers();
}
private void attachEventHandlers() {
}
private String[] getModelItems() {
String[] it = new String[2];
it[0] = "0";
it[1] = "1";
return it;
}
}
Model:
public class ModelA {
private String name;
public Name() {
name = "";
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Name = " + name;
}
}
View:
import javafx.scene.layout.BorderPane;
//You may change this class to extend another type if you wish
public class ViewA extends BorderPane {
public BorderPane bp;
public ViewA(){
this.bp = new BorderPane();
ComboBox comboBox = new ComboBox();
Button button1 = new Button("Populate");
bp.setTop(button1);
bp.setBottom(comboBox);
}
}
Loader:
public class ApplicationLoader extends Application {
private ViewA view;
#Override
public void init() {
//create model and view and pass their references to the controller
ModelA model = new ModelA();
view = new ViewA();
new ControllerA(view, model);
}
#Override
public void start(Stage stage) throws Exception {
//whilst you can set a min width and height (example shown below) for the stage window,
//you should not set a max width or height and the application should
//be able to be maximised to fill the screen and ideally behave sensibly when resized
stage.setMinWidth(530);
stage.setMinHeight(500);
stage.setTitle("Final Year Module Chooser Tool");
stage.setScene(new Scene(view));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You add delegates to your ViewA to allow for access:
public class ViewA extends BorderPane {
ComboBox comboBox;
Button button1;
public ViewA(){
comboBox = new ComboBox();
button1 = new Button("Populate");
setTop(button1);
setBottom(comboBox);
}
// Add delegates for all functionality you want to make available through ViewA
public ObservableList<String> getItems() { return comboBox.getItems(); }
public void setOnButton1Action(...) { ... }
public void setOnButton2Action(...) { ... }
...
}
You can go as broad or as narrow as you like, based on how much you want to manage through ViewA.

Javafx : Domain objects to TreeView

I have three domain objects in my app as follows :
public class Workflow {
private String name;
private List<Sheet> sheets;
}
public class Sheet {
private String name;
private List<Task> tasks;
}
public class Task {
private String name;
}
All three are dependent as Workflow -> Sheet -> Task. My goal is to build TreeView so that it look like below :
-Workflow
|
- workflow name
-Sheets
|
- sheet name
- Tasks
|
- task name
So far, I have build a sample that builds more less what I expect, but it's not generic and 'automated' at all.
public class TreeViewSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tree View Sample");
Workflow w = setup();
TreeItem<String> rootItem = new TreeItem<String> ("Workflow");
rootItem.setExpanded(true);
TreeItem<String> item = new TreeItem<String> (w.getName());
rootItem.getChildren().add(item);
(...)
TreeView<String> tree = new TreeView<String> (rootItem);
StackPane root = new StackPane();
root.getChildren().add(tree);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
private Workflow setup(){
Workflow wflow = new Workflow();
wflow.setName("wflow name");
wflow.setSheets(Arrays.asList(new Sheet("sheet name", Arrays.asList(new Task("task name")))));
return wflow;
}
Can someone advise on how I can recursively go to my domain objects and build the TreeView as in my example ?
You have to create a common Model to all of you models(Workflow, Sheet,Task), since all have a String property, it is quite simple to create one. Let's suppose we have the following model:
public class Model {
private String name;
public Model(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return getName();
}
}
class Workflow {
private String name;
private List<Sheet> sheets = new ArrayList<>();
public Workflow(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<Sheet> getSheets() {
return sheets;
}
}
class Sheet {
private String name;
private List<Task> tasks = new ArrayList<>();
public Sheet(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<Task> getTasks() {
return tasks;
}
}
class Task {
private String name;
public Task(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
I kept there together all of your models, to see them better.
I see you don't use any .fxml file at your app, mine is with .fxml I recommend that you separate at least to separate the Main class from the Controller class, like:
public class Main extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
AnchorPane pane = loader.load();
primaryStage.setScene(new Scene(pane,800,600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Then the Controller class:
public class Controller implements Initializable {
#FXML
private TreeView<Model> treeView;
#Override
public void initialize(URL location, ResourceBundle resources) {
Workflow workflow = createWorkflow(); // This just sets up the models that you are using.
// You have to create a root in your case the "Workflow"
TreeItem<Model> root = new TreeItem<>(new Model(workflow.getName()));
// The foreach sheet you create a branch
workflow.getSheets().forEach(sheet -> {
TreeItem<Model> sheetBranch = new TreeItem<>(new Model(sheet.getName()));
// Then you have to add each branch to the root
root.getChildren().add(sheetBranch);
// Then foreach sheet you create a task item
sheet.getTasks().forEach(task -> {
TreeItem<Model> taskItem = new TreeItem<>(new Model(task.getName()));
// Then you have to add each task to its sheet parent
sheetBranch.getChildren().add(taskItem);
});
});
// Finally, you set the root for the TreeView. Of course this can be done right after instantiating the root.
treeView.setRoot(root);
}
// ------------------- Setup the model -----------------------
private Workflow createWorkflow() {
Workflow workflow = new Workflow("Workflow");
workflow.getSheets().addAll(createSheets());
return workflow;
}
private List<Sheet> createSheets() {
List<Sheet> sheets = new ArrayList<>();
IntStream.range(1, 10).forEach(value -> sheets.add(createSheet()));
return sheets;
}
private Sheet createSheet() {
Sheet sheet = new Sheet("Sheet" + new Random().nextInt(100)); // Random added to have different names
sheet.getTasks().addAll(createTasks());
return sheet;
}
private List<Task> createTasks() {
List<Task> tasks = new ArrayList<>();
IntStream.range(1, 5).forEach(value -> tasks.add(createTask()));
return tasks;
}
private Task createTask() {
return new Task("Task" + new Random().nextInt(100)); // Random added to have different names
}
}
Just in case if you need here is the .fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.tree.Controller">
<TreeView fx:id="treeView"/>
</AnchorPane>
If you don't know the depth of the TreeView you can create all of the branches or leaves using recursion. In this case it is much simpler to use two foreachs instead of creating a recursive method which builds the tree structure.
Lacking a common supertype providing a list of child items you need to use 1 different method/1 nested loop per object containing a list of sub objects, i.e.
private TreeItem<String> createWorkFlow(Workflow workflow) {
TreeItem<String> item = new TreeItem<>(workflow.getName());
for (Sheet sheet : workflow.getSheets()) {
item.getChildren().add(createSheet(sheet));
}
return item;
}
private TreeItem<String> createSheet(Sheet sheet) {
TreeItem<String> item = new TreeItem<>(sheet.getName());
for (Task task : sheet.getTasks()) {
item.getChildren().add(new TreeItem<>(task.getName());
}
return item;
}
Or
private TreeItem<String> createWorkFlow(Workflow workflow) {
TreeItem<String> workflowItem = new TreeItem<>(workflow.getName());
for (Sheet sheet : workflow.getSheets()) {
TreeItem<String> sheetItem = new TreeItem<>(sheet.getName());
for (Task task : sheet.getTasks()) {
sheetItem.getChildren().add(new TreeItem<>(task.getName()));
}
workflowItem.getChildren().add(sheetItem);
}
return item;
}
unless you want to use reflection.
To avoid this you could implement a interface with your types:
public interface Item<T extends Item<?>> {
String getName();
default List<T> getChildren() {
return null; // default for terminal object
}
}
which would allow you to simplify the creating of the TreeItems to
public static <T extends Item<?>> TreeItem<String> createItem(Item<T> item) {
TreeItem<String> treeItem = new TreeItem<>(item.getName());
List<T> children = item.getChildren();
if (children != null) {
for (Item<?> ci : children) {
treeItem.getChildren().add(createItem(ci));
}
}
return treeItem;
}

How to get reference to another controller - JavaFX

Let's say I've got 3 views and 3 controllers:
LogInController, LogInView
MainMenuController, MainMenuView
ListOfPatientsInternalMedicineController, ListOfPatientsInternalMedicineView.
An internalMedicineButtonClicked method change my scene to another (with some other content) but in this new scene, I want to have a button which allows me to go back to MainMenu (goBacktoMainMenuButtonClicked() method). And here occures my problem. How am I able to get reference to MainMenuController (the one which is corresponding with fxml file, created in LogInController) to fill setController() method.
public class LogInController {
MainMenuController mainMenuController = new MainMenuController();
#FXML
private JFXTextField logInTextField;
#FXML
private JFXButton logInButton;
#FXML
private JFXPasswordField passwordTextField;
#FXML
void logInButtonClicked(ActionEvent event) throws IOException {
LogInDAO logInDAO = new LogInDAO();
if(logInDAO.checkIfLoginAndPasswordIsCorrect(logInTextField.getText(),passwordTextField.getText()))
{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/MainMenu.fxml"));
Window window = logInButton.getScene().getWindow();
Stage stage = (Stage) window;
loader.setController(mainMenuController); // here i'm passing original controller corresponding with fmxl
stage.setScene(new Scene(loader.load()));
}
else
{
(...)
}
}
}
MainMenuCotroller class:
public class MainMenuController {
ContentOfPatientTableView patientTableViewModel = new ContentOfPatientTableView();
(..)
#FXML
void internalMedicineButtonClicked(ActionEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ListOfPatientsInternalMedicineView.fxml"));
Button button = (Button) event.getSource();
Scene scene = button.getScene();
Stage stage = (Stage) scene.getWindow();
loader.setController(new ListOfPatientsInternalMedicineController(patientTableViewModel));
stage.setScene(new Scene(loader.load()));
}
And ListOfPatientsInternalMedicineController class;
public class ListOfPatientsInternalMedicineController {
IPatientDAO patientDAO = new PatientDAO();
ContentOfPatientTableView patientTableViewModel;
public ListOfPatientsInternalMedicineController(ContentOfPatientTableView content) {
patientTableViewModel=content;
}
#FXML
public void goBacktoMainMenuButtonClicked(ActionEvent event)
{
FXMLLoader loader = new FXMLLoader(MainMenuController.class.getResource("/fxml/MainMenuView.fxml");
loader.setController(?????????); // Here if I will pass new MainController() i will create new instance, not this which is corresponding with fxml file. How am I able to refer to instance MainController created in LogInController ?
}
}
Consider using another model to represent the current view. You could implement this along the following lines:
public class ViewState {
private final ContentOfPatientTableView patientTableViewModel ;
private final ReadOnlyObjectWrapper<Parent> currentView = new ReadOnlyObjectWrapper<>();
private Parent logInView ;
private Parent mainMenuView ;
private Parent listOfPatientsMainMedicineView ;
public ViewState(ContentOfPatientTableView patientTableViewModel) {
this.patientTableViewModel = patientTableViewModel ;
}
public ReadOnlyObjectProperty<Parent> currentViewProperty() {
return currentView.getReadOnlyProperty();
}
public void showLogIn() {
if (logInView == null) {
try {
FXMLLoader loader = new FXMLLoader("/fxml/LogIn.fxml");
loader.setController(new LogInController(this));
logInView = loader.load();
} catch (IOException exc) {
// fatal...
throw new UncheckedIOException(exc);
}
}
currentView.set(logInView);
}
public void showMainMenu() {
// similarly...
}
public void showListOfPatientsMainMedicineView() {
// ...
}
}
Now your LogInController can do:
public class LogInController {
private final ViewState viewState ;
#FXML
private JFXTextField logInTextField;
#FXML
private JFXButton logInButton;
#FXML
private JFXPasswordField passwordTextField;
public LogInController(ViewState viewState) {
this.viewState = viewState ;
}
#FXML
void logInButtonClicked(ActionEvent event) {
LogInDAO logInDAO = new LogInDAO();
if(logInDAO.checkIfLoginAndPasswordIsCorrect(logInTextField.getText(),passwordTextField.getText()))
{
viewState.showMainMenu();
}
else
{
(...)
}
}
}
Similarly,
public class MainMenuController {
private final ViewState viewState ;
public MainMenuController(ViewState viewState) {
this.viewState = viewState ;
}
#FXML
void internalMedicineButtonClicked(ActionEvent event) throws IOException {
viewState.showListOfPatientsMainMedicineView();
}
}
and similarly for the other controller.
Note that you are instantiating each controller in ViewState, so just give that class access to each of the other models it may need.
Finally, you boot all this up with
public class MyApp extends Application {
#Override
public void start(Stage primaryStage) {
ViewState viewState = new ViewState(/* pass models here...*/);
viewState.showLogIn();
Scene scene = new Scene(viewState.currentViewProperty().get());
scene.rootProperty().bind(viewState.currentViewProperty());
primaryStage.setScene(scene);
primaryStage.show();
}
}

Update Buttons JavaFX when a selected process in TableView ends

I want to disable/enable the buttons under the BorderPane when a process in selected row ends.
I try this
downloadTable.getSelectionModel().getSelectedIndices().addListener(new ListChangeListener<Integer>() {
#Override
public void onChanged(Change<? extends Integer> c) {
int selectedIndex = downloadTable.getSelectionModel().getSelectedIndex();
if (downloadTable.getItems().get(selectedIndex).getStatus() == Download.DOWNLOADING) {
cancelButton.setDisable(false);
} else {
cancelButton.setDisable(true);
}
}
});
but it only works if I switch to an items (download) that is already ended.
What I want to do is to enable/disable buttons while an item is selected.
Thanks all
example of ended download with cancelButton that I want to disable
Maybe something like this can help you:
public class Main {
private Button someButton;
private TableView<?> downloadTable;
private void someMethod() {
//somecode
Callback<TableView<?>, TableRow<?>> baseFactory = downloadTable.getRowFactory();
downloadTable.setRowFactory( new CustomRowFactory<?>( someButton, baseFactory ) );
//somecode
}
}
public class CustomRowFactory<T> implements Callback<TableView<T>, TableRow<T>> {
private final Callback<TableView<T>, TableRow<T>> baseFactory;
private final Button someButton;
public CustromRowFactory( Button someButton, Callback<TableView<T>, TableRow<T>> baseFactory) {
this.someButton = somButton;
this.baseFactory = baseFactory;
}
#Override
public TableRow<T> call(TableView<T> tableView) {
final TableRow<T> row = baseFactory == null ? row = new TableRow<>() : row = baseFactory.call( tableView );
someButton.disableProperty().bind(
row.selectedProperty().and( row.getItem().statusProperty().isNotEquals(Download.DOWNLOADING) )
);
return row;
}
}
or insert binding in some of your TableCell implementation.

Javafx tableview edit method not call cellfactory

I try set edit cell on run program. Set table editable, cellfactory and other.
I can edit the cell, when clicked with the mouse. But the call edit() method of TableView does not create Textfield.
What have I missed?
public class Main extends Application {
TableView <TestClass> tableView;
TableColumn <TestClass, String> stringColumn;
TableColumn <TestClass, String> editColumn;
ObservableList<TestClass> items;
#Override
public void start(Stage primaryStage) throws Exception{
makeTestData();
tableView = new TableView();
tableView.setEditable(true);
stringColumn = new TableColumn<>("Col1");
editColumn = new TableColumn<>("Col2");
tableView.getColumns().addAll(stringColumn, editColumn);
stringColumn.setCellValueFactory(cell -> cell.getValue().stringProperty());
editColumn.setCellValueFactory(cell -> cell.getValue().editProperty());
editColumn.setCellFactory(TextFieldTableCell.<TestClass>forTableColumn());
tableView.setItems(items);
tableView.getSelectionModel().select(1);
tableView.getSelectionModel().focus(1);
tableView.edit(1, editColumn); // !!! not create textfield ???
BorderPane pane = new BorderPane();
pane.setCenter(tableView);
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public void makeTestData(){
items = FXCollections.observableArrayList(
new TestClass("str1", "edit1"),
new TestClass("str2", "edit2"),
new TestClass("str3", "edit3")
);
}
public class TestClass{
StringProperty string = new SimpleStringProperty();
StringProperty edit = new SimpleStringProperty();
public TestClass() {}
public TestClass(String string, String edit) {
this.string = new SimpleStringProperty(string);
this.edit = new SimpleStringProperty(edit);
}
public String getString() { return string.get();}
public StringProperty stringProperty() { return string; }
public void setString(String string) { this.string.set(string);}
public String getEdit() { return edit.get();}
public StringProperty editProperty() { return edit;}
public void setEdit(String edit) { this.edit.set(edit);}
}
}
yes im also getting this problem. the way i solved it is by putting the edit method call inside another fx thread.
Platform.runLater(() -> {
tableView.edit(row, editColumn);
});

Resources