How to render emoji's in with QGraphicsTextItem? - qt

Here is the thing. I've created a little Qt chat application for my work office. It is only for lan connected clients. I implemented this by using QGraphicsTextItem for the messages inside bubbles which are a rounded rectancles.
To really complete it I need to add emojis to it. All chat-like applications have them. The thing is that I don't know how to implement them. I've read that they are part of the unicode standad. (the unicode codes for this link proves it http://unicode.org/emoji/charts/full-emoji-list.html), but I have no idea how to render it in a QGraphicsTextItem. Is it even possible?
I could do a very very manual labor of separating the user string and actually use multiple text items to render the parts fo the strings separated by emojis y and then the emoji themselves as a separate graphical entity, but I rather not do that if I can avoid it.
Any ideas?

There's nothing to do: the emojis are simply characters, you can copy-paste them from the "Brow" column in the page you cited, just like you would any other character e.g. from this answer (including the emoji!). A sane compiler assumes that your source is UTF-8 encoded, as do most editors, so it will be transparent. If the relevant fonts are installed, it will just work.
// https://github.com/KubaO/stackoverflown/tree/master/questions/emoji-text-item-38000855
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QGraphicsScene scene;
QGraphicsView view{&scene};
QGraphicsTextItem item{QStringLiteral("I'm a happy 👧. 😀")};
scene.addItem(&item);
view.ensureVisible(scene.sceneRect());
view.show();
return app.exec();
}

Related

QComboBox - narrow selection when typing more than one letter

I have a combobox
c = QCombobox(self)
c.addItem("001")
c.addItem("002")
c.addItem("003")
c.addItem("004")
c.addItem("010")
c.addItem("011")
c.addItem("012")
Is it possible, when user clicks the combobox c and the list is expanded and when he starts typing e.g. the sequence 011 to automatically select the item "011"?
In a default state only the first number (letter) works to help to narrow the selection.
This code (suggested by V.K. author of HiFile) does not work.
c = qtw.QComboBox(self)
for key, value in self.c_dict.items():
c.addItem(key)
c.setEditable(True)
completer = c.completer()
completer.setFilterMode(QtCore.Qt.MatchStartsWith)
completer.setCompletionMode(qtw.QCompleter.PopupCompletion)
Get a QCompleter object from your combo box using QComboBox::completer() and then set filter mode using QCompleter::setFilterMode(filterMode) where filterMode should be either Qt::MatchContains or Qt::MatchStartsWith, depending or your needs. You may also need to set QCompleter::setCompletionMode(QCompleter::PopupCompletion)... In simple cases everything should work out-of-the box after setting these. In more complex cases, there are lots of completer customizations possible, but be prepared that it is not easy and it does not have intuitive API. Combo boxes and completers are among the beginner-not-so-friendly parts of Qt.
Here is the minimalistic example which works for me. It is in C++ but you can certainly translate it to Python.
#include <QApplication>
#include <QComboBox>
#include <QCompleter>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QComboBox w;
w.setEditable(true);
w.addItems({"10", "01", "11"});
w.setCurrentText(QString());
w.setInsertPolicy(QComboBox::NoInsert);
w.completer()->setCompletionMode(QCompleter::PopupCompletion);
w.show();
return a.exec();
}
Actually I found that I do not need to set filter mode, because filter mode "StartsWith" is the default. But I needed to set the completion mode to popup. Also notice that I am not creating a competer, I am using the one which was created by Qt automatically in the combo box.
And it is also a good thing to change the insert policy, because the default value "insert at bottom" is just plain nonsense, which was left in Qt probably only for backward compatibility... (as I said above, combo box and completer API is a mess).

Svg to pdf converter for Qt project

I have multiple svg files and I wish to convert them in the PDF format. The problem is that I need to work with multiple text fonts and a simple QSvgRenderer is not enough.
For example:
My problem is that I need to integrate a library in my project that does not use external dependencies (just a library that can be used as a standalone entity).
The only library that can partially suit my needs is librsvg but I have no experience and I can not find a method to integrate it in a Qt project.
Can someone give me a few tips or can point me in the right direction?
EDIT
With something basic there is not a single font that made it to the right using QSvgRenderer:
QSvgRenderer renderer;
renderer.load((QString)"E:/TEXT4.svg");
QPrinter printer;
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName("E:/TEXT4.pdf");
QPainter painter(&printer);
renderer.render(&painter);
painter.end();
Maybe a bit late, but had the same problem and found a solution using Qt only.
The problem in your case is you need to install the fonts manually and need a QApplication.
E.g.:
int main(int argc, char** argv)
{
// A QApplication instance is necessary if fonts are used in the SVG
QApplication app(argc, argv);
int id = QFontDatabase::addApplicationFont(":/DejaVuSans.ttf");
if (id != -1)
{
QStringList font_families = QFontDatabase::applicationFontFamilies(id);
// This prints: DejaVu Sans
for (const auto& item : font_families)
{
std::cout << item.toStdString() << std::endl;
}
}
// Load your SVG
QSvgRenderer renderer(QString("myimage.svg"));
QPdfWriter pdfWriter(QString("mydoc.pdf"));
pdfWriter.setPageSize(QPageSize(QPageSize::A4));
QPainter painter(&pdfWriter);
renderer.render(&painter);
}
Also tested with multiple different font families and it worked.
Note: As far as I know, you cannot change the font name in Qt, so in the SVG font-family must match exactly the added font name. In this example, font-family:DejaVu Sans.

