Qt - how to detect line count increase after word wrapping? - c++

I'm using a QPlainTextEdit control with word-wrapping active and I'm wondering how can I detect, in order to update a line counter, when a block of text gets wrapped (causing the number of lines to be incremented).
The underlying QTextDocument has a signal to detect when the block count changes, but not the corresponding one for line count changes.
Is it possible to detect word-wrapping and line count increase for a QTextDocument?

It's a little bit late but perhaps my answer can help somebody.
I had almost the same need in my company and I solved it like this:
// This example show how to get the visual number of lines
QPlainTextEdit *pte = new QPlainTextEdit();
pte->setAttribute(Qt::WA_DontShowOnScreen);
pte->show();
pte->setFixedWidth(50);
pte->setPlainText("Hello world!");
/* The next line return the number of necessary line
to display the text "Hello World!" with width of 50px */
int lineCount = pte->document()->documentLayout()->documentSize().height();
Best regards

I solved by using QAbstractTextDocument's signal documentSizeChanged:
void QAbstractTextDocumentLayout::documentSizeChanged ( const QSizeF &
newSize ) [signal]
This signal is emitted when the size of the
document layout changes to newSize. Subclasses of
QAbstractTextDocumentLayout should emit this signal when the
document's entire layout size changes. This signal is useful for
widgets that display text documents since it enables them to update
their scroll bars correctly.
I know the size of my font and getting the precise size of the new underlying document allowed me to count the lines (wrapped or not) of my text document.

Related

clearing the output stream c++

Hello I'm trying to build a function grapher on the terminal in c++. I want that every time a new function is added the output will be cleared and the new frame will take its place.
I've tried to do system("clear") but I don't want all the terminal to be cleared and only the output stream. I've also thought about printing '\b' a lot of times but that seems inefficient.
I would also like to know how to delete 1 line.
You may try to work with console code.
Console code about cursor
There are two solution.
Here is some C style code, just to demonstate the idea easier, you may convert it into C++ style.
Clear content
//Clear content after (x,y)
printf("\033[%d,%dH\033[J",y,x);
//x the column number,y the row number
Overwrite the content
//Goto (x,y) then print the next frame, it should overwrite the old content
printf("\033[%d,%dH",y,x);
...
//your code to output the next frame

Programmatically expand terminal to a specific size

In my output there are certain lines that are refreshed every few seconds. If I resize the terminal by clicking F11, then output is just as I wanted. If terminal isn't big enough some long lines that are refreshed are splitted in two, and because of that, only one part of line is refreshed, and every time line is refreshed I also get new line.
This could be easily avoided if I could specify default size of terminal (resize terminal from my program). Also it would be great if I could forbid user to change terminal size while program is running.
while(1)
{
cout<<"Long line that is refreshed every 5s... \r";
//if line is splited in two lines, \r will return to beginning of that new line
//and the first part of original line would stay as it is(won't be rewrited)
sleep(5);
}
How do I specify a terminal size or stop terminal resizing?
Some terminal emulators (including the default macOS Terminal.app) support being resized/moved/etc in response to printed control sequences. The sequences are fairly standard but not all terminal emulators implement all of them.
For example:
# set terminal width to 50, height to 100
cout << "\e[8;50;100t";
This answer includes an overview of some other available control sequences.
I don't think you can forbid the user to change the terminal size. A better way would be to catch the SIGWINCH signal that is sent to the process everytime the window size is changed, and use the TIOCGWINSZ / TIOCGSIZE ioctl() to get the dimensions.

Arduino LCD only display fisrt 16 characters on line 1 and 41 to 46 caracters in line 2

