Qt: render QML window to buffer - qt5

My code looks like this:
QApplication app(argc, argv);
QQmlApplicationEngine app_engine;
app_engine.load("qml/main.qml");
return app.exec();
Could somebody please help me how to make Qt render everything to the buffer I provide? OpenGL must be avoided. I could make this work with QWebPage, but this appears to be much more difficult to me...

I've found the way to get the QML output as QImage, but it works only if the QML window has focus. Incomplete code snippet follows:
QApplication app(argc, argv);
QQmalApplicationEngine *appEngine = new QQmlApplicationEngine(this);
appEngine->load(script_path);
...
app.exec();
While app is running, you can grab window contents like this:
QQuickWindow *win = qobject_cast<QQuickWindow *>(appEngine->rootObjects().first());
QImage grabbed = win->grabWindow();
It has several drawbacks (i.e. cursor disappears when the input focus is lost, grabWindow() is very slow etc.).
Furthermore, it's also possible to redirect QML page rendering to a custom FBO, this provides a much faster solution but also suffers from some issues.

Related

Why GTK stops running of the rest of program?

I have a C++ program that I used this simple gtk code at first of my main() function, and my goal is my app shows an image when it started and keeping showing the image and keep the rest of program.
GtkWidget* window;
GtkWidget* image1;
GtkWidget* image2;
gtk_init (NULL,NULL);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
image1 = gtk_image_new_from_file("1.jpg");
image2 = gtk_image_new_from_file("2.jpg");
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT (window), "destroy",
G_CALLBACK (destroy), NULL);
gtk_container_add(GTK_CONTAINER (window), image1);
gtk_widget_show_all(window);
gtk_main();
THE REST OF PROGRAM THAT WONT EXECUTE!
But when it opens a window and shows the image, it stuck there and doesn't execute the rest of code! Why this happens?
After you execute gtk_main, your code will "block", if i can say that, in that line until gtk_main_quit it's called/executed.
This is the nature of graphical user interfaces. Typically, you setup everything, call the main loop and wait for user interaction.
I would suggest that you read GNOME's Getting Started with GTK+.
As an example, if you do any printf below gtk_main, it will get printed after you close the GtkWindow.
Your application logic must be defined previously, then by means of user interaction, pressing buttons and other widgets, the application will do "things".
When you call gtk_main, the main loop starts.
The main event loop manages all the available sources of events for
GLib and GTK+ applications. These events can come from any number of
different types of sources such as file descriptors (plain files,
pipes or sockets) and timeouts.
You can read more about it here.

Qt5. Embed QWidget object in QML

