QPlainTextEdit: different char format for part of text - qt

after scanning several examples I didn't manage to solve this problem:
I created a test for this having a simple MainWindow with a QPlainTextEdit containing some text. On a trigger I want a part of the text to be red and underlined. But that never happens.
Regards
My code:
void MainWindow::on_actionTest_triggered()
{
QTextCursor cur = ui.plainTextEdit->textCursor();
cur.setPosition(49);
QTextCharFormat oldFormat = cur.charFormat();
QTextCharFormat newFormat = oldFormat;
newFormat.setForeground(Qt::darkRed);
newFormat.setUnderlineColor(Qt::darkRed);
newFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
newFormat.setFontUnderline(true);
cur.setCharFormat(newFormat);
cur.setPosition(cur.position()+11);
cur.setCharFormat(oldFormat);
ui.plainTextEdit->setTextCursor(cur);
}
using QTextEdit instead doesn't change anything)

This is your solution:
Using QTextCharFormat to set Underline and background.
void MainWindow::on_pushButton_clicked()
{
int begin = 30; //begin highlight
int end = 40; //end highlight
QTextCharFormat fmt;
fmt.setBackground(Qt::red); // set color red
fmt.setUnderlineStyle(QTextCharFormat::SingleUnderline); //set underline
QTextCursor cursor(ui->plainTextEdit->document());
cursor.setPosition(begin, QTextCursor::MoveAnchor);
cursor.setPosition(end, QTextCursor::KeepAnchor);
cursor.setCharFormat(fmt);
}

Related

Qt connect QScrollBar with QLineEdit

I would like to "activate" the scrollbar when the lineedit text is too long to display on the window. I have already done it.
I want to move the cursor with the scrollbar. I also want to modify the scroll bar slider length with the increment/decrement of the text length.
.h
private:
Ui::MainWindow *ui;
QLineEdit* LineEdit;
QScrollBar* hScrollBar;
void HDScrollBar();
constructor:
resize(400,100);
LineEdit = new QLineEdit(this);
LineEdit->resize(400,100);
LineEdit->setFont(QFont("Times",20));
hScrollBar = new QScrollBar(Qt::Horizontal, LineEdit);
hScrollBar->resize(400,20);
hScrollBar->move(0,80);
hScrollBar->hide();
connect(LineEdit, &QLineEdit::textChanged, this, &MainWindow::HDScrollBar);
hide/display scrollbar
void MainWindow::HDScrollBar() {
QFont myFont(QFont("Times",20));;
QString str = LineEdit->text();
QFontMetrics fm(myFont);
int width = fm.horizontalAdvance(str);
(width >= 400) ? hScrollBar->show() : hScrollBar->hide();
}
As for the first part, you use can scrollbar's valueChanged signal, e.g. this way:
connect(ui->horizontalScrollBar, &QScrollBar::valueChanged, ui->lineEdit, [&](int v) {
auto scrollbarAt = static_cast<double>(v) / ui->horizontalScrollBar->maximum();
auto cursorTo = std::round(ui->lineEdit->text().length() * scrollbarAt);
ui->lineEdit->setCursorPosition(cursorTo);
});
EDIT:
as for the latter part:
you can either alter the PageStep of the scrollbar, or you can set it to 1 and alter its maximum value, then also the first part should simplify:
ui->horizontalScrollBar->setPageStep(1);
ui->horizontalScrollBar->setMaximum(ui->lineEdit.text().length());
connect(ui->horizontalScrollBar, &QScrollBar::valueChanged,
ui->lineEdit, &QLineEdit::setCursorPosition);
//this can also ben done on textChanged, however for the price
//of more frequent execution...
connect(ui->lineEdit, &QLineEdit::cursorPositionChanged,
ui->horizontalScrollBar, [&](int, int n) {
ui->horizontalScrollBar->setMaximum(ui->lineEdit->text().length());
//...one gets the easy way to track the cursor
//with the slider
ui->horizontalScrollBar->setValue(n);
});

save cursor position of qtextedit