Using QSettings in a global static class

My task is to create a QSettings wrapper class (wrapping is mostly needed by QML) which I can reach everywhere from the model, too.
The obvious choice is a static global instance of this class. However the problem is with this approach is that it's destroyed after main, after QApplication is destroyed, thus giving me the following warning:
QApplication::qAppName: Please instantiate the QApplication object first
Here is a simplified, sample code showing a really simple wrapper, and the destruction phases:
#include <QCoreApplication>
#include <QDebug>
#include <QGlobalStatic>
#include <QSettings>
#include <QTimer>
class Settings: public QObject
{
public:
~Settings() { qDebug() << "~Settings"; }
QSettings settings;
};
Q_GLOBAL_STATIC(Settings, globalSettings)
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){qDebug() << "~QCoreApplication aboutToQuit";});
QObject::connect(&app, &QCoreApplication::aboutToQuit, [](){globalSettings->settings.setValue("Hi", 2);});
QObject::connect(&app, &QCoreApplication::destroyed, [](){qDebug() << "~QCoreApplication destroyed";});
QTimer::singleShot(0, &app, SLOT(quit()));
return app.exec();
}
It prints out the following:
~QCoreApplication aboutToQuit
~QCoreApplication destroyed
~Settings
QApplication::qAppName: Please instantiate the QApplication object first
My question is: providing, in my program QSettings is used after QCoreApplication::aboutToQuit is emitted but before QCoreApplication::destroyed, how can I avoid this warning?
Using QSettings
I've used QSettings in pretty much every project I've ever made. Here is the pattern that I tend to use it:
in main.cpp
#include <QSettings>
//...
// Then in main(), after QApplication is instantiated, but before any settings are accessed
QSettings::setDefaultFormat(QSettings::IniFormat);
QApplication::setOrganizationName("MyOrg");
QApplication::setApplicationName("MyApp"):
Then anytime you are about to access QSettings, you just do this:
QSettings s;
s.value(// reading a value
s.setValue(// writing a value
Everything gets saved in the User Scope in an INI text file. It will be located in Windows under C:/Users/<username>/AppData/Roaming/MyOrg/MyApp.ini.
This usage of QSettings (IMHO) is very clean, doesn't require global variables or static references and is very fast and efficient. And it is very readable.
Now to be able to have things load settings at the right times, and not get the errors you mentioned in your question, see the initial example in the links below:
http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application
http://doc.qt.io/qt-5/qtwidgets-mainwindows-application-example.html
It works much better in the timeline of a Qt application and works great. I tend to make a readSettings and writeSettings for most of my GUI classes and for a number of my backend classes. readSettings happens when the widget has its showEvent or constructor happen and the writeSettings happens in the closeEvent. Also if I have a dedicated settings dialog, I emit a signal to have any affected classes to readSettings, right after the settings dialog writes those specific settings.
If you use the QML port of QSettings, it also uses the Organization name and Application name and the default format of QSettings to pick its filename and location.
http://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html
I believe the default functionality of that QML component is just to read the setting when the component is created, and to write the setting whenever QML changes it. So to change it from C++ and have it recognized by QML, you should probably use the standard QML/C++ methods out there such as:
http://doc.qt.io/qt-5/qtqml-cppintegration-topic.html
And lastly if I am planning on installing defaults for a program that are decided for a build and I don't want to hardcode them, I hand write an INI file and have the installer place it in the system scope location: C:/ProgramData/MyOrg/MyApp.ini
And in the case that the settings of your program are more complex than what you want to store in an INI file, I would look into using QJson, and the SaveGame example.
Hope that helps.

Embeding PySide/PyQt widget into Qt/C++ application

I have a C++/Qt application which should have its GUI extensible with modules. Extending should be simple and versatile. I am just checking a concept - is it possible to have this main C++/Qt application that would execute a Python/PySide/PyQt script which would create a QWidget (or derived class) instance and embed this widget into the main C++/Qt application?
Is there any working snippet to demonstrate the feasibility of this task? I.e. how to create and embed the widget? How to pass signals between the application and the widget?
This question is a bit old, but in case someone else is face the same issue, I will try to give a useful answer.
I think it's possible. In the following example, I create a QApplication and QMainWindow in c++, embed a python interpreter, and then, on the python side, I create a QPushButton which I add to the main window.
Try this out:
#include <QApplication>
#include <QMainWindow>
#include <iostream>
#include "Python.h"
class PythonQt
{
public:
PythonQt()
{
char name[] = "test";
Py_SetProgramName(name);
Py_Initialize();
std::string code =
"from PySide import QtGui\n"
""
"all_widgets = QtGui.QApplication.instance().allWidgets()\n"
"window = None\n"
"for widget in all_widgets:\n"
" if str(widget.objectName()) == \"main_window\":\n"
" window = widget\n"
" break\n"
""
"def message_box():\n"
" QtGui.QMessageBox.information(None, 'Test', 'Hello!', \
QtGui.QMessageBox.Ok)\n"
" QtGui.QApplication.instance().quit()\n"
""
"button = QtGui.QPushButton('Press Me')\n"
"button.clicked.connect(message_box)\n"
"window.setCentralWidget(button)\n"
"window.move(600, 300)\n"
"window.show()";
PyRun_SimpleString(code.c_str());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow win;
win.setObjectName("main_window");
PythonQt python_code;
a.exec();
}
The python script is written here in a string to have everything in a single file, but you could of course read it in from a .py file.
Object ownership could be an issue though, as shown in the link given by Trilarion.
I don't think this is possible. PySide/PyQt are wrappers around C++/Qt. That means you create C++ objects and Python wrapper objects and somehow the wrapper objects kind of refer to the C++ objects. This works one way as far as I know.
But you want the other way. Basically a wrapper around Python objects (which themselves are wrappers around C++ objects) to use in C++. I don't think PySide/PyQt are ready for thise. However it is possible to embed Python in other languages.
Also see How to shoot yourself in the foot. about the pitfalls of communication between C++/Qt and Python.

translation doesn't work when executing application

I am using Qt linguist to translate my application (ui file and the rest of the code).
Everything goes alright , the problem is all translations (in ui) work fine except an element added not from designer, here is my code to further explain :
tableWidget = new MyDropTableWidget(ui->verticalLayoutWidget_2);
if (tableWidget->columnCount() < 1)
tableWidget->setColumnCount(1);
tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("My non translated text"))); if (tableWidget->rowCount() < 21)
tableWidget->setRowCount(21);
...
ui->verticalLayout_2->addWidget(tableWidget);
...
all texts in ui are translated fine , only "My non translated text" was not.
*.ts file are ok ("My non translated text" was detected and checked) , .qm file are well placed, I rebuild re lupdate re lrelease my application but in vain: when executing my application, all texts are translated expect "My non translated text" still in the source language.
Any help will be appreciated.
Edit:
in main file:
QTranslator Translator ;
qDebug()<<"Translator->load( lang)"<< Translator.load(":/"+language);
app.installTranslator( &Translator );
in mainwindow constructor:
ui->retranslateUi(this);
A non-translated text in Qt GUI is often caused by a bad order in your code.
It is impossible to answer precisely without reading your whole code. I suggest you to read the content of Ui::retranslateUi() method. You will see many code like:
button->setText(QApplication::translate("MainWindow", "Import", 0));
The retranslateUi metod is generated for you when compiling a *.ui file, but it is only a list of calls to setText(), setToolTip() or another setXXX() methods on widgets you defined in the ui file. When a setText() method is called (for example), the parameter must be tr("my text"), this tr() is replaced by the corresponding translation at the moment you call it. So you should check if when you initialize your table widget item, your Translator has correctly been installed.
For example, if you create your item in the MainWindow constructor and if you have a main function like:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow w;
QTranslator Translator ;
qDebug()<<"Translator->load( lang)"<< Translator.load(":/"+language);
app.installTranslator( &Translator );
w.show();
return app.exec();
}
it will not work, because the translator is installed after MainWindow constructor call, so when the item is initialized, the translator is not set.
To find the issue in your case, I suggest you to put qDebug() calls at some point to check the order of the calls.
Are your .qm located in the same folder as your binary ?
As you are using tr() function, yout text should be translated. I am using this syntax in my program and the translation works fine :
QTranslator translator;
if (QLocale::system().language() == QLocale::French)
translator.load("fr_lang", QCoreApplication::applicationDirPath());
else
translator.load("en_lang", QCoreApplication::applicationDirPath());
app.installTranslator(&translator);
My translation files are fr_lang.qm and en_lang.qm

Resources