USACE SWD 10: Design Documenting for Improved Plot Legend and Plot Layer Control
- Phil Weinstein, David Neumann, Edit Zagona, CADSWES, 3-26-2017. Ready for Review.

Overview

Many enhancements to the RiverWare plotting system have recently been developed for RiverWare 7.0 and 7.1, and are planned. The following evolving document describes the road map for RiverWare plotting:

Recently developed plotting capabilities include:

This document proposes the following enhancements:

(I) Plot Legend-Related Enhancements:

  1. General Legend Enhancements:
    1. Show Legend Items as Checkboxes.
    2. Custom Legend Implementation.
  2. Alternative Legend Placement:
    1. Legend on the top, left, or right.
    2. Legend within the plot canvas.
    3. Label curves in the plot (instead of a legend).
  3. New Legend Item Ordering Operations:
    1. Drag and drop within the legend. (Requires Custom Legend, A2).
    2. Configure Multiple Plots and Curves dialog.
      • Drag and drop
      • Move up and down widgets, operating on a multiple-item selection
  4. "Show in Legend" -- new curve and marker property
    1. Supported in the Configure Multiple Plot and Curves dialog.
    2. Supported in Context Menu on Curves. (Required curve/marker "Hover" state).
  5. "Layer" -- new curve and marker property, distinct from legend order, set in:
    1. Configure Multiple Plots and Curves dialog.
    2. "Layer" context submenu with "Move to Top", "Move Up", "Move Down", and "Move to Bottom" in the Legend Item context menus.
    3. New Curves and Markers context menus (on the plot) with with "Move to Top", "Move Up", "Move Down", and "Move to Bottom" actions. (Requires "G1" below).

(II) Other Plot Enhancements:

  1. Markers without lines; only text label and optional symbol.
  2. Hover highlight and selection of curves and markers.
    1. Hover highlight with initial application to layering control.
    2. Selection highlight, with selection state available for various figure configuration operations..

Possible legend enhancements using the current legend class

Plots in RiverWare 7.0 and prior versions use Qwt's default legend class, QwtLegend with items for individual curves and markers implemented by QwtLegendLabel. Use of this legend class provides less control over the legend than would a custom implementation made possible by new provisions in Qwt -- see the next section.

The following enhancements could be accomplished using this default legend class.

However, this legend implementation won't support the development of these features:

New Legend Capabilities in Qwt 6.1

