I am creating a rectangle with my mouse and i'm just trying to return info from the created object with a mouseover. I've been solving this with a messabox but there's just not many country to mouseover so the number of popup can be monstrous.
I decided to use a tooltip. Thing is it's not working on my mouse over :
JTextField text = new JTextField();
if (coordX > coordXRec && coordX < coordXRec + width && coordY > coordYrec && coordY < coordYrec + height){
text.setToolTipText(i.GetInfoPays());
text.getToolTipText();
}
Naturally, i want it to show when i mouseover my rectangle created :
That green rectangle was made by the user so i can't 'preset' an event
or use it as a panel for my tooltip.
This is what excepted, but using a tooltiptext, not a messabox.
I've been using a MouseEventMoved to know if im hovering over my rectangle. Its working but i'm stuck at sort of changing my messagebox into a tooltip.
Use setToolTipText("..."); with the appropriate String when needed (e.g. during paintComponent()).
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.util.*;
public class MouseShapeDetection {
private JComponent ui = null;
MouseShapeDetection() {
initUI();
}
public final void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
ui.add(new ShapePanel());
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
MouseShapeDetection o = new MouseShapeDetection();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
class ShapePanel extends JPanel {
Point point = new Point(0, 0);
Dimension preferredSize = new Dimension(600, 300);
ArrayList<Shape> shapes = new ArrayList<>();
Color translucent = new Color(0,0,255,87);
Color selectedColor = Color.GREEN;
Color unselectedColor = Color.RED;
public ShapePanel() {
this.addMouseMotionListener(new MotionListener());
setBackground(Color.WHITE);
Random r = new Random();
int x, y, w, h, wP = preferredSize.width, hP = preferredSize.height;
for (int ii = 0; ii < 40; ii++) {
w = r.nextInt(100)+40;
h = r.nextInt(50)+20;
x = r.nextInt(wP - w);
y = r.nextInt(hP - h);
Ellipse2D.Double ellipse = new Ellipse2D.Double(x, y, w, h);
shapes.add(ellipse);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
StringBuilder sb = new StringBuilder("Shapes: ");
Graphics2D g2 = (Graphics2D)g;
for (int ii=0; ii<shapes.size(); ii++) {
Shape shape = shapes.get(ii);
g2.setColor(translucent);
g2.fill(shape);
if (shape.contains(point)) {
g2.setColor(selectedColor);
sb.append(ii);
sb.append(" ");
} else {
g2.setColor(unselectedColor);
}
g2.setStroke(new BasicStroke(2.5f));
g2.draw(shape);
}
setToolTipText(sb.toString());
}
#Override
public Dimension getPreferredSize() {
return preferredSize;
}
class MotionListener extends MouseMotionAdapter {
#Override
public void mouseMoved(MouseEvent e) {
point = e.getPoint();
repaint();
}
}
}
Related
Is there any way to create a feature which allows a user to toggle screen magnification if they are visually impaired.
I'm asking in context of a program like eclipse, where a user who is visually impaired can toggle on and off a feature which magnifies the text, icons and navigation bar.
I use this short code for a general magnifier when cropping images.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class ZoomOnMouse {
Robot robot;
int zoomFactor = 2;
PointerInfo pi;
JPanel gui;
JLabel output;
Timer t;
public ZoomOnMouse() throws AWTException {
robot = new Robot();
gui = new JPanel(new BorderLayout(2, 2));
output = new JLabel("Point at something to see it zoomed!");
gui.add(output, BorderLayout.PAGE_END);
final int size = 256;
final BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_RGB);
final JLabel zoomLabel = new JLabel(new ImageIcon(bi));
gui.add(zoomLabel, BorderLayout.CENTER);
MouseListener factorListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (zoomFactor == 2) {
zoomFactor = 4;
} else if (zoomFactor == 4) {
zoomFactor = 8;
} else if (zoomFactor == 8) {
zoomFactor = 2;
}
showInfo();
}
};
zoomLabel.addMouseListener(factorListener);
ActionListener zoomListener = (ActionEvent e) -> {
pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Rectangle r = new Rectangle(
p.x - (size / (2 * zoomFactor)),
p.y - (size / (2 * zoomFactor)),
(size / zoomFactor),
(size / zoomFactor));
BufferedImage temp = robot.createScreenCapture(r);
Graphics g = bi.getGraphics();
g.drawImage(temp, 0, 0, size, size, null);
g.setColor(new Color(255,0,0,128));
int x = (size/2)-1;
int y = (size/2)-1;
g.drawLine(0,y,size,y);
g.drawLine(x,0,x,size);
g.dispose();
zoomLabel.repaint();
showInfo();
};
t = new Timer(40, zoomListener);
t.start();
}
public void stop() {
t.stop();
}
public Component getGui() {
return gui;
}
public void showInfo() {
pi = MouseInfo.getPointerInfo();
output.setText("Zoom: " + zoomFactor + " Point: " + pi.getLocation());
}
public static void main(String[] args) {
Runnable r = () -> {
try {
final ZoomOnMouse zm = new ZoomOnMouse();
final JFrame f = new JFrame("Mouse Zoom");
f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
f.add(zm.getGui());
f.setResizable(false);
f.pack();
f.setLocationByPlatform(true);
f.setAlwaysOnTop(true);
f.setVisible(true);
WindowListener closeListener = new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
zm.stop();
f.dispose();
}
};
f.addWindowListener(closeListener);
} catch (AWTException e) {
e.printStackTrace();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
I hope some one can help me, this is what I want to do.
I have a JTextPane and I want to take a screenshot to that specific JTextPane coordinates and size, so far I can do a screenshot with the size of the JTextPane but I can't get the specific coordinates my screenshots always gets the (0,0) coordinates.
This is my method:
void capturaPantalla ()
{
try
{
int x = txtCodigo.getX();
int y = txtCodigo.getY();
Rectangle areaCaptura = new Rectangle(x, y, txtCodigo.getWidth(), txtCodigo.getHeight());
BufferedImage capturaPantalla = new Robot().createScreenCapture(areaCaptura);
File ruta = new File("P:\\captura.png");
ImageIO.write(capturaPantalla, "png", ruta);
JOptionPane.showMessageDialog(null, "Codigo de barras guardado!");
}
catch (IOException ioe)
{
System.out.println(ioe);
}
catch(AWTException ex)
{
System.out.println(ex);
}
}
When you call getX() and getY() on any Swing component, you get the x and y relative to the component's container, not the screen. Instead you want the location of the component relative to the screen and get position based on that via getLocationOnScreen()
Point p = txtCodigo.getLocationOnScreen();
int x = p.x;
int y = p.y;
As per MadProgrammer's comment, you could simply call printAll(Graphics g) on your txtCodigo component, passing in a Graphics object obtained from a properly sized BufferedImage and forgo use of a Robot.
Dimension d = txtCodigo.getSize();
BufferedImage img = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
txtCodigo.printAll(g);
g.dispose();
// use ImageIO to write BufferedImage to file
To compare to the two methods:
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class RobotVsPrintAll extends JPanel {
private static final int WORDS = 400;
private JTextArea textArea = new JTextArea(20, 40);
private JScrollPane scrollPane = new JScrollPane(textArea);
private Random random = new Random();
public RobotVsPrintAll() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < WORDS; i++) {
int wordLength = random.nextInt(4) + 4;
for (int j = 0; j < wordLength; j++) {
char myChar = (char) (random.nextInt('z' - 'a' + 1) + 'a');
sb.append(myChar);
}
sb.append(" ");
}
textArea.setText(sb.toString());
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JButton robot1Btn = new JButton(new Robot1Action("Robot 1"));
JButton robot2Btn = new JButton(new Robot2Action("Robot 2"));
JButton printAllBtn = new JButton(new PrintAllAction("Print All"));
JPanel btnPanel = new JPanel();
btnPanel.add(robot1Btn);
btnPanel.add(robot2Btn);
btnPanel.add(printAllBtn);
setLayout(new BorderLayout());
add(scrollPane, BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
private void displayImg(BufferedImage img) {
ImageIcon icon = new ImageIcon(img);
JOptionPane.showMessageDialog(this, icon, "Display Image",
JOptionPane.PLAIN_MESSAGE);
}
private class Robot1Action extends AbstractAction {
public Robot1Action(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
try {
Component comp = scrollPane.getViewport();
Point p = comp.getLocationOnScreen();
Dimension d = comp.getSize();
Robot robot = new Robot();
Rectangle screenRect = new Rectangle(p.x, p.y, d.width, d.height);
BufferedImage img = robot.createScreenCapture(screenRect);
displayImg(img);
} catch (AWTException e1) {
e1.printStackTrace();
}
}
}
private class Robot2Action extends AbstractAction {
public Robot2Action(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
try {
Component comp = textArea;
Point p = comp.getLocationOnScreen();
Dimension d = comp.getSize();
Robot robot = new Robot();
Rectangle screenRect = new Rectangle(p.x, p.y, d.width, d.height);
BufferedImage img = robot.createScreenCapture(screenRect);
displayImg(img);
} catch (AWTException e1) {
e1.printStackTrace();
}
}
}
private class PrintAllAction extends AbstractAction {
public PrintAllAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
public void actionPerformed(ActionEvent e) {
Dimension d = textArea.getSize();
BufferedImage img = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
textArea.printAll(g);
g.dispose();
displayImg(img);
}
}
private static void createAndShowGui() {
RobotVsPrintAll mainPanel = new RobotVsPrintAll();
JFrame frame = new JFrame("Robot Vs PrintAll");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
If you print the text component with printAll, you get the entire text component, even the parts that are not displayed in the JScrollPane's viewport.
You can use the Screen Image class which does all the work for you. All you do is specify the component you want to capture.
The code would be:
BufferedImage bi = ScreenImage.createImage( component );
And you can save the image to a file using:
ScreenImage.writeImage(bi, "imageName.jpg");
This class will use the painting method of the Swing component which is more efficient than using a Robot.
What I want my app to do:
1 - Select an area of Image and get the coordinates. This code below should do this:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
public class ScreenCaptureRectangle {
Rectangle captureRect;
ScreenCaptureRectangle(final BufferedImage screen) {
final BufferedImage screenCopy = new BufferedImage(
screen.getWidth(),
screen.getHeight(),
screen.getType());
final JLabel screenLabel = new JLabel(new ImageIcon(screenCopy));
JScrollPane screenScroll = new JScrollPane(screenLabel);
screenScroll.setPreferredSize(new Dimension(
(int)(screen.getWidth()/3),
(int)(screen.getHeight()/3)));
JPanel panel = new JPanel(new BorderLayout());
panel.add(screenScroll, BorderLayout.CENTER);
final JLabel selectionLabel = new JLabel(
"Drag a rectangle in the screen shot!");
panel.add(selectionLabel, BorderLayout.SOUTH);
repaint(screen, screenCopy);
screenLabel.repaint();
screenLabel.addMouseMotionListener(new MouseMotionAdapter() {
Point start = new Point();
#Override
public void mouseMoved(MouseEvent me) {
start = me.getPoint();
repaint(screen, screenCopy);
selectionLabel.setText("Start Point: " + start);
screenLabel.repaint();
}
#Override
public void mouseDragged(MouseEvent me) {
Point end = me.getPoint();
captureRect = new Rectangle(start,
new Dimension(end.x-start.x, end.y-start.y));
repaint(screen, screenCopy);
screenLabel.repaint();
selectionLabel.setText("Rectangle: " + captureRect);
}
});
JOptionPane.showMessageDialog(null, panel);
System.out.println("Rectangle of interest: " + captureRect);
}
public void repaint(BufferedImage orig, BufferedImage copy) {
Graphics2D g = copy.createGraphics();
g.drawImage(orig,0,0, null);
if (captureRect!=null) {
g.setColor(Color.RED);
g.draw(captureRect);
g.setColor(new Color(255,255,255,150));
g.fill(captureRect);
}
g.dispose();
}
public static void main(String[] args) throws Exception {
Robot robot = new Robot();
final Dimension screenSize = Toolkit.getDefaultToolkit().
getScreenSize();
final BufferedImage screen = robot.createScreenCapture(
new Rectangle(screenSize));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ScreenCaptureRectangle(screen);
}
});
}
}
2 - get the coordinates and use it on getSubimage method.
double w = captureRect.getWidth();
double h = captureRect.getHeight();
double x = captureRect.getX();
double y = captureRect.getY();
int W = (int) w;
int H = (int) h;
int X = (int) x;
int Y = (int) y;
BufferedImage selectImg = screen.getSubimage(x, y, w, h);
3 - this code create a new image file and copy the imageselected.
BufferedImage img = new BufferedImage ( 5000, 5000, BufferedImage.TYPE_INT_RGB );
img.createGraphics().drawImage(selectImg, 0, 0, null);
File final_image = new File("C:/Final.jpg");
ImageIO.write(img, "jpeg", final_image);
The idea of app is:
- Select an area of the image.
- Copy that image and paste in other file. ( when I pressed any button )
- The program will continue run until I press another button.
- Every image that I copy the program will paste it beside the last one.
I think I am near to the solution. Can any one help me to "connect the parts" ?
Start by taking a look at:
How to Write a Mouse Listener
How to Use Buttons, Check Boxes, and Radio Buttons
How to Write an Action Listeners
Performing Custom Painting
Writing/Saving an Image
You need to take the concepts you have and rework them into a coherent workable solution. That is, provide functionality between the areas you need (selecting a region and saving the file) so that they work cleanly together...
The following example takes a screenshot, allows you to select an area, click save and the file will be saved. The example checks to see how many files are already in the current directory and increments the count by 1 so you are not overwriting the existing files...
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScreenImage {
public static void main(String[] args) {
new ScreenImage();
}
public ScreenImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
Robot robot = new Robot();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final BufferedImage screen = robot.createScreenCapture(new Rectangle(screenSize));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(screen));
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (AWTException exp) {
exp.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
public TestPane(BufferedImage image) {
this.master = image;
setLayout(new BorderLayout());
final ImagePane imagePane = new ImagePane(image);
add(new JScrollPane(imagePane));
JButton btnSave = new JButton("Save");
add(btnSave, BorderLayout.SOUTH);
btnSave.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
BufferedImage img = imagePane.getSubImage();
master = append(master, img);
File save = new File("Capture.png");
ImageIO.write(master, "png", save);
imagePane.clearSelection();
JOptionPane.showMessageDialog(TestPane.this, save.getName() + " was saved", "Saved", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(TestPane.this, "Failed to save capture", "Error", JOptionPane.ERROR_MESSAGE);
}
}
public BufferedImage append(BufferedImage master, BufferedImage sub) {
// Create a new image which can hold both background and the
// new image...
BufferedImage newImage = new BufferedImage(
master.getWidth() + sub.getWidth(),
Math.max(master.getHeight(), sub.getHeight()),
BufferedImage.TYPE_INT_ARGB);
// Get new image's Graphics context
Graphics2D g2d = newImage.createGraphics();
// Draw the old background
g2d.drawImage(master, 0, 0, null);
// Position and paint the new sub image...
int y = (newImage.getHeight() - sub.getHeight()) / 2;
g2d.drawImage(sub, master.getWidth(), y, null);
g2d.dispose();
return newImage;
}
});
}
}
public class ImagePane extends JPanel {
private BufferedImage background;
private Rectangle selection;
public ImagePane(BufferedImage img) {
background = img;
MouseAdapter ma = new MouseAdapter() {
private Point clickPoint;
#Override
public void mousePressed(MouseEvent e) {
clickPoint = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
Point dragPoint = e.getPoint();
int x = Math.min(clickPoint.x, dragPoint.x);
int y = Math.min(clickPoint.y, dragPoint.y);
int width = Math.abs(clickPoint.x - dragPoint.x);
int height = Math.abs(clickPoint.y - dragPoint.y);
selection = new Rectangle(x, y, width, height);
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
public void clearSelection() {
selection = null;
repaint();
}
public BufferedImage getSubImage() {
BufferedImage img = null;
if (selection != null) {
img = background.getSubimage(selection.x, selection.y, selection.width, selection.height);
}
return img;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
if (selection != null) {
Color stroke = UIManager.getColor("List.selectionBackground");
Color fill = new Color(stroke.getRed(), stroke.getGreen(), stroke.getBlue(), 128);
g2d.setColor(fill);
g2d.fill(selection);
g2d.setColor(stroke);
g2d.draw(selection);
}
g2d.dispose();
}
}
}
So apart from rendering the selection the hardest part would be generating the resulting image...
Basically, this done by creating a new BufferedImage and painting the old image and the new, sub, image together.
public BufferedImage append(BufferedImage master, BufferedImage sub) {
// Create a new image which can hold both background and the
// new image...
BufferedImage newImage = new BufferedImage(
master.getWidth() + sub.getWidth(),
Math.max(master.getHeight(), sub.getHeight()),
BufferedImage.TYPE_INT_ARGB);
// Get new image's Graphics context
Graphics2D g2d = newImage.createGraphics();
// Draw the old background
g2d.drawImage(master, 0, 0, null);
// Position and paint the new sub image...
int y = (newImage.getHeight() - sub.getHeight()) / 2;
g2d.drawImage(sub, master.getWidth(), y, null);
g2d.dispose();
return newImage;
}
The example replaces the previous (master) image with the one created here, so it will constantly be appending new images to the end of it...
You need more listeners for button pressed and released.. some lines in the mouseMoved also better placed in mousePressed.
You would want to update your captureRect when you release the mouse (in mouseReleased method).
Then you just write it to the file. You may adjust other things according to your needs.
And for clarity maybe it's better to add a save button into your UI.
public class ScreenCaptureRectangle {
Rectangle captureRect;
Point start = new Point();
SimpleDateFormat sdf;
ScreenCaptureRectangle(final BufferedImage screen) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
final BufferedImage screenCopy = new BufferedImage(
screen.getWidth(),
screen.getHeight(),
screen.getType());
final JLabel screenLabel = new JLabel(new ImageIcon(screenCopy));
JScrollPane screenScroll = new JScrollPane(screenLabel);
screenScroll.setPreferredSize(new Dimension(
(int) (screen.getWidth() / 3),
(int) (screen.getHeight() / 3)));
JPanel panel = new JPanel(new BorderLayout());
panel.add(screenScroll, BorderLayout.CENTER);
JButton btnSave = new JButton("SAVE");
btnSave.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
double w = captureRect.getWidth();
double h = captureRect.getHeight();
double x = captureRect.getX();
double y = captureRect.getY();
int W = (int) w;
int H = (int) h;
int X = (int) x;
int Y = (int) y;
BufferedImage selectImg = screen.getSubimage(X, Y, W, H);
try {
String fName = generateFileName();
if (fName != null) {
File f = new File(fName);
if (f.createNewFile()) {
ImageIO.write(selectImg, "jpg", f);
}
}
} catch (IOException ex) {
Logger.getLogger(ScreenCaptureRectangle.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
panel.add(btnSave, BorderLayout.AFTER_LAST_LINE);
final JLabel selectionLabel = new JLabel(
"Drag a rectangle in the screen shot!");
panel.add(selectionLabel, BorderLayout.SOUTH);
repaint(screen, screenCopy);
screenLabel.repaint();
screenLabel.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
Point end = me.getPoint();
captureRect = new Rectangle(start,
new Dimension(end.x - start.x, end.y - start.y));
repaint(screen, screenCopy);
screenLabel.repaint();
selectionLabel.setText("Rectangle: " + captureRect);
}
});
screenLabel.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent me) {
start = me.getPoint();
repaint(screen, screenCopy);
selectionLabel.setText("Start Point: " + start);
screenLabel.repaint();
}
#Override
public void mouseReleased(MouseEvent me) {
int endX = me.getX();
int endY = me.getY();
if (endX > start.x && endY > start.y) {
captureRect = new Rectangle(start.x, start.y, endX-start.x, endY-start.y);
System.out.println("Rectangle of interest: " + captureRect);
}
}
});
JOptionPane.showMessageDialog(null, panel);
}
private String generateFileName() {
return new StringBuilder("screencrop_").append(sdf.format(new Date())).append(".jpg").toString();
}
public void repaint(BufferedImage orig, BufferedImage copy) {
Graphics2D g = copy.createGraphics();
g.drawImage(orig, 0, 0, null);
if (captureRect != null) {
g.setColor(Color.RED);
g.draw(captureRect);
g.setColor(new Color(255, 255, 255, 150));
g.fill(captureRect);
}
g.dispose();
}
public static void main(String[] args) throws Exception {
Robot robot = new Robot();
final Dimension screenSize = Toolkit.getDefaultToolkit().
getScreenSize();
final BufferedImage screen = robot.createScreenCapture(
new Rectangle(screenSize));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ScreenCaptureRectangle(screen);
}
});
}
}
I am just starting to put together a logging tool for my own use that would log statistics from gym/running and the only experience I have with swing/awt is active rendering for games where you have full control over the Graphics2D object and don't rely on implementing swing components with overriden paints.
Anyway, I was hoping to create a dummy JComponent that I can add to one of my panels (this panel will display graphics, statistics etc depending on what I select from another different sidepanel with options) that does nothing else but listen for mouseevents inside the panel mentioned earlier and draws a selection rectangle on mousedrags so that I can zoom in the data if higher resolutions exist. I just don't know how, I have added the component to the panel but it registers nothing inside the panel, instead it seems to have a local space that is limited to the bottom of the panel/frame.
Here is the component
package gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;
#SuppressWarnings("serial")
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
private Color color = new Color(0, 255, 0, 100);
public MarkerRectangle(int width, int height) {
setPreferredSize(new Dimension(width, height));
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked# " + e.getX() + "," + e.getY());
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse entered # " + e.getX() + "," + e.getY());
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Mouse left # " + e.getX() + "," + e.getY());
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed# " + e.getX() + "," + e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse was released # " + e.getX() + "," + e.getY());
}
#Override
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse being dragged, currently# " + e.getX() + ","
+ e.getY());
}
#Override
public void mouseMoved(MouseEvent e) {
System.out.println("I registered a move# " + e.getX() + "," + e.getY());
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
The panel
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
this.add(graph);
this.add(new MarkerRectangle(800, 600));
this.setBackground(Color.gray);
this.setPreferredSize(new Dimension(800, 600));
}
}
The graph, holds random data atm
public class Graph extends JComponent {
private GeneralPath data;
private Stroke stroke;
public Graph() {
this.data = new GeneralPath();
this.stroke = new BasicStroke(3f);
this.setPreferredSize(new Dimension(750, 550));
init();
}
private void init() {
data.moveTo(0, 0);
double cntr = 0;
double[][] points = new double[10][1];
for (double[] point : points) {
cntr += 100;
point[0] = Math.random() * 100;
point[1] = Math.random() * 100;
data.lineTo(cntr, point[1]);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(stroke);
g2d.draw(data);
g2d.dispose();
}
}
I want to implement something like the above because eventually I imagine the GUI to become quite complex and simple events like drawing a rectangle to mark data should not be in the main controller (so as to prevent a lot of if-tests and clutter in code).
Screenshot of what I get:
What I want:
EDIT
While the accepted answer below is the better solution I am posting this in the event that someone may want to use it. It will not work if you resize smaller the prefferedSize.
public class Test {
public static void main(String[] args) {
GeneralJFrame frame = new GeneralJFrame(1200, 800);
frame.addPanel(new GraphPane(new Graph()), BorderLayout.CENTER);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.WEST);
frame.addPanel(new ButtonPane(), BorderLayout.EAST);
frame.addPanel(new ButtonPane(), BorderLayout.NORTH);
frame.start();
}
}
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridheight = GridBagConstraints.REMAINDER;
c.gridx = 0;
c.gridy = 0;
this.setLayout(new GridBagLayout());
this.add(graph);
this.add(new MarkerRectangle(), c);
this.setBackground(new Color(205, 201, 201));
}
}
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
public MarkerRectangle() {
this.addMouseListener(this);
this.addMouseMotionListener(this);
setOpaque(false);
setPreferredSize(new Dimension(800, 600));
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
draw = true;
}
#Override
public void mouseReleased(MouseEvent e) {
draw = false;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
System.out.println(getSize());
if (!draw)
return;
int w = endX-startX;
int h = endY - startY;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(255, 165, 0));
g2d.fillRect(startX, startY, w, h);
g2d.dispose();
}
}
public class ButtonPane extends JPanel {
public ButtonPane() {
add(new JButton("HELLO"));
setBackground(Color.gray);
setBorder(BorderFactory.createEtchedBorder(Color.white,
Color.gray.darker()));
}
}
public class GeneralJFrame {
private JFrame frame;
public GeneralJFrame(int width, int height) {
this.frame = new JFrame("Le Muscles");
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void addPanel(JPanel panel, String location) {
this.frame.add(panel, location);
}
public void start() {
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Outputs: (orange is dragged with mouse)
I still don't fully understand your question. As I see it, you've
created several JPanels
Given one JPanel a MouseListener and MouseMotionListener
You've added that JPanel to the bottom of another JPanel.
The JPanel that's sitting on the bottom registers all mouse events as it has been told to do
So your program is behaving as expected based on your code.
The question I have is this: how is it not behaving as you expect it to?
If you expect that adding a JPanel with MouseListeners and MouseMotionListeners attached will result in all the JPanels of the GUI to be listened to, well of course that won't happen. To have more of the GUI register mouse events, you'll have to add MouseListeners and MouseMotionListeners to those components. And that is my answer so far to your question as I see it. If I didn't answer the true question you currently face, then please clarify it for us.
You state:
What I want is an invisible (transparent) panel on top of the blue one in the above screenshot that is just as large as the blue one, not a subpanel that is sitting in the bottom. I want the blue one to contain this one (should not be a problem since it is just a jcomponent). What I hope to achieve is a sort over "invisible" overlay that registers mousevents so I don't have to implement these events in the blue panel.
Consider using a JLayer for this. As per the JLayer API:
JLayer is a good solution if you only need to do custom painting over compound component or catch input events from its subcomponents.
OK, I've experimented with it a bit and came up with something like this:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
#SuppressWarnings("serial")
public class GraphPane2 extends JPanel {
private static final int GRAPH_WIDTH = 1000;
private static final int GRAPH_HEIGHT = 750;
private Graph2 graph2 = new Graph2(GRAPH_WIDTH, GRAPH_HEIGHT);
public GraphPane2() {
LayerUI<Graph2> myLayerUI = new MyLayerUI<Graph2>();
JLayer<Graph2> panelLayer = new JLayer<Graph2>(graph2, myLayerUI);
setLayout(new BorderLayout());
add(panelLayer);
myLayerUI.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (MyLayerUI.MOUSE_RELEASED.equals(evt.getPropertyName())) {
Rectangle rect = (Rectangle) evt.getNewValue();
System.out.println(rect);
}
}
});
}
private static void createAndShowGui() {
GraphPane2 mainPanel = new GraphPane2();
JFrame frame = new JFrame("Graph Pane2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyLayerUI<V extends JComponent> extends LayerUI<V> {
private static final Color FILL_COLOR = new Color(0, 128, 0, 128);
public static final String MOUSE_RELEASED = "mouse released";
private Point pressedPt;
private Point draggedPt;
private Rectangle rect;
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (rect != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(FILL_COLOR);
g2.fill(rect);
}
}
public void installUI(JComponent c) {
super.installUI(c);
((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
}
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
((JLayer)c).setLayerEventMask(0);
}
#Override
public void eventDispatched(AWTEvent e, JLayer<? extends V> l) {
MouseEvent mEvt = (MouseEvent) e;
int id = mEvt.getID();
int btn = mEvt.getButton();
if (id == MouseEvent.MOUSE_PRESSED && btn == MouseEvent.BUTTON1) {
pressedPt = mEvt.getPoint();
rect = new Rectangle(pressedPt.x, pressedPt.y, 0, 0);
}
if (id == MouseEvent.MOUSE_PRESSED && btn != MouseEvent.BUTTON1) {
pressedPt = null;
}
if (id == MouseEvent.MOUSE_DRAGGED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
}
if (id == MouseEvent.MOUSE_RELEASED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
firePropertyChange(MOUSE_RELEASED, null, rect);
}
l.repaint();
}
}
#SuppressWarnings("serial")
class Graph2 extends JPanel {
private static final int MAX_DATA_POINTS = 100;
private static final int STEP = 10;
private static final Stroke STROKE = new BasicStroke(3f);
private Path2D path2D;
private int width;
private int height;
private int[] data = new int[MAX_DATA_POINTS + 1];
private Random random = new Random();
public Graph2(int width, int height) {
this.width = width;
this.height = height;
init();
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
path2D = new Path2D.Double();
int w = getWidth();
int h = getHeight();
double x = 0;
double y = ((double) MAX_DATA_POINTS - data[0]) * h
/ MAX_DATA_POINTS;
path2D.moveTo(x, y);
for (int i = 1; i < data.length; i++) {
x = (i * w) / (double) MAX_DATA_POINTS;
y = ((double) MAX_DATA_POINTS - data[i]) * h
/ (double) MAX_DATA_POINTS;
path2D.lineTo(x, y);
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (path2D != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(STROKE);
g2d.draw(path2D);
}
};
private void init() {
// create and fill random data
data[0] = 0;
boolean up = true;
// max and min data values -- used for normalization
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 1; i < data.length; i++) {
up = random.nextInt(4) < 3 ? up : !up;
if (up) {
data[i] = data[i - 1] + random.nextInt(STEP);
} else {
data[i] = data[i - 1] - random.nextInt(STEP);
}
if (data[i] > max) {
max = data[i];
}
if (data[i] < min) {
min = data[i];
}
}
// normalize the data
for (int i = 0; i < data.length; i++) {
int datum = (MAX_DATA_POINTS * (data[i] - min)) / (max - min);
data[i] = datum;
}
}
}
This will look like:
I'm trying to set up dragging of an image over a grid of Jlabels.
it's working fine, but the image is "refreshing" and sometimes lags behind the mouse.
Is there a way to improve this and get a perfectly "smooth" movement of the image ?
EDIT: ok now, thanks to Andrew Thompson's advice I updated the paint() method to paintComponent(). But now why does my component disappear when I drag it? I'm probably missing something here...
EDIT2: why is the following behaviour: when using paint() method the component displays on top of the JLabels. But when using paintComponent() the component disappears being masked by the opaque Jlabels?
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class DragNDrop {
public static void main(String[] args)
{
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new DragPanel());
f.pack();
f.setLocation(200,200);
f.setVisible(true);
}
}
class DragPanel extends JPanel {
JLabel[][] labels;
SelectableAction action;
Image image;
Point p;
public DragPanel()
{
p = new Point();
setOpaque(true);
createLabels();
action = new SelectableAction(this);
addMouseListener(action);
addMouseMotionListener(action);
}
private void createLabels() {
labels = new JLabel[8][8];
Dimension dim50 = new Dimension(50,50);
GridBagConstraints gbc = new GridBagConstraints();
this.setLayout(new GridBagLayout());
for (int x=0;x<8;x++){
for (int y=0;y<8;y++){
labels[x][y] = new JLabel();
labels[x][y].setOpaque(true);
labels[x][y].setPreferredSize(dim50);
String str = new String("("+x+","+y+")");
labels[x][y].setText(str);
if ((x+y) % 2 == 0){
labels[x][y].setBackground(Color.lightGray);
} else
{
labels[x][y].setBackground(Color.white);
}
gbc.gridx = x;
gbc.gridy = 7-y;
this.add(labels[x][y],gbc);
}
URL url = getClass().getResource("images/50px-Knight.pgn");
Image img;
try {
img = ImageIO.read(url);
labels[0][0].setIcon(new ImageIcon(img));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public JLabel[][] getLabels()
{
return labels;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(action.dragging)
g.drawImage(image, p.x, p.y, this);
}
public void setImage(Image i)
{
image = i;
}
public void setOrigin(Point p)
{
this.p = p;
repaint();
}
}
/**
* Mouse code to enable image dragging
*/
class SelectableAction extends MouseInputAdapter
{
DragPanel MyPanel;
Image selectedImage;
boolean dragging;
Rectangle r;
Point offset;
public SelectableAction(DragPanel dp)
{
MyPanel = dp;
dragging = false;
offset = new Point();
}
public void mousePressed(MouseEvent e)
{
Point p = e.getPoint();
JLabel[][] labels = MyPanel.getLabels();
for(int i = 0; i < labels.length; i++)
{
for (int j=0;j<labels[0].length;j++){
r = labels[i][j].getBounds();
if(r.contains(p))
{
if ( ((ImageIcon)labels[i][j].getIcon()).getImage() != null) {
selectedImage = ((ImageIcon)labels[i][j].getIcon()).getImage();
MyPanel.setImage(selectedImage);
labels[i][j].setIcon(null);
offset.x = p.x - r.x;
offset.y = p.y - r.y;
dragging = true;
MyPanel.setOrigin(new Point(r.x, r.y));
break;
}
}
}
}
}
public void mouseReleased(MouseEvent e)
{
Point p = e.getPoint();
JLabel[][] labels = MyPanel.getLabels();
for(int i = 0; i < labels.length; i++)
{
for (int j=0;j<labels[0].length; j++){
r = labels[i][j].getBounds();
if(r.contains(p)) {
ImageIcon tmpIcon = new ImageIcon(selectedImage);
labels[i][j].setIcon(tmpIcon);
MyPanel.repaint();
dragging = false;
}
}
}
}
public void mouseDragged(MouseEvent e)
{
if(dragging)
{
r.x = e.getX() - offset.x;
r.y = e.getY() - offset.y;
MyPanel.setOrigin(new Point(r.x, r.y));
}
}
}
+1 to AndrewThompson and GuillaumePolet comments. especially:
It disappears because you have opaque children components that paint
themselves above.
to overcome this we dont need/want to override paintComponent(..) instead we want to override paintChildren(..) and call super.paintChildren(..) followed by our custom code. Thus all components will be drawn in call to super and our image will be drawn after, thus making it appear/visible above all others.
Replace the overridden paintComponent with below code and it will work:
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
if (action.dragging) {
g.drawImage(image, p.x, p.y, null);//also notice i use null and not this, unless the class you are using extends ImageObserver no need for this
}
}