I am programming a GUI with JavaFx and I don't really know how to connect everything.
I have a Main Window and within the main window I have a button that opens another window which allows me to pick a customer.
Now I already struggle with giving the information of the picked customer back to the Main Window because I don't have a return method to open it.
I open it this way:
public void openSecondWindow(ActionEvent event) {
Parent root;
try {
root = FXMLLoader
.load(getClass().getResource("....secondWindow.fxml"));
Stage stage = new Stage();
stage.setTitle("Second Window");
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.show();
I don't initalize the Second Window myself and I don't have an object like
SecondWindow sc = new SecondWindow(); so I could call something like sc.getCustomer.
Probably it's easy to solve but I'm lacking the right vocabulary to google it.
To close the question, my solution looked like this:
SecondWindow sc = loader.getController();
And then I could call my modified Method like scOpenWithData(int id){....}
sc.scOpenWithData(123);
In the whole:
Parent root;
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("....secondWindow.fxml"));
root = loader.load();
SecondWindow sc = loader.getController();
Stage stage = new Stage();
stage.setTitle("Second Window");
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
sc.scOpenWithData(123);
stage.showAndWait();
Related
How to close a secondary stage, which is utilized as an auxiliary window? So I have a primaryStage used as the UI platform and occasionally I need to open a secondary window which is pretty straight forward, but to close it in a method is not clear.
Here's how secondary stage is engaged:
val ivbox = new VBox(children = new Label("Create New Album"))
val stackpane = new StackPane()
sp.children = Seq(ivbox)
val secondstage = new Stage() {
title = "second stage"
scene = new Scene(stackpane, 450, 150) {
stylesheets += getClass.getResource("uistyle.css").toExternalForm
}
x = myproto.stage.x.value + 200 // position in relation to primaryStage / scene
y = myproto.stage.y.value + 100 //
}
In JavaFX I found this clip:
private void closeButtonAction(){
Stage stage = (Stage) closeButton.getScene().getWindow();
stage.close();
}
Not clear how to come by in ScalaFx?
There is no difference in ScalaFX. You close stage using close(). Code is exactly the same in ScalaFX:
stage.close()
I have three COMBO BOXES in my window. It is a family tree application I am developing using JavaFX and SQLIte. Each person has Father, Mother and Spouse info which can be selected from ComboBoxes. ComboBoxes are loaded with data from database.
It takes a while to load these three combo boxes. Actually different delays for different combo boxes.
If I click on a combo box while it is not ready or another combo box is not ready, the screen turns white until the other combo box is ready.
I want to prevent user input on entire window from Mouse, Keyboard and Buttons until entire window is ready to accept user's input. Until then I want change the cursor to something like STOP.
How to do this? Any suggestions?
Thanks,
Hornigold
Run a Task<ObservableList<SomeType>> (or a Task returning some object containing more than a single list) on a seperate Thread. Before starting the thread you disable the root and set the cursor for the scene and when the task finishes you initialize the GUI with the results and reenable the scene again:
#Override
public void start(Stage primaryStage) {
Button loadButton = new Button("load");
ComboBox<String> combo = new ComboBox<>();
VBox root = new VBox(combo, loadButton);
Scene scene = new Scene(root);
loadButton.setOnAction(evt -> {
root.setDisable(true);
// save old cursor to restore after finishing the task
final Cursor oldCursor = root.getScene().getCursor();
scene.setCursor(Cursor.WAIT);
Task<ObservableList<String>> task = new Task<ObservableList<String>>() {
#Override
protected ObservableList<String> call() throws Exception {
ObservableList<String> result = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++) {
result.add(Integer.toString(i));
}
// simulate delay
Thread.sleep(5000);
return result;
}
};
task.setOnSucceeded(e -> {
// use results of task in the GUI
combo.setItems(task.getValue());
// restore cursor and reenable scene
root.setDisable(false);
scene.setCursor(oldCursor);
});
task.setOnFailed(e -> {
// todo: handle exception in Task.call
});
Thread thread = new Thread(task);
thread.start();
});
primaryStage.setScene(scene);
primaryStage.show();
}
Basically I have a method to load an Image from database into an imageView and a second method to change the image I'm sucessfully running both methods without getting an exception but after the setImage in changeImage() method what do I need to update and how (scene,stage) is it possible at all. I know that there is no method like repaint() in swing in javafx, so how do I approach this ?
public class MainMenuController implements Initializable {
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
private AnchorPane stck1;
#FXML
private AnchorPane openSecondWindow(ActionEvent event) throws Exception {
GUIController ctrl = new GUIController();
Stage stage = new Stage();
setStck1((AnchorPane) FXMLLoader.load(InteractiveFictionGame2.class.getResource("GUI.fxml")));
ImageView img_1 = new ImageView(ctrl.loadImg().getImage());
img_1.setPreserveRatio(true);
img_1.setSmooth(true);
img_1.setCache(true);
getStck1().getChildren().add(img_1);
Scene scene = new Scene(getStck1());
stage.setTitle("Interactive Fiction Game");
stage.setScene(scene);
stage.setFullScreen(true);
// stage.sizeToScene();
stage.show();
return getStck1();
}
public class GUIController implements Initializable {
#FXML
private TabPane tb1;
/**
* Initializes the controller class.
*
* #param url
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private ImageView img_1;
public ImageView loadImg() {
try {
con = DriverManager.getConnection(host, unm, pswrd);
stmnt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
rs = stmnt.executeQuery(SQL);
rs.next();
fis = rs.getBinaryStream(4);
imgt = javax.imageio.ImageIO.read(fis);
Image newImg = SwingFXUtils.toFXImage(imgt, null);
img_1 = new ImageView();
img_1.setImage(newImg);
rs.close();
stmnt.close();
con.close();
} catch (Exception e) {
System.out.println("Not working");
}
return img_1;
}
public void changeImage() {
..
fis = rs.getBinaryStream(1);
imgt = javax.imageio.ImageIO.read(fis);
Image newImg = SwingFXUtils.toFXImage(imgt, null);
img_1.setImage(newImg);
...
} catch (Exception e) {
System.out.println("Not working");
}
return img_1;
}
Your Issue
If you have a member node in your controller which you inject using #FXML, you should never create a new object instance using a new constructor and assign that new object to your existing reference. Instead just use the object which FXML created for you.
You have:
#FXML
private ImageView img_1;
That's fine.
Then in loadImg, you have:
img_1 = new ImageView();
img_1.setImage(newImg);
That is bad.
You already have an ImageView which the FXMLLoader created for you when you loaded your FXML document. The FXML Loader then assigned that ImageView to your img_1 reference because you used an #FXML annotation.
How to Fix it
So all you need to do is to stop creating new ImageViews and only write:
img_1.setImage(newImg);
And you are done.
Why it works
The Image property of ImageView is an observable property. The JavaFX system observes the Image property for any changes and if it changes, automatically updates the image displayed on the screen for the ImageView. You don't need to perform any repaint call (there is no such repaint routine to call in any case).
Background Reading
If you want to understand the JavaFX scene graph architecture better, read the Oracle tutorial on it:
Working with the JavaFX Scene Graph.
Some Tips
You can create a JavaFX image directly from an InputStream, you don't need to use ImageIO and SwingFXUtils for this task.
You can use a Task to communicate with a database and your application may be more responsive.
It is probably simpler to read the image from a file or over http rather than from a database.
Disclaimer
Besides the issue pointed out here, there may be other errors in code you have not provided which may prevent you from getting your application to work as you wish.
Java I graduate here:
In my JavaFX term project I had to update an imageView object upon a setOnAction Event (clicking a button). This allowed the program user to click through a series of pictures.
The following worked great:
First create your Image and ImageView instances:
Image imageObject = new Image();
ImageView imageViewObject = new ImageView();
Then down in the code a button event causes the (next) image to be assigned and updated as follows:
btn.setOnAction(e -> {
imageIndex++;
imageFilename = imageNamesArray.get(imageIndex);
imageObject = new Image(imageFilename);
imageViewObject.setImage(imageObject);
}
Note: The filename(s) in my project are jpg file (names) saved as String elements in an ArrayList(). The button click also increments the array index to the next jpg filename (and path or URL) and the new image would appear.
So as in the aforementioned answer you only create one ImageViewObject but you do reassign a new image to the image object "imageObject" each time.
I am trying to load my computer folder images into a wall of thumbnails. I read on a thread from another forum that ImageView "url" instance variable does not support system paths. I tried with the solution there, but it throws an exception: java.lang.OutOfMemoryError: Java heap space as it keeps reading the file.
another problem is it keeps giving me warning of using package javafx.ext -> SwingUtils.toFXImage method.
I have also tried to input the URL like that:
"file://localhost//Users/USER/Pictures/Camera/test/1.JPG"
I tried to display a number of images, but it always only displays 3 to 4 images.
I checked with the error function given from ImageView, it does not indicate that the reading of my images encountered an error.
Are there any alternatives?
Code
function load() {
println("RUNTIME {Runtime.getRuntime().maxMemory()}");
System.gc();
Runtime.getRuntime().freeMemory();
//MAC Folder PATH
var path: String = "/Users/username/Pictures/camera/test/1.JPG";;
var file: File = new File(path);
//http://download.oracle.com/docs/cd/E17802_01/javafx/javafx/1.3/docs/api/javafx.ext.swing/javafx.ext.swing.SwingUtils.html
//public toFXImage(image: java.awt.image.BufferedImage) : Image
//Creates a JavaFX Image from a BufferedImage.
img = SwingUtils.toFXImage(ImageIO.read(file));
}
It is not clear exactly what you are trying to do. If you are talking about JavaFX 2.0, the following code works. If you are loading a lot of images and need to conserve memory, you only have to create enough ImageView's for the number you want to display at one time. Then as you page through the images, you can swap out the Image object contained in the ImageView.
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World");
StackPane root = new StackPane();
Scene scene = new Scene(root, 300, 250);
File file = new File("/System/Library/CoreServices/loginwindow.app/Contents/Resources/LogOut.png");
Image image = new Image(file.toURI().toString());
ImageView iv = new ImageView(image);
root.getChildren().add(iv);
primaryStage.setScene(scene);
primaryStage.show();
}
its simple: open a image with a browser and copy the whole url of the pic and paste it as a parameter of the Image object. DO NOT REMOVE "file///:" because this makes the image loadable. you will get your other logic from there. happy coding e.g
Image image = new Image("file:///C:/Users/Nigel/Desktop/my_image.png");
ImageView imgview = new ImageView(image);
None of the previous answers worked for me. However, this one, which I saw a while back but can't find an original link from, works flawlessly.
#Override
public void start(Stage primaryStage) throws Exception {
//create a pane to hold the image views
Pane pane = new HBox(10);
pane.setPadding(new Insets(5,5,5,5));
//create the image to be used!
Image image = new Image("/Content/vortex.jpg");
//set some custom properties and add an image
ImageView imageView = new ImageView(image);
imageView.setFitHeight(100);
imageView.setFitWidth(100);
pane.getChildren().add(imageView);
//add the second image view with this image and no custom properties
pane.getChildren().add(new ImageView(image));
ImageView imageView2 = new ImageView(image);
imageView2.setRotate(45);
pane.getChildren().add(imageView2);
Scene scene = new Scene(pane, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
The key is that when you create the image, START the path with a '/' (ie: "/Content/vortex.jpg"). Note that in this setup, the root folder is the src folder in most IDEs.
sorry if my answer came a little bit late but that was my approach and it works 100% wherever you are putting your file
1.assign your image to File
2.parse the File to URI
3.assign the uri.toString() to the image
ex code:
File imageFile = new File("path/to/image/outside/your/jar/awesomeless.jpg");
String fileLocation = imageFile.toURI().toString();
Image fxImage = new Image(fileLocation);
or you can simply put it all together like this:
Image fxImage = new Image(new File("path/.../awesomemore.jpg").toURI().toString());
and please if anyone knows a better approach let us know!
Another solution is to pass the InputStream into the Image class constructor; and is working...
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World");
StackPane root = new StackPane();
Scene scene = new Scene(root, 300, 250);
java.io.FileInputStream fis = new FileInputStream("/System/Library/CoreServices/loginwindow.app/Contents/Resources/LogOut.png");
ImageView iv = new ImageView(new Image(fis));
root.getChildren().add(iv);
primaryStage.setScene(scene);
primaryStage.show();
}
I am new to the whole QT things, so please bear with me :-)
I am working on a program that has a QMainWindow with a couple of menu options. I then use QActions to call functions to do something with the menu options.
I have put together a QDialog, which shows when called directly, but not when called through the QAction dialog.
Code:
Constructor Code
View::View(QWidget *parent):QMainWindow(parent)
{
QWidget *topFiller = new QWidget;
topFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(5);
layout->addWidget(topFiller);
setLayout(layout);
createActions();
createMenus();
change(); //Works but displayed as a separate window
QWidget::setWindowTitle( "Stock Editor");
setMinimumSize(300, 300);
resize(680, 520);
}
QAction Snippet
changeact = new QAction(tr("&Change"), this);
connect(saveact, SIGNAL(triggered()), this, SLOT(change()));
Change() Function
void View::change()
{
QDialog *dialogWin = new QDialog(this);
dialogWin->setWindowTitle("Add Item");
QFormLayout *formLayout = new QFormLayout();
QLineEdit *barcodeLineEdit = new QLineEdit;
QLabel *barcodeLabel = new QLabel("Barcode");
QLineEdit *descLineEdit = new QLineEdit;
QLabel *descLabel = new QLabel("Description");
QSpinBox *stockSpinBox = new QSpinBox;
QLabel *stockLabel = new QLabel("Stock");
QSpinBox *priceSpinBox = new QSpinBox;
QLabel *priceLabel = new QLabel("Price");
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel);
// connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
// connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
formLayout->addRow(barcodeLabel, barcodeLineEdit);
formLayout->addRow(descLabel, descLineEdit);
formLayout->addRow(stockLabel, stockSpinBox);
formLayout->addRow(priceLabel, priceSpinBox);
QVBoxLayout *mainLayout = new QVBoxLayout(dialogWin);
mainLayout->addLayout(formLayout);
mainLayout->addWidget(buttonBox);
dialogWin->setLayout(mainLayout);
dialogWin->activateWindow();
dialogWin->show();
}
How can I get the box to appear on top of the Main Window when called?
Thanks for the assistance