I am using Qt5 beta and trying to embed a QWidget-based object into QML. The goal is to use QML as much as possible, and only use QWidget objects where QML does not do what I need. I found a link explaining how to do this for Qt4.7, but I have not found any information explaining how to do this in Qt5.
http://doc.qt.digia.com/4.7/declarative-cppextensions-qwidgets.html
The same example is also available in the Qt5 examples folder under:
examples\qtquick1\declarative\cppextensions\qwidgets
Unfortunately, this example uses QtQuick 1, rather than QtQuick 2, and I would like to use the new features of Qt5. I actually want to embed a qwt widget, but as a first step I would be happy to embed any simple QWidget-based object.
Can anybody help me get the example working under Qt5 / QtQuick 2 ?
Qt Quick 2 uses a scene graph for efficient rendering on the GPU. Unfortunately this makes it impossible to embed classic widgets into the scene. The old approach to embed such widgets with the help of QGraphicsProxyWidget works only with Qt Quick 1, because internally it uses a QGraphicsView for all the heavy lifting and QGraphicsProxyWidget is meant to be used with it.
As of now there are no plans to enable embedding classic QWidgets into the scene graph I know of. I think this is rather unlikely to change, because the concepts of QPainter, the painting framework used for the classic widgets, and the new scene graph doesn't play well with each other.
There some efforts to develop new widgets sets specifically tailored for the needs of QML, but none of them are as powerful and mature as the classic widgets. The most prominent ones are the QML Quick Controls, bundled with Qt since version 5.1.
If you really depend on QWT my advice would be to stick with Qt Quick 1.1 for now. It's still bundled with Qt 5, probably for cases like yours. That way you won't take advantage of the new scene graph, though.
You can embed QWidget to QML by using QQuickPaintedItem class:
http://doc.qt.io/qt-5/qquickpainteditem.html
Qt5 has an example:
http://doc.qt.io/qt-5/qtquick-customitems-painteditem-example.html
You should implement an inherent of QQuickPaintedItem with private widget attribute, that you want to embed. Provide paint method, that just render the QtWidget and provide mouse and other event transmitting from inherit of QQuickPaintedItem to embed QtWidget.
There's also QSG (Qt scene graph API), but my experience with that thing wasn't smooth. I believe the clue in multithreading (performing rendering in the different thread (not the Qt GUI thread one, however on Windows that's not true and all is done in main GUI thread).
I've implemented embedding of QCustomPlot, here's link: github.com/mosolovsa/qmlplot
What could be done is to render the widget to an image and upload as texture.For interaction someone needs to forward events like mouseClick or keyPressed from the sceneGraph, translate to widget coordinates, pass on, render and upload texture again. Just an idea :)
The recommended approach is to stay with a QWidget based application and embed the QML parts using QWidget::createWindowContainer.
Further to Julien's answer - a simple way to achieve this is to use QQuickWidget to display the QML scene, and then add a regular QWidget as a child of the QQuickWidget. You can also add a simple intermediate QObject to anchor the QWidget to an item in the scene.
E.g.:
In main.qml:
Item {
... // layouts, extra items, what have you
Item
{
objectName: "layoutItem"
anchors.fill: parent
}
... // more layouts, extra items, etc.
}
widgetanchor.h:
class WidgetAnchor: public QObject
{
ptr<QWidget> _pWidget;
QPointer<QQuickItem> _pQuickItem;
public:
WidgetAnchor(QWidget* pWidget, QQuickItem* pItem)
: QObject(pWidget), _pWidget(pWidget), _pQuickItem(pItem)
{
connect(_pQuickItem, &QQuickItem::xChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::yChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::widthChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::heightChanged, this, &WidgetAnchor::updateGeometry);
updateGeometry();
}
private:
void updateGeometry()
{
if (_pQuickItem)
{
QRectF r = _pQuickItem->mapRectToItem(0, QRectF(_pQuickItem->x(), _pQuickItem->y(), _pQuickItem->width(), _pQuickItem->height()));
_pWidget->setGeometry(r.toRect());
}
}
};
In main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
auto pqw = new QQuickWidget;
pqw->setSource(QUrl::fromLocalFile("main.qml"));
pqw->setResizeMode(QQuickWidget::SizeRootObjectToView);
pqw->setAttribute(Qt::WA_DeleteOnClose);
auto pOwt = new MyWidget(pqw);
if (auto pOverlayItem = pqw->rootObject()->findChild<QQuickItem*>("overlayItem"))
new WidgetAnchor(pOwt, pOverlayItem);
pqw->show();
return app.exec();
}
The documentation states that using QQuickWidget has advantages over QQuickView and QWidget::createWindowContainer, such as no restrictions on stacking order, but has a 'minor performance hit'.
Hope that helps.

Simple utility to unhide mouse cursor in objective c console app

I'm trying to create a simple console app to unhide the cursor because a program I use has a bug and hides the cursor intermittently (Would rather write a quick util than wait for the bug to be fixed). I have added the AppKit framework and written the following simple console app (main.m):
#import <Foundation/Foundation.h>
#import <AppKit/NSCursor.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
[NSCursor unhide];
}
return 0;
}
but I get an EXC_BAD_ACCESS at the [NSCursor unhide] line. Any idea what I'm doing wrong?
I'm running this on Lion with XCode 4.3.2
I am also open to doing this with AppleScript, but I haven't been able to accomplish it there either.
Thanks!
What you're trying to do isn't going to work. Cursor objects are managed and owned by each application. You can't affect another application's cursor (without code injection).
You get an EXC_BAD_ACCESS because your program here doesn't actually have a cursor. One would be created during the usual GUI app start-up process, i.e., in NSApplicationMain(), if you had created a "Cocoa Application", but you would still only be able to affect your own application's cursor.
AppleScript seems like it would have a better chance of success, since it lets you execute some code that directly affects other apps, but I'm not at all sure that it has functionality to manage the cursor like this -- it seems a little too low-level for AS.

