Getting the monitor's refresh rate with AWT/Swing:
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDisplayMode()
.getRefreshRate()
Question: Is there a similar method/procedure in getting the refresh rate with JavaFX? And if there is one, how to do it?
No equivalent public API in JavaFX
This is the DisplayMode.getRefreshRate() javadoc for the awt API you are currently using:
Returns the refresh rate of the display, in hertz.
There is no equivalent public JavaFX API.
If the public api existed in JavaFX, it would probably be accessible via the Screen class, but there is no accessor for such functionality defined there.
There is an undocumented, unsupported, private API which would appear to offer similar functionality:
com.sun.javafx.tk.Toolkit.getToolkit().getRefreshRate()
JavaFX pulse processing
Related information on how JavaFX works can be found in the pulse documentation for the JavaFX architecture description.
A pulse is an event that indicates to the JavaFX scene graph that it is time to synchronize the state of the elements on the scene graph with Prism. A pulse is throttled at 60 frames per second (fps) maximum and is fired whenever animations are running on the scene graph. Even when animation is not running, a pulse is scheduled when something in the scene graph is changed. For example, if a position of a button is changed, a pulse is scheduled.
When a pulse is fired, the state of the elements on the scene graph is synchronized down to the rendering layer.
The screen hardware refresh rate is different from JavaFX internal pulse processing rate. Though I guess it is technically possible that these could be synchronized in the internal implementation (and that may happen in some cases), there is nothing in the public documentation which mentions whether that actually occurs.
A note on adaptive refresh technologies
Also note, with the advent of technologies such as gsync and free sync the refresh rate can be adapted to the content.
Vertical synchronization is an option in most systems in which the video card is prevented from doing anything visible to the display memory until after the monitor finishes its current refresh cycle . . . technologies like FreeSync and G-Sync reverse the concept and adapts the display's refresh rate to the content coming from the computer. Such technologies require specific support from both the video adapter and the display.
Such technologies make the notion of relying on a hardware refresh rate in the monitor a bit blurry at best.
Answers to FAQs
You state that your intention for querying the display refresh rate is:
Like drawing anything as fast, as long as it's still achievable by the monitor...
JavaFX is, by default intentionally capped at a 60fps frame-rate, regardless of the monitor specifications.
This cap can be overridden using undocumented system properties, as outlined here:
How to ignore the 60fps limit in javafx?
You could use the undocumented feature to remove the frame rate cap in JavaFX and then rely on the underlying video card driver and hardware to render at the maximum rate available within the underlying hardware and software capability for the screen.
Here is the code which deals with that in the openjfx source.
/*
* Called to set the value of PULSE_DURATION or PULSE_DURATION_NS based on
* the refresh rate of the primary screen (unless overridden by the
* FRAMERATE_PROP Setting). If the refresh rate can not be determined the
* default of 60hz is used.
*
* #param precision - precision in (1000 for ms or 1000000000 for ns)
*
* #return pulse duration value, either in ms or ns depending on the
* parameter.
*/
protected int getPulseDuration(int precision) {
int retVal = precision / 60;
// Allow Setting to override monitor refresh
if (Settings.get(FRAMERATE_PROP) != null) {
int overrideHz = Settings.getInt(FRAMERATE_PROP, 60);
if (overrideHz > 0) {
retVal = precision / overrideHz;
}
} else if (Settings.get(PULSE_PROP) != null) {
int overrideHz = Settings.getInt(PULSE_PROP, 60);
if (overrideHz > 0) {
retVal = precision / overrideHz;
}
} else {
// If not explicitly set in Settings, try to set based on
// refresh rate of display
int rate = Toolkit.getToolkit().getRefreshRate();
if (rate > 0) {
retVal = precision / rate;
}
// if unknown, use default
}
return retVal;
}
In the source, the framerate is governed by the undocumented property javafx.animation.framerate, which corresponds to the setting FRAMERATE_PROP in the above source and javafx.animation.pulse which corresponds to the setting PULSE_PROP.
The internal Toolkit class provides a fallback API to use the refresh rate of the display.
So perhaps you could achieve what you want by setting the the system property javafx.animation.framerate, to the value of com.sun.javafx.tk.Toolkit.getToolkit().getRefreshRate() or by setting both javafx.animation.framerate and javafx.animation.pulse to null. I have not tried this, so I do not know. Care would need to be taken to ensure that the values that you set are correctly maintained and not overridden by defaults.
You need to be aware that, if you do this, you are making use of undocumented APIs so this would require opening appropriate module exports so that the APIs are accessible and also may be removed or become incompatible without notice in future versions of the platform (though these settings have been unchanged for many years currently).
So I have been reading the link you provided, and to me, it seems JavaFX is simply stuck at 60fps because it prefers to?
By default, yes, it is a tradeoff.
For the types of applications generally developed using JavaFX 60fps refresh max within the pulse rendering mechanism is generally sufficient regardless of the underlying hardware capabilities of the device. Increasing the rate cap above that level is usually not a good tradeoff for most JavaFX applications as it means use of higher resources (CPU and Video processing capacity and power draw), for gains in refresh updates which are generally undetectable to most users and often not supported by underlying hardware.
That said, if you want to override this default limit, there are undocumented ways of doing so, as mentioned above.
Why not use the same method?
Using the AWT method may be a reasonable alternative in this case.
The hardware refresh won't change depending on the chosen UI toolkit.
Currently (JavaFX 17), the JavaFX system depends on the java.awt package (javafx.base module requires the java.desktop module which contains all of the awt classes). So if JavaFX is available, so is the java.awt.DisplayMode class.
So if the goal is to determine the hardware refresh rate, you can do that using the existing awt API.
The refresh rate won't change when using Swing or JavaFX right?
Hardware refresh rate will not change depending on the UI toolkit used.
The definition of the scene graph pulse rate in JavaFX for updating rendering of the scene graph is somewhat related to refresh rate, but is a JavaFX only concept because Swing does not make use of a scene graph during its rendering phase.
Related
In my Qt GUI, I have a lot of code which updates stylesheets every n milliseconds based on boolean value, like this:
void MainWindow::_update_lock_elements()
{
if(backend::i().get_controller().is_control_locked()){
ui->lockInfoLabel->setText("CONTROL GUI");
ui->lockButton->setEnabled(false);
ui->lockInfoLabel->setStyleSheet("background-color: #35BC19");
ui->lockButton->setStyleSheet("background-color: green");
}
else{
ui->lockInfoLabel->setText("MONITORING GUI");
ui->lockButton->setEnabled(true);
ui->lockInfoLabel->setStyleSheet("background-color: #FF8888;");
ui->lockButton->setStyleSheet("");
}
}
I have noticed this to hog up a lot of cpu power. If I cache the last value for the if condition, and only set the style sheets if the value has changed with respect to the last value, the performance is improved massively.
Is there a more efficient way of doing this in Qt so I don't have to cache and compare the last values everywhere?
Actually, no. Setting stylesheets is not cheap. Better avoid using them if possible especially for frequent interface updates.
What I do in such cases - create a state machine and change GUI only at state transitions. In general, this is still comparing last values, but in a more organized way.
AFAIK, the only way to make a GUI application use less CPU is to minimize draw calls. This is not a videogame, we do not need to redraw the whole screen every frame. That is why the signal/slot system is so well suited for Qt - everything is driven by events, not periodically.
More specifically, I have a slider (which I call clock) whose value I increase with a periodic callback:
clock = Slider(start=0, end=1000, value=0, step=1, title='clock')
def increase_slider_value():
clock.value = (clock.value + 1) % 1000
period_ms = 50
curdoc().add_periodic_callback(increase_slider_value, period_ms)
When the value of the clock changes the sources of some plots are updated:
clock.on_change('value', update_sources)
Updating the sources is expensive and fairly optimized, using patches and if statements for key values of the clock.
This works fine if the callback changes the clock. But if the users grabs the slider and moves it around the patches do not create the desired sources anymore, i.e., create invalid states of the sources and rubbish plots.
The only half-elegant way I can see to fix this is to treat value changes that are caused by the user differently from value changes that are caused by the callback. User-caused updates would require a new computation of the sources while the periodic callback would continue to utilize patches.
A) Does this make sense to you / do you approve?
B) How can I get the clock.on_change(..) to distinguish between the event that triggered the change? Are there "slider drag" events?
There is not any way to distinguish the events directly. I would suggest you utilize the slightly hacky but serviceable approach described in Throttling in Bokeh application. This serves two purposes:
You can specify a different callback on the "fake" data source than the clock.on_change(...) one.
You can actually throttle user updates from the slider to only "finished" states so the expensive callback does not get executed unnecessarily often.
I'm developing a CAD application using Qt 5.6.2 that is required to run in cheap computers at the same time that it needs to handle thousands of items in the same scene. Because of that, I had to make a lot of experimentations in order to get the best performance.
I decided to create this post to help others and also myself, as long as other people also contribute with more optimization tips.
My text is still a work in progress and I may update it if I discover better techniques (or that I said something really stupid).
Disable Scene Interaction
Event handling is responsible by a good part of the CPU usage by the QGraphicsView engine. At each mouse movement the view asks the scene for the items under the mouse, which calls the QGraphicsItem::shape() method to detect intersections. It happens even with disabled items. So, if you don't need your scene to interact with mouse events, you can set QGraphicsView::setIntenteractive(false). In my case, I had two modes in my tool (measurement and move/rotate) where the scene was basically static and all edit operations were executed by QGraphicsView instead. By doing that, I was able to increase the frame rate in 30%, unfortunately ViewportAnchor::AnchorUnderMouse stopped working.
Reuse Your QPainterPaths
Cache your QPainterPaths inside your QGraphicsItem object. Constructing and populating it can be very slow. In my case it was taking 6 seconds to read a file just because I was converting a point-cloud with 6000 points into a QPainterPath with multiple rectangles. You won't want to do it more than once.
Also, in Qt 5.13 it is now possible to reserve the QPainterPaths 's internal vector size, avoiding multiple copies as it grows.
Simplify your QGraphicsItem::shape()
This method is called multiple times during mouse events, even if the item is not enabled. Try to make it as efficient as possible.
Sometimes, even caching the QPainterPath is not enough since the path intersection algorithm, executed by the scene, can be very slow for complex shapes. In my case I was returning a shape with around 6000 rectangles and it was pretty slow. After downsampling the point-cloud I was able to reduce the number of rectangles to around 1000, that improved the performance significantly but still wasn't ideal since shape() was still being called even when the item was disabled. Because of that I decided to maintain the original QGraphicsItem:shape() (which returns the bounding box rectangle) and just return the more complex cached shape when the item is enabled. It improved the frame-rate when moving the mouse in almost 40% but I still think it is a hack and will update this post if I come up with a better solution. Despite that, in my tests I didn't have any issue as long as I keep its bounding box untouched. If it is not the case, you'll have to call prepareGeometryChange() and then update the bounding box and the shape caches elsewhere.
Test Both: Raster and OpenGL engines
I was expecting that OpenGL would always be better than raster, and it is probably true if all you want is to reduce CPU usage for obvious reasons. However, if all you want is to increase the number of frames per second, especially in cheap/old computers, it worth trying to test raster as well (the default QGraphicsView viewport). In my tests, the new QOpenGLWidget was slightly faster than the old QGLWidget, but the number of FPS was almost 20% slower than using Raster. Of course, it can be application specific and the result can be different depending on what you are rendering.
Use FullViewportUpdate with OpenGL and prefer other partial viewport update method with raster (requires a more strict bounding rectangle maintaining of the items though).
Try to disable/enable VSync to see which one works better for you: QSurfaceFormat::defaultFormat().setSwapInterval(0 or 1). Enabling can reduce the frame rate and disabling can cause "tearing".
https://www.khronos.org/opengl/wiki/Swap_Interval
Cache Complex QGraphicsItems
If your QGraphicsItem::paint operation is too complex and at the same type mostly static, try to enable caching. Use DeviceCoordinateCache if you are not applying transformation (like rotation) to the items or ItemCoordinateCache otherwise. Avoid call QGraphicsItem::update() very often, or it can be even slower than without caching. If you need to change something in your item, two options are: to draw it in a child, or use QGraphicsView::drawForeground().
Group Similar QPainter Drawing Operations
Prefer drawLines over calling drawLine multiple times; favor drawPoints over drawPoint. Use QVarLengthArray (uses stack, so can be faster) or QVector (uses heap) as container. Avoid change the brush very often (I suspect it is more important when using OpenGL). Also QPoint can be faster and is smaller than QPointF.
Prefer Drawing Using Cosmetic Lines, Avoiding Transparency and Antialiasing
Antialiasing can be disabled, especially if all you are drawing are horizontal, vertical or 45deg lines (they actually look better this way) or you are using a "retina" display.
Search for Hotspots
Bottlenecks may occur in surprising places. Use a profiler (in macOS I use Instruments/Time Profiler) or other methods like elapsed timer, qDebug or FPS counter (I put it in my QGraphicsView::drawForeground) to help locate them. Don't make your code ugly trying to optimize things you are not sure if they are hotspots or not. Example of a FPS counter (try to keep it above 25):
MyGraphicsView:: MyGraphicsView(){
...
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(oneSecTimeout()));
timer->setInterval(1000);
timer->start();
}
void MyGraphicsView::oneSecTimeout()
{
frameRate=(frameRate+numFrames)/2;
qInfo() << frameRate;
numFrames=0;
}
void MyGraphicsView::drawForeground(QPainter * painter, const QRectF & rect)
{
numFrames++;
//...
}
http://doc.qt.io/qt-4.8/qelapsedtimer.html
Avoid Deep-copy
Use foreach(const auto& item, items), const_iterator or items.at(i) instead of items[i], when iterating over QT containers, to avoid detachment. Use const operator and call const methods as much as possible. Always try to initialize (reserve() ) your vectors/arrays with a good estimation of its actual size.
https://www.slideshare.net/qtbynokia/optimizing-performance-in-qtbased-applications/37-Implicit_data_sharing_in_Qt
Scene Indexing
Favor NoIndex for scenes with few items and/or dynamic scenes (with animations), and BspTreeIndex for scenes with many (mostly static) items. BspTreeIndex allows fast searching when using QGraphicsScene::itemAt() method.
Different Paint Algorithms for Different Zoom Levels
As in the Qt 40000 Chips example, you don't need to use the same detailed drawing algorithm to draw things that will look very small at the screen. You can use 2 different QPainterPath cached objects for this task, or as in my case, to have 2 different point-cloud vectors (one with a simplified subset of the original vector, and another with the complement). So, depending on the zoom level, I draw one or both. Another option is to shuffle your point-cloud and draw just the n first elements of the vector, according to the zoom level. This last technique alone increased my frame rate from 5 to 15fps (in a scene where I had originally 1 million points). Use in your QGraphicsItem::painter() something like:
const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());
const int n = qMin(pointCloud.size(), pointCloud.size() * lod/0.08);
painter->drawPoints(pointCloud.constData(), n);
Oversize Your QGraphicsScene::sceneRect()
If you are constantly increasing your scene rectangle in size, reindexing can freeze your application for a short period of time. To avoid that, you can set a fixed size or add and remove a temporary rectangle to force the scene to increase to a bigger initial size:
auto marginRect = addRect(sceneRect().adjusted(-25000, -25000, 25000, 25000));
sceneRect(); // hack to force update of scene bounding box
delete marginRect;
Disable the Scroolbars
If the view is flickering when you scrool the scene, disabling the scroolbars can fix it:
setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
Apply Mouse-controlled Transformations to Multiple Items Using Grouping
Grouping with QGraphicsScene::createItemGroup() avoids calling QGraphicsItem::itemChange multiple times during the transformation. It is only called twice when the group is created and destroyed.
Compare multiple Qt versions
I didn't have enough time to investigate it yet, but in my current project at least, Qt 5.6.2 (on Mac OS) was much faster than Qt 5.8.
My application, while not exactly a CAD program, is CAD-like in that it allows the user to construct a "blueprint" of various items in a space, and the user is allowed to add as many items as he likes, and some users' designs can get quite crowded and elaborate, with hundreds or thousands of items present at once.
Most of the items in the view are going to be more or less static (i.e. they move or change appearance only when the user clicks/drags on them, which is rarely). But there are often also a few foreground items in the scene that are constantly animated and moving around at 20fps.
To avoid having to re-render complex static elements on a regular basis, I pre-render all the static elements into the QGraphicsView's background-cache whenever any of them change, or whenever the zoom/pan/size settings of the QGraphicsView change, and exclude them from being rendered as part of the normal foreground-view-redrawing process.
That way, when there are moving elements running around the QGraphicsView at 20fps, all of the very-numerous-and-elaborate-static-objects are drawn (by the code in QGraphicsScene::drawBackground()) via a single call to drawPixmap() rather than having to algorithmically re-render each item individually. The always-moving elements can then be drawn on top in the usual fashion.
Implementing this involves calling setOptimizationFlag(IndirectPainting) and setCacheMode(CacheBackground) on the QGraphicsView(s), and also calling resetCachedContent() on them whenever any aspect of the any of the static-items changes (so that the cached-background-image will be re-rendered ASAP).
The only tricky part is getting all of the "background" QGraphicsItems to render inside the QGraphicsScene's drawBackground() callback, and also to not render inside the usual QGraphicsScene::drawItems() callback (which is typically called much more often than QGraphicsScene::drawBackground()).
In my stress-test this reduces my program's steady-state CPU usage by about 50% relative to the "vanilla" QGraphicsScene/QGraphicsView approach (and by about 80% if I'm using OpenGL via calling setViewport(new QOpenGLWidget) on my QGraphicsView).
The only downside (other than added code complexity) is that this approach relies on using QGraphicsView::setOptimizationFlag(IndirectPainting) and QGraphicsView::drawItems(), both of which have been more-or-less deprecated by Qt, so this approach might not continue to work with future Qt versions. (It does work at least as far as Qt 5.10.1 though; that's the latest Qt version I've tried it with)
Some illustrative code:
void MyGraphicsScene :: drawBackground(QPainter * p, const QRectF & r)
{
if (_isInBackgroundUpdate == false) // anti-infinite-recursion guard
{
QGraphicsScene::drawBackground(p, r);
const QRectF rect = sceneRect();
p->fillRect(rect, backgroundBrush().color());
// Render the scene's static objects as pixels
// into the QGraphicsView's view-background-cache
this->_isInBackgroundUpdate = true; // anti-infinite-recursion guard
render(p, sceneRect());
this->_isInBackgroundUpdate = false;
}
}
// overridden to draw only the items appropriate to our current
// mode (foreground items OR background items but not both!)
void MyGraphicsScene :: drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[], QWidget *widget)
{
// Go through the items-list and only keep items that we are supposed to be
// drawing in this pass (either foreground or background, depending)
int count = 0;
for (int i=0; i<numItems; i++)
{
const bool isItemBackgroundItem = (_backgroundItemsTable.find(items[i]) != _backgroundItemsTable.end());
if (isItemBackgroundItem == this->_isInBackgroundUpdates) items[count++] = items[i];
}
QGraphicsScene::drawItems(painter, count, items, options, widget);
}
I've found a lot of suggestions to use Timeline with KeyFrame.onFinished to schedule tasks in the JavaFX. But the AnimationTimer doc says, that its handle method is called 60 times per second.
It is unclear from documentation but it seems that Timeline uses AnimationTimer internally. Does that means that the timeline solution for scheduling imposes CPU-intensive polling mode? If that is actually how JavaFX works, what other methods for scheduling is recommended?
Advised Usage Rules for Scheduling in JavaFX
Given how Timeline works (see below), some basic rules can be derived:
Use a Timeline when you want to schedule quick operations that modify attributes of the scene graph.
Do not use a Timeline when you want to do any of the following:
Perform operations that are themselves time-consuming (e.g. the work for a given scheduled activity takes longer than 1/30th of a second).
Perform work which really doesn't have anything to do with the scene graph.
Have real-time scheduling at a resolution greater than a pulse (1/60th of a second).
Control your scheduling using traditional java.util.concurrent mechanisms or a third party library.
Run your scheduled tasks on their own threads.
Also, for a one-off execution that updates the scene graph after a fixed delay, I like to use a PauseTransition.
Answers to specific questions and concerns
It is unclear from documentation but it seems that Timeline uses AnimationTimer internally.
No, Timeline does not use AnimationTimer internally. However, Timeline is still pulse based and will receive code to update the current state of the timeline will be invoked on each pulse (usually 60 times a second). To understand what a pulse is read the background section below.
Does that means that the timeline solution for scheduling imposes CPU-intensive polling mode?
Yes, there is some overhead, but it is likely minimal enough that it can be disregarded. The JavaFX system is going to be running some work on each pulse anyway, regardless whether you create any timelines yourself. Unless you are heavily animating objects or doing a lot of work on each pulse, then the time to process each pulse will be completely trivial as there is simply very little work to be done on each pulse.
If that is actually how JavaFX works, what other methods for scheduling is recommended?
There are numerous alternatives to Timeline:
Use a javafx.concurrent facility such as a Task or Service, and specifically a ScheduledService.
This is good if you want to provide feedback (such as progress, message updates and return values) to the JavaFX UI.
Use a java.util.concurrent facility.
This is good if you don't need the additional features of javafx.concurrent for progress and messaging or if you want the additional control provided by java.util.concurrent facilities.
If you use this, you can still get intermittent or final feedback to your JavaFX UI by invoking Platform.runLater().
Use a java.util.TimerTask.
Simpler API than java.util.concurrent, no direct interface for JavaFX feedback, though again you can use Platform.runLater() for that.
Use a third party scheduling system (such as Quartz):
More complicated than other solutions, but also more flexible, though it adds a dependent library and requires Platform.runLater if you want to modify scene graph elements.
If you do use something other than a timeline, you need to take care:
You invoke Platform.runLater if you want to modify anything on the scene graph from your non-JavaFX application threads.
You don't invoke Platform.runLater too often so as to overload the JavaFX application thread (e.g. by coalescing update calls).
Background Info
Timeline Operation
For example, let's say you have a Timeline which has a Keyframe that has a duration of 1 second. The internal Timeline will still be updated each pulse. This is required because a Timeline is more than just a scheduler, it has other properties and tasks. For instance the Timeline has a currentTime property. The currentTime will be updated each pulse, so that it always reflects the current time accurately. Similarly, internal logic with the Timeline will check to see if there are any KeyValues associated with the Timeline, and update their values on each pulse, in accordance with their associated Interpolator. And the internal logic will check on each pulse to see if the Timeline is finished, to call the onFinished handler. It will also evaluate the current time versus the duration of each keyframe and, if there is a match, fire the appropriate action event for the keyframe.
So the Timeline is acting like a scheduler in that it can execute key frames at specific times, but it is more than a scheduler as it also maintains it's current time, current state and ability to continuously vary associated key values. Additionally, you can vary the rate, direction and number of cycles of a timeline, as well as pause it in position, then resume it, which also differs from a traditional scheduler.
An aspect of the Timeline is that because it is based upon callbacks from pulses in the JavaFX system; everything runs on the JavaFX application thread. Therefore, when you use timelines (even 1000 of them), no additional threads are spawned, so from that perspective it is lightweight. Also all logic invoked by the timeline occurs on the JavaFX application thread. Because everything runs on the JavaFX application thread, this makes the Timeline nice for manipulating elements and attributes of the active JavaFX scene graph (such manipulations must be performed on the JavaFX application thread). However, a timeline would be a bad choice if you wanted to do a lot of CPU time-consuming work or blocking I/O in the associated timeline keyframes. This is because the JavaFX application thread will be blocked while that work occurs, freezing your UI.
Pulses
The JavaFX runtime system is based upon pulses.
A pulse is an event that indicates to the JavaFX scene graph that it is time to synchronize the state of the elements on the scene graph with Prism. A pulse is throttled at 60 frames per second (fps) maximum and is fired whenever animations are running on the scene graph. Even when animation is not running, a pulse is scheduled when something in the scene graph is changed. For example, if a position of a button is changed, a pulse is scheduled.
When a pulse is fired, the state of the elements on the scene graph is synchronized down to the rendering layer. A pulse enables application developers a way to handle events asynchronously. This important feature allows the system to batch and execute events on the pulse.
The JavaFX animation system stores a list of all of the animations and animation timers which been created and not garbage collected. On each pulse it will iterate through this list. Each animation timer will have it's handle callback invoked on each pulse. Each timeline or transition will similarly have its internal state update and update any associated key values or fire key frame action events as appropriate.
You can use ScheduledExecutorService
final Runnable runnable = new Runnable() {
public void run() {
//Code goes here
}
}
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);
I have a QGraphicsSvgItem that I need to rotate a certain amount based on an external variable coming into my application. Currently I'm directly setting the rotation property of the item, but this appears to significantly drop my frame rate. (117.0 fps -> 98.0 fps) is this normal? Is this the correct way to animate a QGraphicsItem? I realize that the framerate sounds plenty high, but I'm concerned that as more items in the view are animated, that the performance will become a problem.
The code below is being called via a QTimer timeout signal at an interval of 0.
Note that I have also tried using a QGraphicsItem with a custom QPainterPath to draw the item rather than an SVG and it made no noticeable performance difference.
qreal diff = m_rope_length_ft[0] - m_rope_length_ft_prev[0];
qreal angle = diff * 5.0;
m_rope_reel[0]->setRotation(m_rope_reel[0]->rotation() + angle);
m_rope_reel[1]->setRotation(m_rope_reel[1]->rotation() + angle);
m_rope_reel[0]->update();
m_rope_reel[1]->update();
A more proper way to animate in Qt is to use QPropertyAnimation. It requires that:
your graphics item is inherited from both QObject and QGraphicsSvgItem;
rotation is registered as property with Q_PROPERTY (getters and setters already exist so there is no need to implement them).
Also you should try to tune graphics item's cache mode. ItemCoordinateCache seems to be suitable for your case. There is also view cache mode, but it's hard to say which is better in your case.
Note that performance optimization should be done using average amount of items your program should be able to process. Optimizing a single item rendering may be pointless.