Java/Swing: How to pass properties from Boundary to Model and vice versa

huangapple go评论61阅读模式
英文:

Java/Swing: How to pass properties from Boundary to Model and vice versa

问题

我正在构建一个应用程序,用户可以上传图像并在其上绘制矩形进行注释,还可以将评论附加到特定的区域/矩形,就像在Word或Docs中一样。关于如何在类之间建立连接以传递信息,我有些困惑。用户绘制一个矩形并点击它后,他们将能够在提供的JTextField中编写评论,程序将把该矩形与该评论关联起来。我有三个类:BoxDrawingAreaImageAnnotator。Box(即矩形)的一个字段/属性是bComment,在ImageAnnotator类中我有一个JButton,点击它时需要将从ImageAnnotator类中检索到的字符串设置为Box.bComment。我知道onClick方法应该位于ImageAnnotator类中,因为按钮就在那里,但我对如何将此字符串传递给Box有些困惑。我应该在ImageAnnotator内导入Box,并在onClick方法内设置它吗?那么现在反过来,当点击矩形时,如何使用从Box.bComment检索到的字符串设置ImageAnnotator中的JTextField?也就是说,当用户再次点击相同的矩形时,程序将在文本字段中显示先前添加的评论。所有点击矩形的监听器都在DrawingArea类中,所以我需要以某种方式从ImageAnnotator获取文本字段,并用从Box.bComment检索到的字符串填充文本字段。为了更清楚起见,我提供了以下类的代码:

ImageAnnotator.java:

public class ImageAnnotator extends JFrame {
    // ... 其他部分

    public ImageAnnotator(Model m) {
        super();
        // ... 其他部分

        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String comment = textField.getText(); // 获取文本框中的评论
                Box selectedBox = drawingArea.getSelectedBox(); // 获取选定的矩形
                if (selectedBox != null) {
                    selectedBox.setComment(comment); // 将评论设置到选定的矩形
                    drawingArea.repaint(); // 重新绘制以显示评论
                }
            }
        });

        // ... 其他部分
    }
    
    // ... 其他部分
}

对于BoxDrawingArea类的更改,请查看以下代码:

Box.java:

public class Box {
    // ... 其他部分

    public void setComment(String comment) {
        bComment = comment;
    }

    // ... 其他部分
}

DrawingArea.java:

public class DrawingArea extends JPanel implements BoxSelectionListener {
    // ... 其他部分

    private Box selectedBox; // 添加一个字段来存储选定的矩形

    public Box getSelectedBox() {
        return selectedBox;
    }

    // ... 其他部分

    class MyMouseListener extends MouseInputAdapter {
        // ... 其他部分

        public void mouseReleased(MouseEvent e) {
            if (shape != null) {
                // ... 其他部分
            } else {
                for (Box b : rectangles) {
                    if (b.getRectangle().contains(e.getPoint())) {
                        didSelect(b);
                        selectedBox = b; // 选定这个矩形
                        break;
                    } else {
                        b.setForeground(Color.black);
                    }
                }
            }

            startPoint = null;
            shape = null;
        }
    }

    // ... 其他部分
}

请注意,上面提供的代码片段是根据你的描述进行的修改,以便在点击按钮时将评论关联到选定的矩形,并在点击矩形时从矩形中检索评论并显示在文本框中。你可能需要根据实际情况对代码进行调整和完善。

英文:

I'm building an app where user can upload an image and draw rectangles on top of it for annotation and also attach comments to that certain region/rectangle, like in Word or Docs. I'm a bit confused on how to make the connection between the classes to pass information around. Once user draws a rectangle and clicks on it, they will be able to write comments in the JTextField provided, and the program will associate that rectangle with that comment. I have three classes Box, DrawingArea, and ImageAnnotator. One of the fields/properties of Box (which is the rectangle) is bComment, and inside the ImageAnnotator class I have a JButton which upon click, will need to set the String retrieved from the JTextField in the ImageAnnotator class to Box.bComment. I know the onClick method should be inside the ImageAnnotator class since that's where the button is but I'm a bit confused to how to pass this String over to Box. Should I import Box inside the ImageAnnotator and set it within an onClick method? So now the reverse way, upon clicking the rectangle, how do I set the JTextField in ImageAnnotator with the String retrieved from Box.bComment, that is, when the user clicks on the same rectangle again, the program will display the previously added comment in the text field. All the listeners for clicking on rectangle is inside the DrawingArea class so I would need to somehow get the Text field from ImageAnnotator and fill the text field with String retrieved from Box.bComment. For more clarity, I have provided the classes below.

ImageAnnotator.java:

public class ImageAnnotator extends JFrame {

