How to get HTML text without meta information from component QTextDocument - qt

Description
I created a TextArea component in QML, and similar to this example, I created a DocumentHandler class based on a pointer to a QQuickTextDocument, which is taken through the textDocument property. I need this in order to be able to format the text, that is, make it bold, underlined, italic, strikeOut etc.
What I need
I need to get a text where the formatted parts will be presented as HTML tags.
e.g. Bold text ultimately I would like to get in the form <b>Bold text</b>. Or for example Bold and italic text I would like to get in the form <b><i>Bold and italic text</i></b> (the order in which the tags are placed does not matter).
What I tried
I tried to use the toHtml() function, but this function does not suit me because:
It generates a lot of unnecessary information that I don't need. For example for Bold text it returned the following result:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Roboto'; font-size:14px; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Bold text</span></p></body></html>
The usual tags that I need to represent the text (<b>, <i> etc.), this function is formed in the form of the style attribute of the <span> tag. So it changes the bold with this line: <span style=" font-weight:600;">.

Description
If I understood correctly, at the moment there is no way to get formatted text with HTML tags without meta information that is generated by the QTextDocument using the toHtml() function. Therefore, I decided to manually do this work using the QTextCursor class.
Code
I have a structure that provides information about tag:
struct Tag
{
Tag(const QString& openTag,
const QString& closeTag,
const std::function<bool(const QTextCursor& cursor)>& canBeOpened);
QString getOpenTag() const;
QString getCloseTag() const;
bool isOpened() const;
bool isClosed() const;
bool canBeOpened(const QTextCursor& cursor) const;
void open();
void close();
private:
std::function<bool(const QTextCursor&)> m_canBeOpened;
QString m_openTag;
QString m_closeTag;
bool m_isOpened{ false };
bool m_isClosed{ true };
};
And I have a std::vector of such structures which I initialize as follows:
m_tags{ { "<b>", "</b>", [](const QTextCursor& cursor) { return cursor.charFormat().fontWeight() == QFont::Bold; } },
{ "<i>", "</i>", [](const QTextCursor& cursor) { return cursor.charFormat().fontItalic(); } },
{ "<u>", "</u>", [](const QTextCursor& cursor) { return cursor.charFormat().fontUnderline(); } },
{ "<s>", "</s>", [](const QTextCursor& cursor) { return cursor.charFormat().fontStrikeOut(); } } }
And the most important thing is the getFormattedText() function that uses this vector of Tag objects to return the formatted text. The main idea is to manually place tags in plain text, that is, the opening tag is placed where the formatting begins, and the closing tag is where it ends. Information about where in the text what formatting is used can be taken from the QTextCursor class, which object we can create based on the QTextDocument class. As a result, we have the following function:
QString getFormattedText()
{
QTextCursor cursor{ textCursor() };
if (!cursor.isNull())
{
QString result{ cursor.document()->toPlainText() };
for (int i{}, offset{}; i < cursor.document()->characterCount(); ++i)
{
cursor.setPosition(i)
for (auto& tag : m_tags)
{
if (tag.canBeOpened(cursor))
{
if (!tag.isOpened())
{
result.insert(i - (i > 0 ? 1 : 0) + offset, tag.getOpenTag());
offset += tag.getOpenTag().size();
tag.open();
}
}
else if (!tag.isClosed())
{
result.insert(i - (i > 0 ? 1 : 0) + offset, tag.getCloseTag());
offset += tag.getCloseTag().size();
tag.close();
}
}
}
for (int i = m_tags.size() - 1; i >= 0; --i)
{
if (!m_tags[i].isClosed())
{
result.insert(result.size(), m_tags[i].getCloseTag());
m_tags[i].close();
}
}
return result;
}
return {};
}
Bold and italic text will look like this <b><i>Bold and italic text</i></b>

Related

Qt: QFormLayout::addRow(QWidget* label, QWidget* field) not adding a new row

