Selecting Easy Chart pens at runtime

Is there a built-in method of allowing users to select which pens are to be displayed (not just made visible) in an Easy Chart at runtime?

If not, I would like to display 2 lists on the screen: the first will display all the possible pens in the historical database (I’m currently using a SQL query like

SHOW Columns FROM fsqlgroup WHERE Field NOT IN ("fsqlgroup_ndx", "t_stamp", "quality_code")
unless there is a better way); the second will display pens which have been selected by the user. Buttons will be available to move the pens in and out of each list - they should only be displayed in one of the lists, not both.

Even if a built-in method is available, I would appreciate knowing how to add data to a list programmatically.

Absolutely - the way to alter the set of pens in an EasyChart at runtime is to manipulate the pens dataset (an expert property of the easy chart component). One of the most powerful features of the easy chart is that all of its configuration is stored in datasets. This means that you can alter those datasets programmatically using scripting, or, even better, alter them by binding them to SQL queries.

Take a look at “Option 3” under “Dynamic Pens” in the documentation - it is somewhat similar to your situation.

In your case, I would set up a database table with all of your pen configuration, and its simply a matter of selecting the correct pens out of all possible pens.

Hope this helps,

Carl,

I can see the advantage in ease of configuration in keeping a table of pens in the database separate from the datalogging table. However, this does mean extra effort in keeping the 2 in synch. I reckoned it was better to generate a list on the fly to avoid this.

Even if I implement my idea of having 2 lists (one with non-selected pens, the other with selected pens) with SQL queries, I think you would still have to be able to add and delete pens from the chart’s dataset (and probably the lists datasets) using scripting - I have drawn a complete blank on this. Could you give me some pointers on how I can go about this?

“Option 3” under “Dynamic Pens” in the documentation proposes a similar design, but then says “If you’re curious about more details of how to set this up, please call us.” I’m calling :slight_smile:

First of all - don’t be shy in adding supporting meta-data into your database. Tables are free, and lets be honest - you’ve got to keep your FPMI project and your database in synch no matter what!

If you insist on doing it via scripting, the approach is as follows. Datasets are immutable, so to modify the dataset, you’ll copy the existing data into some Python data structures, modify those structures, and then convert it all back

[code]chart = event.source.parent.getComponent(“Chart”)
data = fpmi.db.toPyDataSet(chart.getPens())
myPens =[]
columns = [“NAME”, “COL_NAME”, “TABLE_NAME”, “COLOR”]

Copy exsiting pens

for row in data:
pen=[]
for col in columns:
pen.append(row[col])
myPens.append(pen)

Add a new pen

pen=[]
pen.append(“NewName”)
pen.append(“NewCol”)
pen.append(“NewTable”)
pen.append(fpmi.gui.color(255,0,0))
myPens.append(pen)

Assign new dataset back into the chart

chart.setPens(fpmi.db.toDataSet(columns, myPens))[/code]

So, that code 1) copies the pens into a structure, 2) adds a new pen, and 3) puts the new pen set back into the chart.

Note that there are lots of attributes that make up a pen -you can pick and choose which ones you care about, the rest will get default values. There are lots attributes that make up a pen, they are:
[ul]
[li]NAME[/li]
[li]COL_NAME[/li]
[li]TABLE_NAME[/li]
[li]XVAL_COL_NAME[/li]
[li]AXIS[/li]
[li]SUBPLOT[/li]
[li]ENABLED[/li]
[li]DATASOURCE[/li]
[li]COLOR[/li]
[li]DASH_PATTERN[/li]
[li]RENDER_STYLE[/li]
[li]LINE_WEIGHT[/li]
[li]SHAPE[/li]
[li]FILL_SHAPE[/li]
[li]GROUP_NAME[/li]
[li]WHERE_CLAUSE[/li]
[li]DIGITAL[/li]
[li]OVERRIDE_AUTOCOLOR[/li][/ul]

Hope this helps,

Carl,

Thanks for the further help on the phone yesterday.

In an attempt to put in a solution with a minimum of scripting (and to copy the system I am replacing), I have changed the proposed historical trending screen: the users will be able to select pre-saved configurations (consisting of groups of up to 8 pens) from a list. This will then cause the Easy Chart to refresh and display the data for those pens.

