diff --git a/Parallel_Fiji_Visualizer/src/main/java/edu/abc/berkeley/PWZGUI.java b/Parallel_Fiji_Visualizer/src/main/java/edu/abc/berkeley/PWZGUI.java index 9d94578..a22f2cf 100644 --- a/Parallel_Fiji_Visualizer/src/main/java/edu/abc/berkeley/PWZGUI.java +++ b/Parallel_Fiji_Visualizer/src/main/java/edu/abc/berkeley/PWZGUI.java @@ -1,11 +1,7 @@ package edu.abc.berkeley; import javax.swing.*; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableModel; -import ij.IJ; -import ij.ImagePlus; import ij.ImageStack; import java.awt.*; @@ -14,128 +10,73 @@ import java.io.File; -/* - * PREVIOUS MEETING DISCUSSION - * - Implement constructor that takes in this many arguments. (As shown below) - * ** PWZGUI(string, long, long, long, long, long, long, long, long, long, string) // filepath: string, Starting XYZ: long, ending XYZ: long, chunkSize: long, compressor: string - * CHANGES NEEDED TO BE MADE TO THE INTERFACE - * 1.) Crop Button. By brinding down the crop box, and positioning the widgets similarily to what is being shown in the XML diagrams that we discussed and promptly designed in diagrams.io - * --> Purpose: Cropping the file size of the zarr file we are trying to load by clicking the browse widget. - * 2.) Bring Down and adjust crop widget. Crop widget adjust to the XML formatting. (near/in front of starting and ending XYZ) - * 3.) Make Starting and ending XYZ, with chunk and default compressor seperate from each other - - * SIDE NOTE: (Describing by thought process.) - * 1. table for starting & ending XYZ - * 2. another for chunk and compressor. - * NOTE: Dont actually write default, just put compressor. - */ - -/* - * NOTES - * 1.) Fix interface to look similarily to the wxWidget diagram from diagram.io (DONE) - * 2.) [IMPORTANT] Integrate the JFileChooser to being used by this interface. (DONE) - * 3.) Seperate Chunk Size and Compressor into their own rows and cols fields. (DONE) - * SOMETHING TO NOTE: Dont actually write default, just put compressor. - * 4.) Re-adjusting the crop file size widget to a similar position referenced to the diagrams.io design. (DONE) - * 5.) Once implemented. Refactor and clean up code. Debugging and troubleshooting, so there is no unecessary code not being used. (DONE) - * - * IN-MEETING DISCUSSION - * - Proposing there be a coordinates class. So, instead of passing in 6 variables, we can pass two objects. Also for better readability to the programmer. - * - Easier to handly the X,Y, and Z coordinates. - * - Or we can keep both implementation, in case we may need to edit the specific values directly, possibly. - */ - - /* - * WRAP UP NOTES FOR THIS PROJECT - * - Constructor chnage variable names (like start_x to startX) - * - Change default_x to chunkX - * - Submit button to make changes. - * - Compressor default is lz4 not iz4. -*/ - -/* - C++ MAIN PROJECT. - * 1.) Release and get matlab working with C++ Qt (version, 6.2.4) - */ - -public class PWZGUI implements ActionListener{ - JFrame window; - JPanel textAreaPanel; - +public class PWZGUI extends JFrame implements ActionListener{ + /* + Panel contains three fields. + GroupLayout - Layout that groups these specific widgets together + searchField - JTextField + browse - JButton + filepathLabel - JLabel + */ + JPanel textfieldPanel; + GroupLayout textGroupLayout; // layout to group these widgets together + JTextField searchField; JButton browse; + JButton save; + JLabel filepathLabel; - // Crop check box - JLabel checkboxLabel; - JCheckBox checkBox; - - // Text box - JLabel textAreaLabel; - JTextArea textArea; - - // Starting and ending XYZ + // Setting up starting and ending coords JTable and JTablePanel. + JPanel coordsPanel; // panel containing JTable JTable table; JScrollPane scrollPane; - - // Data retrievers. - JLabel startCoordsLabel; - JLabel endCoordsLabel; - public String filepath; - public String compressor; - public long startX=0, startY=0, startZ=0; - public long endX=0, endY=0, endZ=0; + // Starting and ending XYZ + public long startX, startY, startZ; + public long endX, endY, endZ; - // Default values for the chunk sizes. - // Also, not reusing other starting and ending, so doesnt alter those variables. - public long chunkSizeX=256, chunkSizeY=256, chunkSizeZ=256; + // Chunk Table-related properties + JPanel chunkPanel; + JTable chunkTable; + JScrollPane chunkScrollPane; - JTable chunkTable; // Displaying the chunk sizes that are the default XYZ coords. (Though not expected to be changed.) - JScrollPane chunkScrollPane; // Scroll pane, to help display the default values of the chunk size. - - // Default Compressor + public long chunkSizeX, chunkSizeY, chunkSizeZ; + + // Compression labels and textFields and save JButton + JPanel compressorPanel; + GroupLayout compressorLayout; JLabel compressorLabel; + JComboBox compressorComboBox; - JButton saveChangesButton; // To save changes when + String filepath; + String compressor; - public PWZGUI() { - filepath = ""; - compressor = "lz4"; // Compressor is lz4. (NOTE, this value does not change by default whatsoever) + ImageStack imageStack; + Object[] cImageObj; + long bits; - // Starting coords - // Starting and ending coordinates are the expected values to be changed by user using the intercface. + + public PWZGUI(){ + filepath = ""; + compressor = "lz4"; // Default expected to be lz4. (Can be modiefied) + imageStack = null; startX = 0; startY = 0; startZ = 0; - - // Ending coords endX = 0; endY = 0; endZ = 0; - - // These default values are for the chunk sizes. That should remain as the default. chunkSizeX = 256; chunkSizeY = 256; chunkSizeZ = 256; - // Creates a thread. Lets be called and execute object in its own instance. - // Basically like its own thread. - Runnable r = new Runnable() { - @Override - public void run(){ - init(); - } - }; - - // Swing GUIs should be created and updated on the EDT - // http://docs.oracle.com/javase/tutorial/uiswing/concurrency - SwingUtilities.invokeLater(r); + initialize(); } - // This commented constructor was just an idea (will deleted, if this implementation may not be needed.) - // public PWZGUI(String filepath, Coords starting, Coords ending, String compressor){ - public PWZGUI(String filepath, long startX, long startY, long startZ, long endX, long endY, long endZ, long chunkSizeX, long chunkSizeY, long chunkSizeZ, String compressor){ + public PWZGUI(String filepath, ImageStack imageStack, long startX, long startY, long startZ, long endX, long endY, long endZ, long chunkSizeX, long chunkSizeY, long chunkSizeZ, String compressor, long bits){ this.filepath = filepath; this.compressor = compressor; + this.imageStack = imageStack; + this.cImageObj = imageStack.getImageArray(); // Is this how we want to pass in an array // Starting coords this.startX = startX; @@ -150,75 +91,120 @@ public PWZGUI(String filepath, long startX, long startY, long startZ, long endX, this.chunkSizeY = chunkSizeY; this.chunkSizeZ = chunkSizeZ; + this.bits = bits; + + initialize(); + } + + // This helps us create one instance to run the interface and call in each of the constructor + // Rather then having the same runnable in both, we create a function to call once to initialize the itnerface + public void initialize(){ // Creates a thread. Lets be called and execute object in its own instance. // Basically like its own thread. Runnable r = new Runnable(){ @Override public void run(){ - init(); + setup(); } }; SwingUtilities.invokeLater(r); } - private void init(){ - - window = new JFrame("Write Zarr"); - textAreaPanel = new JPanel(); // Main Panel - compressorLabel = new JLabel(); + private void setup(){ + setTitle("Write Zarr"); + setSize(800, 600); + // Grab the primary monitor screen dimension + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + // set the location of the frame to be in the center of the primary monitor + setLocation(screenSize.width/2 - getWidth()/2, screenSize.height/2 - getHeight()/2); - setupTextProperties(); // Handling button widget - setupCropProperties(); // User type text label, and text box field. - grabCoordinates(); - chunkAndCompressorProperties(); - saveChangesButton(); // update the table changes. - show(); // Handling adding all the widgets into the panel, while panel is being referred by the JFrame. + filepathProperties(); + startingEndingCoords(); + chunkProperties(); - // Setting window/jframe properties - window.setSize(600, 300); - window.setVisible(true); + pack(); } - // setting up text input properties. - private void setupTextProperties(){ + private void filepathProperties(){ + textfieldPanel = new JPanel(); + textGroupLayout = new GroupLayout(textfieldPanel); + textfieldPanel.setLayout(textGroupLayout); + + searchField = new JTextField(30); browse = new JButton("Browse"); - browse.setBounds(300, 5, 15, 15); + save = new JButton("Save"); + filepathLabel = new JLabel("Filepath"); + browse.addActionListener(this); + save.addActionListener(this); - // Handling text box and label - textAreaLabel = new JLabel("Filepath"); - textAreaLabel.setBounds(125, 5, 75, 15); + textGroupLayout.setAutoCreateGaps(true); + textGroupLayout.setAutoCreateContainerGaps(true); + + // Handles horizontally aligning are widgets and set the JPanel using GroupLayout to BorderLayout.NORTH + // sets the components grouped up together in the order we want them to be layed out. + textGroupLayout.setHorizontalGroup(textGroupLayout.createSequentialGroup() + .addComponent(filepathLabel) + .addComponent(searchField) + .addComponent(browse) + .addComponent(save) + ); + + // Handles vertically aligning are widgets and set the JPanel using GroupLayout to BorderLayout.NORTH + textGroupLayout.setVerticalGroup(textGroupLayout.createParallelGroup() + .addComponent(filepathLabel) + .addComponent(searchField) + .addComponent(browse) + .addComponent(save) + ); + + getContentPane().add(textfieldPanel, BorderLayout.NORTH); + } - textArea = new JTextArea(1, 6); - textArea.setBounds(215, 5, 75, 25); - textArea.setLayout(new FlowLayout()); + private void startingEndingCoords(){ + coordsPanel = new JPanel(new GridBagLayout()); + scrollPane = new JScrollPane(); + table = new JTable(10, 10); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.weightx = 1.0; + gbc.gridheight = 50; + gbc.gridwidth = 100; + gbc.fill = GridBagConstraints.HORIZONTAL; // This fills the interface with horizontally. Uncomment to see changes. + + update(); // update table + + coordsPanel.add(scrollPane, gbc); + + add(coordsPanel, BorderLayout.CENTER); } - // Setup cropping properties and JLabels corresponding to given check box widget. - private void setupCropProperties(){ - // User type text label, and text box field. - checkBox = new JCheckBox("Crop"); - checkBox.setBounds(160, 110, 75, 15); // Higher x is more to the right we go, lower value y is the more we go upwards. - checkBox.addActionListener(this); + private void chunkProperties(){ + chunkPanel = new JPanel(); + chunkPanel.setLayout(new BorderLayout()); + + compressorPanel = new JPanel(); + compressorLayout = new GroupLayout(compressorPanel); + compressorPanel.setLayout(compressorLayout); + + compressorLabel = new JLabel("Compressor"); + + String[] options = {"lz4", "zstd", "blosclz", "lz4hc", "zlib", "gzip"}; // dropdown menu options important to less important + compressorComboBox = new JComboBox(options); + + chunkData(); + compressorLayoutProperties(); + + chunkPanel.add(compressorPanel, BorderLayout.SOUTH); + add(chunkPanel, BorderLayout.SOUTH); } - - private void grabCoordinates(){ - // Labels allowing the user using the interface to know - startCoordsLabel = new JLabel("Start"); - endCoordsLabel = new JLabel("End"); - startCoordsLabel.setBounds(115, 90, 150, 150); - endCoordsLabel.setBounds(115, 110, 150, 150); - - Object[][] data = { - {startX, startY, startZ}, - {endX, endY, endZ}, - }; + // Update table + public void update(){ DefaultTableModel tableModel = new DefaultTableModel(){ @Override public boolean isCellEditable(int row, int col) { @@ -228,23 +214,30 @@ public boolean isCellEditable(int row, int col) { }; table = new JTable(tableModel); - table.setBounds(30, 30, 950, 950); + table.setBounds(235, 55, 115, 25); + + Object[][] data1 = {{startX, startY, startZ, endX, endY, endZ}}; // Adding columns to the table - tableModel.addColumn("X"); - tableModel.addColumn("Y"); - tableModel.addColumn("Z"); + tableModel.addColumn("Start X"); + tableModel.addColumn("Start Y"); + tableModel.addColumn("Start Z"); + tableModel.addColumn("End X"); + tableModel.addColumn("End Y"); + tableModel.addColumn("End Z"); + // Adding rows to the table. - for(int i = 0; i < data.length; i++) tableModel.addRow(data[i]); + for(int i = 0; i < data1.length; i++) tableModel.addRow(data1[i]); + scrollPane = new JScrollPane(table); scrollPane.setPreferredSize(new Dimension(250, 100)); // Actually setting how large the scroll pane is. - scrollPane.setBounds(172, 145, 250, 53); // Set where we want the JTable to be located in the JFrame. (Actually adjusts X and Y coords, and width and height) + scrollPane.setBounds(195, 55, 230, 53); scrollPane.setVisible(true); } - private void chunkAndCompressorProperties(){ + private void chunkData(){ // Setting up JTable specifically for Chunk Size. Object[] data = {chunkSizeX, chunkSizeY, chunkSizeZ}; DefaultTableModel model = new DefaultTableModel(); @@ -259,138 +252,98 @@ private void chunkAndCompressorProperties(){ model.addRow(data); chunkScrollPane = new JScrollPane(chunkTable); - // Original width = 210, height = 75 - chunkScrollPane.setPreferredSize(new Dimension(200, 100)); // Actually setting how large the scroll pane is. - chunkScrollPane.setBounds(170, 205, 255, 40); // Set where we want the JTable to be located in the JFrame. (This line is what updates the X, Y coordinates of widget) - chunkScrollPane.setVisible(true); - - compressorLabel.setText("Compressor: " + compressor); - compressorLabel.setBounds(180, 245, 130, 20); // NOTE: Higher the Y-axis lower widgets are positioned. Higher X axis, more to right the widgets positioned at. + chunkScrollPane.setPreferredSize(new Dimension(20, 90)); + chunkScrollPane.setLocation(200, 250); } - // Function to help organize the widget that handles the saving changes button. - private void saveChangesButton() { - saveChangesButton = new JButton("Save Changes"); - // saveChangesButton.setBounds(230, 55, 115, 25); - saveChangesButton.setBounds(235, 55, 115, 25); - - saveChangesButton.addActionListener(this); + private void compressorLayoutProperties(){ + compressorLayout.setAutoCreateGaps(true); + compressorLayout.setAutoCreateContainerGaps(true); + + + // Handling vertical and horizontally alignments in the windodw. + //horizontal alignment + compressorLayout.setHorizontalGroup( + compressorLayout.createSequentialGroup() + .addComponent(chunkScrollPane) + .addComponent(compressorLabel) + .addComponent(compressorComboBox) + ); + + //vertical alignment + compressorLayout.setVerticalGroup( + compressorLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(chunkScrollPane) + .addGap(20) + .addComponent(compressorLabel) + .addComponent(compressorComboBox) // dropdown menu with options + ); } - // show function, has keeps track of all the added components into JFrame -> going to -> JPanel -> widgets/checkboxes/etc. - private void show(){ - // NOTE: Add to panel - // Adding/packing all widgets into the panel, then referring that panel to the window. - // This portion adds in the user input interface interaction widgets into there own panel. - textAreaPanel.add(textAreaLabel); - textAreaPanel.add(textArea); - textAreaPanel.add(browse); - - textAreaPanel.setSize(50, 50); - - // Adds the other widgets into the JFrame window themselves. - window.add(checkBox); // UPDATE: Add this into the window frame. So we can move this widget however we see fit. - window.add(startCoordsLabel); - window.add(endCoordsLabel); - window.add(scrollPane); - window.add(chunkScrollPane); - window.add(compressorLabel); - - - window.add(saveChangesButton); - - - - - // Setting window/jframe properties - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.add(textAreaPanel, BorderLayout.CENTER); // This is how the TextArea and Labels, are being centered to the TOP of the interface. - window.pack(); - window.setLocation(new Point(900, 250)); // Hopefully puts the screen in the center of the monitor (vary depending on monitoring) - window.setVisible(true); - // window.setResizable(false); - } - - // Handling events happening with the interface. - @Override - public void actionPerformed(ActionEvent e){ - String filepath = textArea.getText(); - boolean emptyString = filepath.equals(""); // We dont want to browse anything that isn't a string. (Don't assume, everyone will type the right input. Just precautionary) - - // Just add this just in case, we do not want empty characters - if(emptyString) return; - - if(e.getSource() == browse){ - // We want to check if the checkbox is clicked before we compress that file. - if(checkBox.isSelected()) checkboxClicked(); - if(!emptyString) loadfile(filepath); - } - - // Updates and clears the table. - if(e.getSource() == saveChangesButton) updateTable(); + // Display error message window, if the file is not a zarr file. + private void errorMessage(){ + String message = "File must be a zarr file"; + JOptionPane.showMessageDialog(this, message); + return; } - // Loading filepath given. - private void loadfile(String filepath){ - // File Handling stuff. (Referenced PWZ.java) - // PWZC pwzc = new PWZC(); + // Load new/selected file. + private void loadfile(){ JFileChooser chooser = new JFileChooser(); chooser.setApproveButtonText("Save"); chooser.setDialogTitle("Save as Zarr"); File file = null; - int returnValue = chooser.showOpenDialog(null); - + int returnValue = chooser.showDialog(null, "Save"); if(returnValue == JFileChooser.APPROVE_OPTION) file = chooser.getSelectedFile(); + if(file == null) return; - ImagePlus cImagePlus = IJ.getImage(); - - if(cImagePlus == null) return; + this.filepath = file.getPath(); + searchField.setText(this.filepath); // updates text area. + } - ImageStack cImageStack = cImagePlus.getImageStack(); - Object[] cImageObj = cImageStack.getImageArray(); + // helper function to check if file extension has .zarr + private boolean checkExtension(String path){ + String extension = ""; + int i = path.lastIndexOf('.'); + if(i != -1) extension = path.substring(i+1); - int bits = cImageStack.getBitDepth(); // May need to get bits another way - String fileName = file.getPath(); - int x = cImageStack.getWidth(); - int y = cImageStack.getHeight(); - int z = cImageStack.getSize(); + if(extension.equals("zarr")) return true; - // pwzc.parallelWriteZarr(fileName, cImageObj, 0, 0, 0, y, x, z, 256, 256, 256, 1, "lz4", 1, bits); + return false; } - - private void checkboxClicked(){ System.out.println("[DEBUGGING]: Check Box Clicked!"); } // Does smthing when this check box is clicked. - - // When "Save Changes" button is clicked, the given inputted information is updated - // Little redundant, for now was trying to get interface working with updating the JTable. - private void updateTable(){ - startX = 123; - startY = 456; - startZ = 890; - - endX = 1024; - endY = 1036; - endZ = 1048; - - chunkSizeX = 256; - chunkSizeY = 256; - chunkSizeZ = 256; - - table.getModel().setValueAt(startX, 0, 0); - table.getModel().setValueAt(startY, 0, 1); - table.getModel().setValueAt(startZ, 0, 2); - table.getModel().setValueAt(endX, 1, 0); - table.getModel().setValueAt(endY, 1, 1); - table.getModel().setValueAt(endZ, 1, 2); + // "Submit" changes when "Save" clicked then updates interface. + private void submit(){ + if(!checkExtension(this.filepath) && !checkExtension(searchField.getText())) { + errorMessage(); // Checks if the file is a zarr file before we save and update + return; // Do not want to continue so leave function. (OutofBounds error will occur, if this does not leave function call) + } - chunkTable.getModel().setValueAt(chunkSizeX, 0, 0); - chunkTable.getModel().setValueAt(chunkSizeY, 0, 1); - chunkTable.getModel().setValueAt(chunkSizeZ, 0, 2); + // Manually adding inputs into the charts. Maybe a better way, but for now. + startX = (long) table.getModel().getValueAt(0, 0); + startY = (long) table.getModel().getValueAt(0, 1); + startZ = (long) table.getModel().getValueAt(0, 2); + endX = (long) table.getModel().getValueAt(1, 0); + endY = (long) table.getModel().getValueAt(1, 1); + endZ = (long) table.getModel().getValueAt(1, 2); + + chunkSizeX = (long) chunkTable.getModel().getValueAt(0, 0); + chunkSizeY = (long) chunkTable.getModel().getValueAt(0, 1); + chunkSizeZ = (long) chunkTable.getModel().getValueAt(0, 2); + + bits = imageStack.getBitDepth(); + + PWZC pwzc = new PWZC(); + + pwzc.parallelWriteZarr(filepath, cImageObj, startX, startY, startZ, endX, endY, endZ, chunkSizeX, chunkSizeY, chunkSizeZ, 1, compressor, 1, bits); } - - // For testing and debugging the interface with a main method. - // public static void main(String[] args) { new PWZGUI(); } -} \ No newline at end of file + + @Override + public void actionPerformed(ActionEvent e){ + if(e.getSource() == browse) loadfile(); // We want to check if the checkbox is clicked before we compress that file. + if(e.getSource() == save) submit(); // Updates the changes to interface. + } +}