I am trying to add a new row to my QFormLayout after I have loaded two QLineEdits from a file, which works, but when I run my code it doesnt add anything, ot atleast anything that I can see. And I am also not able to add any wigets using QLayout::addWidget(QWidget* widget) anymore, which I used to be able to.
Thanks
The code, where it doesnt work:
void Kegelbuch::load(QString path, QString tab) {
//Load json file
Datastream* loadStream = new Datastream;
QJsonObject data = loadStream->loadData(path);
//Declaring all important Variables
QJsonArray keglerFullName;
QJsonArray keglerShortName;
QFormLayout* formLayout = (QFormLayout*)ui.keglerTab->layout();
int defaultRows = 2, width = 155;
//Retriev arrays from file
if (data.contains("KeglerFullName") && data["KeglerFullName"].isArray()) {
keglerFullName = data["KeglerFullName"].toArray();
}
if (data.contains("KeglerShortName") && data["KeglerShortName"].isArray()) {
keglerShortName = data["KeglerShortName"].toArray();
}
//Correctly add QLineEdits to the FormLayout
for (auto names : boost::combine(keglerFullName, keglerShortName)) {
QLineEdit fullNameEdit;
QLineEdit shortNameEdit;
QJsonValue fullNameValue, shortNameValue;
boost::tie(fullNameValue, shortNameValue) = names;
if (fullNameValue.isString()) {
fullNameEdit.setText(fullNameValue.toString());
fullNameEdit.setObjectName("fullName");
fullNameEdit.setMinimumWidth(width);
}
if (shortNameValue.isString()) {
shortNameEdit.setText(shortNameValue.toString());
shortNameEdit.setMaximumWidth(width);
shortNameEdit.setObjectName("shortName");
}
/*
if (keglerFullName.at(1).isString()) {
fullNameEdit->setText(keglerFullName.at(1).toString());
fullNameEdit->setObjectName("fullName");
fullNameEdit->setMinimumWidth(width);
}
if (keglerShortName.at(1).isString()) {
shortNameEdit->setText(keglerShortName.at(1).toString());
shortNameEdit->setMaximumWidth(width);
shortNameEdit->setObjectName("shortName");
}
*/
formLayout->addRow(&fullNameEdit, &shortNameEdit);
}
}

Save a single character

everyone.
I've created a simple textedit application.
I would like to take the single character whenever the user writes it using the keyboard.
I've thought to solve the problem in the following way:
void MainWindow::on_textEdit_textChanged()
{
QString str= ui->textEdit->toPlainText();
if (str.size()==0){
pos=0;
} else {
if(pos<str.size()) {
QChar char_prel=str.at(pos);
pos++;
chars.push_back(char_prel);
} else {
pos=0;
QString str=ui->textEdit->toPlainText();
chars.clear();
for(int i = 0; i < str.length(); i++) {
QChar char_prel=str.at(i);
chars.push_back(char_prel);
pos++;
}
}
}
}
The solution doesn't work because everytime, i read the entire string on the edit block using:
QString str= ui->textEdit->toPlainText();
and from that string i take the last inserted character.
I want to do the same thing without using the toPlaintText().
Thanks for answering
If you handle the KeyPressEvent of the QTextEdit, you'll have a parameter of QKeyEvent* type, let's call this one "e".
Then you can use "e->text()" to get the corresponding character.

BlackBerry iterate through selectedItems list

