|
Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-18-2021 18:08 |
|
I recently discovered that version 7 of ChartDirector was released in May and is on the verge of being officially announced.
I downloaded the C++ package and started to adjust my chart viewer implementation for wxWidgets, wxChartDir, to the new version. In this course I came across a few questions, I'd like to ask here.
1) Enhanced tooltips
A major addition affecting the chart viewer implementation seems to be support for enhanced resp CDML tooltips. Are there other (maybe more subtle) changes to the chart viewer implementation?
2) ImageMapHandler key "dynamic"
The chart viewer implementations coming with ChartDirector now check for the key "dynamic" of the hot spot tester. However, I found no hint in the documentation, when and under which circumstances this key is set. Could you explain the purpose of this key?
3) Resource loader
I intend to add some sort of a resource loader to wxChartDir, although wxWidgets itself doesn't offer a generic platform-independent mechanism for loading resources. I see that the MFC chart viewer retrieves a resource loader (I assume a default implementation) for loading cursor icons, without explicitly setting a resource loader. My question is, if and how an explicitly set resource loader interferes with the default resource loader under Windows. Does ChartDirector invoke the default Windows resource loader first, before calling an explicitly set resource loader? Or is it necessary to retrieve the address of the default resource loader and to call it explicitly from the own resource loader implementation to make use of it?
Thanks in advance.
Most likely more questions will pop up while I work on adjusting wxChartDir.
Kind regards,
Ulrich Telle |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jun-18-2021 20:07 |
|
Hi Ulrich,
1) The two most significant changes in the CChartViewer are:
High DPI support
===========
We need to auto-detect whether the application is running in high DPI mode. Apart from drawing the chart in high DPI, we also need to map the MFC mouse and display coordinates to the chart coordinates. The custom mouse cursors (like the zoom in cursor) needs to be multi-resolution. Instead of hard coded the cursor file (*.cur) data in the CChartViewer, we now put the cursors as resources in the chartdir70.dll.
We use the standard Win32 API LoadCursorA to load the cursor, because it can automatically select the correct mouse cursor resolution to use. That API expects a "handle" to the executable or DLL that contains the resource. The "getResourceLoader" simply returns the handle of the DLL.
After we implement this, we found that Qt does not support cursor file (*.cur) at all. Instead it uses PNG images as cursors. So there is an alternative implementation in Qt that uses ChartDirector to draw the cursor PNG image directly and no resource is used. (We cannot use this method in MFC, as MFC does not support using PNG images as cursors.)
If the wxWidgets can use PNG images as cursors (or any image type that ChartDirector can generate), you may use the QASE_CursorManager in qchartviewer.cpp. Personally, I like this implementation better as it does not depend on resources.
CDML Tooltip support
=============
As we need to support transparency and flicker free movement of the tooltip, the implementation is a little bit complicated.
The "dynamic" is an internal undocumented flag. It means the tooltip can change as the mouse moves.
Normally, a hot spot is a region. For example, a bar in a bar chart can be a hot spot. There is a tooltip for the hot spot. The tooltip is not expected to change even if the mouse move as long as the mouse remain on the bar.
For a contour chart, there entire chart is a single continuous surface. The (x, y, z) value of the contour chart can change from pixel to pixel. In previous version of ChartDirector, the contour chart does not support tooltip. In ChartDirector 7, it is implemented as a "dynamic" tooltip that can change from pixel to pixel. The code in CChartViewer can detect if a hot spot is "dynamic". If it is, the CChartViewer will need to get an updated tooltip every time the mouse move.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-18-2021 21:47 |
|
Hi Peter,
thanks a lot for your quick response.
The two most significant changes in the CChartViewer are:
High DPI support
===========
Yes, I saw that, too, of course. And in principle, wxWidgets has support for high DPI. However, my "problem" is that I currently don't have a high DPI screen at hand for testing. So, until I have found a way to test in high DPI mode, I will have to postpone adding high DPI support to wxChartDir. Hopefully only for a short period of time.
We need to auto-detect whether the application is running in high DPI mode. Apart from drawing the chart in high DPI, we also need to map the MFC mouse and display coordinates to the chart coordinates. The custom mouse cursors (like the zoom in cursor) needs to be multi-resolution. Instead of hard coded the cursor file (*.cur) data in the CChartViewer, we now put the cursors as resources in the chartdir70.dll.
Is this also the case for other platforms like Linux or macOS?
We use the standard Win32 API LoadCursorA to load the cursor, because it can automatically select the correct mouse cursor resolution to use. That API expects a "handle" to the executable or DLL that contains the resource. The "getResourceLoader" simply returns the handle of the DLL.
After we implement this, we found that Qt does not support cursor file (*.cur) at all. Instead it uses PNG images as cursors. So there is an alternative implementation in Qt that uses ChartDirector to draw the cursor PNG image directly and no resource is used. (We cannot use this method in MFC, as MFC does not support using PNG images as cursors.)
Thanks for the clarification.
If the wxWidgets can use PNG images as cursors (or any image type that ChartDirector can generate), you may use the QASE_CursorManager in qchartviewer.cpp. Personally, I like this implementation better as it does not depend on resources.
The wxCursor class in wxWidgets can use resources as well as any bitmap images. That is, I will look into the implementation of QASE_CursorManager. Most likely the code can be adjusted without too much effort to create a wxCursor object.
CDML Tooltip support
=============
As we need to support transparency and flicker free movement of the tooltip, the implementation is a little bit complicated.
The "dynamic" is an internal undocumented flag. It means the tooltip can change as the mouse moves.
Normally, a hot spot is a region. For example, a bar in a bar chart can be a hot spot. There is a tooltip for the hot spot. The tooltip is not expected to change even if the mouse move as long as the mouse remain on the bar.
For a contour chart, there entire chart is a single continuous surface. The (x, y, z) value of the contour chart can change from pixel to pixel. In previous version of ChartDirector, the contour chart does not support tooltip. In ChartDirector 7, it is implemented as a "dynamic" tooltip that can change from pixel to pixel. The code in CChartViewer can detect if a hot spot is "dynamic". If it is, the CChartViewer will need to get an updated tooltip every time the mouse move.
Thanks for the detailed explanation.
I'm currently experimenting with various wxWidgets implementation options. I hope to find one that is fully operational.
Thanks again for your excellent support. I will drop a note when an updated wxChartDir version is available.
Kind regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-22-2021 22:02 |
|
I'm making good progress. For example, I have the enhanced tooltips already working, even the dynamic ones. However, I have further questions in view of high dpi support.
Although I don't have a high dpi monitor, I tried to set the dpi output option for the enhanced tooltip just as an experiment to dpi=144. The generated bitmap had 1.5 times as many pixels as without this option (as expected). However, querying the drawarea width and height resulted in the number of pixels at dpi=96.
This brings me to the question, which drawarea dimension is reported for a dpi-aware application, when the screen dpi actually differs from 96 dpi? And which values do I use for setting the size of a drawarea? The real number of pixels at the screen dpi, or at 96 dpi?
And how should font sizes be determined? Always in points (= 1/72 inch)? |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jun-23-2021 01:08 |
|
Hi Ulrich,
To test high DPI on Windows, you just need to go to Settings/System/Display, and change the scale factor of your monitor from 100% to 150% (or some other scale factor).
The BaseChart or DrawArea do not use GUI. They always report the size they are configued. For example, if the code sets the size to 200 x 100, it will report its size as 200 x 100, irrespective of what is the DPI of the monitor. The font size in points will always be converted to pixel size with 1 point = 1.333 pixel (which is 96 dpi). Note that the pixel is the so called "logical pixel". It is not the actual pixel unit on the monitor.
The DPI is managed by the control - the CChartViewer and QChartViewer in our code. If the control detects that the application is high DPI aware, it will use setOutputOptions to set a scale factor before calling makeChart to obtain the chart image. In this way, the DrawArea or BaseChart will produce a resized image. You can obtain the actual size by examining that output image. For example, if a scale factor of 150% is used, the actual image produced will be 300 x 150, but the DrawArea will still report 200 x 100.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jun-23-2021 01:33 |
|
Hi Ulrich,
The chart or DrawArea size should be as if the monitor is 96dpi. When the program is run, the control will cause the actual size to change to match the DPI setting of the monitor. The mechanism is implemented in the CChartViewer and QChartViewer controls. In our sample code, all the sizes are hard coded, and they can be used on monitors of different DPIs without modification.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-23-2021 02:08 |
|
Hi Peter,
To test high DPI on Windows, you just need to go to Settings/System/Display, and change the scale factor of your monitor from 100% to 150% (or some other scale factor).
Thanks for the hint. I'll try this to test high DPI mode.
The BaseChart or DrawArea do not use GUI. They always report the size they are configued. For example, if the code sets the size to 200 x 100, it will report its size as 200 x 100, irrespective of what is the DPI of the monitor.
Ah, good to know. I already wondered how BaseChart or DrawArea could possibly know which DPI value to apply, especially on a multi monitor system with different resolutions.
I just saw your second post:
The chart or DrawArea size should be as if the monitor is 96dpi. When the program is run, the control will cause the actual size to change to match the DPI setting of the monitor. The mechanism is implemented in the CChartViewer and QChartViewer controls. In our sample code, all the sizes are hard coded, and they can be used on monitors of different DPIs without modification.
I was about to ask exactly this. Thanks for answering my question even before I could ask it. Using a fixed resolution of 96 DPI then means that actual cursor positions need to be converted to match the image map values. I will have to adjust wxChartViewer accordingly.
The font size in points will always be converted to pixel size with 1 point = 1.333 pixel (which is 96 dpi). Note that the pixel is the so called "logical pixel". It is not the actual pixel unit on the monitor.
Ok, otherwise it would be necessary to adjust the font size. And that would really be painful. It's certainly better to measure chart dimensions and font sizes in a device independent matter.
The DPI is managed by the control - the CChartViewer and QChartViewer in our code. If the control detects that the application is high DPI aware, it will use setOutputOptions to set a scale factor before calling makeChart to obtain the chart image. In this way, the DrawArea or BaseChart will produce a resized image. You can obtain the actual size by examining that output image. For example, if a scale factor of 150% is used, the actual image produced will be 300 x 150, but the DrawArea will still report 200 x 100.
Ok, this explains my observations.
Thanks for your detailed descriptions and explanations. I highly appreciate your support.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-25-2021 17:22 |
|
Hi Peter,
I have most existing samples already working. Now I started to adjust the new samples (like Contour Plot Zoom/Scroll). I have a strange effect with the enhanced tooltips. In the wxWidgets implementation (screenshot 1) the background color of the tooltip is much darker than in the MFC implementation (screenshot 2). This happens for opaque as well as for translucent tooltips.
I know that most likely not the ChartDirector library, but my own wxWidgets implementation is causing this problem. However, I have no idea, why this happens. Maybe with your experience you have an idea what may cause this effect.
The tooltip is created with
c->setOutputOptions("alpha=3");
c->makeChart(Chart::BMP);
and then drawn on the wxWidgets device context. Most likely the latter operation does not do what it should do.
Regards,
Ulrich
|
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jun-25-2021 21:25 |
|
Hi Ulrich,
If it only happens for translucent tooltip, one possibility is the alpha type. May be try to use "alpha=1" to see if it makes any difference.
I cannot think of a reason why non-transparent tooltip can have a different color.
Is it possible to save the screenshot in PNG? I understand the PNG file size is too big for our 250KB upload limit. You can try to chop only the tooltip and the surrounding region. I will try to examine the two images pixel by pixel, and compare that to the transparency calculation by hand, to see if I can find some hint on how this problem occur. (The JPG image has too much JPG noise for pixel by pixel analysis.)
If the BMP is using "pre-multiplied alpha" (alpha=3), but the way the tooltip is rendered expects "non-pre-multiplied alpha" (alpha=1), the translucent part will look darker than expected. If I know the pixels accurately, I can compute to see if check this can explain the problem.
To check if the tooltip itself is really too dark, after c->makeChart(Chart::BMP);, please add a line c->makeChart("c:\\path\\to\\mytooltip.png");. This saves the same tooltip image in PNG format. We can examine the file to see if the tooltip is correct.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-25-2021 21:58 |
|
Hi Peter,
If it only happens for translucent tooltip, one possibility is the alpha type. May be try to use "alpha=1" to see if it makes any difference.
In fact, it happens only for translucent tooltips. Using option "alpha=1" did the trick.
In the meantime I had made a few experiments by changing the intermediate image format from BMP to PNG. This worked, too.
I cannot think of a reason why non-transparent tooltip can have a different color.
There isn't a reason. The opaque tooltips work as expected. Almost, I should add, because they, too, have small transparent parts, when using rounded corners (as the samples do). However, "alpha=1" solves that, too.
Is it possible to save the screenshot in PNG?
I think this will not be necessary, as the option "alpha=1" solves the problem.
If the BMP is using "pre-multiplied alpha" (alpha=3), but the way the tooltip is rendered expects "non-pre-multiplied alpha" (alpha=1), the translucent part will look darker than expected. If I know the pixels accurately, I can compute to see if check this can explain the problem.
Yes, this seems to be the full explanation.
However, this leads me to another question. My wxWidgets chart viewer implementation used "alpha=3" all the time for the main graphics. I guess this usually makes no difference, because the main graphics are typically non-transparent anyway. Is there a sample included in ChartDirector where main graphics has transparent parts, so that I could check out, whether I should use "alpha=1" for the main graphics, too?
An alternative could be to use the PNG format instead of the BMP format for converting to the required wxBitmap type. However, from a performance point of view this may not be a good idea. I suppose that converting from ChartDirector's internal format to BMP, and then from BMP to wxWidgets' internal format is faster than using PNG as the intermediate format.
To check if the tooltip itself is really too dark, after c->makeChart(Chart::BMP);, please add a line c->makeChart("c:pathtomytooltip.png");. This saves the same tooltip image in PNG format. We can examine the file to see if the tooltip is correct.
I had done that already an can confirm that the original tooltip is correct.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jun-26-2021 01:11 |
|
Hi Ulrich,
Transparency is not standardized in BMP, but it is standardized in PNG. So PNG should work well. PNG is a compressed format, and data compression has high overhead (typically 100% to 400%).
There is actually a special mode Chart::QTIMG which outputs BMP for opaque images, and "non-compressed PNG" for translucent images. We use this in our QChartViewer implementation. Being non-compressed, the PNG has much lower overhead, though still higher than BMP. It relies on the fact that Qt can automatically detect the image type, so using either BMP or PNG will work. May be you can try QTIMG to see if it works in your case.
You mentioned the wxWidgets uses "alpha=3" for the chart. Is it possible the tooltip is rendered in a different way? In our MFC CChartViewer, if there is a CDML tooltip, the tooltip and the chart will first merge onto a buffer (double buffering). The alpha type for rendering to the buffer (memory device context) can be different from rendering to the display device context.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-26-2021 02:11 |
|
Hi Peter,
Transparency is not standardized in BMP, but it is standardized in PNG. So PNG should work well. PNG is a compressed format, and data compression has high overhead (typically 100% to 400%).
Since graphics on screen have to be redrawn frequently, especially in case of realtime data, I think wxChartViewer should prefer BMP over PNG.
There is actually a special mode Chart::QTIMG which outputs BMP for opaque images, and "non-compressed PNG" for translucent images. We use this in our QChartViewer implementation. Being non-compressed, the PNG has much lower overhead, though still higher than BMP. It relies on the fact that Qt can automatically detect the image type, so using either BMP or PNG will work. May be you can try QTIMG to see if it works in your case.
wxWidgets can usually detect the image type automatically, too. However, this also incurs a slight overhead, because different image handlers are tested sequentially, until one is found that can read the format.
I just tested the format QTIMG. It works, too. However, now that I successfully tested "alpha=1" for BMP, I should probably stay with BMP as it incurs less overhead.
You mentioned the wxWidgets uses "alpha=3" for the chart.
Yes. Back in 2018 we had a discussion about the alpha options for BMP. However, as far as I remember you didn't mention the difference "premultiplied alpha" vs "non-premultiplied alpha" back then, and therefore I chose "alpha=3" as the more modern variant, which was used in the MFC implementation. And nobody complaint about that so far...
In fact, I have to apply a 3-step transformation:
internal ChartDirector format -> BMP -> wxImage (platform independent, internally raw 32-bit image data) -> wxBitmap (platform dependent, internally optimized DIB on Windows)
Is it possible the tooltip is rendered in a different way? In our MFC CChartViewer, if there is a CDML tooltip, the tooltip and the chart will first merge onto a buffer (double buffering).
In fact, I had to choose a similar approach, because on Windows platforms wxWidgets does not support windows with a transparent background. So, I extract the part from the chart that will be covered by the tooltip window and use it as the background image for the tooltip window. The tooltip image is then drawn onto that "background". Works like a charm.
Using the output option "alpha=1" seems to work well. So, the transparency issue seems to be solved now.
Thanks for your helpful comments and information.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-29-2021 18:49 |
|
Hi Peter,
at the moment I'm working on adapting the samples "3D Interactive Chart Rotation" and "Contour Cross Section". In principle, both work already. However, I experience rather heavy flickering of the dynamic tooltip, when rotating the chart resp dragging the crosshair lines. The tooltip does not flicker, when simply moving the mouse. Most likely my wxWidgets implementation of the chart viewer is to blame. However, maybe you have an idea how to tackle the problem. Thanks in advance.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jun-30-2021 01:30 |
|
Hi Ulrich,
The CDML tooltip is very difficult to implement due to transparency and flickering issues. It is different for every GUI framework. We tried may be a dozen methods before we found a way that works in MFC.
We found that for MFC, if we implement the tooltip in a separate control floating on top of the chart, it will flicker if both the chart and the tooltip update on mouse move. It is because the drawing sequence will be:
- draw and display the chart
- draw and display the tooltip
- draw and display the chart
- draw and display the tooltip
.........
For MFC, the "draw and display the chart" will overwrite the previous tooltip on the display. So the tooltip disappear. Although the tooltip is redrawn immediately, it is too late and the tooltip will flicker.
Some GUI frameworks (like Windows Forms) have the concept of parent/child relationship. If the tooltip control is the child of the chart control, when the chart control displays, it will paint both the chart and its child (the tooltip) to the screen. So the chart does not overwrite the tooltip. However, MFC does not have this concept.
What we did was to emulate the parent/child mechanism ourselves. When the chart control paints to the screen, MFC will call the OnPaint method. We override the OnPaint method so that it paints both the chart and the tooltip. Furthermore, we paint them to a memory buffer first, before painting the buffer the screen. So the chart and the tooltip is always painted in one piece. This avoids flickering.
The draw back of the above method is that the tooltip must stay inside the chart. (The default tooltip can partly or fully lie outside the control.) Our tooltip positioning code is designed to ensure the tooltip stays inside the chart.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-30-2021 06:12 |
|
Hi Peter,
The CDML tooltip is very difficult to implement due to transparency and flickering issues.
Yes, indeed. At the moment my wxWidgets implementation uses a separate control. And this works sufficiently well, except for the mentioned samples. It seems that I will have to take a different approach to avoid the flickering issue.
It is different for every GUI framework. We tried may be a dozen methods before we found a way that works in MFC.
In fact, I implemented the wxWidgets chart viewer along the lines of the QT chart viewer implementation. However, wxWidgets uses native controls wherever possible, while QT draws all window elements on its own (as far as I know).
We found that for MFC, if we implement the tooltip in a separate control floating on top of the chart, it will flicker if both the chart and the tooltip update on mouse move. It is because the drawing sequence will be:
- draw and display the chart
- draw and display the tooltip
- draw and display the chart
- draw and display the tooltip
.........
Yes, this is almost exactly what happens in my current implementation.
I will take a closer look at the MFC implementation.
Some GUI frameworks (like Windows Forms) have the concept of parent/child relationship. If the tooltip control is the child of the chart control, when the chart control displays, it will paint both the chart and its child (the tooltip) to the screen. So the chart does not overwrite the tooltip. However, MFC does not have this concept.
Well, wxWidgets has the concept of parent/child relationship. So, maybe there is still a chance that I can tweak my code to work flicker-free.
Thanks for the insights into the implementation approaches you chose.
The draw back of the above method is that the tooltip must stay inside the chart. (The default tooltip can partly or fully lie outside the control.) Our tooltip positioning code is designed to ensure the tooltip stays inside the chart.
Yes, I noticed this and without this restriction I wouldn't have been able to support transparent tooltips.
Ok, I will continue my experiments with adjustments to the tooltip implementation and hopefully I can come up with a flicker-free variant.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jun-30-2021 13:15 |
|
Hi Ulrich,
Some GUI frameworks, such as Qt and WPF, do not have this issue. I guess Qt has internal buffering. Everything drawn in the mouse event, including the chart and the tooltip, will be painted to a memory device context. When our code exits from the mouse event, Qt will copy the memory device to the screen device. By that time, the image contains both the chart and the tooltip, so the tooltip is not overwritten on the screen and there is no flickering.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jun-30-2021 20:44 |
|
Hi Peter,
thanks to your comments I managed to implement the dynamic tooltips in an almost flicker-free way. Sporadically the chart viewer window shortly flickers, but the heavy flickering I previously had is gone.
Some GUI frameworks, such as Qt and WPF, do not have this issue. I guess Qt has internal buffering. Everything drawn in the mouse event, including the chart and the tooltip, will be painted to a memory device context. When our code exits from the mouse event, Qt will copy the memory device to the screen device. By that time, the image contains both the chart and the tooltip, so the tooltip is not overwritten on the screen and there is no flickering.
In my wxWidgets implementation the dynamic tooltips are now also drawn together with the chart itself. Maybe an approach with a child window for the tooltip would also work somehow, but due to the lack of support for transparent background of separate windows in wxWidgets on all platforms it is necessary to access the chart bitmap anyway. So simply combining the chart bitmap with the tooltip bitmap if necessary is more efficient.
In a next step I'll try to use QASE_CursorManager for the cursor icons. Thereafter I'll have to go through all samples once more to apply changes necessary for high DPI support. And then I'll have to update the documentation. If all goes well a new release of wxChartDir will happen in the course of July.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-01-2021 05:48 |
|
While adjusting my implementation to use the QASE_CursorManager I experienced memory leaks, and I think the reason is a small flaw in the implementation of method
QCursor &getZoomCursor(QCursor *cache, double scale, int flags)
The cache pointer (corresponding to hZoomInCursor, hZoomOutCursor, and hNoZoomCursor respectively) is always 0 on entering the method, because hZoomInCursor, hZoomOutCursor, and hNoZoomCursor are passed by value and will therefore not be updated by assigning a new value to the cache pointer.
IMHO the method's signature has to be changed to
QCursor &getZoomCursor(QCursor** cache, double scale, int flags)
and a level of indirection has to be added to the cache pointer within the method. |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jul-01-2021 15:52 |
|
Hi Ulrich,
Thank you very much for reporting this to use. You are correct. There is a memory leak and the cursor is not really cached. "QCursor**" should be used instead of "QCursor*" and reference to "cache" within the method should be "*cache". I will need to update the release in the new few days to fix this issue.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-01-2021 21:02 |
|
Hi Peter,
[...] There is a memory leak and the cursor is not really cached. "QCursor**" should be used instead of "QCursor*" and reference to "cache" within the method should be "*cache". I will need to update the release in the new few days to fix this issue.
From my side there is no hurry regarding a fix as I don't use QT.
Additionally, it may be necessary to apply further changes to the cursor manager class. In a DPI aware per monitor application it would possibly be necessary to cache cursor instances for more than one resolution, while the current implementation only caches a cursor instance for the first resolution asked for and will then always return this cache instance regardless of the requested resolution.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jul-01-2021 22:26 |
|
Hi Ulrich,
I am aware of the "DPI aware per monitor" issue. So far I have not tested exactly what this means for different GUI frameworks. I need to do more tests on how it works.
For MFC, I suspect "DPI aware per monitor" means the DPI of the Window will equal to the DPI of the monitor that displays the Window initially (as opposed to equal to the DPI of the main monitor). Different Windows can have different DPIs. It does not necessarily mean the Window can change DPI when it is dragged from one monitor to the other monitor.
(I can change the monitor DPI by using Settings/Display, but so far I am unable to make an existing MFC Window change DPI. It always stay at the DPI when it is first created.)
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-01-2021 23:12 |
|
Hi Peter,
I am aware of the "DPI aware per monitor" issue. So far I have not tested exactly what this means for different GUI frameworks. I need to do more tests on how it works.
Unfortunately, I have only a single-monitor system at hand, so I'm not able to actually test the "DPI aware per monitor" use case.
Since QASE_CursorManager is a singleton, there is a problem, if multiple ChartViewer windows are displayed on different screens with different DPIs, because only a single resolution is cached at the moment.
For MFC, I suspect "DPI aware per monitor" means the DPI of the Window will equal to the DPI of the monitor that displays the Window initially (as opposed to equal to the DPI of the main monitor). Different Windows can have different DPIs. It does not necessarily mean the Window can change DPI when it is dragged from one monitor to the other monitor.
I have only theoretical knowledge, but as far as I know Windows sends a message to the application if a window was moved from one screen to another with a different DPI, so that the application can recreate graphics in the required resolution. However, I have no idea, how the case is handled, if a window is partially displayed on one screen and partially on another.
(I can change the monitor DPI by using Settings/Display, but so far I am unable to make an existing MFC Window change DPI. It always stay at the DPI when it is first created.)
Regarding wxWidgets work is in progress to fully support "DPI aware per monitor". However, at the moment this feature is still experimental and incomplete.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jul-02-2021 04:09 |
|
Hi Ulrich,
I speculate Qt will probably display the cursor at the correct size, even if the DPI changes. In Qt, our code notifies Qt of the image DPI. If the image DPI does not match the Qt GUI DPI, Qt will automatically resize the image. The drawback is that if Qt needs to resize the image to make it larger, the image will look blurred.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-02-2021 18:56 |
|
Hi Peter,
I speculate Qt will probably display the cursor at the correct size, even if the DPI changes.
In Qt, our code notifies Qt of the image DPI. If the image DPI does not match the Qt GUI DPI, Qt will automatically resize the image. The drawback is that if Qt needs to resize the image to make it larger, the image will look blurred.
Yes, AFAIK Qt takes care of automatically adjusting graphics, and if the application does not provide the graphics in a suitable resolution, Qt will scale them - with the known visual effects like blurred images.
So, using cached cursors with a "wrong" resolution will still work, but may not look nice.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jul-01-2021 16:50 |
|
Hi Ulrich,
For the remaining occasional flickering, it may be due to the framework painting the control background.
MFC controls by default will request the parent window to paint the control background to erase the previous pixels of the control. The control will then paint the foreground. If this sequence repeats rapidly, it may cause flickering.
In our code, we override the OnPaint method to paint the background onto a memory buffer, then paint the chart and the tooltip to the buffer, before finally painting the buffer to the display device. This avoids flickering.
Painting the background is not necessary for a completely opaque control as the foreground will hide the background, in which case our code will skip painting the background.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-01-2021 21:19 |
|
Hi Peter,
For the remaining occasional flickering, it may be due to the framework painting the control background.
I have not yet further analyzed what exactly causes the sporadic flickering, but most likely the wxWidgets event handling is responsible. It could be that the parent window somehow receives a repaint event, clears the background and asks the child windows to repaint their content. And the clearing of the background then causes the flicker. However, I will have to analyze the sequence of paint events to verify whether my assumption is correct or not.
In our code, we override the OnPaint method to paint the background onto a memory buffer, then paint the chart and the tooltip to the buffer, before finally painting the buffer to the display device. This avoids flickering.
In fact, I did almost the same in my wxWidgets implementation: the OnPaint method simply paints the chart and the tooltip on the wxDC (wxWidgets drawing context), and I explicitly asked the Refresh method to not erase the background. This removed flickering almost completely. As I wrote above I will have to further analyze whether in the course of the event handling wxWidgets clears the background in some other method, and if yes, what I possibly can do about it.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-07-2021 23:03 |
|
Regarding the flickering I experienced more intense flickering for the dynamic tooltips in the heatmap samples than for 3D surface charts (without changing anything in the implementation). Interestingly, the flickering was almost neglectible, when I added some logging to a text window. So, it seems to depend on timing effects. After some more experiments I have the impression that the logic for the delayed mouse events is somehow reponsible - at least partially. I reduced the event distance from 10ms to 5ms, and that reduced the flickering. I even removed the delay logic completely, and the samples still seem to work flawlessly - with only minor flickering.
I'm a bit undecided wether to keep the delay logic or not. Maybe the underlying mouse event handling of wxWidgets is different from that of QT, so that this extra logic is not needed. However, I'm not an expert for the core details of wxWidgets.
Regarding DPI awareness I followed your advice to test this by simply adjusting the screen resolution factor in Windows from 100% to 125%. This worked very well, and allowed me to detect a few minor glitches in my implementation. |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-08-2021 02:03 |
|
Hi Peter,
while testing for DPI awareness I experienced a strange phenomenon.
I have implemented sort of a resource loader, which at the moment just inspects a number of common directories to locate a resource file. In principal, this works well.
However, in conjunction with the sample procedure surfacetexture in democharts.cpp my resource loader reports "file not found" for the ID "texture", if the screen resolution is set to 125% or 150%. If the screen resolution is set to 100%, no error is reported. This suggests, that the resource loader is asked to load the resource "texture" (which is created by the demo code and registered with setResource), but only if the screen resolution is not 100%.
Since my resource loader implementation is called from the ChartDirector library, I'd like to ask you to check whether the resource loader is called on purpose, when the resolution is not 100%, or whether this might be a bug. (My resource loader simply returns "false" in such a case, and the sample is displayed correctly. So, from my side it does not impose a real problem.)
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jul-08-2021 14:06 |
|
Hi Ulrich,
We support many GUI frameworks (MFC/Qt in C++ and other frameworks in .NET, Java, classical VB/VBA, etc). Our experience is that GUI frameworks are very different. So it is not surprise the wxWidget framework is different.
In MFC, if the tooltip moves or changes, we only "invalidate" the part of the chart that is affected, not the entire chart. The part that is affected is the "union" of the old and new tooltip box bounding box, which is quite small. So the redrawing should be very fast. If there is still flickering, it should only occur to the tooltip, and not the entire chart.
For your case, may be you can do a test to see how wxWidget behave. In your "OnPaint" event, try to do nothing or just painting small part of the screen (may be a small rectangle of random color) and consume some time (eg. sleep for 20ms). Since your code does not erase the background, the chart should still be visible and there should be no flickering. This can determine if wxWidget is still erasing the background anyway.
If wxWidget also supports invalidating part of the control, you can try to invalidate only the bottom right corner to see if the entire chart still flickers.
The delay mouse move is to avoid too much mouse move events. Some GUI frameworks can generate lots of mouse events (say 100 per second), and mouse events can jump the message queue. If the mouse move events take too long to process, the system will not have time to process the "OnPaint" event. If our code detects the mouse move events are happening too fast, we will set up a timer and update the chart in the timer event. This combines multiple mouse move events into one timer event.
May be wxWidgets handles painting in the mouse move events different from the timer event, because the mouse move event is an "interactive event".
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-08-2021 20:41 |
|
Hi Peter,
Our experience is that GUI frameworks are very different. So it is not surprise the wxWidget framework is different.
Of course, it was to be expected that wxWidgets has its own rough edges, and although I have several years of experience with the wxWidgets framework, I'm nevertheless just a user of wxWidgets.
In MFC, if the tooltip moves or changes, we only "invalidate" the part of the chart that is affected, not the entire chart. The part that is affected is the "union" of the old and new tooltip box bounding box, which is quite small. So the redrawing should be very fast.
I have to admit that I was too lazy to keep track of the actual bounding box of the enhanced tooltips and issued a refresh for the whole chart. In fact, that worked remarkably well for most samples, and only the tooltip itself kept flickering a bit.
Now I followed your description refreshing only the tooltip rectangle itself. This improves the situation a lot. From my point of view it now works as a user would expect it.
The delay mouse move is to avoid too much mouse move events. Some GUI frameworks can generate lots of mouse events (say 100 per second), and mouse events can jump the message queue. If the mouse move events take too long to process, the system will not have time to process the "OnPaint" event. If our code detects the mouse move events are happening too fast, we will set up a timer and update the chart in the timer event. This combines multiple mouse move events into one timer event.
Up to now I have tested my new wxWidgets implementation on Windows only. As far as I can tell the mouse event delay logic as used in your QT implementation seems not to improve the behaviour for a wxWidgets application. Therefore I intend to inactivate the logic for now, but will keep the code in place, so that I can reactivate it easily, if necessary on other platforms.
May be wxWidgets handles painting in the mouse move events different from the timer event, because the mouse move event is an "interactive event".
I would have to ask one of the wxWidgets core developers, how mouse events are actually handled internally.
Regards,
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Peter Kwan on Jul-09-2021 00:13 |
|
Hi Ulrich,
The delayed mouse event system was designed 15 years ago. By combining mouse events, it makes the GUI more responsive when we need to redraw the a large chart during mouse move, such as for "drag to scroll" or "programmable track cursor" on a full screen chart.
Computers are much faster now, so the delayed mouse event may be less significant. For small updates such as the tooltip, it may have no useful effect at all.
Regards
Peter Kwan |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-09-2021 00:41 |
|
Hi Peter,
The delayed mouse event system was designed 15 years ago. By combining mouse events, it makes the GUI more responsive when we need to redraw the a large chart during mouse move, such as for "drag to scroll" or "programmable track cursor" on a full screen chart.
Now, that I have the enhanced tooltips and the selection box working without annoying flicker, I returned to "drag to scroll" and discovered that it din't work properly without the mouse delay logic - I experienced chart update delays.
Computers are much faster now, so the delayed mouse event may be less significant. For small updates such as the tooltip, it may have no useful effect at all.
At least for the wxWidgets implementation of the enhanced tooltips it has a negative effect, if I use an event distance of 10ms (as in the QT implementation). With 10ms I experience flickering tooltips (at least for the heatmap samples). I reduced the event distance to 5ms, and this seems to be a reasonable compromise: neglectable flicker for the enhanced tooltips and working "drag to scroll".
Basically, my wxWidgets implementation for CD 7 is now completed. I will do some further tests on a Linux machine to check that it works there, too.
Regards
Ulrich |
Re: Questions while adjusting wxChartDir to Chartdirector 7 |
Posted by Ulrich Telle on Jul-10-2021 06:53 |
|
The adjusted implementation of wxChartDir is now available from https://github.com/utelle/wxchartdir. It has been tested with wxWidgets 3.1.x on Windows and Linux.
A release will be made after ChartDirector 7 will have been officially announced. |
|