Splash-screen doesn't show it contents

I'm having some trouble showing a splashscreen during some heavy work in my application in Python-gtk. I've been searching on Google and found this link: http://code.activestate.com/recipes/577919-splash-screen-gtk/. Since the example is quite clear, I downloaded the program and ran it on my computer. However, the splashscreen didn't show its contents.
Could it be that some configuration parameter of Gtk is set wrong? How come adding the code:
while gtk.events_pending():
gtk.main_iteration()
right after showing the splashscreen doesn't work in my case?
Thanks for your help
I tried adding:
while gtk.events_pending():
gtk.main_iteration()
right after the self.window.show_all() in splashScreen, and that worked. Before that, it didn't display the text, "This shouldn't take too long... :)".
This works because it ensures that the splash screen is rendered immediately, rather than leaving it to chance, which I guess must randomly work for some people (the guy who wrote this code and the other guy who replied here) and not for others (you and me).
I tested the code in the link you gave and it does display the splashscreen's components. But it may have appeared to not display them for you perhaps because the splashscreen window isn't given a size? I added:
self.window.set_size_request(300,300)
to the sample code and sure enough the label does appear.
I know this is a very old question, but I had the same issue with Gtk+3, and using Gtk.events_pending() had no effect, so I took a different route.
Basically, I just put a button to manually clear the splash screen, which I've seen plenty of commercial apps do. Then I call window.present() on the splash screen after creating the main window to keep the splash screen in front. This seems to be just the pause Gtk+3 needs to actually show the splash screen content before showing the main window opening behind it.
class splashScreen():
def __init__(self):
#I happen to be using Glade
self.gladefile = "VideoDatabase.glade"
self.builder = Gtk.Builder()
self.builder.add_from_file(self.gladefile)
self.builder.connect_signals(self)
self.window = self.builder.get_object("splashscreen")
#I give my splashscreen an "OK" button
#This is not an uncommon UI approach
self.button = self.builder.get_object("button")
#Button cannot be clicked at first
self.button.set_sensitive(False)
#Clicking the button will destroy the window
self.button.connect("clicked", self.clear)
self.window.show_all()
#Also tried putting this here to no avail
while Gtk.events_pending():
Gtk.main_iteration()
def clear(self, widget):
self.window.destroy()
class mainWindow():
def __init__(self, splash):
self.splash = splash
#Tons of slow initialization steps
#That take several seconds
#Finally, make the splashscreen OK button clickable
#You could probably call splash.window.destroy() here too
self.splash.button.set_sensitive(True)
if __name__ == "__main__":
splScr = splashScreen()
#Leaving this here, has no noticeable effect
while Gtk.events_pending():
Gtk.main_iteration()
#I pass the splashscreen as an argument to my main window
main = mainWindow(splScr)
#And make the splashscreen present to make sure it is in front
#otherwise it gets hidden
splScr.window.present()
Gtk.main()

Is it possible to render web content over a clear background using WebKit?