So I have FINALLY gotten to the point where I can select multiple items on a ListView:
ListView {
id: lv_stuffs
horizontalAlignment: HorizontalAlignment.Fill
dataModel: _app.personDataModel //REFERENCE 1
multiSelectAction: MultiSelectActionItem {
}
multiSelectHandler {
actions: [
// Add the actions that should appear on the context menu
// when multiple selection mode is enabled
ActionItem {
title: "Search for stuffs"
onTriggered: {
_app.search(lv_stuffs.selectionList());
}
...
And I am sending this selection list through to my search method:
void ApplicationUI::search(const QVariantList &list)
{
alert(QString("%1 items selected").arg(list.length()));
alert(((Person)list.at(0)).firstName);//<---- THIS IS THE PROBLEM
}
I am trying to get the "Person" object out of the GroupedDataModel that originally bound to the item... and I have to say I am more than a little stumped. The person is being added to the personDataModel via a simple insert method in a database class:
personDataModel->insert(person);
and the items are then bound to the ListView in the QML (REFERENCE 1 above). The binding is all fine and the items are visible in the list. What I can't figure out is how to now extract these "Person" objects out of the QVariantList I am sent via the MultiSelectionMethod.
My person class:
Person::Person(QObject *parent) : QObject(parent){}
Person::Person(const QString &id, const QString &firstname, const QString &lastname, QObject *parent)
: QObject(parent)
, m_id(id)
, m_firstName(firstname)
, m_lastName(lastname)
{
}
QString Person::customerID() const
{
return m_id;
}
QString Person::firstName() const
{
return m_firstName;
}
QString Person::lastName() const
{
return m_lastName;
}
void Person::setCustomerID(const QString &newId)
{
if (newId != m_id) {
m_id = newId;
emit customerIDChanged(newId);
}
}
void Person::setFirstName(const QString &newName)
{
if (newName != m_firstName) {
m_firstName = newName;
emit firstNameChanged(newName);
}
}
void Person::setLastName(const QString &newName)
{
if (newName != m_lastName) {
m_lastName = newName;
emit lastNameChanged(newName);
}
}
I have been PAINFULLY following this tutorial here, https://developer.blackberry.com/cascades/documentation/ui/lists/list_view_selection.html, which conveniently stops right where my question begins.
Are you perhaps looking for the value function?
void ApplicationUI::search(const QVariantList &list)
{
alert(QString("%1 items selected").arg(list.length()));
alert(((Person)list.value(0)).firstName);
}
(syntax of the extraction of the firstName value may not be right there, depends on your implementation)
Your Person class will be stored in a QVariant. To accomplish this, Qt has to generate some code specific to your class. It can be done by adding this right after your class definition, in the header file for example: Q_DECLARE_METATYPE(Person). You can read more about this here: http://qt-project.org/doc/qt-4.8/qmetatype.html#Q_DECLARE_METATYPE
Now, to extract the value as a Person object, you can use QVariant::value<T>() (http://qt-project.org/doc/qt-4.8/qvariant.html#value):
alert(list.at(0).value<Person>().firstName());

QlineEdit with some default text for which cursor should not be moved?

In QT, a created lineEdit shows a text using the setText() method.
But the cursor is movable for the default text. I want the cursor should not be movable for the default text.
My lineEdit type has been set as password. Hence the default text('Password') is also displayed as '********'. Whenever user types the type has to be changed as password and when there is no text or until the user have not typed any text, the lineEdit should display the plain text 'password'
Any idea to fix the above two issues?
In the constructor put
ui->lineEdit->setPlaceholderText("password");
ui->lineEdit->setReadOnly(1);
And in on_lineEdit_selectionChanged() SLOT, put
ui->lineEdit->setText("");
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit->setReadOnly(0);
I noticed this question has tag pyqt so I'll put an actual answer related to that tag for those actually looking for a python way instead of c++.
self.searchEditText = QtGui.QLineEdit()
self.searchEditText.setPlaceholderText("Search for word")
I managed to do what you want by deriving a class from QLineEdit as per following..
Constructor..
QCustomLineEdit::QCustomLineEdit(QWidget *parent) :
QLineEdit(parent)
{
connect(this, SIGNAL(textChanged(QString)), this, SLOT(onTextChanged(QString)));
connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int)));
setEchoMode(QLineEdit::Password); // Echo mode in your case..
m_echoMode = echoMode(); // Member variable to store original echo mode..
m_placeHolderText = "Password"; // Member variable..
m_isPlaceHolderActive = true; // Member varible..
// Default case..
setPlaceholderText("");
setStyleSheet("QCustomLineEdit{color: gray;}");
setEchoMode(QLineEdit::Normal);
setText(__placeHolderText);
}
Override keyPressEvent..
void QCustomLineEdit::keyPressEvent(QKeyEvent *e)
{
if(m_isPlaceHolderActive)
{
if(e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
e->accept();
else
QLineEdit::keyPressEvent(e);
return;
}
QLineEdit::keyPressEvent(e);
}
Cursor position change event..
void QCustomLineEdit::onCursorPositionChanged(int /*oldPos*/, int newPos)
{
if(m_isPlaceHolderActive)
{
if(newPos != 0)
setCursorPosition(0);
}
}
Text change event..
void QCustomLineEdit::onTextChanged(const QString &text)
{
if(m_isPlaceHolderActive)
{
if(text.compare(m_placeHolderText) != 0)
{
m_isPlaceHolderActive = false;
// Remove the 'placeHolderText' from 'text' itself..
QString temp = text;
temp = temp.mid(0, text.lastIndexOf(m_placeHolderText));
setStyleSheet("QCustomLineEdit{color: black;}");
setEchoMode(m_echoMode);
setText(temp);
}
else
{
setEchoMode(QLineEdit::Normal);
setText(m_placeHolderText);
setStyleSheet("QCustomLineEdit{color: gray;}");
setCursorPosition(0);
}
}
else
{
if(text.isEmpty())
{
m_isPlaceHolderActive = true;
setStyleSheet("QCustomLineEdit{color: gray;}");
setEchoMode(QLineEdit::Normal);
setText(m_placeHolderText);
}
}
}
I have written it very hastily to just show you. Test it yourself and feel free to point any mistake(s) or optimization(s). Hope this helps.
For question 1, in Qt 5.0 and higher, setPlaceholderText does what you want. https://codereview.qt-project.org/#change,45326

Button with image and text [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Text on an Image button in c# asp.net 3.5
I want a asp.net button with text on left and image on right
Here's one I wrote:
public class WebImageButton : LinkButton, IButtonControl
{
protected override void OnPreRender(EventArgs e)
{
if (!this.DesignMode)
{
// Apply the image
if (this.Image.Length > 0)
{
this.Style.Add("background-image", "url(" + this.Image + ")");
this.Style.Add("background-repeat", "no-repeat");
this.Style.Add("background-position", this.ImageHorizontalOffset + " " + this.ImageVerticalOffset);
}
}
base.OnPreRender(e);
}
[DescriptionAttribute("The path to the default image to be displayed.")]
public string Image
{
get
{
if (_image == null)
{
return string.Empty;
}
return _image;
}
set
{
_image = value;
}
}
private string _image;
[DescriptionAttribute("The unit to offset the image by horizontally.")]
public string ImageHorizontalOffset
{
get
{
return _imageHorizontalOffset;
}
set
{
_imageHorizontalOffset = value;
}
}
private string _imageHorizontalOffset = "0px";
[DescriptionAttribute("The unit to offset the image by vertically.")]
public string ImageVerticalOffset
{
get
{
return _imageVerticalOffset;
}
set
{
_imageVerticalOffset = value;
}
}
private string _imageVerticalOffset = "center";
}
Then the CSS that accompanies it:
.ImageButton
{
background:#666;
border:solid 1px #000;
color:#FFF;
font-size:10pt;
font-weight:bold;
padding:4px;
text-align:center;
cursor:hand;
}
And an example of its use:
<ctrl:WebImageButton ID="WebImageButton1" runat="server"
OnClick="WebImageButton1_Click" CssClass="ImageButton" Text="Click me"
ImageHorizontalOffset="4px" />
You could use CSS to apply the image to the background of the input element. Have a read up in the background-image CSS method:
http://www.w3schools.com/css/pr_background-image.asp
I'm not sure if there are any asp controls that allow you to do this (but I could be wrong). What you will want to fool around with is possibly divs and their onclick events. Then you can do something like onclick="Javascript:this.form.submit();". You will be able to size the div how you please and insert what you want (in your case text and an image).
Edit: Then in the css you could also add #mydiv:hover { cursor: pointer; } To get the hand icon when users move their cursor over the div.
But it looks like gage provided a link to someones solution :P Good luck

Resources