Приложение с интерфейсом Java

Мне нужно сделать приложение, которое управляет очередью в почтовом отделении только с одним входом и 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();
    }
}

Я пробовал искать решения в Интернете, но безрезультатно.

Дополнительный совет: удалите отвлекающий и бесполезный текст из вашего вопроса, например: «Я пытался найти решения в Интернете, но безуспешно»*. Вместо этого, если вы хотите предоставить нам эту информацию в полезной форме, расскажите нам, что именно вы искали, что вы нашли и почему это вам не помогло.

Hovercraft Full Of Eels 11.04.2024 22:36

Обратите внимание, что этот код нарушает правила потоковой обработки Swing, и хотя это может и не вызывать текущую проблему, это, скорее всего, будет способствовать возникновению проблем в будущем.

Hovercraft Full Of Eels 11.04.2024 22:41

Я бы создал класс модели, который содержит очередь, и предоставил бы ему поддержку изменения свойств Swing, позволяя другим классам добавлять или удалять прослушиватель изменения свойств в этом классе с помощью методов добавления и удаления прослушивателя. Всякий раз, когда очередь изменяется, я просил службу поддержки уведомлять слушателей, а затем обновлять графическое представление очереди, возможно, JList, содержащий объекты Customer, и с помощью настраиваемого средства рендеринга ячеек списка. Я бы использовал SwingWorker или Swing Timer, чтобы эмулировать обработку клиентов в очереди.

Hovercraft Full Of Eels 11.04.2024 23:50

У меня нет проблем с вашим кодом — как только я предоставил свои собственные изображения

MadProgrammer 12.04.2024 00:34

«но вместо того, чтобы удалять его по одному, он удаляет их все». - Итак, это немного сбивает с толку. Во-первых, если у вас в очереди менее 4 клиентов, ВСЕ они будут обслужены сразу (более или менее). Если у вас есть ход 4 таможен (4 отрезаны плюс дополнительные), то необслуженные клиенты будут поставлены в очередь. Ваш код, хотя и ошибочный в реализации, похоже, работает.

MadProgrammer 12.04.2024 01:20

Плохое название. Перепишите, чтобы кратко изложить вашу конкретную техническую проблему.

Basil Bourque 12.04.2024 01:23
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня нет проблем с запуском вашего кода, однако, как только я предоставлю свои собственные изображения, у меня возникнет ряд проблем с вашим кодом.

Кажется, это зря потраченное время и силы...

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();
}
  1. Поскольку вы просто добавляете всех клиентов в очередь, вы можете просто вызвать removeAll на queuePanel, чтобы в любом случае удалить все существующие компоненты, это будет проще и чище.
  2. 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();
        }
    }
}

Другие вопросы по теме