I'm trying to implement a Barcode Reader for a CK71 ATEX Intermec Scanner. The operating system is Windows Embedded Handheld 6.5 and as JVM I'm using phoneME Personal Profile. I did install the DC_Java_WM6_Armv4i.cab (see the picture)
When I run the code below I get the following error: ITCScan failed to load. java.lang.UnsatisfiedLinkError: no ITCScan.dll in java.library.path
How can I fix this error? I've tried everything.
Note that before, I was using CreME JVM and everything was working fine. I gave up CreME when my 30 days evaluation version expired.
The content of the .lnk file (instead of myProject.MainClass are the real names of course):
255#"\Program Files\pMEA PP\bin\cvm.exe" "-Xopt:stdioPrefix=\My Documents,useConsole=false" -cp "\My Documents\Trasabilitate.jar;\My Documents\DataCollection.jar" myProject.MainClass
Here is the complete code:
/*
* BarcodeSample.java
*
* COPYRIGHT (c) 2004 INTERMEC TECHNOLOGIES CORPORATION, ALL RIGHTS RESERVED
*/
import java.awt.*;
import com.intermec.datacollection.*;
/**
* This sample demonstrates using the BarcodeReader class to
* read barcode data into a text field.
*/
public class BarcodeSample extends Frame implements BarcodeReadListener
{
BarcodeReader bcRdr;
TextField txtFieldData;
Button btnClose;
Label labelStatus;
public BarcodeSample(String aTitle)
{
super(aTitle);
initComponents();
try
{
bcRdr = new BarcodeReader();
bcRdr.addBarcodeReadListener(this);
// Starts asynchronous barcode read
bcRdr.threadedRead(true);
}
catch (BarcodeReaderException e)
{
System.out.println(e);
labelStatus.setText(e.getMessage());
//*****
//* Since m_labelStatus was not initialized with text,
//* doLayout() is required on some platforms in order
//* to show the new label text for the first setText()
//* call.
//*****
doLayout();
}
}
private void initComponents()
{
setLayout(new FlowLayout());
txtFieldData = new TextField(20);
add(txtFieldData);
btnClose = new Button("Close");
add(btnClose);
labelStatus = new Label();
add(labelStatus);
btnClose.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(java.awt.event.ActionEvent e)
{
exitApp();
}
});
btnClose.addKeyListener(new java.awt.event.KeyListener() {
public void keyPressed(java.awt.event.KeyEvent e) {
if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER)
{
exitApp();
}
}
public void keyReleased(java.awt.event.KeyEvent e) {}
public void keyTyped(java.awt.event.KeyEvent e) {}
});
}
/**
* This method is invoked when the BarcodeReadEvent occurs.
*/
public void barcodeRead(BarcodeReadEvent aBarcodeReadEvent)
{
/**
* Uses EventQueue.invokeLater to ensure the UI update
* executes on the AWT event dispatching thread.
*/
final String sNewData = aBarcodeReadEvent.strDataBuffer;
EventQueue.invokeLater(new Runnable() {
public void run() {
// Displays the scanned data in the text field
txtFieldData.setText(sNewData);
}
});
}
public void exitApp()
{
if (bcRdr != null)
bcRdr.dispose(); // Release system resources used by BarcodeReader
setVisible(false);
dispose(); // Dispose the frame
System.exit(0);
}
public static void main(String[] args)
{
final BarcodeSample asyncReader =
new BarcodeSample("Barcode Sample");
asyncReader.addWindowListener(new java.awt.event.WindowAdapter()
{
public void windowClosing(java.awt.event.WindowEvent e)
{
asyncReader.exitApp();
};
});
asyncReader.setVisible(true);
}
}
I finally got it to work. I found out what my java path was using the snippet here (I'll post it below in case something happens to the link):
Properties p = System.getProperties();
Enumeration keys = p.keys();
while (keys.hasMoreElements()) {
String key = (String)keys.nextElement();
String value = (String)p.get(key);
System.out.println(key + ": " + value);
}
And then I added my ITCScan.dll to the folder where java.library.path was set (in my case, \ProgramFiles\pMEA PP\bin.
I don't know if this is the most elegant solution, but it works. Hope it'll help somebody someday.
Related
I'm practicing Swing and I coded a download progress bar to download an image when the user presses the "Start download" button. The download works. The problem is that in my Terminal, I can see that the same event (propertyChange) is being fired multiple times, the number of times increasing with every subsequent download. I've debugged my code with checkpoints, but I'm still not sure why this is happening.
To be more specific, in my Terminal I'm seeing something like
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
when I expect to see "...100% completed" only once. The number of "...100% completed" that is displayed accumulates with every download. I'm not sure if this is affecting the performance of my download, but I'm wondering why it's happening.
ProgressBar.java:
package download_progress_bar;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ProgressBar {
private JFrame frame;
private JPanel gui;
private JButton button;
private JProgressBar progressBar;
public ProgressBar() {
customizeFrame();
createMainPanel();
createProgressBar();
createButton();
addComponentsToFrame();
frame.setVisible(true);
}
private void customizeFrame() {
// Set the look and feel to the cross-platform look and feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) {
System.err.println("Unsupported look and feel.");
e.printStackTrace();
}
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
private void createMainPanel() {
gui = new JPanel();
gui.setLayout(new BorderLayout());
}
private void createProgressBar() {
progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true); // renders a progress string
}
private void createButton() {
button = new JButton("Start download");
}
private void addComponentsToFrame() {
gui.add(progressBar, BorderLayout.CENTER);
gui.add(button, BorderLayout.SOUTH);
frame.add(gui);
frame.pack();
}
// Add passed ActionListener to the button
void addButtonListener(ActionListener listener) {
button.addActionListener(listener);
}
// Get progress bar
public JProgressBar getProgressBar() {
return progressBar;
}
// Enable or disable button
public void turnOnButton(boolean flip) {
button.setEnabled(flip);
}
}
Downloader.java:
package download_progress_bar;
import java.net.*;
import java.io.*;
import java.beans.*;
public class Downloader {
private URL url;
private int percentCompleted;
private PropertyChangeSupport pcs;
public Downloader() {
pcs = new PropertyChangeSupport(this);
}
// Set URL object
public void setURL(String src) throws MalformedURLException {
url = new URL(src);
}
// Add passed PropertyChangeListener to pcs
public void addListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void download() throws IOException {
// Open connection on URL object
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Check response code (always do this first)
int responseCode = connection.getResponseCode();
System.out.println("response code: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
// Open input stream from connection
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
// Open output stream for file writing
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("cat.jpg"));
int totalBytesRead = 0;
//int percentCompleted = 0;
int i = -1;
while ((i = in.read()) != -1) {
out.write(i);
totalBytesRead++;
int old = percentCompleted;
percentCompleted = (int)(((double)totalBytesRead / (double)connection.getContentLength()) * 100.0);
pcs.firePropertyChange("downloading", old, percentCompleted);
System.out.println(percentCompleted); // makes download a bit slower, comment out for speed
}
// Close streams
out.close();
in.close();
}
}
}
Controller.java:
package download_progress_bar;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import java.awt.event.*;
import java.util.List;
import java.net.*;
import java.io.*;
import java.beans.*;
public class Controller {
private ProgressBar view;
private Downloader model;
private JProgressBar progressBar;
private SwingWorker<Void, Integer> worker;
public Controller(ProgressBar theView, Downloader theModel) {
view = theView;
model = theModel;
progressBar = view.getProgressBar();
// Add button listener to the "Start Download" button
view.addButtonListener(new ButtonListener());
}
class ButtonListener implements ActionListener {
/**
* Invoked when user clicks the button.
*/
public void actionPerformed(ActionEvent evt) {
view.turnOnButton(false);
progressBar.setIndeterminate(true);
// NOTE: Instances of javax.swing.SwingWorker are not reusable,
// so we create new instances as needed
worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("progress")) {
progressBar.setIndeterminate(false);
progressBar.setValue(worker.getProgress());
}
}
});
worker.execute();
}
}
class Worker extends SwingWorker<Void, Integer> implements PropertyChangeListener {
/*
* Download task. Executed in worker thread.
*/
#Override
protected Void doInBackground() throws MalformedURLException {
model.addListener(this);
try {
String src = "https://lh3.googleusercontent.com/l6JAkhvfxbP61_FWN92j4ulDMXJNH3HT1DR6xrE7MtwW-2AxpZl_WLnBzTpWhCuYkbHihgBQ=s640-h400-e365";
model.setURL(src);
model.download();
} catch (IOException ex) {
System.out.println(ex);
this.cancel(true);
}
return null;
}
/*
* Executed in event dispatching thread
*/
#Override
protected void done() {
try {
if (!isCancelled()) {
get(); // throws an exception if doInBackground throws one
System.out.println("File has been downloaded successfully!");
}
} catch (InterruptedException x) {
x.printStackTrace();
System.out.println("There was an error in downloading the file.");
} catch (ExecutionException x) {
x.printStackTrace();
System.out.println("There was an error in downloading the file.");
}
view.turnOnButton(true);
}
/**
* Invoked in the background thread of Downloader.
*/
#Override
public void propertyChange(PropertyChangeEvent evt) {
this.setProgress((int) evt.getNewValue());
System.out.println("..." + this.getProgress() + "% completed");
}
}
}
Main.java:
package download_progress_bar;
import javax.swing.SwingUtilities;
/**
* Runs the download progress bar application.
*/
public class Main {
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Create view
ProgressBar view = new ProgressBar();
// NOTE: Should model/controller be created outside invokeLater?
// Create model
Downloader model = new Downloader();
// Create controller
Controller controller = new Controller(view, model);
}
});
}
}
EDIT: I've updated my code to reflect the suggested changes. But even after making the changes, the problem persists. I am still seeing multiple invocations of "...100% completed", the number of invocations increasing with every subsequent download. For example, I run the application and press the download button for the first time, I will see
...100% completed
I press the download button again. I see
...100% completed
...100% completed
I press the download button again...
...100% completed
...100% completed
...100% completed
and so on. Why is this happening?
It's possible that, because of the way the percentage is calculated that it will report 100% when there is still some more work to be completed
During my testing I observed...
//...
98
...
99
99
...
100
So a lot of the values were repeated before the code completed.
I noted some issues/oddities in your download code, mostly the fact that you completely ignore the percentCompleted property, so I changed it to something more like...
public void download() throws IOException {
// Open connection on URL object
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Check response code (always do this first)
int responseCode = connection.getResponseCode();
System.out.println("response code: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
// Open input stream from connection
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
// Open output stream for file writing
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("cat.jpg"));
int totalBytesRead = 0;
//int percentCompleted = 0;
int i = -1;
while ((i = in.read()) != -1) {
out.write(i);
totalBytesRead++;
int old = percentCompleted;
percentCompleted = (int) (((double) totalBytesRead / (double) connection.getContentLength()) * 100.0);
pcs.firePropertyChange("downloading", old, percentCompleted);
System.out.println(percentCompleted); // makes download a bit slower, comment out for speed
}
// Close streams
out.close();
in.close();
}
}
For me, I'd change the code slightly, instead of doing...
#Override
protected void process(List<Integer> chunks) {
int percentCompleted = chunks.get(chunks.size() - 1); // only interested in the last value reported each time
progressBar.setValue(percentCompleted);
if (percentCompleted > 0) {
progressBar.setIndeterminate(false);
progressBar.setString(null);
}
System.out.println("..." + percentCompleted + "% completed");
}
/**
* Invoked when a progress property of "downloading" is received.
*/
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("downloading")) {
publish((Integer) evt.getNewValue());
}
}
You should take advantage of SwingWorkers inbuilt progress support, for example...
/**
* Invoked when a progress property of "downloading" is received.
*/
#Override
public void propertyChange(PropertyChangeEvent evt) {
setProgress((int)evt.getNewValue());
}
This will mean you will need to attached a PropertyChangeListener to the SwingWorker
/**
* Invoked when user clicks the button.
*/
public void actionPerformed(ActionEvent evt) {
view.turnOnButton(false);
progressBar.setIndeterminate(true);
// NOTE: Instances of javax.swing.SwingWorker are not reusable,
// so we create new instances as needed
worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setIndeterminate(false);
progressBar.setValue(worker.getProgress());
}
}
});
worker.execute();
}
The side effect to this is, you know have a means to also be notified when the SwingWorker's state changes to check to see when it is DONE
Updated
Okay, after going over the code, again, I can see that you're adding a new PropertyChangeListener to model EVERY TIME you execute the SwingWorker
/*
* Download task. Executed in worker thread.
*/
#Override
protected Void doInBackground() throws MalformedURLException, InterruptedException {
model.addListener(this); // Add another listener...
try {
String src = "https://lh3.googleusercontent.com/l6JAkhvfxbP61_FWN92j4ulDMXJNH3HT1DR6xrE7MtwW-2AxpZl_WLnBzTpWhCuYkbHihgBQ=s640-h400-e365";
model.setURL(src);
model.download();
} catch (IOException ex) {
System.out.println(ex);
this.cancel(true);
}
return null;
}
Because the model is an instance field of Controller, this is having an accumulative effect.
One solution might be to just add the Downloader as the listener to the model, but that would require you to ensure that any updates you perform to the UI are synced properly.
A better, general, solution would be to add support to remove the listener once the worker completes
public class Downloader {
//...
public void removeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
And then in the SwingWorkers done method, remove the listener...
/*
* Executed in event dispatching thread
*/
#Override
protected void done() {
model.removeListener(this);
As shown here and here, SwingWorker maintains two bound properties: state and progress. Invoking setProgress() ensures that "PropertyChangeListeners are notified asynchronously on the Event Dispatch Thread." Simply add a PropertyChangeListener to your progress bar and call setProgress() in your implementation of doInBackground(), or a method that it calls such as download(). Conveniently, "For performance purposes, all these invocations are coalesced into one invocation with the last invocation argument only."
I'm using the current version of SWT to build my applications and I want to run it under Mac OS X (Yosemite).
My problem is now that I'm not be able to capture clicks on the "About", "Preferences" and "Quit" menu items which were automatically added to my application.
I already searched a lot and found the following class which seems very helpful to me http://www.transparentech.com/files/CocoaUIEnhancer.java.
And that's my code to initialize it:
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
public class Test {
private Display display;
private Shell shell;
public Test(Display display) {
this.display = display;
initUI();
}
public void open() {
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
private void initUI() {
shell = new Shell(display);
shell.setSize(808, 599);
shell.setText("Test");
AboutHandler aboutHandler = new AboutHandler();
PreferencesHandler preferencesHandler = new PreferencesHandler();
QuitHandler quitHandler = new QuitHandler();
CocoaUIEnhancer uienhancer = new CocoaUIEnhancer("Test");
uienhancer.hookApplicationMenu(display, quitHandler, aboutHandler, preferencesHandler);
}
private class AboutHandler implements Listener {
public void handleEvent(Event e) {
}
}
private class PreferencesHandler implements Listener {
public void handleEvent(Event e) {
}
}
private class QuitHandler implements Listener {
public void handleEvent(Event e) {
}
}
}
I can compile it without any errors but if I start the program then I will get the following exception:
Exception in thread "main" java.lang.NoSuchMethodError: actionProc
at org.eclipse.swt.internal.Callback.bind(Native Method)
at org.eclipse.swt.internal.Callback.<init>(Unknown Source)
at org.eclipse.swt.internal.Callback.<init>(Unknown Source)
at org.eclipse.swt.internal.Callback.<init>(Unknown Source)
at CocoaUIEnhancer.initialize(CocoaUIEnhancer.java:124)
at CocoaUIEnhancer.hookApplicationMenu(CocoaUIEnhancer.java:92)
at Test.initUI(Test.java:50)
at Test.<init>(Test.java:18)
It's probably an error in the native libraries but I can't figure it out!
I didn't use CocoaUIEnhancer at all, as it was causing issues as well.
So here's what I ended up doing in my applications:
/**
* Convenience method that takes care of special menu items (About, Preferences, Quit)
*
* #param name The name of the menu item
* #param parent The parent {#link Menu}
* #param listener The {#link Listener} to add to the item
* #param id The <code>SWT.ID_*</code> id
*/
private void addMenuItem(String name, Menu parent, Listener listener, int id)
{
if (OSUtils.isMac())
{
Menu systemMenu = Display.getDefault().getSystemMenu();
for (MenuItem systemItem : systemMenu.getItems())
{
if (systemItem.getID() == id)
{
systemItem.addListener(SWT.Selection, listener);
return;
}
}
}
/* We get here if we're not running on a Mac, or if we're running on a Mac, but the menu item with the given id hasn't been found */
MenuItem item = new MenuItem(parent, SWT.NONE);
item.setText(name);
item.addListener(SWT.Selection, listener);
}
Just call it with SWT.ID_PREFERENCES, SWT.ID_ABOUT and SWT.ID_QUIT respectively. Hand in a fallback menu item name, a fallback Menu and the actual Listener you want to add to the menu item.
So for example:
addMenuItem("Quit", myMenu, new Listener()
{
#Override
public void handleEvent(Event event)
{
// Close database connection for example
}
}, SWT.ID_QUIT);
It looks like this the actionProc
int actionProc( int id, int sel, int arg0 )
in CocoaUIEnhancer probably needs to use long rather than int for the arguments to work with 64 bit SWT.
You need to modify CocoaUIEnhancer.java, to make it work with pure SWT application as described in this tutorial:
Modify the getProductName() method to return a String when no product is found (instead of null)
Wrap the code in hookWorkbenchListener() in a try-catch (IllegalStateException e) block
Wrap the code in modifyShells() in a try-catch (IllegalStateException e) block
Add some code to the actionProc(...) method, to bring up an About-Dialog and Preferences-Dialog (since we aren’t using commands):
static long actionProc(long id, long sel, long arg0) throws Exception {
// ...
else if (sel == sel_preferencesMenuItemSelected_) {
showPreferences();
} else if (sel == sel_aboutMenuItemSelected_) {
showAbout();
}
return 0;
}
private static void showAbout() {
MessageDialog.openInformation(null, "About...",
"Replace with a proper about text / dialog");
}
private static void showPreferences() {
System.out.println("Preferences...");
PreferenceManager manager = new PreferenceManager();
PreferenceDialog dialog = new PreferenceDialog(null, manager);
dialog.open();
}
// ...
Finally, we add the following lines to our main() method:
public static final String APP_NAME = "MyApp";
public static void main(String[] args) {
//in your case change the Test constructor
Display.setAppName(APP_NAME);
Display display = Display.getDefault();
//insert in initUI method call the earlysetup
if (SWT.getPlatform().equals("cocoa")) {
new CocoaUIEnhancer().earlyStartup();
}
Shell shell = new Shell(display);
shell.setText(APP_NAME);
...
}
Quoted code.
Baz's solution works great! If you'd rather not import OSUtils just to test if you are on a Mac, use instead:
System.getProperty("os.name").contentEquals("Mac OS X")
I'm trying to populate a comboBox in my server GUI with some data. Specifically, i would like to add the id of the clients registered to the Server.
I must have implemented it wrong, because changes after the initialize() method of the gui don't apply.
I copy here the involved code:
In server:
public class Server implements ServerInterface {
private static List<ClientInterface> clients;
static ServerGui gui;
public Server() throws RemoteException
{
super();
clients = new ArrayList<ClientInterface>( );
gui = new ServerGui();
}
*.... other methods not involved ....*
public static void main(String[] args)
{
try {
Server statsServer = new Server();
ServerInterface stub = (ServerInterface) UnicastRemoteObject.exportObject(statsServer, 1099);
Registry r=LocateRegistry.createRegistry(1099);
r.rebind("aaa", stub);
System.out.println("Server ready");
//gui.fullCombobox(clients);
gui.main(null);
}
/* If any communication failures occur... */
catch (RemoteException e) {
System.out.println("Communication error " + e.toString());
}
}
}
In GUI
import java.awt.EventQueue;
public class ServerGui {
private JFrame frame;
public JComboBox<String> comboBox;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ServerGui window = new ServerGui();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ServerGui() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 725, 487);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
comboBox = new JComboBox();
comboBox.addItem("ciao");
comboBox.addItem("miao");
comboBox.setBounds(162, 113, 182, 38);
frame.getContentPane().add(comboBox);
}
public void fullCombobox(List <ClientInterface> lista) throws RemoteException
{
Iterator<ClientInterface> it= lista.iterator();
while(it.hasNext())
{
ClientInterface c = ( ClientInterface ) it.next();
Integer i = c.getId();
comboBox.addItem(i.toString());
}
}
}
When i invoke a Server method through Client, i invoke at the end of it the fullComboBox method too. I supposed the combobox would reload automatically, but it doesn't change at all.
It seems that all i do after the initialize() method doesn't work.
How can i resolve that?
And am i following the right way to do a gui for a Server? I had some doubts if to put a gui object inside the server (as i did) or to put a server inside the gui. I'm really confused.
Thanks in advance to everyone.
From reviewing your code shortly, it seems that you are not even calling the method
public void fullCombobox(List<ClientInterface> lista) throws RemoteException {}
You either forgot to show the call in your code or it was the mistake that made you think that it is a problem with the JComboBox.
I borrowed a design that I found on stackoverflow to redirect console output to a GUI. It worked fine until I started reading from text files in my program. Now when I run the program using the GUI no output is displayed and the GUI freezes and then closes on its own eventually. Here is a slimmed down version of my GUI code:
public class GreenhouseControlsGUI {
public static class MyGui extends JFrame implements ActionListener {
// Start button
JButton Start = new JButton("Start");
/..................................../
/**
* The constructor.
*/
public MyGui(){
super("Greenhouse Controls");
/............................../
bottomPanel.setLayout(new FlowLayout());
bottomPanel.add(Start);
/............................../
getContentPane().add(holdAll, BorderLayout.CENTER);
Start.addActionListener(this);
/............................../
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
public void actionPerformed(ActionEvent e){
if (e.getSource() == Start)
GreenhouseControls.startMeUp(); // Start program...
/............................../
}
public static void main(String[] args){
MyGui myApplication = new MyGui();
// redirect output to GUI
myApplication.redirectSystemStreams();
// Specify where will it appear on the screen:
myApplication.setLocation(10, 10);
myApplication.setSize(500, 300);
// Show it!
myApplication.setVisible(true);
}
private void updateTextArea(final String text) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
myText.append(text);
}
});
}
private void redirectSystemStreams() {
OutputStream out = new OutputStream() {
#Override
public void write(int b) throws IOException {
updateTextArea(String.valueOf((char) b));
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
updateTextArea(new String(b, off, len));
}
#Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
};
System.setOut(new PrintStream(out, true));
System.setErr(new PrintStream(out, true));
}
}
}
I'm pretty sure my problem starts with reading from the 'filename' path in the code below because I don't have this problem when I comment the 'filename' variable declaration out. I thought the methods to redirect console output to my GUI were only redirecting output.... Why is it screwing everything up when I read from a file? I am new to programming and I have probably overlooked something obvious, but I can't figure it out.
Here is the static startMeUp() method invoked inside the GUI class:
public static void startMeUp() {
try {
String option = "-f"; // (-f) to start program or (-d) to restore program
filename = "src/greenhouse/examples3.txt"; // read file from here
dumpFile = "dump.out"; // restore program from here
// if option is invalid invoke corresponding print statement
if ( !(option.equals("-f")) && !(option.equals("-d")) ) {
System.out.println("Invalid option");
printUsage();
}
GreenhouseControls gc = new GreenhouseControls(); // Create GreenhouseControls object
Restart restart = new Restart(0,filename, gc); // Create Restart object
Restore restore = new Restore(0,dumpFile); // Create Restore object
// if the option value is -f ...
if (option.equals("-f")) {
gc.addEvent(restart); // add new Restart object to addEvent()
}
gc.run();
// if the option value is -d ...
if (option.equals("-d")) {
gc.addEvent(restore); // add new Restore object to addEvent()
}
gc.run();
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid number of parameters");
printUsage();
}
}
You need to invoke startMeUp in a new thread, because your console program is blocking the event dispatch thread. Like this:
new Thread () {
#Override public void run () {
GreenhouseControls.startMeUp();
}
}.start();
instead of just
GreenhouseControls.startMeUp();
I have a customized JFileChooser
Its approveSelection() method is slightly modified:
public void approveSelection()
{
File file = getSelectedFile();
changeGui();
final Object a = makeALongCalcualtion(file);
if (a != null)
{
super.approveSelection();
SwingWorker<Document, Void> open = new SwingWorker<Document, Void>()
{
#Override
protected Document doInBackground() throws Exception
{
return createADocument(a);
}
#Override
protected void done()
{
try
{
if(get() != null)
{
changeGui();
}
else
{
//TODO error message
changeGui();
}
}
catch (InterruptedException | ExecutionException e)
{
//TODO error message
changeGui();
}
}
};
open.execute();
}
else
{
//TODO error message
changeGui();
}
}
The changeGui() method sets a JProgressBar to indeterminate and updates a JLabel with a new string.
If file provided to makeALongCalcualtion(file) is of invalid type, it will return null, otherwise it returns info that is passed to SwingWorker which can use it to acutally create the representation of a file in the program (the Document object).
However, this doesn't work as it should because makeALongCalcualtion(file) isn't called within SwingWorker method, and that blocks the EDT.
In order to fix the problem, I would have to call makeALongCalcualtion(file) within a SwingWorker. I could move that part of the code into the SwingWorker without any problems, but then I would have to (due to my code logic) move super.approveSelection() along with it.
So the bottom line is, how do I call super.approveSelection() from within doInBackground() for this specific case?
//More info
What is supposed to happen:
User selects and opens a file
JLabel and JProgressBar are updated, indeterminate progress starts to play.
If makeALongCalcualtion(file) return null user is warned with an error window, but the JFileChooser stys open, making it possible to choose again when the error window is closed.
Otherwise, super.approveSelection() is called, allowing the chooser to close.
A document is created (but the method that creates the document return null if something goes wrong).
If everything is fine, JLabel updates and progressBar animation is stopped (indeterminate is set to false).
If something goes wrong same thing happens as in step 6, only with different message in JLabel.
What happens:
same
same
same, but when makeALongCalculation(file); begins, progressBar freezes.
same
same
same, but the animation isn't stopped (since the progressbar is frozen), only the frozen "picture" is removed and progressbar returns to it's previous state.
same
EDIT
I have made some alterations to my program and I now have this:
approveSelection():
public void approveSelection()
{
File file = getSelectedFile();
Main.getStatusBar().startOpen();
final WorkOpen open = new WorkOpen(file);
open.execute();
open.addPropertyChangeListener(new PropertyChangeListener()
{
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
if (evt.getNewValue().equals("DONE"))
{
if (open.failed())
{
//TODO error message
Main.getStatusBar().endOpen(false);
}
else
{
Main.getStatusBar().endOpen(true);
}
}
}
}
});
}
SwingWorker:
class WorkOpen extends SwingWorker<Document, Void>
{
boolean failed = false;
File file;
public boolean failed()
{
return failed;
}
#Override
protected Document doInBackground() throws Exception
{
ArrayList<String> data = Opener.extractData(file);
if (data != null)
{
//My little path/name/similar managing system
FileComplex fullPath = new FileComplex(file.toString());
return Opener.createDocument(fullPath.getFullName(), fullPath.getFullPath(), data);
}
else
{
failed = true;
return null;
}
}
#Override
protected void done()
{
try
{
if(get() != null)
{
Main.addDocument(get());
}
}
catch (InterruptedException | ExecutionException e)
{
failed = true;
}
}
WorkOpen(File file)
{
this.file = file;
}
}
The problem now is where to call super.approveSelection(). It has to wait for the worker to finish executing, yet I can't call it from the property change listener.
What to do here?
EDIT 2
As HovercraftFullOfEels suggested, I fixed my code and it compiled and ran. But the problem of JProgressBar freezeing remained. Also, I had to introduce something I don't know if I should have:
private void superApproveSelection()
{
super.approveSelection();
}
public void approveSelection()
{
final File file = getSelectedFile();
class OpenWorker extends SwingWorker<Boolean, Void>
{
Document document;
Document getDocument()
{
return document;
}
#Override
protected Boolean doInBackground() throws Exception
{
ArrayList<String> data = Opener.extractData(file);
if (data != null)
{
//I had to start the progressBar here, because if invalid
//file was selected (extractData(file) returns null if it was),
//nothing should happen (maybe an error
//window later, handled with a new Runnable() same as this:
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
Main.getStatusBar().startOpen();
}
});
FileComplex fullPath = new FileComplex(file.toString());
document = Opener.createDocument(fullPath.getFullName(), fullPath.getFullPath(), data);
return true;
}
else
{
return false;
}
}
};
final OpenWorker opener = new OpenWorker();
opener.addPropertyChangeListener(new PropertyChangeListener()
{
#Override
public void propertyChange(PropertyChangeEvent evt)
{
if ("state".equals(evt.getPropertyName()))
{
if (evt.getNewValue() == SwingWorker.StateValue.DONE)
{
if(opener.getDocument() != null)
{
superApproveSelection();
Main.addDocument(opener.getDocument());
Main.getStatusBar().endOpen(true);
}
else
{
try
{
//I'm retrieveing doInBackground()'s value to see if
//progressBar needs stoping (it also displays some
//text, so it must not be called unless the
//progressBar was started).
if (opener.get())
{
Main.getStatusBar().endOpen(false);
}
}
catch (InterruptedException | ExecutionException e)
{
//TODO error something went wrong
}
}
}
}
}
});
opener.execute();
}
"In order to fix the problem, I would have to call makeALongCalcualtion(file) within a SwingWorker. I could move that part of the code into the SwingWorker without any problems, but then I would have to (due to my code logic) move super.approveSelection() along with it."
No, not true at all. super.approveSelection() would not have to be called inside of the SwingWorker.
Why not simply create a SwingWorker, add a PropertyChangeListener to it, and when the SwingWorker's state is done, call super.approveSelection() if indicated?
OK, here is my example below. Explanation to follow:
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.text.*;
#SuppressWarnings("serial")
public class ApproveSelectionTest extends JPanel {
private JTextArea textArea = new JTextArea(30, 60);
public ApproveSelectionTest() {
textArea.setEditable(false);
textArea.setFocusable(false);
JPanel btnPanel = new JPanel();
btnPanel.add(new JButton(new MyGetFileAction("Get Text File Text")));
setLayout(new BorderLayout());
add(new JScrollPane(textArea), BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
private class MyGetFileAction extends AbstractAction {
public MyGetFileAction(String text) {
super(text);
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
MyFileChooser myFileChooser = new MyFileChooser();
int result = myFileChooser.showOpenDialog(ApproveSelectionTest.this);
if (result == JFileChooser.APPROVE_OPTION) {
Document doc = myFileChooser.getDocument();
textArea.setDocument(doc);
}
};
}
private static void createAndShowGui() {
ApproveSelectionTest mainPanel = new ApproveSelectionTest();
JFrame frame = new JFrame("ApproveSelectionTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyFileChooser extends JFileChooser {
private WorkOpen workOpen = null;
private JDialog progressDialog = null;
public MyFileChooser() {
}
#Override
public void approveSelection() {
JProgressBar progBar = new JProgressBar();
progBar.setIndeterminate(true);
Window win = SwingUtilities.getWindowAncestor(this);
progressDialog = new JDialog(win, "Checking File", ModalityType.APPLICATION_MODAL);
progressDialog.getContentPane().add(progBar);
progressDialog.pack();
progressDialog.setLocationRelativeTo(null);
File file = getSelectedFile();
workOpen = new WorkOpen(file);
workOpen.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (SwingWorker.StateValue.DONE == pcEvt.getNewValue()) {
if (progressDialog != null) {
progressDialog.dispose();
}
try {
boolean bool = workOpen.get().booleanValue();
if (bool) {
superApproveSelection();
} else {
JOptionPane.showMessageDialog(MyFileChooser.this, "Invalid File Chosen");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
workOpen.execute();
progressDialog.setVisible(true);
}
// ****** this is key *****
private void superApproveSelection() {
super.approveSelection();
}
public Document getDocument() {
if (workOpen == null) {
return null;
} else {
return workOpen.getDocument();
}
}
}
class WorkOpen extends SwingWorker<Boolean, Void> {
private static final long SLEEP_TIME = 4 * 1000;
private Document document = null;
private File file = null;
public WorkOpen(File file) {
this.file = file;
}
#Override
protected Boolean doInBackground() throws Exception {
if (file == null || !file.exists()) {
return Boolean.FALSE;
}
Thread.sleep(SLEEP_TIME);
String fileName = file.getName();
if (fileName.contains(".txt")) {
Scanner scan = new Scanner(file);
StringBuilder stringBuilder = new StringBuilder();
while (scan.hasNextLine()) {
stringBuilder.append(scan.nextLine() + "\n");
}
document = new PlainDocument();
document.insertString(0, stringBuilder.toString(), null);
return Boolean.TRUE;
}
return Boolean.FALSE;
}
public Document getDocument() {
return document;
}
}
Explanation and key points from my example:
This example behaves very simply. You choose a file, and if the file exists and contains ".txt" in its name, then it reads in the document and displays the text in a JTextField.
Else it displays a warning message but leaves the JFileChooser displayed.
Probably the key point: I've given my MyFileChooser class a private superApproveSelection() method that can be called by my PropertyChangeListener. This exposes the super's approveSelection() method to the inner class, one of the problems you were having.
The order of calling code is important in my approveSelection() override.
In this method I first create my JProgressBar and its dialog but don't yet display it immediately. It really doesn't have to be created first, but it sure needs to be displayed last.
I create my SwingWorker, workOpen, but don't yet execute it.
I add my PropertyChangeListener to the SwingWorker before executing the SwingWorker.
I then execute my SwingWorker
I then display my modal JDialog with the indeterminate JProgressBar.
My SwingWorker is structured so that its doInBackground returns a Boolean, not a Document.
I have it create a (very simple) Document if all works out OK that holds the content of the text file, and set a private "doc" field obtainable by a getter method, and then have doInBackground return Boolean.TRUE if all works well.
I've given my doInBackground a Thread.sleep(4000) just to pretend that its action takes a lot of time. Yours of course won't have this.
In the PropertyChangeListener if the SwingWorker is DONE, I'll dispose of the progress bar dialog and then call get() on the SW to get the Boolean result.
If it's Boolean.TRUE, then call the superApproveSelection() method described above.
Else show an error message. Note that since the super's approveSelection() isn't called, the file chooser dialog remains displayed.
If the approveSelection is called then the code that displays my file chooser dialog will get the appropriate return value, will extract the Document from the file chooser and displays the Document in a JTextArea.