【问题标题】:Pan chart using mouse - Jfreechart使用鼠标平移图表 - Jfreechart
【发布时间】:2015-07-02 19:31:47
【问题描述】:

我们可以在 JfreeChart 中将平移功能实现为鼠标拖动事件吗?现在我按下 CTRL 并拖动鼠标来平移图表。我想通过拖动鼠标来实现平移功能。这可能吗?

【问题讨论】:

    标签: java jfreechart


    【解决方案1】:

    显然不可能使用当前的 JFreeChart API 更改修饰键,正如 here 所讨论的(但它正在筹备中)。

    但是,一切都可以以编程方式平移图表,因此您可以尝试以下操作:

    • MouseMotionListener 添加到您的ChartPanel 以跟踪mouseDragged() 事件。
    • 根据这些事件,计算图表的请求移动。
    • 直接调用XYPlot.panDomainAxes()XYPlot.panRangeAxis()(APIhere)。

    ChartPanelsource code获得灵感:

    /** 
     * Temporary storage for the width and height of the chart 
     * drawing area during panning.
     */
    private double panW, panH;
    
    /** The last mouse position during panning. */
    private Point panLast;
    
    /**
     * The mask for mouse events to trigger panning.
     *
     * @since 1.0.13
     */
    private int panMask = InputEvent.CTRL_MASK;
    
    ...
    
    /**
     * Handles a 'mouse pressed' event.
     * <P>
     * This event is the popup trigger on Unix/Linux.  For Windows, the popup
     * trigger is the 'mouse released' event.
     *
     * @param e  The mouse event.
     */
    @Override
    public void mousePressed(MouseEvent e) {
        if (this.chart == null) {
            return;
        }
        Plot plot = this.chart.getPlot();
        int mods = e.getModifiers();
        if ((mods & this.panMask) == this.panMask) {
            // can we pan this plot?
            if (plot instanceof Pannable) {
                Pannable pannable = (Pannable) plot;
                if (pannable.isDomainPannable() || pannable.isRangePannable()) {
                    Rectangle2D screenDataArea = getScreenDataArea(e.getX(),
                            e.getY());
                    if (screenDataArea != null && screenDataArea.contains(
                            e.getPoint())) {
                        this.panW = screenDataArea.getWidth();
                        this.panH = screenDataArea.getHeight();
                        this.panLast = e.getPoint();
                        setCursor(Cursor.getPredefinedCursor(
                                Cursor.MOVE_CURSOR));
                    }
                }
                // the actual panning occurs later in the mouseDragged() 
                // method
            }
        }
        else if (this.zoomRectangle == null) {
            ...
        }
    }
    
    ...
    
    /**
     * Handles a 'mouse dragged' event.
     *
     * @param e  the mouse event.
     */
    @Override
    public void mouseDragged(MouseEvent e) {
    
        // if the popup menu has already been triggered, then ignore dragging...
        if (this.popup != null && this.popup.isShowing()) {
            return;
        }
    
        // handle panning if we have a start point
        if (this.panLast != null) {
            double dx = e.getX() - this.panLast.getX();
            double dy = e.getY() - this.panLast.getY();
            if (dx == 0.0 && dy == 0.0) {
                return;
            }
            double wPercent = -dx / this.panW;
            double hPercent = dy / this.panH;
            boolean old = this.chart.getPlot().isNotify();
            this.chart.getPlot().setNotify(false);
            Pannable p = (Pannable) this.chart.getPlot();
            if (p.getOrientation() == PlotOrientation.VERTICAL) {
                p.panDomainAxes(wPercent, this.info.getPlotInfo(),
                        this.panLast);
                p.panRangeAxes(hPercent, this.info.getPlotInfo(),
                        this.panLast);
            }
            else {
                p.panDomainAxes(hPercent, this.info.getPlotInfo(),
                        this.panLast);
                p.panRangeAxes(wPercent, this.info.getPlotInfo(),
                        this.panLast);
            }
            this.panLast = e.getPoint();
            this.chart.getPlot().setNotify(old);
            return;
        }
    
        ...
    
    }
    
    ...
    
    /**
     * Handles a 'mouse released' event.  On Windows, we need to check if this
     * is a popup trigger, but only if we haven't already been tracking a zoom
     * rectangle.
     *
     * @param e  information about the event.
     */
    @Override
    public void mouseReleased(MouseEvent e) {
    
        // if we've been panning, we need to reset now that the mouse is 
        // released...
        if (this.panLast != null) {
            this.panLast = null;
            setCursor(Cursor.getDefaultCursor());
        }
    
        ...
    
    }
    

    编辑: 注意到当前 API 的唯一问题是 panMask 是私有的,为什么不尝试通过反射来破解该字段:

    Field mask = ChartPanel.class.getDeclaredField("panMask");
    mask.setAccessible(true);
    mask.set(yourChartPanel, Integer.valueOf(0)); // The "0" mask is equivalent to no mask. You could also set a different modifier.
    

    【讨论】:

      【解决方案2】:

      新版本仍然没有提供解决方案,所以我把对我来说完美无缺的东西,当你滚动时它也会自动缩放范围轴:

          ChartPanel panel = new ChartPanel(chart);
          panel.setMouseZoomable(false);
          panel.setMouseWheelEnabled(true);
          panel.setDomainZoomable(true);
          panel.setRangeZoomable(false);
          panel.setPreferredSize(new Dimension(1680, 1100));
          panel.setZoomTriggerDistance(Integer.MAX_VALUE);
          panel.setFillZoomRectangle(false);
          panel.setZoomOutlinePaint(new Color(0f, 0f, 0f, 0f));
          panel.setZoomAroundAnchor(true);
          try {
              Field mask = ChartPanel.class.getDeclaredField("panMask");
              mask.setAccessible(true);
              mask.set(panel, 0);
          } catch (NoSuchFieldException e) {
              e.printStackTrace();
          } catch (IllegalAccessException e) {
              e.printStackTrace();
          }
          panel.addMouseWheelListener(arg0 -> panel.restoreAutoRangeBounds());
      

      现在 JFreeChart 的行为与任何其他专业图表软件一样。

      【讨论】:

        【解决方案3】:

        这是我的解决方案。

        问候 助教

         ChartPanel cp = new ChartPanel(chart)
          {
             /**
              * A hack to change the zoom and panning function.
              * With this override we can pan around by dragging.
              * If SHIFT is pressed we get the zoom rectangle.
              * @param e
              */
             @Override
             public void mousePressed(MouseEvent e)
             {
                int mods = e.getModifiers();
        
                int panMask = MouseEvent.BUTTON1_MASK;
        
                if (mods == MouseEvent.BUTTON1_MASK+MouseEvent.SHIFT_MASK)
                {
                   panMask = 255; //The pan test will match nothing and the zoom rectangle will be activated.
                }
        
                try
                {
                   Field mask = ChartPanel.class.getDeclaredField("panMask");
                   mask.setAccessible(true);
                   mask.set(this, panMask);
        
                }
                catch (Exception ex)
                {
                   ex.printStackTrace();
                }
        
                super.mousePressed(e);
             }
          };
        

        【讨论】:

        • 在 Stack Overflow 上回答时,最好解释一下您的代码的作用。由于提出问题的 OP 可能无法理解仅代码的答案
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-03
        相关资源
        最近更新 更多