Reputation: 716
I just recently picked up Java and I've been working on a time keeping app (we all have those dreaded time sheets to fill in these days...).
I've been making good progress and finding the Window Builder in Eclipse pretty awesome but I've been stumped completely by the last thing on my todo list. I am trying to update a label on the UI with a time string that I've calculated based on a start timestamp minus a current timestamp which is calculated every second. This will represent how much time the current task is taking.
This is my code so far and everything else is working apart from updating the label on the UI. It's 20(ish) lines from the bottom of the code.
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.*;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.wb.swt.SWTResourceManager;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Table;
public class mainDisplay {
private static Text txtTask;
static boolean timerRunning = false;
static long timePassed = 0;
static long startTime = 0;
static long currTime = 0;
static String timeString = "";
/**
* Launch the application.
* @param args
*/
public static String timeString(long timePassed) {
if(timePassed < 10) {
timeString = "00:00:0"+String.valueOf(timePassed);
}
else if(timePassed < 60 && timePassed >= 10){
timeString = "00:00:"+String.valueOf(timePassed);
}
else if(timePassed >= 60 && timePassed < 600){
timeString = "00:0" + String.valueOf(timePassed/60) + ":";
if(timePassed % 60 < 10){
timeString = timeString + "0"+String.valueOf(timePassed%60);
}
else{
timeString = timeString + String.valueOf(timePassed%60);
}
}
else if(timePassed >= 600 && timePassed < 3600){
timeString = "00:" + String.valueOf(timePassed/60) + ":";
if(timePassed % 60 < 10){
timeString = timeString + "0"+String.valueOf(timePassed%60);
}
else{
timeString = timeString + String.valueOf(timePassed%60);
}
}
else if(timePassed >= 3600 && timePassed < 36000){
// Hours
timeString = "0" + String.valueOf(timePassed/3600) + ":";
// Mins
if((timePassed%3600)/60 < 10){
timeString = timeString + "0" + String.valueOf((timePassed%3600)/60) + ":";
}
else if((timePassed%3600)/60 >= 10){
timeString = timeString + String.valueOf((timePassed%3600)/60) + ":";
}
// Secs
if((timePassed%3600)%60 < 10){
timeString = timeString + "0" + String.valueOf((timePassed%3600)%60);
}
else if((timePassed%3600)%60 >= 10){
timeString = timeString + String.valueOf((timePassed%3600)%60);
}
}
return timeString;
}
public static void main(final String[] args) {
Display display = Display.getDefault();
final Shell shlSot = new Shell();
shlSot.setBackground(SWTResourceManager.getColor(SWT.COLOR_TITLE_BACKGROUND));
shlSot.setSize(455, 299);
shlSot.setText("SOT 1.0");
txtTask = new Text(shlSot, SWT.BORDER);
txtTask.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
txtTask.setText("");
}
});
txtTask.setFont(SWTResourceManager.getFont("Segoe UI", 11, SWT.NORMAL));
txtTask.setText("Add a task...");
txtTask.setBounds(10, 62, 198, 27);
Label lblTitle = new Label(shlSot, SWT.NONE);
lblTitle.setFont(SWTResourceManager.getFont("Script MT Bold", 22, SWT.BOLD));
lblTitle.setBackground(SWTResourceManager.getColor(SWT.COLOR_TITLE_BACKGROUND));
lblTitle.setBounds(45, 15, 175, 37);
lblTitle.setText("Sands of Time");
final Table tblTasks = new Table(shlSot, SWT.BORDER | SWT.FULL_SELECTION);
tblTasks.setBounds(10, 107, 417, 144);
tblTasks.setHeaderVisible(true);
tblTasks.setLinesVisible(true);
TableColumn clmTask = new TableColumn(tblTasks, SWT.NONE);
clmTask.setText("Task");
TableColumn clmTime = new TableColumn(tblTasks, SWT.NONE);
clmTime.setText("Time");
tblTasks.getColumn(0).pack();
tblTasks.getColumn(1).pack();
Label lblHourGlass = new Label(shlSot, SWT.NONE);
lblHourGlass.setBackground(SWTResourceManager.getColor(SWT.COLOR_TITLE_BACKGROUND));
lblHourGlass.setFont(SWTResourceManager.getFont("Wingdings", 30, SWT.NORMAL));
lblHourGlass.setBounds(14, 11, 32, 46);
lblHourGlass.setText("6");
final Label lblTimer = new Label(shlSot, SWT.NONE);
lblTimer.setAlignment(SWT.CENTER);
lblTimer.setFont(SWTResourceManager.getFont("Segoe UI", 19, SWT.NORMAL));
lblTimer.setBackground(SWTResourceManager.getColor(SWT.COLOR_TITLE_BACKGROUND));
lblTimer.setBounds(268, 17, 113, 32);
lblTimer.setText("00:00:00");
final Button btnExport = new Button(shlSot, SWT.NONE);
final Button btnClear = new Button(shlSot, SWT.NONE);
final Button btnStart = new Button(shlSot, SWT.NONE);
final Button btnStop = new Button(shlSot, SWT.NONE);
btnClear.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
int dialogButton = 0;
// Prompt the user whether to go ahead or not
int dialogResult = JOptionPane.showConfirmDialog (null, "Are you sure you want to clear all progress?","Warning",dialogButton);
if(dialogResult == JOptionPane.YES_OPTION){
// Stop the timer
timerRunning = false;
// Clear the timer on the UI
lblTimer.setText("00:00:00");
// Enable/Disable buttons and reset text
btnStop.setEnabled(false);
btnExport.setEnabled(true);
btnStart.setEnabled(true);
txtTask.setEnabled(true);
txtTask.setText("Add a task...");
// Empty the list on the UI
tblTasks.removeAll();
}
}
});
btnClear.setBounds(370, 63, 57, 25);
btnClear.setText("Clear");
btnExport.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Export the contents of the list to CSV
try{
FileWriter writer = new FileWriter("sotExport.csv");
writer.append("Task");
writer.append(',');
writer.append("Time");
writer.append('\n');
TableItem [] items = tblTasks.getItems ();
for(int i=0; i<items.length; i++) {
writer.append(items[i].getText(0));
writer.append(',');
writer.append(items[i].getText(1));
writer.append('\n');
}
writer.flush();
writer.close();
}
catch(IOException e1){
e1.printStackTrace();
}
}
});
btnExport.setBounds(319, 63, 46, 25);
btnExport.setText("Export");
btnStop.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Stop button stuff
// Add contents of txtTask to list
//lstTasks.add(txtTask.getText());
TableItem item = new TableItem(tblTasks, SWT.NONE);
item.setText(0, txtTask.getText());
item.setText(1, timeString);
tblTasks.getColumn(0).pack();
tblTasks.getColumn(1).pack();
btnStop.setEnabled(false);
btnClear.setEnabled(true);
btnExport.setEnabled(true);
btnStart.setEnabled(true);
txtTask.setEnabled(true);
timerRunning = false;
}
});
btnStop.setBounds(267, 63, 46, 25);
btnStop.setText("Stop");
btnStop.setEnabled(false);
btnStart.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Update UI options
btnStop.setEnabled(true);
btnStart.setEnabled(false);
txtTask.setEnabled(false);
btnClear.setEnabled(false);
btnExport.setEnabled(false);
// Start timer and update lblTimer
timerRunning = true;
startTime = (System.currentTimeMillis() / 1000L);
System.out.println(startTime);
new Thread(new Runnable(){
public void run(){
while (timerRunning){
// Get current time and calculate timer
currTime = System.currentTimeMillis() / 1000L;
timePassed = currTime - startTime;
timeString = timeString(timePassed);
try {
// Why do you not work?!!?!? >:(
lblTimer.setText(timeString);
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}).start();
}
});
btnStart.setBounds(214, 63, 46, 25);
btnStart.setText("Start");
shlSot.open();
shlSot.layout();
while (!shlSot.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
When I click start I receive the following Exception;
Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4397)
at org.eclipse.swt.SWT.error(SWT.java:4312)
at org.eclipse.swt.SWT.error(SWT.java:4283)
at org.eclipse.swt.widgets.Widget.error(Widget.java:472)
at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:363)
at org.eclipse.swt.widgets.Label.setText(Label.java:386)
at mainDisplay$5$1.run(mainDisplay.java:246)
at java.lang.Thread.run(Unknown Source)
Not sure why it cut off the exception first time round but there it is.
Is this a problem with threading? Can I not access the display thread from within my new thread to update the label? Any idea how to get around it?
Thanks
Steve
Upvotes: 1
Views: 790
Reputation: 716
Figured it out..
I just replaced;
lblTimer.setText(timeString);
with;
display.asyncExec(new Runnable() {
public void run() {
lblTimer.setText(timeString);
}
});
If someone still wants to provide a good explanation that would be much appreciated.
Upvotes: 1