I'm trying to gauge the possibility of a patch to WebKit which would allow all rendered graphics to be rendered onto a fully transparent background.
The desired effect is to render web content without any background at all, it should appear to float over the desktop (or whatever is displayed behind the browser window).
Has anyone seen an app do this? (I can think of some terminal emulators that can.) If anyone has worked inside of WebKit (or possibly Gecko?) do you think it would be possible to do this?
Update: I've come to realize that Mac OSX dashboard widgets use this exact technique. So, this must be possible.
Update 2: I've compiled WebKit on linux and noticed the configure options include:
--enable-dashboard-support
enable Dashboard support default=yes
I'm getting closer. Can anyone help?
Update 3: I continue to find references to this in posts on various related mailing lists.
https://lists.webkit.org/pipermail/webkit-dev/2008-September/005019.html
https://lists.webkit.org/pipermail/webkit-dev/2009-June/008182.html
Solved!
Through ongoing research, scouring forums and source code repositories, I peiced together the necessary steps to accomplish this using only libwebkit and a standard compiz desktop (any Xorg desktop with compositing should do).
For a current libwebkit (1.1.10-SVN), there is an Ubuntu PPA:
deb http://ppa.launchpad.net/webkit-team/ppa/ubuntu jaunty main
deb-src http://ppa.launchpad.net/webkit-team/ppa/ubuntu jaunty main
As far as the code goes, the key is calling webkit_web_view_set_transparent.
And of course the system you're running it on should have a capable graphics card (intel, radeon, or nvidia) and be running a compositing window manager (like Compiz).
And finally, to actually see transparency, the content you're viewing must set a transparent background using CSS3, otherwise it's still completely opaque.
It's as simple as:
BODY { background-color: rgba(0,0,0,0); }
Here' is the full sample for the simplest possible webkit browser app, with transparency support:
#include <gtk/gtk.h>
#include <webkit/webkit.h>
static void destroy_cb(GtkWidget* widget, gpointer data) {
gtk_main_quit();
}
int main(int argc, char* argv[]) {
gtk_init(&argc, &argv);
if(!g_thread_supported())
g_thread_init(NULL);
// Create a Window, set colormap to RGBA
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GdkScreen *screen = gtk_widget_get_screen(window);
GdkColormap *rgba = gdk_screen_get_rgba_colormap (screen);
if (rgba && gdk_screen_is_composited (screen)) {
gtk_widget_set_default_colormap(rgba);
gtk_widget_set_colormap(GTK_WIDGET(window), rgba);
}
gtk_window_set_default_size(GTK_WINDOW(window), 800, 800);
g_signal_connect(window, "destroy", G_CALLBACK(destroy_cb), NULL);
// Optional: for dashboard style borderless windows
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
// Create a WebView, set it transparent, add it to the window
WebKitWebView* web_view = web_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
webkit_web_view_set_transparent(web_view, TRUE);
gtk_container_add (GTK_CONTAINER(window), GTK_WIDGET(web_view));
// Load a default page
webkit_web_view_load_uri(web_view, "http://stackoverflow.com/");
// Show it and continue running until the window closes
gtk_widget_grab_focus(GTK_WIDGET(web_view));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Back in Safari 1.3 and 2, there was a hidden debug menu (invoked via the Terminal: defaults write com.apple.Safari IncludeDebugMenu 1) that included a “Use Transparent Window” option.
http://www.macosxhints.com/article.php?story=20050418015445258
http://www.wiredatom.com/blog/2005/10/20/safari-transparency/
Not sure if this was a WebKit thing or a Safari thing though.
(In Safari 3, the debug menu seems to have been replaced by the “Develop” menu (enable in Preferences > Advanced) which doesn’t have the transparent window option.)
Basically you want to be setting the ARGB colour space to be sending to the window manager. Obviously only window managers that support compositing will be able to take advantage of this.
You might want to talk to the screenlet and compiz developers they should be able to help out more.
This gist works for me, as of 2013, tested only with ubuntu:
https://gist.github.com/pouria-mellati/7771779
I have a new solution which is really easy to do, for a single screenshot. It's using node.js with phantom.js library.
install node.js
run 'npm install -g phantomjs' in console/terminal
save the following as script.js and run it from console 'phantomjs script.js'
var page = require('webpage').create();
page.viewportSize = { width: 1920, height: 1500 };
page.open("http://www.theWebYouWantToRender");
page.onLoadFinished = function(status) {
page.evaluate(function() {
document.body.style.background = 'transparent';
});
page.render('render.png');
phantom.exit();
};
profit? :) enjoy