Мне нужно сделать приложение, которое управляет очередью в почтовом отделении только с одним входом и 4 доступными стойками. Я не понимаю, почему, когда я добавляю нового клиента, значки ставятся в очередь, но вместо того, чтобы удалять их по одному, они удаляются все.
import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.LinkedList;
import java.util.Queue;
public class MyFrame extends JFrame {
private static final int NUM_DESKS = 4;
private final JLabel[] deskLabels = new JLabel[NUM_DESKS];
private final Queue<String> customerQueue = new LinkedList<>();
private int customerNumber = 1;
private final int serviceTime = 3000;
public MyFrame() {
super("Post Office Simulator");
setExtendedState(JFrame.MAXIMIZED_BOTH);
setDefaultCloseOperation(EXIT_ON_CLOSE);
initializeUI();
}
private void initializeUI() {
JPanel mainPanel = new JPanel(new BorderLayout());
add(mainPanel);
mainPanel.add(createDesksPanel(), BorderLayout.NORTH);
mainPanel.add(createQueuePanel(), BorderLayout.CENTER);
mainPanel.add(createAddCustomerButton(), BorderLayout.SOUTH);
}
private JPanel createDesksPanel() {
JPanel desksPanel = new JPanel(new GridLayout(1, NUM_DESKS, 10, 5));
desksPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
for (int i = 0; i < NUM_DESKS; i++) {
deskLabels[i] = new JLabel("Free", SwingConstants.CENTER);
deskLabels[i].setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
deskLabels[i].setFont(new Font("Arial", Font.BOLD, 20));
desksPanel.add(deskLabels[i]);
}
desksPanel.setBackground(Color.WHITE);
return desksPanel;
}
private JPanel createQueuePanel() {
JPanel queuePanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 5));
queuePanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20), "Customers Waiting", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Arial", Font.BOLD, 24), Color.BLUE));
queuePanel.setBackground(Color.WHITE);
return queuePanel;
}
private JButton createAddCustomerButton() {
JButton addCustomerButton = new JButton("Add Customer");
addCustomerButton.setFont(new Font("Arial", Font.BOLD, 20));
addCustomerButton.setHorizontalTextPosition(SwingConstants.LEADING);
addCustomerButton.setForeground(Color.WHITE);
addCustomerButton.setBackground(Color.GREEN.darker());
addCustomerButton.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
addCustomerButton.addActionListener(this::addCustomer);
return addCustomerButton;
}
private void addCustomer(ActionEvent e) {
// Name of the customer's icon file
String iconName = "man.png";
// Load the icon to represent the customer
ImageIcon customerIcon = new ImageIcon(getClass().getResource(iconName));
// Resize the icon
Image image = customerIcon.getImage();
Image scaledImage = image.getScaledInstance(50, 50, Image.SCALE_SMOOTH);
ImageIcon scaledIcon = new ImageIcon(scaledImage);
// Add the customer's ID to the queue
customerQueue.offer(iconName);
// Update the queue display
updateQueueDisplay(scaledIcon);
// Serve the next customer
serveNextCustomer();
}
private void serveNextCustomer() {
for (int i = 0; i < NUM_DESKS; i++) {
if (deskLabels[i].getText().equals("Free") && !customerQueue.isEmpty()) {
String customerId = customerQueue.poll();
deskLabels[i].setText(customerId);
simulateServiceTime(i);
updateQueueDisplay(new ImageIcon(getClass().getResource(customerId))); // Correction here
return;
}
}
}
private void simulateServiceTime(final int deskIndex) {
new Thread(() -> {
try {
Thread.sleep(serviceTime); // Simulate service time
deskLabels[deskIndex].setText("Free");
serveNextCustomer(); // Check if there are more customers to serve
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
private void updateQueueDisplay(ImageIcon customerIcon) {
JPanel queuePanel = (JPanel) ((JPanel) getContentPane().getComponent(0)).getComponent(1);
// Remove only the JLabels that have an icon
for (Component component : queuePanel.getComponents()) {
if (component instanceof JLabel) {
JLabel customerLabel = (JLabel) component;
if (customerLabel.getIcon() != null) {
queuePanel.remove(customerLabel);
}
}
}
// Add a JLabel for each customer in the queue
for (String customerId : customerQueue) {
JLabel customerLabel = new JLabel(customerId);
customerLabel.setIcon(customerIcon); // Set the icon for the customer
customerLabel.setPreferredSize(new Dimension(50, 50)); // Set preferred dimensions for the icon
queuePanel.add(customerLabel);
}
// Update the layout of the queue panel
queuePanel.revalidate();
queuePanel.repaint();
}
}
Я пробовал искать решения в Интернете, но безрезультатно.
Обратите внимание, что этот код нарушает правила потоковой обработки Swing, и хотя это может и не вызывать текущую проблему, это, скорее всего, будет способствовать возникновению проблем в будущем.
Я бы создал класс модели, который содержит очередь, и предоставил бы ему поддержку изменения свойств Swing, позволяя другим классам добавлять или удалять прослушиватель изменения свойств в этом классе с помощью методов добавления и удаления прослушивателя. Всякий раз, когда очередь изменяется, я просил службу поддержки уведомлять слушателей, а затем обновлять графическое представление очереди, возможно, JList, содержащий объекты Customer, и с помощью настраиваемого средства рендеринга ячеек списка. Я бы использовал SwingWorker или Swing Timer, чтобы эмулировать обработку клиентов в очереди.
У меня нет проблем с вашим кодом — как только я предоставил свои собственные изображения
«но вместо того, чтобы удалять его по одному, он удаляет их все». - Итак, это немного сбивает с толку. Во-первых, если у вас в очереди менее 4 клиентов, ВСЕ они будут обслужены сразу (более или менее). Если у вас есть ход 4 таможен (4 отрезаны плюс дополнительные), то необслуженные клиенты будут поставлены в очередь. Ваш код, хотя и ошибочный в реализации, похоже, работает.
Плохое название. Перепишите, чтобы кратко изложить вашу конкретную техническую проблему.




У меня нет проблем с запуском вашего кода, однако, как только я предоставлю свои собственные изображения, у меня возникнет ряд проблем с вашим кодом.
Кажется, это зря потраченное время и силы...
private void updateQueueDisplay(ImageIcon customerIcon) {
JPanel queuePanel = (JPanel) ((JPanel) getContentPane().getComponent(0)).getComponent(1);
// Remove only the JLabels that have an icon
for (Component component : queuePanel.getComponents()) {
if (component instanceof JLabel) {
JLabel customerLabel = (JLabel) component;
if (customerLabel.getIcon() != null) {
queuePanel.remove(customerLabel);
}
}
}
// Add a JLabel for each customer in the queue
for (String customerId : customerQueue) {
JLabel customerLabel = new JLabel(customerId);
customerLabel.setIcon(customerIcon); // Set the icon for the customer
customerLabel.setPreferredSize(new Dimension(50, 50)); // Set preferred dimensions for the icon
queuePanel.add(customerLabel);
}
// Update the layout of the queue panel
queuePanel.revalidate();
queuePanel.repaint();
}
removeAll на queuePanel, чтобы в любом случае удалить все существующие компоненты, это будет проще и чище.JPanel queuePanel = (JPanel) ((JPanel) getContentPane().getComponent(0)).getComponent(1); опасно и подвержено ошибкам. Вы должны поддерживать реальную ссылку на панель и использовать ее напрямую. Нет никакой гарантии, что порядок компонентов останется таким, каким вы его ожидаете.Следующий...
private void simulateServiceTime(final int deskIndex) {
new Thread(() -> {
try {
Thread.sleep(serviceTime); // Simulate service time
deskLabels[deskIndex].setText("Free");
serveNextCustomer(); // Check if there are more customers to serve
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Swing НЕ является потокобезопасным (и является однопоточным). По сути, это означает, что вам не следует обновлять пользовательский интерфейс из любого потока, кроме потока диспетчеризации событий. Хотя есть несколько способов исправить это, вы можете обнаружить, что Swing Timer предлагает подходящее решение. Помните, что большее количество потоков не всегда означает, что будет выполнено больше работы.
Если бы я подходил к этой проблеме, я бы...
Однако я постараюсь сделать это немного проще. В следующем примере добавляется дополнительный массив для отслеживания того, когда каждая стойка начала обслуживать клиента. Swing Timer используется для мониторинга столов и определения того, когда они станут свободными, а затем направляет следующий заказ на следующий свободный стол.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Post Office");
frame.add(new PostOfficePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PostOfficePane extends JPanel {
private static final int NUM_DESKS = 4;
private final JLabel[] deskLabels = new JLabel[NUM_DESKS];
// Stores the time a service desk started serving a customer
private final Instant[] deskServiceTimes = new Instant[NUM_DESKS];
private final Map<String, JLabel> customerLabels = new HashMap<>();
private final Queue<String> customerQueue = new LinkedList<>();
private int customerNumber = 1;
private final int serviceTime = 3000;
private Timer timer;
private JPanel queuePanel;
public PostOfficePane() {
initializeUI();
}
private void initializeUI() {
JPanel mainPanel = new JPanel(new BorderLayout());
add(mainPanel);
mainPanel.add(createDesksPanel(), BorderLayout.NORTH);
mainPanel.add(getQueuePane(), BorderLayout.CENTER);
mainPanel.add(createAddCustomerButton(), BorderLayout.SOUTH);
}
private JPanel createDesksPanel() {
JPanel desksPanel = new JPanel(new GridLayout(1, NUM_DESKS, 10, 5));
desksPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
for (int i = 0; i < NUM_DESKS; i++) {
deskLabels[i] = new JLabel("Free", SwingConstants.CENTER);
deskLabels[i].setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
deskLabels[i].setFont(new Font("Arial", Font.BOLD, 20));
desksPanel.add(deskLabels[i]);
}
desksPanel.setBackground(Color.WHITE);
return desksPanel;
}
private JPanel getQueuePane() {
if (queuePanel != null) {
return queuePanel;
}
JPanel queuePanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 5));
queuePanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20), "Customers Waiting", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Arial", Font.BOLD, 24), Color.BLUE));
queuePanel.setBackground(Color.WHITE);
this.queuePanel = queuePanel;
return queuePanel;
}
private JButton createAddCustomerButton() {
JButton addCustomerButton = new JButton("Add Customer");
addCustomerButton.setFont(new Font("Arial", Font.BOLD, 20));
addCustomerButton.setHorizontalTextPosition(SwingConstants.LEADING);
addCustomerButton.setForeground(Color.WHITE);
addCustomerButton.setBackground(Color.GREEN.darker());
addCustomerButton.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
addCustomerButton.addActionListener(this::addCustomer);
return addCustomerButton;
}
private ImageIcon getCustomerIcon() {
// Name of the customer's icon file
String iconName = "/resources/icons/apple48.png";
// Load the icon to represent the customer
ImageIcon customerIcon = new ImageIcon(getClass().getResource(iconName));
return customerIcon;
}
private void serveNextCustomer() {
if (timer != null && timer.isRunning()) {
return;
}
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < NUM_DESKS; i++) {
// If the desk has a service time, check if it's done...
if (deskServiceTimes[i] != null) {
Instant startTime = deskServiceTimes[i];
Instant endTime = Instant.now();
Duration duration = Duration.between(startTime, endTime);
if (duration.toMillis() < serviceTime) {
continue;
}
deskServiceTimes[i] = null;
deskLabels[i].setText("Free");
} else if (customerQueue.peek() != null) {
// Else the desk is free and we can serve the
// next customer
String customerId = customerQueue.poll();
deskLabels[i].setText(customerId);
deskServiceTimes[i] = Instant.now();
getQueuePane().remove(customerLabels.get(customerId));
getQueuePane().revalidate();
getQueuePane().repaint();
}
}
// You could revalidate and repaint the pane, but that
// might be overkill based on the functionality of the app
}
});
timer.start();
}
private void addCustomer(ActionEvent e) {
String id = Integer.toString(customerNumber);
customerQueue.offer(id);
customerNumber++;
JLabel label = new JLabel(getCustomerIcon());
label.setName(id);
// Associate the id with the label for easier lookup
customerLabels.put(id, label);
getQueuePane().add(label);
getQueuePane().revalidate();
getQueuePane().repaint();
serveNextCustomer();
}
}
}
Дополнительный совет: удалите отвлекающий и бесполезный текст из вашего вопроса, например: «Я пытался найти решения в Интернете, но безуспешно»*. Вместо этого, если вы хотите предоставить нам эту информацию в полезной форме, расскажите нам, что именно вы искали, что вы нашли и почему это вам не помогло.