I wish to get more information than just success = false in loadFinished (this is most often just a canceled load). From the documentation and other posts on this site, I gathered I should subclass QWebPage and override the extension() method to handle the ErrorPageExtension.
However, I'm not getting it to work, i.e., no matter what I try my extension method does not get called. I'm probably doing something really stupid but not seeing it. Basically my class looks like this:
class MyWebPage : public QWebPage
{
Q_OBJECT
public:
MyWebPage(QObject* parent = 0) : QWebPage(parent) {}
virtual bool extension(Extension extension,
const ExtensionOption* option = 0,
ExtensionReturn* output = 0)
{
// blah
}
virtual bool supportsExtension(Extension extension)
{
// blah
}
};
The implementation of the methods is not the problem, I have a breakpoint there and it never gets called. I create an instance like
MyWebPage* page = new MyWebPage(this);
mUi.WebView->setPage(page);
I'm a bit uncertain about the life time of a QWebPage object in QWebView, but from my tests it seems the QWebPage always remains the same instance and simply loads new content. So I assumed I should simply give my page to the QWebView, I didn't see another way to make it use my derived class. But when loading bogus URLs, non-existing local files, or unsupported content, either via the WebView or directly via the mainframe of the page, I never get the call with ErrorPageExtension information.
Any help is appreciated. This is using Qt 4.8.2.
There is a bit mistake:
...
virtual bool supportsExtension(Extension extension) const // const!!!
{
return QWebPage::ErrorPageExtension === extension;
}
...
You forgot to copy the const modifier.
Related
I want to change my dns for every one of my request for some reasons! I work with Qt 5.3 and QtWebkit! I do search but i can not find anything that can help me!
Actually QDnsLookup can't force QtWebkit to set its load() function to use specific look up and it use the system Dns setting at the end!
Any idea?!
Create you own QNAM subclass and QWebPage subclass. Implement you DNS resolver there. Then set it for each QWebPage you create. This way you will get full network control of what happens in WebKit.
To be sure all WebPages will get your QNAM, subclass QWebView too and set your QWebPage subclass as page in constructor. Also overload createWindow function so all new QWebView pages(like popups) will be created as your QWebView subclass.
YourWebView::YourWebView(QWidget *parent):QWebView(parent)
{
this->setPage(new YourWebPageSubclass());
...
QWebView * YourWebView::createWindow(QWebPage::WebWindowType type)
{
YourWebView * view = Q_NULLPTR;
switch(type)
{
case QWebPage::WebBrowserWindow:
view = new YourWebView(0);
break;
case QWebPage::WebModalDialog:
view = new YourWebView(0);
view->setWindowModality(Qt::ApplicationModal);
break;
}
return view;
}
YourWebPageSubclass::YourWebPageSubclass(QObject *parent):QWebPage(parent)
{
this->setNetworkAccessManager(new YourQNAM(this));
...
I'm using the QT 5.9 WebEngine framework to display web pages. I'm injecting javascript into a page when it loads, and want to allow the javascript to be able to access a QT object.
I get the QWebchannel callback in JS to be invoked but the object method and properties are undefined.
NOTE: I NEED to inject all JS code and cannot change the html code of the loaded page. Meaning i cannot include any js script nor write any js code directly in the html pages.
Here is my code:
I create the QWebEnginePage object with a profile and inject the JS files as below:
// file: webview.cpp, webview extends **QWebEngineView**
QWebEngineScript script;
script.setSourceCode(qwebchannelsource); // source is qwebchannel.js
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
profile->scripts()->insert(script);
QWebEnginePage page* = new QWebEnginePage(profile, this);
QWebChannel *channel = new QWebChannel(page);
page->setWebChannel(channel);
channel->registerObject(QStringLiteral("jshelper"), &JSHelper::instance());
JSHelper
class JSHelper : public QObject
{
public:
static JSHelper &instance();
QString someString;
int someInt;
Q_INVOKABLE int getInt();
Q_PROPERTY(int myIntInCppSide READ getInt)
private:
JSHelper();
};
JS code
// also injected into the page (like qwebchannel.js) at QWebEngineScript::DocumentReady
new QWebChannel(qt.webChannelTransport, function (channel) {
var jshelper = channel.objects.jshelper;
console.warn('qwebchannel triggered');
// do what you gotta do
if (jshelper === undefined) {
console.warn("jshelper is undefined");
}
else {
console.warn("jshelper is awesome");
}
console.warn("jshelper value of string " + jshelper.somestring);
console.warn("jshelper value of int " + jshelper.myIntInCppSide);
jshelper.myIntInCppSide(function (n) {
console.warn("jshelper value of int " + n);
});
});
my output:
qwebchannel triggered"
jshelper is awesome"
jshelper value of string undefined"
jshelper value of int undefined"
Uncaught TypeError: jshelper.myIntInCppSide is not a function"
As you can see I have tried various suggestions from several answers but nothing seems to fix my issue that the function does not exist.
Even when using the remote debugging I can see the jshelper is of type QObject but it does not have the properties nor methods of my class.
QT QWebEnginePage::setWebChannel() transport object
Undefined properties and return types when using QWebChannel
JSHelper class misses Q_OBJECT macro. As the official documentation says,
Notice that the Q_OBJECT macro is mandatory for any object that
implements signals, slots or properties. You also need to run the Meta
Object Compiler on the source file. We strongly recommend the use of
this macro in all subclasses of QObject regardless of whether or not
they actually use signals, slots and properties, since failure to do
so may lead certain functions to exhibit strange behavior.
Also the constructor of this class needs to be made public for Qt's introspection facilities to be able to work with the class.
We are in the process of converting C# code to C++, but we need to do so in phases. I am at a point now where I need to instantiate several native objects from within managed code. These native objects I cannot change, and their declaration looks like this:
public class NativeA();
public class NativeB(std::shared_ptr<NativeA> obj);
Both NativeA and NativeB need to be instantiated from managed code as:
void main() {
ManagedA ObjectA = gcnew ManagedA();
ManagedB ObjectB = gcnew ManagedB(ObjectA);
}
The problem comes in with getting the shared_ptr of NativeA in the constructor of NativeB. Niether NativeA nor NativeB will be manipulated in managed code, they just need to be instantiated. Ideally, something like this:
public ref class ManagedA {
public:
ManagedA() { _object = new NativeA(); }
~ManagedA() { delete _object; }
NativeA * Get() { return _object; }
private:
NativeA *_object;
};
public ref class ManagedB {
public:
ManagedB(ManagedA^ objectA ) {
_object = new NativeB(std::make_shared<NativeA>(*objectA->Get());
}
~ManagedB() { delete _object; }
private:
NativeB *_object;
};
But, this is not allowed in c++/cli because native types are declared as private. Defining #pragma make_public(NativeA) does not solve this either.
My intent is not to work with the native objects in managed code, they just need to be instantiated, so I really don't care about trying to marshal the native pointers and deal with .NET GC if I don't have to, and I don't want to perform a copy. I just want to wrap the classes in order to pass them around.
Is there a clean and simple way to do this?
It appears that the answer was not due to a syntax or usage problem. The two managed objects were in different DLLs and could not be passed across them via .NET. Once the code was compiled in the same project, the issue was resolved.
Although the error message indicated the problem was an accessibility issue in VS 2015, and because it reported it during the link phase, I suspect the cause was because the linker would not have known about the implementation of the NativeA in NativeB without declaring an extern. Being wrapped in CLR, it surfaced as a different issue.
I'm wondering what might be the best approach to the following situation:
I have a QML file that is load from a HTTP server to a Qt/QML android app to display the UI.
The user can tap on thumbnails of catalogs and make the app download another QML file for each catalog. The catalog QML is downloaded and stored on the device for offline use.
This means I have a number of directories that have a unique ID to store the catalog QML and assets. Something like this:
/my/app_data_path/catalogs/CATALOG_001
/my/app_data_path/catalogs/CATALOG_007
/my/app_data_path/catalogs/CATALOG_010
In the UI I'd like to show an indicator that tells if a catalog has been downloaded already to the device. What would be the best approach within QML to show/hide an indicator depending on that?
Rectangle {
id: indicator
visible: MyApp.catalogIsLoaded('some ID here')
}
This is something that came to my mind, but I don't think it's the best way to do this since I'd need a method to pass the catalog ID in order to check if the data directory exists. Also I'd have to figure out a way to re-evaluate the visible state/call the catalogIsLoaded method from time to time - especially after downloading or deleting catalogs.
Is there a better/cleaner approach to this?
A better way would be to associate your indicator visibility with a property binding.
As you wish to find a catalog by its id, provide an invokable method on your 'MyApp' class to return a catalog reference.
Q_INVOKABLE Catalog* findCatalogById(const QString& id);
Catalog will be a QObject, expose a isLoaded property to QML.
class Catalog : public QObject {
Q_OBJECT
Q_PROPERTY(bool isLoaded READ isLoaded NOTIFY isLoadedChanged)
public:
Catalog(QObject* parent = 0) : QObject(parent) {
}
bool isLoaded() const { return m_isLoaded; }
void setIsLoaded(bool loaded) {
if (m_isLoaded != loaded) {
m_isLoaded = loaded;
emit isLoadedChanged();
}
}
signals:
void isLoadedChanged();
private:
bool m_isLoaded; //should probably be initialized to false
};
Of course, the catalog reference provided by your context should be memorize somewhere (like in a QHash<QString, Catalog*>), and be updated accordingly when its status changed.
Is there an easy way to setup the User-Agent the QWebView class is using?
The only relevant link I found searching was this
http://www.qtforum.org/article/27073/how-to-set-user-agent-in-qwebview.html
I'm learning C++/Qt right now and I don't really understant what's explained on that website. Maybe someone knows an easy way to do it? Or can help me understand that code?
Qt allows you to provide a user agent based on the URL rather than a single user agent no matter what the URL is. The idea then is to return the user agent any time a new webpage is created:
class UserAgentWebPage : public QWebPage {
QString userAgentForUrl(const QUrl &url ) const {
return QString("My User Agent");
}
};
In order to use that page instead of the standard page that is created, you can set that page on the browser control object before making the request:
yourWebView->setPage(new UserAgentWebPage(parent));
I would actually expect a factory to be present somewhere that will guarantee that the webpage created is always of a certain type, but I didn't see one.
Yet another option should be to set the user agent header within the QNetworkRequest:
QNetworkRequest request = new QNetworkRequest();
request->setRawHeader(
QString("User-Agent").toAscii(),
QString("Your User Agent").toAscii()
);
// ... set the URL, etc.
yourWebView->load(request);
You would actually want to check whether it's toAscii() or toUtf8() or one of the other variants as I'm not sure exactly what the HTTP standard allows.
simply,
class myWebPage : public QWebPage
{
virtual QString userAgentForUrl(const QUrl& url) const {
return "your user agent";
}
};
//Attention here is new myWebPage() not new myWebPage(parent)
UI->webView->setPage(new myWebPage());