Актуализиране на етикет от отделна нишка

Съвсем наскоро взех Java и работя върху приложение за отчитане на времето (всички имаме онези страховити таблици за време, които трябва да попълваме в наши дни...).

Напредвам добре и намирам Window Builder в Eclipse за доста страхотен, но бях напълно объркан от последното нещо в моя списък със задачи. Опитвам се да актуализирам етикет в потребителския интерфейс с времеви низ, който съм изчислил въз основа на начално времево клеймо минус текущо времево клеймо, което се изчислява всяка секунда. Това ще покаже колко време отнема текущата задача.

Това е моят код досега и всичко останало работи освен актуализирането на етикета в потребителския интерфейс. Това е 20 (иш) реда от дъното на кода.

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();
        }
    }
}
}

Когато щракна върху старт, получавам следното изключение;

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)

Не знам защо изключи изключението за първи път, но го има.

Това проблем с нишката ли е? Мога ли да нямам достъп до нишката за показване от моята нова нишка, за да актуализирам етикета? Някаква идея как да го заобиколя?

Благодаря

Стив


person Urbley    schedule 28.07.2014    source източник


Отговори (1)


Разбрах го..

Току що замених;

lblTimer.setText(timeString);

с;

display.asyncExec(new Runnable() {
   public void run() {
      lblTimer.setText(timeString);
   }
});

Ако някой все пак иска да даде добро обяснение, ще бъдем много благодарни.

person Urbley    schedule 28.07.2014
comment
Това е така, защото компонентът Label не е безопасен за нишки. Следователно не можете директно да го актуализирате от нишка, в която не е създаден - person TFischer; 28.07.2014
comment
По принцип не можете да актуализирате компоненти на потребителския интерфейс от нишки, различни от основната. Модификациите на потребителския интерфейс се сериализират чрез публикуване на съобщения в цикъла на съобщенията, които стартират събития, които променят потребителския интерфейс в главната нишка. - person mrmcgreg; 28.07.2014
comment
Вижте wiki.eclipse.org/ - person isnot2bad; 28.07.2014