Остановить цикл в Java с графическим интерфейсом (кнопки)
Я создал графический интерфейс в Java и 2 кнопки.
Моя цель:
1) Когда я нажимаю на первую кнопку, возникает цикл, в котором обрабатываются разные задачи (кнопка "Пуск"). Между каждой петлей есть остановка на 10 секунд
2) Когда я нажимаю на вторую кнопку, цикл обрабатывается сразу в последний раз, но затем останавливается. (Я также хотел бы сделать всплывающее окно, показывающее, что это было остановлено, но это не главный вопрос, я думаю, что могу сделать это.)
Я попробовал следующий код, но сначала я думаю, что это более простые способы решения моей проблемы. Плюс я могу скомпилировать, но это не работает, цикл не останавливается, окно вылетает:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
globalStop="Run";
while (globalStop.equals("Run")) {
System.out.println("GO");
// Other stuff
// For the break ?
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
}
}
System.out.println("done");
}
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
globalStop = "Stop";
System.out.println("Bouton2");
}
Я надеюсь, что я достаточно ясно, если это не так, пожалуйста, дайте мне знать, и я перефразирую. Заранее всем спасибо за помощь.
4 ответа
Я задавался вопросом, сколько времени мне понадобится, чтобы создать графический интерфейс для транспортных сигналов типа Соединенных Штатов. Это заняло 75 минут. Я смог быстро создать графический интерфейс, потому что много Swing является образцом. Создав один графический интерфейс, вы можете скопировать некоторые классы для вашего следующего графического интерфейса.
Вот изображение графического интерфейса светофора.
Когда вы нажимаете кнопку "Пуск", сигнал светофора меняется с зеленого на желтый и красный. Светофор будет повторяться до тех пор, пока вы не нажмете кнопку Стоп.
Когда вы нажимаете кнопку Стоп, сигнал светофора станет красным. Он будет всегда красным, пока вы не нажмете кнопку "Пуск".
Когда вы нажимаете кнопку "Пуск" во время циклического движения светофора, цикл с зеленого на желтый или красный начинается заново.
В основном, следующие шаги показывают вам, как создать любой графический интерфейс Swing. Я не создавал код в этом порядке, но имеет смысл объяснить код в логическом порядке. Итак, давайте углубимся в код.
Это класс модели для графического интерфейса. Каждый графический интерфейс должен иметь свою собственную модель, отдельную от модели приложения. Для этого графического интерфейса модель проста.
package com.ggl.traffic.signal.model;
import java.awt.Dimension;
public class TrafficSignalModel {
public static final int RED_LIGHT_TIME = 15;
public static final int YELLOW_LIGHT_TIME = 5;
public static final int GREEN_LIGHT_TIME = 10;
public static final Dimension LIGHT_SIZE = new Dimension(32, 32);
}
Мы устанавливаем время светового сигнала в модели, а также размер светофора.
Для более сложного графического интерфейса мы будем отслеживать значения полей в модели.
Далее у нас есть основной класс графического интерфейса светофора.
package com.ggl.traffic.signal;
import javax.swing.SwingUtilities;
import com.ggl.traffic.signal.view.TrafficSignalFrame;
public class TrafficSignal implements Runnable {
@Override
public void run() {
new TrafficSignalFrame();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new TrafficSignal());
}
}
Этот класс гарантирует, что GUI сигнала трафика находится в потоке событий Swing. Это все, что делает этот класс. Вы можете увидеть, как вы можете скопировать этот класс для запуска любого графического интерфейса.
Далее у нас есть класс Frame GUI.
package com.ggl.traffic.signal.view;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class TrafficSignalFrame {
protected ButtonPanel bPanel;
protected JFrame frame;
protected TrafficSignalPanel tsPanel;
public TrafficSignalFrame() {
createPartControl();
}
protected void createPartControl() {
tsPanel = new TrafficSignalPanel();
bPanel = new ButtonPanel();
bPanel.setTrafficSignalPanel(tsPanel);
frame = new JFrame();
frame.setTitle("Traffic Signal");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
exitProcedure();
}
});
frame.setLayout(new FlowLayout());
frame.add(bPanel.getPanel());
frame.add(tsPanel.getPanel());
frame.pack();
// frame.setBounds(100, 100, 400, 200);
frame.setVisible(true);
}
public void exitProcedure() {
frame.dispose();
System.exit(0);
}
public JFrame getFrame() {
return frame;
}
}
Этот класс является стандартным, за исключением отдельных JPanels, которые составляют GUI. Если в вашем JFrame есть JMenu, это место, где вы можете прикрепить JMenu к вашему JFrame.
Обратите внимание, что я не расширил JFrame для создания этого класса. Единственный раз, когда вы расширяете компонент Swing, это когда вы переопределяете один или несколько методов компонента. Если мне нужен фактический JFrame, я вызываю метод getFrame(). Использование компонентов Swing вместо расширения компонентов Swing позволяет отделить мои методы от методов Swing.
Далее мы рассмотрим панель светофора. Эта панель составляет один из 3-х огней в светофоре.
package com.ggl.traffic.signal.view;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class TrafficSignalLightPanel extends JPanel {
private static final long serialVersionUID = 1L;
protected boolean lightOn;
protected Color lightColor;
protected Color darkColor;
public TrafficSignalLightPanel(Color lightColor) {
this.lightColor = lightColor;
this.darkColor = Color.WHITE;
this.lightOn = false;
}
public void setLightOn(boolean lightOn) {
this.lightOn = lightOn;
this.repaint();
}
@Override
public void paintComponent(Graphics g) {
if (lightOn) {
g.setColor(lightColor);
} else {
g.setColor(darkColor);
}
g.fillRect(0, 0, getWidth(), getHeight());
}
}
Этот класс расширяет JPanel, потому что мы хотим переопределить метод paintComponent. Это простой класс. Все, что он делает - это окрашивает панель в белый или белый цвет.
Далее посмотрим на панель светофоров. Эта панель создает 3 световые панели и размещает их в вертикальном ряду.
package com.ggl.traffic.signal.view;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.border.Border;
import com.ggl.traffic.signal.model.TrafficSignalModel;
public class TrafficSignalPanel {
protected JPanel panel;
protected TrafficSignalLightPanel redLight;
protected TrafficSignalLightPanel yellowLight;
protected TrafficSignalLightPanel greenLight;
public TrafficSignalPanel() {
createPartControl();
}
protected void createPartControl() {
Border border = BorderFactory.createLineBorder(Color.BLACK, 4);
redLight = new TrafficSignalLightPanel(Color.RED);
redLight.setBorder(border);
redLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);
yellowLight = new TrafficSignalLightPanel(Color.YELLOW);
yellowLight.setBorder(border);
yellowLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);
greenLight = new TrafficSignalLightPanel(Color.GREEN);
greenLight.setBorder(border);
greenLight.setPreferredSize(TrafficSignalModel.LIGHT_SIZE);
panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.setPreferredSize(
new Dimension(TrafficSignalModel.LIGHT_SIZE.width + 10,
TrafficSignalModel.LIGHT_SIZE.height * 3 + 25));
panel.add(redLight);
panel.add(yellowLight);
panel.add(greenLight);
}
public JPanel getPanel() {
return panel;
}
public TrafficSignalLightPanel getRedLight() {
return redLight;
}
public TrafficSignalLightPanel getYellowLight() {
return yellowLight;
}
public TrafficSignalLightPanel getGreenLight() {
return greenLight;
}
}
Довольно простое создание JPanel из 3 JPanels. Я установил предпочтительный размер JPanel, чтобы огни были в вертикальном ряду.
Далее мы рассмотрим панель кнопок. Вы можете в значительной степени скопировать этот код в любой графический интерфейс с панелью кнопок.
package com.ggl.traffic.signal.view;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import com.ggl.traffic.signal.thread.TrafficSignalCycle;
public class ButtonPanel {
protected JButton startButton;
protected JButton stopButton;
protected JPanel panel;
protected TrafficSignalCycle thread;
protected TrafficSignalPanel tsPanel;
public ButtonPanel() {
this.thread = null;
createPartControl();
}
protected void createPartControl() {
panel = new JPanel();
panel.setLayout(new FlowLayout());
startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
if (thread != null) {
thread.stopRunning();
}
tsPanel.getRedLight().setLightOn(false);
tsPanel.getYellowLight().setLightOn(false);
tsPanel.getGreenLight().setLightOn(false);
thread = new TrafficSignalCycle(tsPanel);
thread.start();
}
});
panel.add(startButton);
stopButton = new JButton("Stop");
stopButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
if (thread != null) {
thread.stopRunning();
thread = null;
}
tsPanel.getRedLight().setLightOn(true);
tsPanel.getYellowLight().setLightOn(false);
tsPanel.getGreenLight().setLightOn(false);
}
});
panel.add(stopButton);
setButtonSizes(startButton, stopButton);
}
protected void setButtonSizes(JButton ... buttons) {
Dimension preferredSize = new Dimension();
for (JButton button : buttons) {
Dimension d = button.getPreferredSize();
preferredSize = setLarger(preferredSize, d);
}
for (JButton button : buttons) {
button.setPreferredSize(preferredSize);
}
}
protected Dimension setLarger(Dimension a, Dimension b) {
Dimension d = new Dimension();
d.height = Math.max(a.height, b.height);
d.width = Math.max(a.width, b.width);
return d;
}
public void setTrafficSignalPanel(TrafficSignalPanel tsPanel) {
this.tsPanel = tsPanel;
}
public JPanel getPanel() {
return panel;
}
}
Действия кнопок были достаточно простыми, чтобы я мог держать их на панели кнопок. Если вы хотите, вы можете кодировать отдельные классы действий.
Наконец, вот код, который запускает цикл светофора. Это расширение класса Thread, поэтому его можно запускать в отдельном потоке от GUI. Всегда полезно выполнять работу в потоках отдельно от потока GUI.
package com.ggl.traffic.signal.thread;
import javax.swing.SwingUtilities;
import com.ggl.traffic.signal.model.TrafficSignalModel;
import com.ggl.traffic.signal.view.TrafficSignalLightPanel;
import com.ggl.traffic.signal.view.TrafficSignalPanel;
public class TrafficSignalCycle extends Thread {
protected boolean isRunning;
protected boolean isFinished;
protected TrafficSignalPanel tsPanel;
public TrafficSignalCycle(TrafficSignalPanel tsPanel) {
this.tsPanel = tsPanel;
this.isRunning = true;
this.isFinished = false;
}
@Override
public void run() {
while (isRunning) {
signalLightOn(tsPanel.getGreenLight(), TrafficSignalModel.GREEN_LIGHT_TIME);
signalLightOn(tsPanel.getYellowLight(), TrafficSignalModel.YELLOW_LIGHT_TIME);
signalLightOn(tsPanel.getRedLight(), TrafficSignalModel.RED_LIGHT_TIME);
}
this.isFinished = true;
}
protected void signalLightOn(TrafficSignalLightPanel light, int seconds) {
if (isRunning) {
setLightOn(light, true);
}
for (int i = 0; i < 1000 && isRunning; i++) {
try {
Thread.sleep(1L * seconds);
} catch (InterruptedException e) {
}
}
setLightOn(light, false);
}
protected void setLightOn(final TrafficSignalLightPanel light,
final boolean isLightOn) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
light.setLightOn(isLightOn);
}
});
}
public void stopRunning() {
this.isRunning = false;
while (!isFinished) {
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
}
}
}
}
Метод, который фактически изменяет цвет сигнальной лампы, должен выполняться в потоке событий Swing. Это то, что метод setLightOn делает, вызывая SwingUtilities.
Цикл синхронизации немного сложен, потому что мы хотим иметь возможность остановить поток за несколько миллисекунд. Логическое значение isFinished гарантирует, что поток полностью остановлен, чтобы можно было установить свет.
Это довольно длинный ответ, но я надеюсь, что он пригодится любому, кто создает графический интерфейс Swing.
Вы не должны зацикливаться на потоке пользовательского интерфейса и не просить его спать. По сути, вы должны держать поток пользовательского интерфейса максимально свободным.
Если вам нужно, чтобы что-то происходило на регулярной основе в пользовательском интерфейсе Swing в потоке пользовательского интерфейса, используйте таймер Swing.
Однако неясно, что вы делаете в "другом" - возможно, вам следует делать это в другом потоке целиком и использовать (скажем) AtomicBoolean
чтобы указать, когда вы хотите остановиться.
1. Вы всегда должны сохранять поток пользовательского интерфейса для работы с пользовательским интерфейсом и поток без пользовательского интерфейса для работы без пользовательского интерфейса.
2. В графическом интерфейсе Java main()
не является долгоживущим, после назначения конструкции GUI Event Dispatcher Thread
, main()
выходит, и теперь ответственность EDT обрабатывает GUI.
3. Поэтому, когда вы нажимаете кнопки, и работа, которую вы выполняете, выполняет какой-то тяжелый процесс или отнимает много времени.... Separate thread
,
4. Вы можете использовать Thread
или же SwingWorker
,
Пример:
Button b = new Button("Click me");
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Thread t = new Thread(new Runnable(){
public void run(){
// Do the Heavy Processing work.....
}
});
t.start();
}
});
Простой, но грязный способ:
Многопоточность вашей программы и один поток выполняет ваш цикл, а второй поток контролирует ваши кнопки. Попросите кнопку изменить вашу переменную globalStop
Не такой простой, но более чистый способ:
Заставьте кнопку бросить прерывание, чтобы изменить значение. После прерывания цикл for продолжится до конца.