How to modify the -webkit-scrollbar styles in a QWebView - c++

Webkit provides special css properties for styling scrollbars, for example:
::-webkit-scrollbar-track {
background-color:white;
}
Normally I'd put these inside a <style> tag inside <head>, but unfortunately QWebElement doesn't seem to be able to modify anything inside <head>. I can use the setHtml() function to specify initial styling, but not modify it later. Is there an alternative way to apply CSS styles to the scrollbars in a QWebFrame?

Is possible using QWebSettings::setUserStyleSheetUrl, see example:
const QString path = PATH_OF_CSS_FILE;
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(QUrl(path));
Example
If dynamic CSS is a string, you can create a method and use QTemporaryFile, like this:
void MainWindow::setStyle(const QString data)
{
QTemporaryFile file;
if (file.open()) {
const QString path = file.fileName();
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(QUrl(path));
}
}
Usage:
setStyle("::-webkit-scrollbar-track { background-color:white;}")
If needs load dynamic file, you can create an alternative method like this:
void MainWindow::setStyle(const QUrl url)
{
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(url);
}
Using QRC
Is part of the answer is just a tip for other implementations;
You can use resources (QRC) in your project for put default stylesheet for all QWebViews, see example:
Click right button in your project > Add New ... > Qt > Qt Resource File > Put name "resources.qrc"
Click right button in "resources.qrc" > Open in Editor
Put a CSS file with name scrollbar.css (css file must be in the same folder as your project).
Put this in your "main.cpp":
#include <QWebSettings>
#include <QUrl>
...
const QString path = "qrc:/scrollbar.css";
QWebSettings *settings = QWebSettings::globalSettings();
settings->setUserStyleSheetUrl(QUrl(path));

Related

How can I interact with html elements use QT