	private JPanel contentPane;
	Model model;
    DrawingArea drawingArea;
	GroupLayout gl_contentPane;
	private JTextField textField;
	private JButton btnNewButton;
	/**
	 * Create the frame.
	 */
	public ImageAnnotator(Model m) {
		super();
		this.model = m;
		setTitle("Image Annotator");
		...
		setContentPane(contentPane);
		drawingArea = new DrawingArea();
		
		//ImageName = new JLabel(drawingArea.imageName);
		buttonPanel = new ButtonPanel( drawingArea );
		
		textField = new JTextField(); // this is the text field which will be later set to Box.bComment
		
		btnNewButton = new JButton("Add this comment");//this is the button which needs onClick listener
		
		gl_contentPane = new GroupLayout(contentPane);
		gl_contentPane.setHorizontalGroup(...);
		gl_contentPane.setVerticalGroup(...);
		contentPane.add(drawingArea);
		contentPane.setLayout(gl_contentPane);
	}
    
    // onClick listener should be here for JButton
}

Box.java:

public class Box {
	int bWidth, bHeight, bX, bY;
    String bImageName, bComment;
    
    Color foreground;
	Rectangle rectangle;
    
    public Box(int width, int height) {
    	bWidth = width;
    	bHeight = height;
    }
    
    public Box(Color foreground, Rectangle rectangle) {
    	this.foreground = foreground;
		this.rectangle = rectangle;
    }
    
    public void setComment(String comment) { bComment = comment; }
    public String getComment() { return bComment; }
    
    public void setX(int x) { bX = x; }
    public int getX() { return bX; }
    
    public void setY(int y) { bY = y; }
    public int getY() { return bY; }
   
	public Rectangle getRectangle()
	{
		return rectangle;
	}
}

DrawingArea.java:

public class DrawingArea extends JPanel implements BoxSelectionListener
	{
		private final static int AREA_SIZE = 490;
		private BufferedImage image =
			new BufferedImage(AREA_SIZE, AREA_SIZE, BufferedImage.TYPE_INT_ARGB);
		private Rectangle shape;
		private ArrayList<Box> rectangles = new ArrayList<Box>();
		//public String imageName = ""; // this will store the image/file name

		public DrawingArea()
		{
			setBackground(Color.WHITE);
			MyMouseListener ml = new MyMouseListener();
			addMouseListener(ml);
			addMouseMotionListener(ml);
		}
		
		public void addBoxSelectionListener(BoxSelectionListener listener) {
		    listenerList.add(BoxSelectionListener.class, listener);
		}

		public void removeBoxSelectionListener(BoxSelectionListener listener) {
		    listenerList.remove(BoxSelectionListener.class, listener);
		}
			
		protected void fireBoxSelected(Box box) {
		    BoxSelectionListener[] listeners = listenerList.getListeners(BoxSelectionListener.class);
		    // Normally, I'd create a event object, which would wrap the source (this) and
		    // the Box together, but if there are no listeners, it's a bit of
		    // a waste to do so, so I return early in those cases
		    if (listeners.length == 0) {
		        return;
		    }
		    for (BoxSelectionListener listener : listeners) {
		        listener.didSelect(box);
		    }
		}

		public void didSelect(Box box) {
		    // Probably assign this to a "assigned" or "selected" property
		    // so it can painted differently
		    // And now we want to notify some kind of listener so that
		    // it can update the UI as required
			box.setForeground(Color.red);
			box.getComment();
			repaint();
		    fireBoxSelected(box);
		}
		
		class MyMouseListener extends MouseInputAdapter
		{
			private Point startPoint;

		    public void mousePressed(MouseEvent e) {
		        // Mark the clip point
		        startPoint = e.getPoint();
		    }

		    public void mouseDragged(MouseEvent e) {
		        // Only create the shape when dragging starts
		        if (shape == null) {
		            shape = new Rectangle();
		        }
		        int x = Math.min(startPoint.x, e.getX());
		        int y = Math.min(startPoint.y, e.getY());
		        int width = Math.abs(startPoint.x - e.getX());
		        int height = Math.abs(startPoint.y - e.getY());

		        shape.setBounds(x, y, width, height);
		        repaint();
		    }

		    public void mouseReleased(MouseEvent e) {
		        if (shape != null) {
		            if (shape.width != 0 || shape.height != 0) {
		                addRectangle(shape, e.getComponent().getForeground());
		            }
		        } else {
		            for (Box b : rectangles) {
		                if (b.getRectangle().contains(e.getPoint())) {
		                    didSelect(b);
		                    break;
		                }
		                else 
		                	b.setForeground(Color.black);
		            }
		        }

		        startPoint = null;
		        shape = null;
		    }
		}
		
	}

答案1

得分: -1

我通过将一些字段设置为静态字段(例如 TextField),使其在另一个类中可以引用,从而使这些内容正常工作。我的错误在于我在 DrawingArea 类中创建了 ImageAnnotator 类的实例,所以当我尝试在 DrawingArea 中引用 ImageAnnotator 中的 TextField 时,它是空的。因此,我的解决方案是直接调用 ImageAnnotator 而不是创建一个实例,并将 TextField 设置为静态字段。

英文:

I was able to get those working by making some fields static (the TextField) so that I can reference it from another class, and my mistake was that I was creating instances of the ImageAnnotator class in my DrawingArea class so when I try to reference the TextField from ImageAnnotator inside DrawingArea, it was null. So my solution was instead I called ImageAnnotator directly without creating an instance and I set the TextField to static.

huangapple
  • 本文由 发表于 2020年9月28日 10:03:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/64095071.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定