setCurrentCharFormat() function does not take a current cursor position as a parameter. And so in order to set the char format for arbitrary text in the control I have to save the curent cursor position, set the char format and then restore it.
However, I don't see any thing like cursorPosition() in the docs.
Am I missing something?
Or maybe there is a better way of doing what I want?
I think you're looking for the QTextEdit::textCursor() method which returns a copy of the editor's QTextCursor. You can then manipulate the cursor as needed (including changing char. format and inserting text with specific format). If you need the cursor changes to persist (like the char. format), then make sure to QTextEdit::setCursor() afterwards.
A very basic example of inserting some text:
QTextCursor cursor(ui->textEdit->textCursor());
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor, 1);
cursor.insertText(text);
ADDED:
Perhaps the Qt Text Edit example will help. Specifically, in textedit.cpp where we see something like this:
void TextEdit::textBold()
{
QTextCharFormat fmt;
fmt.setFontWeight(actionTextBold->isChecked() ? QFont::Bold : QFont::Normal);
mergeFormatOnWordOrSelection(fmt);
}
void TextEdit::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = textEdit->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
textEdit->mergeCurrentCharFormat(format);
}
2nd ADDITION (based on comment):
So, if my cursor is at the line 3 col 10 and I call the function like this: SetCharFormat( 2, 5, attr ); it stores the current cursor position as (3, 10), then selects the text for text characters 2 and 5, set the text attribute for the selection, and then cursor will move back to the old position/selection.
Below is a specific example of what I think you're describing. One thing overall though, the text cursor position only has one dimension, which is, essentially, the number of visible characters from the start of the document. (The text cursor should not be confused with a QCursor which represents a mouse pointer with x,y coordinates.)
This simple test shows an editor with the text "I like this program." and a button. The button (or Alt-D) will toggle bold formatting on the word "like" while keeping the visible cursor position (and any selection) unchanged.
I've also included some sample code which moves the visible cursor initially, and in the formatting function there is a commented-out example of how to save and restore the cursor position programmatically. It is not needed in this particular example because the visible cursor is never modified.
#include <QtWidgets>
class Dialog : public QDialog
{
public:
Dialog(QWidget *parent = nullptr) : QDialog(parent)
{
QTextEdit *textEdit = new QTextEdit("I like this program.", this);
// Position cursor at end of sentence (just as an example)
QTextCursor cursor(textEdit->textCursor());
cursor.movePosition(QTextCursor::End);
textEdit->setTextCursor(cursor); // required for the visible cursor to actually move
QToolButton *btnTest = new QToolButton(this);
btnTest->setText("&Do it");
btnTest->setCheckable(true);
connect(btnTest, &QToolButton::toggled, this, [textEdit, btnTest](bool checked)
{
// Function to toggle bold formatting on a section of text in the editor.
const int start = 2; // start of "like"
const int end = start + 4; // length of "like"
// the formatting to be applied
QTextCharFormat format;
format.setFontWeight(checked ? QFont::Bold : QFont::Normal);
format.setForeground(checked ? QBrush(Qt::red) : QPalette().text());
format.setBackground(checked ? QBrush(Qt::gray) : QPalette().base());
QTextCursor cursor(textEdit->textCursor()); // get a copy of the editor's cursor
// const int oldCursorPos = cursor.position(); // save cursor position (not needed for this example)
cursor.setPosition(start, QTextCursor::MoveAnchor); // move w/out selection
cursor.setPosition(end, QTextCursor::KeepAnchor); // move and select
cursor.mergeCharFormat(format); // apply format to selection
// cursor.setCharFormat(format); // alternative to mergeChatFormat()
// cursor.setPosition(oldCursorPos); // restore cursor position
// cursor.setPosition(end); // or move it to the end of the affected text
// textEdit->setTextCursor(cursor); // required for the visible cursor to move
btnTest->setText(checked ? "Un&do it" : "Re&do it");
});
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(textEdit);
layout->addWidget(btnTest);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
return Dialog().exec();
}

QLabel change font color without changing any other style

I want to change the color of the text in a QLabel dynamically.
I have defined the color and style of the QLabel in the ui file and I want to change it when a certain event takes place.
I want to change the color without changing any other style of my QLabel.
I have found several answers adressing the issue of changing text color in a QLabel (1, 2, 3) and they all use the function setStyleSheet. This function works fine but it changes my font size and other styles related to the QLabel.
I have seen that the problem is related to setStyleSheet ignoring any previous style. The solution proposed there involves retrieving all the styles I want to maintain and setting them again together with the text color change.
This is cumbersome and difficult to maintain. If more styles were defined in the future I would need to review this part of the code to be able to reset all of them.
I would like to be able to change QLabel text color without altering any other syle. Is it possible?
If you want to manage the text color of QLabel you could wrap it with customized class.
For example:
class ColorLabel : public QLabel
{
public:
ColorLabel(const QString &text, QWidget *parent = nullptr)
: QLabel(text, parent)
{
setAutoFillBackground(true);
}
void setTextColor(const QColor &color)
{
QPalette palette = this->palette();
palette.setColor(this->backgroundRole(), color);
palette.setColor(this->foregroundRole(), color);
this->setPalette(palette);
}
};
And to use it in your code:
ColorLabel * poColorLabel = new ColorLabel("My string", this);
poColorLabel->setTextColor(Qt::red); // set label text in red color
FYI: I tested it on Fedora, Qt5.12 and it works fine.
A pragmatic approach:
Utilize the cascadingness of CSS.
Wrap your QLabel in a QWidget (don't forget a QLayout).
Set your default style on the surrounding QWidget.
Set the font color as the QLabel's only style.
You can create some style class to control a widget's style:
class WidgetStyleSheet
{
public:
// change some style's value
void setValue(const QString& styleKey, const QString& value)
{
_styleMap[styleKey] = value;
}
// to default state
void reset() {}
// form stylesheet
QString toStyleSheet() const
{
QString styleSheet;
QMapIterator<QString, QString> iter(_styleMap);
while( iter.hasNext() )
styleSheet += QString("%1: %2").arg(iter.key()).arg(iter.value());
return styleSheet;
}
private:
QMap<QString, QString> _styleMap;
}
Somewhere in your code:
WidgetStyleSheet labelSS;
// ...
labelSS.setValue("color", QString("%1").arg( QColor(255, 10, 0).name() );
labelSS.setValue("background-color", "...");
// ...
label->setStyleSheet(labelSS);
The following works fine. But it is not that elegant. This is in python. You have to pass the button name (or any other) to the following as is defined in the array
btns = ['self.hBeamBtn','self.lBeamBtn','self.allTestBtn','self.prnStatusBtn']
for btn in btns:
if str(btn_name) == str(btn):
styl = btn+'.setStyleSheet("font: bold;background-color: red;font-size: 12px;height: 28px;width: 80px;")'
eval(styl)

OpenCV and Qt: Mat to QLabel QPixmap

I'm having some trouble with the following code. I'm attempting to set a Mat as a QLabel pixmap. When I call QLabel::setPixmap(), nothing happens! The label in the dialog box stays exactly the same, showing placeholder text. It has been properly promoted to MyLabel.
//include stuff above
MyLabel::MyLabel(QWidget *parent): QLabel(parent)
{
boats = cv::imread("C:/boats.jpg");
setPixmap(boats);
}
void MyLabel::setPixmap(cv::Mat image){
cv::Mat converted;
cv::cvtColor(image, converted, CV_BGR2RGB);
QImage result = QImage((const unsigned char*)(converted.data),
converted.cols, converted.rows, QImage::Format_RGB888);
QLabel::setPixmap(QPixmap::fromImage(result));
}
I have also tried setting the pixmap in the following way:
QLabel::setPixmap(QPixmap("C:/boats.jpg"));
But this produces the same effect. Any ideas?
You should set image to the interface. For instance, you can set like this way:
ui->label_15->setPixmap(QPixmap::fromImage(result));

Qt how to move selection to one line up

I have a trouble. Here my code is:
void TextEditor::moveToLineUp()
{
QTextCursor cur = textCursor();
if(cur.hasSelection()) {
int start = cur.selectionStart();
int end = cur.selectionEnd();
QTextBlock startBlock = document()->findBlock(start);
QTextBlock endBlock = document()->findBlock(end);
cur.setPosition(startBlock.position());
cur.setPosition(endBlock.position()+endBlock.length(), QTextCursor::KeepAnchor);
} else {
// I'll fill it later
}
// ??? I don't know how not to write remove action in undo stack
QString text = cur.selectedText();
cur.removeSelectedText();
QTextCursor ncur(cur.block().previous());
ncur.insertText(text);
}
When user select some text and click on button "Line up" this function is called.
Every line which has selection should move to one line up. But after that undo stack have to steps: remove text and paste text. What should I do? I want to make it simple operation in one step.

Resources