when I use <LiquidCrystal.h library and lcd-write() it only show the first 16 char in the fist row and start in the 41st char in the second row.
I started with lcd.begin(16,2).
Here an example of the code.
The result of this code will be:
row 1: 0123456789112345
row 2: 4123456789
#include <LiquidCrystal.h>
LiquidCrystal lcd(2,3,4,5,6,7);
void setup() {
// put your setup code here, to run once:
lcd.begin(16, 2);
Serial.begin(9600);
lcd.print("0123456789");
lcd.print("1123456789");
lcd.print("2123456789");
lcd.print("3123456789");
lcd.print("4123456789");
}
void loop() {
}
Its that the expected functionality?
There are a way so I can get the 17th char be displayed in second row.
Yes, that is the expected behaviour. The library allows you to control HD44780-based LCD modules. This LCD controller can drive displays up to 40 character by 2 lines in size. When you use a smaller module, the lines are still stored in the same locations in the DDRAM: the first line starts at location 0, the second line starts at location 40. See the datasheet for more information:
https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
You can use the entire memory and scroll left and right since the 16-character wide display is a window into the DDRAM. You can use the scrollDisplayLeft and scrollDisplayRight to shift the display left and right. These functions change which DDRAM address is used for the first character on the far left of the display. Both lines scroll in unison.
I'm not completely sure why your Arduino has this behaviour but you can pass a complete line in your case 16 characters every time you print and look how it works, also parsing in variables ant passing it to the .print method.
Another way is to set manually the data in the row that you want using the setCursor() method, this method helps you to move and print in the position that you want, it receives two parameters the column and the row, I'll let you the URL with more information.
https://www.arduino.cc/en/Tutorial/LiquidCrystalSetCursor

QTextEdit delete whole line at given position

I need to delete a specific line from QTextEdit (NoWrap option is active) manualy from the program. I found a solution which explains how to remove first line, but i wonder how can I remove whole line at specific index.
I've also found a solution here Remove a line/block from QTextEdit , but I don't know what these blocks are. Do they represent single lines or not? Should i iterate through these blocks and if i reach block at given index, then delete it?
You can remove the line at lineNumer with :
QTextCursor cursor = textEdit->textCursor();
cursor.movePosition(QTextCursor::Start);
cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, lineNumer);
cursor.select(QTextCursor::LineUnderCursor);
cursor.removeSelectedText();
cursor.deleteChar(); // clean up new line
textEdit->setTextCursor(cursor);
Here you put the cursor at the beginning of the document, move down lineNumer times, select the specific line and remove it.
You can do the following:
QTextEdit te;
// Three lines in the text edit
te.setText("Line 1\nLine 2\nLine 3");
const int lineToDelete = 1; // To delete the second line.
QTextBlock b = te.document()->findBlockByLineNumber(lineToDelete);
if (b.isValid()) {
QTextCursor cursor(b);
cursor.select(QTextCursor::BlockUnderCursor);
cursor.removeSelectedText();
}
I know that this question has been accepted and that it's fairly dead here, but I'm going to posit my experiences with QTextEdit as a caution to those that follow.
My problem space was similar to OP's in that I wanted to remove a single line from a text edit. I following the given solution here, improving upon it slightly, and ultimately believed that I had found success.
That success, however, was only realized while the text edit was being viewed, or if it had appeared on screen at least once throughout the course of the program. While I cannot confirm this, I believe that it has to do with cursor manipulation.
Here's a more in-depth explanation:
I desired to create a message history between a UI and the remote unit to which it was conversing. The messages would be color-coded, one for the UI's sent messages, the other for the received messages. In order to prevent a massive memory impact, the idea is to limit the number of lines to a specific amount, say 1000.
My original code was much like the accepted answer:
If the number of lines exceeded my setpoint, move cursor to the beginning and delete the first line.
After some time, however, I began to notice a slowdown in the runtime execution of the program. After adding debug in, I found that, so long as I had not actually viewed the location to which the text was sent, the line-limiter never actually erased the lines. The QTextEdit to which the text was sent was in a tabbed widget. This means that I had to cycle through to that tab, otherwise the algorithm wouldn't work.
Here is a working solution for my problem space:
void ScrollingEdit::append(QString text, QColor color)
{
QString pc = QString("<body style=\"color:rgb(%1,%2,%3);\">").
arg(color.red()).arg(color.green()).arg(color.blue());
QString ac = QString("</body>");
text.prepend( pc );
text.append( ac );
mText.append( text );
QString delim = "</body>";
if ( mText.count( delim ) > mMaxLine )
{
mText.remove(0, mText.indexOf( delim ) + delim.size() );
}
mEdit->clear();
mEdit->setHtml( mText );
QTextCursor cursor = mEdit->textCursor();
cursor.movePosition( QTextCursor::End );
mEdit->setTextCursor(cursor);
mEdit->ensureCursorVisible();
}
Where mText is a member variable QString that acts as the "model" for the text edit, mMaxLine is a user-configurable int that sets the maximum number of allowable lines, and mEdit is the UI QTextEdit. Note that cursor manipulation still exists, but where it matters, which is when the user is viewing the element.

