1

I have a chart embedded in a ChartPanel. When the panel is first rendered, everything looks good:

Initial ratio

But when the size changes, the panel content is stretched, making the text harder to read:

Bigger size Smaller size

It's like JFreeChart is pre-rendering the display so it can quickly draw it on screen, which is nice for e.g. PNG export (and I know it's a feature David Gilbert, JFreeChart author, wants to keep), but when the cart size changes too much, the display doesn't look good.

Is there a way to tell JFreeChart to somehow re-render the chart on container resize?

Here is the SSCCE:

public static void main(String[] args) {
    JFrame frm = new JFrame("JFreeChart resizing");
    frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    CategoryDataset ds = new DefaultCategoryDataset();
    JFreeChart chart = ChartFactory.createLineChart("Title", "X axis", "Y axis", ds);
    ChartPanel cp = new ChartPanel(chart);
    frm.getContentPane().add(cp, BorderLayout.CENTER);
    
    frm.pack();
    frm.setVisible(true);
}
Matthieu
  • 2,736
  • 4
  • 57
  • 87
  • Am I correct that you want the chart to resize smoothly as the frame is resized? Why [tag:aspect-ratio]? Why not [tag:swing]? – trashgod Oct 26 '21 at 18:51
  • @trashgod yes. Specifically the text appears too wide when the frame is bigger, and too narrow when the frame is smaller, when I'd like to have its size independent of the frame size. Hence the `aspect-ratio` tag because it doesn't feel like a Swing (layout manager) problem. I'll try your solution when I get to my computer, thanks for your time. – Matthieu Oct 26 '21 at 19:28

1 Answers1

2

Solving Common Layout Problems suggests, "be sure that your component's container uses a layout manager that respects the requested size of the component." Your fragment uses BorderLayout.CENTER, which is a good choice: it allows the the ChartPanel to resize smoothly as the frame is resized, but it ignores the panel's minimum size. Eventually, resampling artifact and distortion appear. As you want to retain the resize behavior, one approach is to set the chart panel's minimum draw width and heigh to zero and (optionally) limit the frame's minimum size accordingly:

cp.setMinimumDrawWidth(0);
cp.setMinimumDrawHeight(0);
// optionally
f.setMinimumSize(new Dimension(
    cp.getMinimumDrawWidth(),
    cp.getMinimumDrawHeight()));

The draw width and heigh may also be specified in the constructor, as shown here. In the example below, note:

Preferred size: initial

Smaller size: smaller

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;

/**
 * @see https://stackoverflow.com/q/69720552/230513
 */
public class ChartTest {

    private static final int W = 320;
    private static final int H = 240;

    private void display() {
        JFrame f = new JFrame("JFreeChart Resizing");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        CategoryDataset ds = new DefaultCategoryDataset();
        JFreeChart chart = ChartFactory.createLineChart(
            "Chart Title", "X axis", "Y axis", ds);
        ChartPanel cp = new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(W, H);
            }
        };
        f.add(cp, BorderLayout.CENTER);
        f.add(new JLabel("Java v" + System.getProperty("java.version")
            + "; JFreeChart 1.5.3", JLabel.CENTER), BorderLayout.PAGE_END);
        cp.setMinimumDrawWidth(0);
        cp.setMinimumDrawHeight(0);
        f.setMinimumSize(new Dimension(cp.getMinimumDrawWidth(), cp.getMinimumDrawHeight()));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new ChartTest()::display);
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for the links. Unfortunately the texts still show distorted when the frame size changes. I believe the problem lies in JFreeChart itself and not Swing Layout Managers. – Matthieu Oct 26 '21 at 20:58
  • (And about the EDT, it is [spawned](https://stackoverflow.com/q/10769895/1098603) when the first `setVisible(true)` is called so it's not too much of a problem here, for an SSCCE, but it's true that Swing-related stuff should be handled there, otherwise (not-so-)funny stuff happen) – Matthieu Oct 26 '21 at 21:00
  • 1
    I can't reproduce this until the frame's size falls below the chart panel's minimum draw width and height. Do you have a layout between the frame and the chart panel? Also check the version; in v1.0.13+, the `useBuffer` default changed. – trashgod Oct 26 '21 at 21:13
  • 1
    Interesting... I was using 1.0.16 and just tested on a [freshly compiled](https://stackoverflow.com/a/69653678/1098603) 1.5.3 but with the same result, with Java 14.0.2 on Ubuntu Linux. I guess I'll have to check on a Windows VM to see if I get the same result: the problem is better seen when the frame has a big width and a short height. I'll run a few more tests tomorrow night and update my question with more details if I find anything... – Matthieu Oct 26 '21 at 22:16
  • I've suggested a less empirical, more specific approach above. – trashgod Oct 27 '21 at 16:03
  • Thanks a lot @trashgod, it looks good. I'll check that asap (can't get to my computer until tomorrow morning...). – Matthieu Oct 27 '21 at 20:06
  • The `setMinimumDrawWidth()` (and height) did it (even with version 1.0.16)! I guess JFreeChart must be using it to start stretching the display. And indeed, it states exactly that in [the javadoc](https://www.jfree.org/jfreechart/javadoc/org/jfree/chart/ChartPanel.html#setMaximumDrawHeight(int)). When you know where to look... – Matthieu Oct 28 '21 at 07:58
  • I'd add that there was no need to override preferred size or set frame minimum size to make it work. Just calling `set{Min|Max}imumDraw{Width|Height}()` was enough. I think your time deserves a bounty :) – Matthieu Oct 28 '21 at 08:00
  • 1
    Glad you got it sorted, and I appreciate your time working through this; ordinarily, I rely on the default behavior to display thumbnails. As an aside, I habitually override `getPreferredSize`, as the defaults are subject to change upstream. – trashgod Oct 28 '21 at 12:07