|
Displayig chart on screen |
Posted by Medic89 on Sep-05-2021 21:13 |
|
I want to display my chart on my MFC form. I have a multichart object:
MultiChart* alles = new MultiChart(1500, 3000);
No i tried to set a line in the OnInitDialog section of my form.cpp, but he doesn't know what "alles" is:
c_Diagramm.setChart(alles); //c_Diagramm is my CChartViewer variable |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-05-2021 21:45 |
|
Maybe I should add, I followed your instructions on how to implement chartdirector to my own project, and I copied the this function to the cpp file, that holds the code for the form, i want to diplay the chart in:
void markzone2(CChartViewer* viewer, int /* chartIndex */) |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-06-2021 11:35 |
|
Hi Medic89,
If the error is that the alles is now known, it just means the alles does not exist. Please make sure the alles exists in your code and is within the scope of OnInitDialog.
You mentioned you have a line of code "MultiChart* alles = new MultiChart(1500, 3000);". It looks like a local variable to me. If this is the case, is the above line inside the OnInitDialog method?
Note that just declaring a variable somewhere in your code does not mean that it exists everywhere in your code. The variable only exists within its scope. You can see in typical C/C++ code there are many variables with the same name, but they do not conflict or interfere with each others because they are only visible within their scope. The scope can be the function in which it is defined, or a code block, or a class, etc.. For a variable that can be used in OnInitDialog, make sure it is defined in a scope that is visible in OnInitDialog.
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-06-2021 22:14 |
|
Hi Peter,
ah I see, now I made en entrace on the Init Dialog section as seen on the screen below. But how do I get the data into "alles"? All the code for the chart is stored in a void function which is in the Dialog.cpp.
|
Re: Displayig chart on screen |
Posted by Medic89 on Sep-06-2021 22:41 |
|
Now it works, I placed the chart function where everything gets set up in the Header file and put the code as seen below in the cpp file.
But what happens, if I want to change some items during the runtime of my App, is it possible to "refresh" single Items of my Multichart?
|
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-07-2021 01:58 |
|
Hi Medic89,
There are several examples (the Real-time Chart examples) included in ChartDirector that demonstrates charts with changing data.
In brief, your code should store the data in arrays. The arrays are then used in the charting code to draw the chart. If anything change, just the arrays, and draw the chart again.
For your case, after you have change the data arrays, use the same code that you use to draw the chart in the first place:
MultiChart *alles = .....;
markzone2(.....);
In brief, if you data changes, no additional charting code is need to update the chart. You can use the same code to draw the first chart and to update the chart. You can imagine the chart is a photograph of your data. If your data change, you take a new photograph (instead of modifying an existing photograph).
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-07-2021 03:14 |
|
Hello Peter,
doesn’t it make more sense just to redraw those parts that have changed? This would go faster. |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-07-2021 12:31 |
|
Hi Medic89,
In practice, "only the changed parts" does not necessarily go faster, because everything on the chart is interrelated. For example, when you change a data point, it may affect the axis scale (the scale may need to change to accommodate the data point). As a result, the axis label and everything on the chart can shift.
So for "only the changed parts" to work, ChartDirector must keep track of everything and analyze everything to see if they are affected. Even if nothing is really affected, ChartDirector still must keep track of everything and analyzer everything to determine that nothing is affected.
Instead, ChartDirector is designed like a graphics engine in a computer game. Everything is redrawn regardless of what has changed. The drawing code is faster and more memory efficient, because it does not need to keep track of anything. This is how all the real-time chart works in ChartDirector. In ChartDirector 7.0, we even include an example with millions of data points updating at a rate of 1000 points per second.
Your code is simpler too. It means you do not need to write additional code to update anything. You just need to write one subroutine that draws the chart with your current data. You can change any number of data points, change the color, labels, etc..
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-09-2021 01:53 |
|
Hi Peter,
I got it to work, and updating the whole chart is surprisingly fast.
One more question:
When moving the mouse over data points I get a tooltip with the information, but how can I add a handler that starts when I click on those points, or when I click on certain parts of the chart (e.g. inside the plot area from y = 0 to y = 100)? |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-09-2021 13:11 |
|
Hi Medic89,
When the mouse clicks on something, the GUI will receive a mouse click event. In MFC, that event is implemented as a BN_CLICKED notification. In the message handler, you can query ChartDirector to determine which point has been clicked. See:
There are MFC examples included in ChartDirector that demonstrate handling mouse clicks. For example, in the mfcdemo projectm when you click on the hot spots (the objects that provide tooltips), it will pop up a dialog box displaying the details of that data point. There are also several track cursor examples that demonstrate how to obtain various information from the mouse coordinates.
In brief, first attach a BN_CLICKED handler for the CChartViewer control. In the mfcdemo sample code, the message handler is implemented in mfcdemoDemo.cpp and it is called "OnClickHotSpot". (The "OnClickHotSpot" is just a name. You can use any name you like in your code.) You can examine the code inside to see how it uses the ImageMapHandler to determine which hot spot has been clicked on how to obtain the hot spot details.
https://www.advsofteng.com/doc/cdcpp.htm#CChartViewer.BN_CLICKED.htm
https://www.advsofteng.com/doc/cdcpp.htm#ImageMapHandler.htm
In the mouse event handler, you can obtain the mouse coordinates. You can use XYChart::getYValue and XYChart::getXValue to convert them to axis coordinates (the coordinates display on the chart axis). The "Crosshair with Axis Labels" is example:
https://www.advsofteng.com/doc/cdcpp.htm#crosshair.htm
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-09-2021 23:09 |
|
Hi Peter,
So far I got it to work, but is it possible to get a popup, when I click inside a Zone which was added with "addZone()"? Also, is there a way to control which parameters are being transmitted? In my case, I would like to display the Axislabel at a certain position on a y-Axis instead of the numeric value (in my case, the index number of the array that holds the yAxis major ticks with the array number, that holds the axis labels). |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-11-2021 02:20 |
|
Hi Medic89,
When the user click at any point, you can determine its y axis value. You can write code to test if that point is within your zone. If it is within your zone, you can write code to pop up a window or dialog or something else.
After drawing the zone, ChartDirector may forget about it to save memory. So ChartDirector cannot know if the point is within a zone. Since the zone is defined by your code, your code already know the y-range of the zone, and it can easily determine if the y axis value is within the zone.
For the second question, do you mean you want to display your own labels on the y-axis instead of the numeric labels, or is it in addition to the numeric labels? Is the overall y-axis scale automatically determined by ChartDirector, or is it specify by your code?
If the complete y-axis scale is specified by your code, the most flexible method is to create an axis scale with no labels, then use Axis.addLabel to add the label one by one. This method works even if the labels are not evenly spaced. It is like:
// Axis scale 0 to 160 with no label
c->yAxis()->setLinearScale(0, 160, StringArray(0, 0));
// Add the labels
for (int i = 0; i < myLabelCount; ++i)
c->yAxis()->addLabel(myTick[i], myLabel[i]);
Hope this can help.
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-12-2021 19:40 |
|
Hello Peter,
so far it works, i used the onclickhotspot handler and during the drawing of the chart i created an array, which lists all the y-coordinates of the zones on the y-axis, so when I click I can get the correlative Label of the y-Axis. Pretty cool, but a little bit of work...
One thing I discovered is, that sometimes the chart is bigger than the screen itself, I tried to add a vertical scroll bar to the main window that contains the chart, but no success. Do I have to implement the scroll function within the chartdirector code? |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-13-2021 19:31 |
|
Hi Medic89,
I am not too sure how you want to "scroll". If you want to scroll the "main window" (that is, the content in the "main window", including the chart, will scroll up and down), you can just scroll it using the standard method in MFC. It is the same as scrolling an image or any other control. The exact code depends on your window type. (There are many types of window in MFC, like a CDialog, CView, CFormView, CScrollView, etc.) In the simplest case, you just configure the window style to have a vertical scrollbar. Then in the OnVScroll message handler, you can call ScrollWindow or just use MoveWindow to move the controls inside the window upwards or downloads.
The following is an example that comes from the Microsoft knowledge base Q262954. It scrolls everything in a CDialog window.
https://jeffpar.github.io/kbarchive/kb/262/Q262954/
You only need to use the scrolling features in ChartDirector if you want to scroll something internal to the chart. For example, consider the "Zooming and Scrolling" examples that come with ChartDirector. The chart object itself does not move at all. (The chart border, plot area border, chart title, axes, etc., remains stationary.) What is does is to change the content inside the plot area when the scrollbar moves. This type of "scrolling" can only be done with the ChartDirector API. However, if what you want is to move the chart object itself, then MFC scrolling should be used.
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-14-2021 00:00 |
|
Hello Peter,
actually I'd prefer, if the chart itself scrolls. On my screenshot you can see my current chart, I'd like to have everything above the red line to be shown all the time, and if the part below the red line exceeds the screen height, I want to have the ability to scroll down.
|
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-15-2021 11:38 |
|
Hi Medic89,
For your case, the scrolling is internal to the chart. There is no scrolling as far as the window is concerned.
The easiest method I can think of to scroll up one row is not to draw the top row. That means when you build the table, it starts from the second row. You can do it repeatedly to scroll up one row again. Your code draws the table row by row until it reaches the bottom of the chart, which should be sized so that the entire chart is within the window. If there is no enough space, the rest of the rows are not drawn as they are invisible anyway.
The above method is similar to how Microsoft Excel scroll a spreadsheet. It allows you to have an infinitely large spreadsheet yet it uses very little memory and can be scrolled quickly, because it only draws what is visible.
First, you need to add a user interface to the window so that use can scroll the table. You may put an MFC scrollbar control in a suitable position. Your code should keep track of a number indicating which one is the top row. For example, the number can be simply an integer 0, 1, 2, 3, ... indicating whether the top row should start with row 0, 1, 2, or 3 ... When the user moves the scrollbar, there will be a scrollbar event. The number will be updated by the scrollbar event handler, and the chart is redrawn.
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-16-2021 23:27 |
|
Hello Peter,
thank you for your tipp, unfortunately my chart is divided into groups, in the picture above I have about 6 different charts who are located underneath each other, so I thought there is an "easy" way to have a function, that scrolls just everything belov the diagramm (which in the picture are the groups 2 to 9). |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-17-2021 12:42 |
|
Hi Medic89,
Scrolling part of a chart is like scrolling part of a picture or image. If you have a photograph in a Picture control, and you want to scroll part of the photograph, there is no built in method in MFC that can scroll part of an image. However, you can always write your own code to achieve the effect you want.
For your case, I think the easiest and most general way is to draw only the part of the content that is visible. For your case, I assume the content under the red line is a table. In this way, your code simply skip adding the rows that are invisible. It should not matter how many groups or rows that are in the table. Your code just need to keep track of the visible row range.
It is similar to how a browser draws a web page. A web page can be arbitrarily long and can contain complex contents. The end user may have an illusion that there is actually a long page, and they are scrolling the page like a physical object. To the programmer, what the browser actually does is to only draw the part that is visible. It is fast and efficient. The browser only needs to keep track of what is visible and skip other content.
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-17-2021 00:21 |
|
One thing I'm a little lost at:
I want to open a new window to edit values when I click at a hotspot. The new window has two DateTimePicker, and I want to set the date of the value into those DTP (one is for the date, the other one for the time), is that possible?
Also, the points im clicking at have individual labels which are added by using "addtext", is it possible to get the labeltext and put it into a variable when I click? |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-17-2021 18:59 |
|
Hi Medic89,
The MFC sample code that comes with ChartDirector demonstrates how to pop up a window that displays the hot spot information when a hot spot is clicked. In the MFC sample code, the hot spot refers to the data representation (sector in pie chart, symbols in scatter chart, line segments in line chart, etc). For objects that have x-coordinates, the information displayed includes the x-coordinates.
Would you mind to clarify which "hot spot" you are referring to, and so far what has been achieved with your code? Are you able to define hot spots and clicked on them, pop up a window, obtain the x-coordinates and obtain the date/time from the x-coordinates?
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-17-2021 23:53 |
|
Whe I click on the hotspot, the second value of hotSpotData->getValue() is the time of the event I clicked on. But how do I get this time into a dateTimePicker? I tried the following but without luck:
time_t TimeStamp;
tm* now;
TimeStamp = stod(hotSpotData->getValue(1));
now = localtime(&TimeStamp);
struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
};
CTime timeTime(now->tm_year + 1900 + ZuschlagJahr, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, 0);
VERIFY(v_DTP1.SetTime(&timeTime)); |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-18-2021 00:26 |
|
I solved the problem, I just set
v_DTP1.SetTime(TimeValueFromCChart - 62132925600); |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-18-2021 11:34 |
|
Hi Medic89,
If the x coordinates you use to create the chart is constructed with Chart::chartTime, I would obtain the time components yyyy-mm-dd hh:nn:ss back using:
__int64 t = (__int64)stod(hotSpotData->getValue(1));
int yyyymmdd = Chart::getChartYMD(t);
int dd = yyyymmdd % 100;
int mm = yyyymmdd / 100 % 100;
int yyyy = yyyymmdd / 10000;
int ss = t % 60;
int nn = t / 60 % 60;
int hh = t / 3600 % 24;
The Unix timestamp (seconds after January 1, 1970 UTC) is a format invented long ago and is easy to implement in the hardware at that time. Unluckily, there is no reliable method to convert this time format to common human readable format.
Human generally expects the time to be displayed as local time, which is the time you see in your clock. This requires two somewhat "random" factors - the time zone, and daylight savings time rules. The daylight saving times (DST) rule do change over time. Modern operating systems, if kept up to date, knows the current DST time rules, but not historical ones, and certainly they do not know future DST rules. (It is not uncommon to plot charts with future time for predictions.) In some countries, the DST rules change every year. That's why there is no reliable way to convert historical UNIX timestamps to human readable time.
The timezone can also change, although it is less common. So it is not 100% sure what is the timezone of a location for historical time.
The chartTime is just an encoding of the time components yyyy-mm-dd hh:nn:ss into a number. If you set a certain yyyy-mm-dd hh:nn:ss, you will get back the exact yyyy-mm-dd hh:nn:ss. On the other hand, if you use a UNIX timestamp, you may get back a different date/time in the future or in another computer, because the DST rule changes or the time zone differences.
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-18-2021 22:23 |
|
Hello Peter,
I solved the scrolling problem with two buttons (Move up and down), I set the top diagram in the foreground and with a white Background color, so with the button everything below moves up and down by one row.
BTW:
I tried to make a release candidate with VC, but the compiler says, it couldn't find the "Chartdir.h" file, but I included it as suggested in your online help. |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-20-2021 18:06 |
|
Hi Medic89,
Since you can run the code, I assume you have successfully compile the code.
You mentioned about compiling a "release candidate". Do you mean you can compile the code using Visual Studio in "Debug" mode, but not in "Release" mode?
If the compiler says it cannot find "chartdir.h", it means "chartdir.h" is not in its header file search path. If you configure the search path only for the Debug mode, then it is possible the compile can find it in Debug mode but not Release mode. Similarly, there is Win32 and x64 platforms. If you configure for one platform only, it is possible the code will not compile in the other target.
When you configure the "Additional Include Directories" in the Visual Studio project, please make sure you choose "All Configurations" and "All Platforms" from drop down list box at the top of the window, so that the configuration applies to all modes and platforms.
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-20-2021 22:50 |
|
Hi Peter,
thanks, I didn't noticed that in the property section you have to make the same includes for the release separately. Now it works. |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-22-2021 19:11 |
|
Hi Peter,
one more thing I experienced: After drawing the chart with the symbols as seen on my screenshot above, I drawed some line to connect the data line with the down pointing arrows with the up pointing arrows. Unfortunately those lines are always in front of the data points, is there a way to move those line in the background? The data points as well as the lines are all added to the same XYChart object. |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-23-2021 02:03 |
|
Hi Medic89,
I am not sure how you add the "line to connect the data line with the down pointing arrows with the up pointing arrows". In general, if the line is added as a "Layer" (such as using addLineLayer or other layer types), the new layer will be added behind all existing layers. So to reverse the layer ordering, you just need to reverse the order of adding the layers to the chart. Alternative, you can use Layer.moveFront or Layer.moveBack to move the one layer in front or behind the other layer.
https://www.advsofteng.com/doc/cdnet.htm#Layer.moveBack.htm
https://www.advsofteng.com/doc/cdnet.htm#Layer.moveFront.htm
For example:
// Move the layer behind all existing layers
myLineLayer->moveBack();
If the line is not added as a "Layer" but is added using BaseChart.addLine, you can use Line.setZOrder to move the line to the back of the plotarea. See:
https://www.advsofteng.com/doc/cdcpp.htm#Line.setZOrder.htm
For example:
Line *myLine = c->addLine(....);
myLine->setZOrder(Chart::PlotAreaZ);
Regards
Peter Kwan |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-23-2021 22:13 |
|
Hi Peter,
I added them using AddLine(), your method works, but now the lines are partially covered by the grid lines of my chart. |
Re: Displayig chart on screen |
Posted by Medic89 on Sep-24-2021 22:48 |
|
Hi Peter,
thank you, it worked:)
Just one more "complaint": I also added a mark line to my diagramm, which is now in front of my added lines, is there a way to fix that? I mean I want to have the base diagram with its grid lines, above that the mark line and on top my custom drawn lines. |
Re: Displayig chart on screen |
Posted by Peter Kwan on Sep-28-2021 00:14 |
|
Hi Medic89,
If your mark line is added using Axis.addMark, then you can use Mark.setDrawOnTop to configure the line to not drawing on the top. It is like:
Mark *m = c->yAxis()->addMark(.....);
m->setDrawOnTop(false);
Regards
Peter Kwan |
|