Aligning text in QTextEdit?

If I have a QTextEdit box, how can I align different pieces of text within the box in different ways? For example, I would like to have one sentence be aligned-left, and the next sentence in the box be aligned-right. Is this possible? If not, how might I achieve this effect in Qt?
As documentation said:
void QTextEdit::setAlignment(Qt::Alignment a) [slot]
Sets the alignment of the current paragraph to a. Valid alignments are Qt::AlignLeft, Qt::AlignRight, Qt::AlignJustify and Qt::AlignCenter (which centers horizontally).
Link: http://qt-project.org/doc/qt-5/qtextedit.html#setAlignment
So as you can see you should provide some alignment to each paragraph.
Little example:
QTextCursor cursor = ui->textEdit->textCursor();
QTextBlockFormat textBlockFormat = cursor.blockFormat();
textBlockFormat.setAlignment(Qt::AlignRight);//or another alignment
cursor.mergeBlockFormat(textBlockFormat);
ui->textEdit->setTextCursor(cursor);
Which result I get on my computer?
Or something closer to your question:
ui->textEdit->clear();
ui->textEdit->append("example");
ui->textEdit->append("example");
QTextCursor cursor = ui->textEdit->textCursor();
QTextBlockFormat textBlockFormat = cursor.blockFormat();
textBlockFormat.setAlignment(Qt::AlignRight);
cursor.mergeBlockFormat(textBlockFormat);
ui->textEdit->setTextCursor(cursor);
ui->textEdit->append("example");
cursor = ui->textEdit->textCursor();
textBlockFormat = cursor.blockFormat();
textBlockFormat.setAlignment(Qt::AlignCenter);
cursor.mergeBlockFormat(textBlockFormat);
ui->textEdit->setTextCursor(cursor);
Result:
To left align to a textbox "TerminalOutput":
string Wibble="wibble";
TerminalOutput->append(QString::fromStdString(Wibble));
TerminalOutput->setAlignment(Qt::AlignLeft);
To right align to a textbox:
string Wobble="wobble";
TerminalOutput->append(QString::fromStdString(Wobble));
TerminalOutput->setAlignment(Qt::AlignRight);
Now, sometimes, and this happens with Kosovan's answer too, the alignment in my code sets the previous line instead of the current. This is inexplicable to me. I cannot work out why this is. If anyone knows why this is, please leave a comment because it's driving me nuts.
Edit, I found the problem. So the alignment works fine, until you select some text with the cursor. The instant you do that, the alignment stops aligning the previous line and then decides to affect the following line instead of the previous and this affects all other append formats henceforth. In fact clicking anywhere inside the textbox, will do this, it must be that it's interfering with qt's internal notion of cursor position.
I fixed this problem by doing the following before appending to the textbox:
QTextCursor newCursor = TerminalOutput->textCursor();
newCursor.movePosition(QTextCursor::End);
TerminalOutput->setTextCursor(newCursor);
And what that does is just moves the cursor to the end of the textbuffer so that when you append, it clears out any cursor position of a user clicking anywhere within the textbox, and this solves my strange little problem.