What I needed to do to achieve this was dynamically change the SQL query which was populating the pens dataset for the chart. I created a table in the database to hold the pen configuration. This table had the following columns

Trend_name Pen_number Pen_name Col_name Table_Name
The key is made from a combination of the Trend_name and the Pen_number.

I then tried to bind the pens dataset to a SQL query, but I found that I could not reference the currently selected value of the list. I tried to set the pens dataset from a (small :wink: ) script tied to the propertyChange action of the list. However, this complained that I was trying to set a dataset equal to a string.

I resorted to using the script to set a dynamic property of the root container - I then bound the pens property to the dynamic property. This works - when the user selects a new entry in the list, the trend updates as required.

I am left with one problem - if the dynamic variable is empty, the chart throws an error, which is going to happen when the window is first opened. Any way round this?

What I have done does seem a bit involved - have I happened upon the correct way of doing things, or could it be simplified?

Al

Al,
I’ve put in a feature request for you to add properties to the column selector (reporting plugin component) that would make this easier. It also prompted other similar properties from other components.

With only 8 fixed pens you can easily build this with checkboxes and no scripting. Use an expression to build up your SQL query or WHERE clause. Bind the Easy Chart Pens property to the query.

"SELECT * FROM pens WHERE false" + if({Root Container.p1.selected}, " OR pen=1", "") + if({Root Container.p1.selected}, " OR pen=2", "")

A key point is that I use a query that doesn’t return anything as the default when nothing is selected. This is OK to do as opposed to running a NULL string as the query. Be sure to turn polling off. If you need to allow the user to select multiple pens before running the query then use a submit button, Jython is required.

As Carl alluded to, Metadata in your pens table is key. You can do things like allowing users to add/edit pens to their own profiles (sets of pens), then view theirs or anyones by selection (dropdown list, etc). I would use the same approach as I mentioned above where you’re primarily using expressions. I would only use Jython to write to the database when users are modifying their profiles. That approach also works well for session specific charts, particularly when users want “click to chart” capability. In an example I’ve seen users had hundreds of graphical objects over many screens and wanted to be able to right click any to add that pen to the graph. Through scripting you could take that a step further and allow each user to define as many profiles as they want and add the pen to any profile. Again, if I were implementing this I would only use expressions and binding on the Chart and user’s selection, and use scripting, if necessary, to build my pens database table. In your example the pens database table is static.

Um, I hate to point out the obvious, but aren’t you (both) re-inventing the wheel here? I mean, checkboxes or list boxes to select pens? Thats what the easy chart does for you. Just set up your pens in a plain, vanilla easy chart, and let the user turn them on and off. Isn’t that what you’re getting at?

If that isn’t what you’re getting at, Al, why did you move back to the Easy Chart vs the standard chart like we talked about on the phone?

One more thing - you can bind into the selected value of a list - see the example under the Table documentation titled “Tips and Tricks - Binding into a Table”. Its the same technique for a List.

I think there has been a bit of a misunderstanding here. I’m not actually wanting to select from a static list of 8 points on a chart - I want to allow the user to select a chart configuration from a list, each of which will populate the chart with up to 8 unique points. (The current system has 23 pre-defined charts, with up to 8 points on each.)

I chose to do this in the Easy Chart because I was trying to keep coding to a minimum. Rather than having to manually build up a list of pens, I reckoned I would just have to alter the chart’s pen dataset using an SQL query based on which entry in the list was selected.

The way I got this going works with very little code but has the 2 problems I outlined: it seems a bit long-winded (code fired by the list writes into a dynamic variable which is used by the chart as its SQL statement); and it fails when there is nothing selected in the list (which will happen when the window is first opened).

It would be cleaner to be able to include the selected value from the list directly into the SQL query populating the chart’s pen dataset. I had a look at the example you pointed out Carl, and came up with the following code to get the current value in a list:

{Root Container.lstAvailableTrends.data}[{Root Container.lstAvailableTrends.selectedIndex},0]

