I have to make an application that manages the queue at the post office with only one entrance and 4 available counters. I don't understand why when I go to add a new customer it queues up the icons but instead of removing it one at a time it removes them all.
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();
}
}
I tried searching online for solutions but to no avail
I don't have any issues running your code, once I provide my own images, I do, however, have a number of issues with your code.
This seems like a lot of wasted time and effort...
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
on the queuePanel
to remove all the existing components anyway, it would be simpler and cleaner.JPanel queuePanel = (JPanel) ((JPanel) getContentPane().getComponent(0)).getComponent(1);
is dangerous and error prone. You should be maintaining an actual reference to the panel and using it directly. There's no guarantee that the component order will remain the way you're expecting it.Next...
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 is NOT thread safe (and is single threaded). This essentially means that you should not be updating the UI from any thread other than the Event Dispatching Thread. While there are few ways you might fix this, you might find that a Swing Timer
provides a suitable solution. Remember, more threads doesn't always mean more work gets done.
If I was approaching this problem I would...
However, I'm going to try and keep it a little simpler. The following example adds an additional array to keep track of when each desk started serving a customer. A Swing Timer
is used to monitor the desks and determine when they become free and then direct the next custom to the next free desk.
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();
}
}
}