For example, I have a simple HTML page with button and label (or something else). How can I change the text in label (or something else) and catch the button click use QT.
I try to use QWebEngineView to show html, but I don`t know how to interact with elements from QT modul, just change the url, but I dont think its a better way
To be able to interact with HTML rendered with QWebEngine you need to use QWebChannel. You can find the basic guidelines at Qt WebChannel JavaScript API page.
To implement intercommunication with JavaScript in your HTML page you need:
Add Qt += webchannel in your project file
Implement a QObject derived class that should be a proxy between C++ and JavaScript. The simpliest way to make it usable in JavaScript is to create getters, setters and signals for the values you intend to use in JavaScript, and expose them as Q_PROPERTY.
Example:
class ProxyClass: public QObject
{
Q_OBJECT
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit ProxyClass(QObject *parent = nullptr);
QString value() const;
void setValue(const QString &aValue);
signals:
void valueChanged(const QString &aValue);
};
Set HTML to QWebEngineView with QWebEngineView::setHtml, instantiate your "proxy" class and create QWebChannel for the page (note that you can register multiple objects in QWebChannel). Example:
//create QWebEngineView and set HTML from resources
auto webView = new QWebEngineView;
QFile htmlFile(":/page.html");
htmlFile.open(QIODevice::ReadOnly);
QString html = htmlFile.readAll();
webView->setHtml(html);
//create and set up the instance of your "proxy" class
auto proxy = new ProxyClass;
//create QWebChannel and set it for the page
QWebChannel *webChannel = new QWebChannel(webView->page());
webChannel->registerObject(QStringLiteral("proxy"), proxy);
webView->page()->setWebChannel(webChannel);
Embed qwebchannel.js file in HTML. File is located at <Qt installation directory>/Examples/Qt-<version>/webchannel/shared directory. You can include it in application resources and embed in HTML with <script type="text/javascript" src="qrc:/qwebchannel.js"></script>
Create onload event handler in HTML and initialize QWebChannel in it. Example:
function loaded() {
new QWebChannel(qt.webChannelTransport, function (channel) {
<!--get and assign "proxy"-->
window.proxy = channel.objects.proxy;
<!--now you can-->
<!--get values-->
let proxyValue = proxy.value;
<!--connect signals-->
proxy.valueChanged.connect(() => {
...
});
});
}
function someFunction(newValue) {
<!--set values-->
proxy.value = newValue;
}
...
<body onload="loaded()">...</body>
Once you initialized a web channel and assigned "proxy" object to the window object, you are free to use it everywhere in JavaScript.

QFileDialog combine MIME type filters to "All formats"

I am using Qt 5.9 to open a file dialog asking the user to select an image file:
QStringList mimeTypeFilters;
const QByteArrayList supportedMimeTypes = QImageReader::supportedMimeTypes();
foreach(const QByteArray& mimeTypeName, supportedMimeTypes) {
mimeTypeFilters.append(mimeTypeName);
}
mimeTypeFilters.sort();
QFileDialog* fileDialog = new QFileDialog(this, "Select image");
fileDialog->setMimeTypeFilters(mimeTypeFilters);
fileDialog->setFileMode(QFileDialog::ExistingFile);
fileDialog->exec();
All supported image formats are added as MIME type filters to the dialog, which is working quite well. However, I want to add an additional filter (such as "All formats" or "All supported") that allows the user to select an image of ANY of the supported formats, as selecting the correct format prior to selecting the image is quite tedious. What is the most elegant solution to achieve this, without subclassing any of the involved Qt classes?
Thanks to SteakOverflow's comment, here is the solution I came up with:
// get supported image file types
QStringList mimeTypeFilters;
const QByteArrayList supportedMimeTypes = QImageReader::supportedMimeTypes();
foreach(const QByteArray& mimeTypeName, supportedMimeTypes) {
mimeTypeFilters.append(mimeTypeName);
}
mimeTypeFilters.sort(Qt::CaseInsensitive);
// compose filter for all supported types
QMimeDatabase mimeDB;
QStringList allSupportedFormats;
for(const QString& mimeTypeFilter: mimeTypeFilters) {
QMimeType mimeType = mimeDB.mimeTypeForName(mimeTypeFilter);
if(mimeType.isValid()) {
allSupportedFormats.append(mimeType.globPatterns());
}
}
QString allSupportedFormatsFilter = QString("All supported formats (%1)").arg(allSupportedFormats.join(' '));
QFileDialog* fileDialog = new QFileDialog(this, "Select image");
fileDialog->setFileMode(QFileDialog::ExistingFile);
fileDialog->setMimeTypeFilters(mimeTypeFilters);
QStringList nameFilters = fileDialog->nameFilters();
nameFilters.append(allSupportedFormatsFilter);
fileDialog->setNameFilters(nameFilters);
fileDialog->selectNameFilter(allSupportedFormatsFilter);
It is basically the same implementation QFileDialog uses internally to convert mime type filters to name filters. The new name filter for all supported formats will be added at the bottom of the filter list and pre-selected. The filter string is quite long and not fully visible in the dialog at once, but will become fully visible once the user opens the drop-down menu.

qt/c++ context menu - disable an item

I'm currently developing an app such as the browser using Qt and c++.
I have create a contextual menu to allow on right click action such as delete, rename and add folder.
void MyTreeWidget::createContextMenu() {
contextMenu = new QMenu();
setContextMenuPolicy(Qt::ActionsContextMenu);
addFolderAction = new QAction("Add Folder", contextMenu);
addAction(addFolderAction);
connect(addFolderAction, SIGNAL(triggered()),this,SLOT(onAddFolderActionTree()));
deleteAction = new QAction("Delete", contextMenu);
addAction(deleteAction);
connect(deleteAction, SIGNAL(triggered()),this,SLOT(onDeleteAction()));
RenameAction = new QAction("Rename", contextMenu);
addAction(RenameAction);
connect(RenameAction, SIGNAL(triggered()),this,SLOT(onRenameAction()));
}
This is working fine. This contextual menu is used when you select a file or folder in my treewidget and make a right click. My issue is that I propose the "add folder" option even if I select a file. You can't create a folder inside a file.
What I want is to disable the option when a file is selected and enable it when it's a folder.
I can know if it's file or folder by getting the TreeWidgetItem class I have overloaded:
Thanks
You can disable QAction. In this case "Add Folder" menu item will be disabled:
addFolderAction->setEnabled(false);
Use the QAction::setEnabled(bool) method on your 'addFolderAction'.
One way to use it is like this:
void
MyTreeWidget::updateMenuActions()
{
if(!contextMenu)
return;
bool addFolderEnabled = <check TreeWidgetItem here to enable / disable>;
addFolderAction->setEnabled(bEnabled);
}
Call the updateMenuActions() method just before you display the context menu.
I actually prefer the code below in case you have situations where you can have NULL pointers to actions (for cases where you don't even add them):
void
MyTreeWidget::updateMenuActions()
{
if(!contextMenu)
return;
bool addFolderEnabled = <check TreeWidgetItem here to enable / disable>;
updateAction(addFolderAction, bEditEnabled);
}
void
MyTreeWidget::updateAction(QAction* pAction, const bool& bEnabled)
{
if(pAction)
pAction->setEnabled(bEnabled);
}
Enjoy.

Automatically saving a file with QFileDialog

I have to automate a test using QTest, Qt, C++:
I write text in a tab (part of tabwidget) and then try to close it, afterwards a QFileDialog appears ( because I made changes to the plaintext in the tab), I try to "catch" the QFileDialog like this:
QWidgetList topWidgets = QApplication::topLevelWidgets();
foreach (QWidget *w, topWidgets) {
if (QFileDialog *fd = qobject_cast<QFileDialog *>(w)) {
fd->setFileMode(QFileDialog::ExistingFiles);
fd->selectFile("/tmp/test.txt");
}
}
After getting the QFileDialog object I want my changes from the tab to be saved in the file "test.txt" which I created before in the tmp directory. When I execute this nothing happens, the QFileDialog pops up, but test.txt is not selected and not saved, how can I achieve this?
The selectFile method does not work if the filedialog is visible and if the focus is set to the line edit widget. From the qfiledialog.cpp (QT 5.2):
if (!isVisible() || !d->lineEdit()->hasFocus())
d->lineEdit()->setText(file);
For our automated tests, we just hide the filedialog for a moment, call selectFile() and show it again
Try this:
QWidgetList topWidgets = QApplication::topLevelWidgets();
foreach (QWidget *w, topWidgets) {
if (QFileDialog *fd = qobject_cast<QFileDialog *>(w)) {
fd->hide();
fd->selectFile("/tmp/test.txt");
fd->show();
fd->exec();
}
}

How to create a dialog window to choose a file path

I'm new to qt, and I have a button, if I click on it I want to get a dialog box to choose a path where I want to save a file. My question is, how could I create this kind of dialog box, which returns with a string of the path? I use linux, if it matters with qt:)
ps.: I use only gedit, so I'd like to solve it that way. :)
Use QFileDialog which has several useful static member functions including
QString myDir = QFileDialog::getExistingDirectory();
which returns a directory that you select. I think that is what you want, see the docmentation here
http://qt-project.org/doc/qt-5.0/qtwidgets/qfiledialog.html
In addition to the answer by #Muckle_ewe, there is a the static function QFileDialog::getSaveFileName, which will present the standard open / save file dialog and allow the user to both select the path and input a name for the file.
It's definition is this: -
QString QFileDialog::getSaveFileName(QWidget * parent = 0, const QString & caption = QString(), const QString & dir = QString(), const QString & filter = QString(), QString * selectedFilter = 0, Options options = 0)
An example of its usage is: -
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"/home/untitled.png",
tr("Images (*.png *.xpm *.jpg)"));
As the docs state,
This is a convenience static function that will return a file name
selected by the user. The file does not have to exist.