This displays the correct value in a label, but when put in the SQL statement the {Root Container.lstAvailableTrends.data} part resolves to “DataSet [3R x 1C]” and the [{Root Container.lstAvailableTrends.selectedIndex},0] part to “[1,0]” (if the second entry was selected).

Nathan, if I use your

SELECT * FROM pens WHERE false

statement to initialise the dynamic variable, I still get a “Query returned no rows” error.

Are you binding that query to the Pens dataSet of an Easy Chart?

[quote]Nathan, if I use your
Code:
SELECT * FROM pens WHERE false

statement to initialise the dynamic variable, I still get a “Query returned no rows” error.[/quote]

Also, for the size of your configuration, I would have one pens table that has an additional profile column. You will list each pen multiple times for different profiles. A given profile will be all rows (pens) with the same profile value. It wouldn’t be too bad to flex your SQL muscles textbook style with 2 tables (pens and pen_profile_mapping).

You could populate the dropdown list with a query like this:

SELECT DISTINCT profile FROM pens

Then use the WHERE clause and the technique you described above for binding to the selected value in a list.

So you should have a dynamic property query that’s a string, which is bound to an expression like this:

'SELECT * FROM pens WHERE profile =' + {Root Container.lstAvailableTrends.data}[{Root Container.lstAvailableTrends.selectedIndex},0]

Next protect from an unselected index. You might then wrap the expression in an if statement as shown below or use my above false condition with the OR as described above.

if ({Root Container.lstAvailableTrends.selectedIndex} != -1, myQuery, myAltQuery)

You then bind the Easy Chart’s Pens property query to that dynamic property.

{Root Container.query}

I haven’t figured out how you’re getting an error from returning no rows. The chart should just have no values.

Thanks for your help - I’ve finally worked out what I was doing wrong and have used Nathan’s suggestion to come up with a simpler solution.

To deal with what was going wrong first, I was binding the

SELECT * FROM pens WHERE false

as a SQL query of the Dynamic Property, rather than as an expression (the Dynamic Property is a string). This was then complaining immediately that the query returned no rows.

I was also putting the expression to find the selected value of the list

{Root Container.lstAvailableTrends.data}[{Root Container.lstAvailableTrends.selectedIndex},0]

into an SQL query tied to the chart’s Pens dataset, instead of into an expression. Instead of returning the selected value, it returned a string like

DataSet [3R x 1C][1,0]

which of course totally wrecked the query it was embedded in.

Here’s what I finally have:

  1. A list tied to the following SQL statement:

SELECT DISTINCT Trend_Name FROM trend_configuration ORDER BY Trend_Name

(I have also linked its Selected Index to an expression of -1, so that no value is selected in the list when the window is opened, despite it being cached.)

  1. A string Dynamic Property of the root container called “TrendQuery”, which has the following expression:
if ({Root Container.lstAvailableTrends.selectedIndex}!=-1,"SELECT Trend_Name as Name, Pen_Name, Col_Name, Table_Name, Axis FROM Trend_Configuration WHERE Trend_Name='" + {Root Container.lstAvailableTrends.data}[{Root Container.lstAvailableTrends.selectedIndex},0] + "' ORDER BY Pen_Number","SELECT * FROM Trend_Configuration WHERE false")
  1. An Easy Chart with its pen dataset SQL query set to:
{Root Container.TrendQuery}

This all works well, with one reservation: if the user opens the Trending window and selects a configuration, data will be displayed in the graph. If the user then displays some other windows and returns to the Trending window while it is still in the cache, there will be a delay while data for the last graph is fetched and drawn, before the graph is then cleared.

Is there any way of clearing the graph of data on the way out, short of just stopping the window from being cached? I can’t seem to set either the list Selected Index or the Chart Pen dataset from code when exiting.

Have you tried setting the selected index of the list to -1 on internalFrameDeactivated?

Success! :slight_smile: I was putting code in internalFrameClosing. I used

fpmi.gui.getParentWindow(event).getComponentForPath('Root Container.lstAvailableTrends').selectedIndex=-1

What I did notice was that the selectedIndex property does not come up in the list when you use the Insert Property Reference button on the RHS. Is this an oversight?

yep, thanks for pointing that one out, we’ll fix it.