Qwt 6.1 (we're using Qwt 6.1.3, the latest available version, with Qt 5.5.1) provides two new unrelated legend capabilities. A plot can actually have one of each, but these capabilities do not otherwise complement each other.

  1. The new QwtAbstractLegend class provides a way of implementing our own custom legend with any Qt widgets. (See the "Stockchart" example cited in the appendix). As with the default legend class (QwtLegend), a custom legend based on QwtAbstraceLegend can be attached to any of the four sides of the plot -- but outside of the plot canvas (with a call to QwtPlot::insertLegend). Doing so includes the legend in printed output or generated image files (with the use of the QwtPlotRenderer class). It is also possible to deploy a legend entirely apart from the plot (e.g. in its own popup window). In such cases, the legend needs to register for Qt legendDataChanged signals generated by the QwtPlot class.

    Use of QwtAbstractLegend is discussed in the following sections:
  2. The new QwtPlotLegendItem class implements a legend which can be deployed at certain positions within the plot canvas. (See the "Legend Item" aspects of the "Legend Demonstration" example cited in the appendix). This, however, cannot be composed with Qt widgets at the application level. This legend cannot be directly placed anywhere within the plot canvas. It can be placed in any corner, or at the center of any of the four edges (or the absolute center), with a distance setting (number of pixels) away from the plot canvas edge. Furthermore, it doesn't support interactivity -- not even at the level of conventional QwtLegend-implemented legends.
 

This class directly supports the following settable properties, which could be presented as persistent settings, or just hard-coded values:

Use of QwtPlotLegendItem is discussed in section:

Dynamically Drawn Plot Ornaments

As demonstrated in one of the examples provided with Qwt, some graphical features can be drawn on the plot canvas at the tail end of "paint event" processing. The "Interactive Plot" example (cited in the appendix) does this for one simple ornamentation: the "currently selected" curve point (in that little demonstration program). Such drawn ornaments do not exist as objects; we wouldn't be able to directly implement any mouse interaction with those features.

Implementation of this sort of drawing mechanism would be required for the following features. (See the cited sections, below).

(A) General Legend Enhancements

There are some general legend enhancements we would like to provide. These are broken out as those that can be accomplished with the current default legend implementation (using QwtLegend), and those requiring a custom legend implementation (a QwtAbstractLegend custom subclass).

  1. Show Legend Items as Checkboxes.
  2. Custom Legend Implementation

(A1) Show Legend Items as Checkboxes

Note: There are two general functional legend enhancements described in this document which don't require reimplementation of the legend using the QAbstractLegend class. See the prior section, "Possible legend enhancements using the current legend class".

  1. Show legend items as checkboxes -- the topic of this section.
  2. Alternative placement of the conventionally-implemented legend. This is explored further in section (B1) "Legend on the top, left, or right".

An option to show legend items as checkboxes instead of two-state pushbuttons (for showing or hiding the corresponding curve or marker within the plot). The presentation of legend items as checkboxes works particularly well with the legend on the left or right side of the plot, where the items would be presented in a single column.

Development tasks include:

  1. Add a "Show legend items as checkboxes" persistent setting for GUI and persistence level (PlotInfo) data models.
  2. Add to plot configuration controls (or alternatively, plot page configuration controls) either a checkbox or a pair of radio buttons for this setting.
  3. Implement the effect of this setting: See QwtLegendLabel::setItemMode (QwtLegendData::Mode).

(A2) Custom Legend Implementation

Some of the proposed enhancements require the implementation of a custom legend, as a subclass of QwtAbstractLegend. See the prior section, "New Legend Capabilities in Qwt 6.1". The enhancements include:

A substantial amount of development will be required to implement a custom legend. We'll want to make sure that its functionality is complete and correct with respect to that of the default QwtLegend class. For one thing, the behavior of "wrapping" legend items (e.g. when they appear below the plot) according to the available width of the plot dialog is implemented with a special Qt layout manager provided by Qwt (class QwtDynGridLayout, a direct Qt5 QLayout subclass). During implementation, we will want to examine the source code for the QwtLegend class, which is, itself, a QwtAbstractLegend subclass.

(B) Alternative Legend Placement

Plots in RiverWare 7.0 and prior versions unconditionally show the legend below the plot. Three distinct alternatives are considered in subsequent sections:

  1. Legend on the top, left, or right
  2. Legend within the plot canvas.
  3. Label curves in the plot (instead of a legend).

Although the implementations of all three alternatives are quite different -- and technically could be simultaneously shown -- we prefer to show the legend in only one way for a particular plot at any given time. A Plot Legend selector (ComboBox) will be added to the plot configuration with the following items -- depending on what options are actually implemented:

  1. Bottom
  2. Left
  3. Right
  4. Top
  5. Inside plot (requires additional settings) -- see section (B2)
  6. Label Curves (requires additional settings) -- see section (B3)

This choice needs to be represented within both the Plot GUI and "PlotInfo" data model (with serialization).

(B1) Legend on the top, left, or right

As mentioned above, the Qwt plot legend doesn't have to be below the plot. It can instead be placed on the top or right or left sides. This is an option which could readily be applied to the current legend implementation (directly using the QwtLegend class), or to a new implementation (derived from QwtAbstractLegend). [See the current use of QwtPlot::insertLegend in RiverWare code].

The image below demonstrates placing the legend to the right of the plot, instead of below. This is using Qwt's default legend implementation (class QwtLegend).

... compare this to the current RiverWare 7.0 support of the legend being shown along the bottom of the plot:

Notes:

  1. The spacing of legend items cannot be adjusted with the default legend class. That would be possible only with a custom legend implementation.
  2. The images above demonstrate a known inconsistency with dotted and dashed patterns in the plot and legend, when the line width is more than one pixel. This issue is documented as Gnats 5864, and can be fixed in the current RiverWare 7.0 plot implementation.

(B2) Legend within the plot canvas

Limitation: Display Only; no interaction, with the exception of a context menu.

We can support an option to show the legend within the plot, position in any of the four corners, or centered on any of the four sides. The distance from the sides (in pixels) can also be set.

Legends shown this way will lack these features:

Implementation of the legend context menu on this legend will require some custom coding using an event filter.

Technical Analysis:

As mentioned in the "New Legend Capabilities in Qwt 6.1" summary, it is possible to show a static legend within the plot. (An example of this is available in the Qwt "Legend Demonstration" example, making use of the cited in the appendix). Unfortunately, the relevant class doesn't make available to the application any mouse event notifications. The individual items within the legend do not support a toggle state. The following is from the description of the QwtPlotLegendItem class:

In opposite to QwtLegend, the legend item is not interactive. To identify mouse clicks on a legend item an event filter needs to be installed catching mouse events on the plot canvas. The geometries of the legend items are available using legendGeometries(). [http://qwt.sourceforge.net/class_qwt_plot_legend_item.html].

So, it would be technically possible to support some interactivity within this in-plot legend, but it would require low-level coding which we are not recommending we pursue, other than possibly for a context menu.

Note that the relevant Qwt terminology is confusing. QwtPlotLegendItem is an "item" in that it is a QwtPlotItem (a thing that lives in the plot canvas, like a curve or marker). It represents the entire legend. But it, itself, has child "item" components -- for the QwtPlotItems (curves and markers) represented within the legend.

(B3) Label curves in the plot (instead of a legend)

Qwt plot markers (not slot curves) natively support text labels which can be "aligned" (positioned) in the standard nine positions relative to its reference coordinates. (The interpretation of this alignment depends on the marker type: Horizontal, Vertical, or Cross).

 

It would be difficult, though possible, to show text labels in the plot for slot curves. This would require an unusual provision in Qwt to perform some "extra" painting at the tail end of a paint event, making use of an event filter and the QwtPlotDirectPainter class. The Interactive plot: Event Filter example cited in the appendix shows a simple example of such a mechanism.

We would also need to define and implement a text placement algorithm for the curve text label. This needs to appear on the plot at any zoom level when the curve is visible within the plot. This could have the following provisions for each curve:

Development tasks include:

  1. Designing and implementing setting controls and persistent settings.
  2. Computation of the initial reference point (leftmost or rightmost curve location, relative to the visible plot canvas rectangle).
  3. Application of the alignment adjustment to the reference point.
  4. Implementation of the event filter mechanism to cause the custom drawing at the appropriate times. (See the calls to CanvasPicker::showCursor in the Interactive plot: Event Filter example).
  5. Within that mechanism, custom drawing the the text with a temporarily visible special plot marker instance. (This is not part of the cited example).

(C) New Legend Item Ordering Operations:

In RiverWare 7.0, legend reordering is done in a separate popup dialog:

 

Two alternative approaches to reordering legend items are described in subsequent sections:

  1. Drag and drop within the legend.
  2. Configure Multiple Plot and Curves dialog.

(C1) Legend Reordering: Drag and drop within the legend.

It doesn't seem to be possible to implement drag-and-drop operations within the current legend implementation (using the default QwtLegend class). We would first need to reimplement the legend by subclassing QwtAbstractLegend. See the following two sections, above:

A drag and drop behavior on legend items would be active only within the Plot Page Editor dialog -- not the Plot Page Viewer dialog. The implementation would require low level mouse processing using legend item geometry determined at the application layer. There would also be a little work to implement context menus on these legend items. Note that legend items have a binary, toggleable state (either as two-state pushbuttons, or checkboxes). We would probably not attempt to disambiguate the initial click as a toggle action.

(C2) Legend Reordering: Configure Multiple Plots and Curves dialog.

The new Configure Multiple Plot and Curves dialog (to be released in RiverWare 7.1 -- see the document cited above) presents a list with items for all curves and markers in the plots of a single plot page. The initially developed version (completed in March 2017) does not support reordering of curves and markers.

It is important for this dialog to support multiple-item selection, as particular settings can be applied to multiple items in a single operation. Multiple-item selection poses some usability problems with drag-and-drop operation, as an unintended drag operation can be started when the user intends only to define a new multiple-item selection (by dragging the mouse across multiple items). However, we have allowed both mutliple-item selection and drag-and-drop reordering in some item lists in RiverWare. One way to disambiguate these mouse behaviors would be to enable drag-and-drop operation when a "Reorder Items" checkbox is checked.

Alternatively (or additionally), we could provide move-up and move-down arrow icon buttons to operate on the selected items, similar to those implemented in the RiverWare 7.0 Reorder Plot Legend Items popup dialog.

(D) "Show in Legend" -- new curve and marker property, set in the new Configure Multiple Plots and Curves dialog.

Currently, in RiverWare 7.0, all curves unconditionally show up in the legend, and all of the markers in a plot can be either shown or hidden. (There is one "Display legend items for markers" checkbox for each plot). We would like to support "Show in Legend" checkboxes for each individual curve and marker. An partial provision for this was provided in the initial implementation of the new "Configure Multiple Plots and Curves" dialog -- see the "Show in Legend" column in the screenshot above.

Development tasks include:

  1. Add a new "show in legend" property to both the GUI and "persistence" data models for curves and markers.
  2. Implement checkboxes in the Configure Multiple Plot and Curves dialog's "Show in Legend" column.
  3. Implement the effect of those states in the various legend implementations. (Currently, there is just one such implementation, but this document is proposing a couple other implementations).

(E) "Layer" -- new curve and marker property, distinct from legend order.

Currently, in RiverWare 7.0, curves and markers are "layered" in their legend order. We would like legend order and "layer" to be distinct properties.

We have not yet made use of a "Z"-order property on Qwt curves and markers. Not having done so is what causes the legend order to determine the effective layering (painting) order. (The QwtPlotItem "Z"-order property is a floating point number, but we would expose only integers in the user interface).

Development tasks include:

  1. Add a new "Z value" property to both the GUI and "persistence" data models for curves and markers.
  2. Apply the "Z value" property to live instances of RiverWare SlotCurves and PlotMarkers.

(E1) Editable "Layer" column in new Configure Multiple Plots and Curves dialog

Development tasks include:

  1. Add a "Layer" (or "Layer Order") column displaying the "Z value" property (integer).
  2. Support in-cell editing by deploying an integer spinner, with values of 1 to the number of items in the curve or marker's containing plot. Edits are initiated by double-clicking in the cell. Stepping the integer spinner up or down -- or typing a value into the spinner -- would adjust the "Z" value of the other curves or markers in the same plot.

(E2) "Layer" context submenu with "Move to Top", "Move Up", "Move Down", and "Move to Bottom" in the Legend Item context menus.

Development tasks include:

  1. Add a "Layer" context submenu with "Move to Top", "Move Up", "Move Down", and "Move to Bottom" to the context menus of these legend implementations (as applicable):

(E3) New Curves and Markers context menus (on the plot) with with "Move to Top", "Move Up", "Move Down", and "Move to Bottom" actions.

Deploying context menus on curves and markers within a plot requires the proximity calculations needed to implement an "Armed" curve/marker state showing a "Hover" ornamentation, and should be provided only, in fact, that Hover ornamentation for armed curves and markers is implemented. See section G.

With Armed ("Hover") curve and marker identification and ornamentation in place, it is a simple matter to deploy a context menu for the armed curve or marker. In the SlotPlot::contextMenuEvent() method, if an armed curve or marker is active, deploy a context menu for that curve or marker in place of the existing plot context menu.

The motivating requirement for this context menu is providing "Move to Top", "Move Up", "Move Down", and "Move to Bottom" actions as right click operations right on curves and markers shown in the plot canvas. Those could either be top-level menu items at (or near) the top of a curve and marker context menu -- OR -- in a "Layer" (or "Layer Order") submenu of that context menu.

Development tasks include the following (after implementing the "Hover" features described in section G).

  1. In Q3GUI/SlotPlot, define a set of actions which operate on the "armed" curve or marker, including actions for "Move to Top", "Move Up", "Move Down", and "Move to Bottom" operations.
  2. Implement handlers (Qt slots) for those "armed" curve or marker actions.
  3. Define a method to build an "armedPlotItem_contextMenu" QMenu for a specified curve or marker.
  4. Enhance the SlotPlot::contextMenuEvent(), method as described above, to show a curve/marker context menu in place of the plot context menu.

(F) Markers without lines; only text label and optional symbol

Markers in RiverWare 7.0 can be either a horizontal line, vertical line, or both ("Cross") -- at constant values on one axis or two perpendicular axes. This is set with the Marker Type radio buttons in the Marker Configuration dialog.

A text label can be specified, shown either along one of the plot edges (for Horizontal and Vertical types) or relative to point identified by two coordinates.

Qwt markers also support a "No Line" mode which results in showing ONLY the text label (plus, optionally, a symbol). We would like to provide that option. The inability for RiverWare to accomplish this use of markers was the subject of Gnats 5901. (The user had hoped that selecting "None" for the marker's Line Style would provide this feature; that was a reasonable expectation, but unfortunately does not work, and was difficult to implement).

Our recommended solution is to:

  1. Change the "Marker Type" radio buttons to two independent checkboxes for the Horizontal and Vertical lines.
  2. Remove the "None" line style option (which has been changed to a combo box in the mockup image on the right side, below).

When a marker has neither a Horizontal or Vertical line, specifications of both the X-Position and a Y-Position are significant. A marker Symbol Style could still be specified; the symbol would be shown at the at the reference point and the label would be shown relative to that point, according to the marker's Label Alignment.

    

(G) Hover highlight and selection of curves and markers, with initial application to layering control.

Active "hover" and "selected item" ornamentation requires the following:

  1. The ability to ascertain the plot item (curve or marker) closest to the mouse pointer. This needs to be assessed on each mouse move.
  2. The ability to receive mouse move and click events.
  3. The ability to apply ornaments to the graph to indicate "hover" and "selected" states.

We do have working examples of most of these technical requirements, either in RiverWare, or in the example applications provided with Qwt 6.1.3. This includes:

  1. Curve proximity calculations used in RiverWare method, bool SlotPlot::getPlotToolTip (const QPoint&, QString& tipString) const. This makes use of the method, int QwtPlotCurve::closestPoint (const QPoint&, double* dist=NULL) const.
  2. Both mouse event processing and the application of ornaments, in the Qwt "Interactive Plot" example cited in the appendix.

We would have to implement the proximity calculation for Plot Markers, though this is much simpler, being that those are straight lines and/or a single point.

The initial application for the hover state is showing a curve or marker-oriented context menu when the user right-clicks on the plot canvas in proximity to a marker. This would include the "Layer" submenu described in Section E, above.

The selection state isn't initially required for any features being proposed in this document, but would lay the foundation for operations on multiple curves selected graphically, with the mouse. It would also provide a more persistent ornament, e.g. for use in the context of a live presentation. The plot item selection -- of multiple curves and markers -- would function as follows:

Hover state development tasks include:

  1. Enhance the SlotPlot::getPlotToolTip() method to support also Markers. This involves implementing a point-proximity calculation for markers, in plot canvas coordinates.
  2. Making use of a QTimer, after a brief time (maybe 200 milliseconds) since the last mouse move event, identify and record the plot's "hover" item (curve or marker).
  3. Implement a hover ornamentation, applied when the plot's "hover" item is set. Erase that ornament when that item is cleared.
  4. When the plot's context menu is shown, if an active hover item exists, show the context menu for that item (curve or marker).

Selected state development tasks include:

  1. Define a non-persistent "selected plot items" data structure (a list of curves and markers) in the SlotPlot class (a QwtPlot subclass).
  2. On mouse click events, modify the selected plot items list as described above.

Appendix: Useful Qwt Examples

(1) Stockchart: Widget-based Legend

 

(2) Legend Demonstration: New Qwt 6.1 Legend Classes

 

(3) Interactive plot: Event Filter

 

Development Estimates

USACE SWD 10: Design Documenting for Improved Plot Legend and Plot Layer Control / Estimates
- Phil Weinstein, CADSWES, 3-26-2017 (a).

  Basic
Features
Est.
Hours
Features
Requiring
Custom
Legend
Features
Requiring
Dynamic
Drawing
Estimate revision: 3-26-2017 (Phil).
A1 12.0     Show Legend Items as Checkboxes
A2   24.0  

Custom Legend Implementation -- needed for:
(a) Drag and drop reordering within the legend (C1).
(b) Changing the spacing between legend items.

B 6.0     Alternative Legend Placement:
Preliminary task: Selection GUI and persistence.
B1 4.0    
  • Legend on the top, left, or right.
B2 16.0    
  • Legend within the plot canvas, including control design. We would need to determine which QwtPlotLegendItem configuration capabilities to expose in the user interface.
B3     24.0
  • Label curves in the plot (instead of a legend)
C1   16.0   Legend Reordering: Drag and drop within the legend.
C2       Legend Reordering: Configure Multiple Plots and Curves dialog.
  16.0    
  • Drag and drop of selected items within the plot item list view.
  8.0    
  • Move up and down widgets, operating on a multiple-item selection.
D 16.0     "Show in Legend" -- new curve and marker property, supported in the new Configure Multiple Plots and Curves Dialog.
E 8.0     "Layer" -- new curve and marker property.
Preliminary task: "Z value" property to both the GUI and "persistence" data models for curves and markers.
E1 8.0    
  • Editable "Layer" column in new Configure Multiple Plots and Curves dialog
E2 6.0    
  • "Layer" context submenu with "Move to Top", "Move Up", "Move Down", and "Move to Bottom" in the Legend Item context menus.
E3     10.0
  • New Curves and Markers context menu (on the plot) with a "Layer" context submenu with "Move to Top", "Move Up", "Move Down", and "Move to Bottom" (Requires Hover state and ornamentation).
F 16.0     Markers without lines; only a text label and an optional symbol.
G       Hover highlight and selection of curves and markers, with initial application to layering control (E3).
G1     24.0
  • Hover state and ornamentation.
G2     10.0
  • Selected state and ornamentation.
  116 40 68 TOTAL (all specified features: 224 Hours) [Estimate revision: 3-26-2017 (Phil)].

--- (end) ---