void QTextDocument::contentsChange(int position, int charsRemoved, int charsAdded) [signal]
This signal is emitted whenever the document's content changes; for example, when text is inserted or deleted, or when formatting is applied.
User can click cut/press delete/backspace or any other way and will remove text.
Problem is this signal emitted after text is removed. So I don't know which text was deleted.Now position and charsRemoved are of no use.
I want to find out deleted text of QPlainTextEdit. Is there any way to achieve this?
The trick with calling undo and redo suggested by Googie is nice, to make it even more efficient one can use a QTextCursor to extract the text instead of calling toPlainText:
void TextEdit::slotCheckRange(int pos, int removed, int added){
if(removed > 0){
undo();
QTextCursor c(textCursor());
c.setPosition(pos);
c.setPosition(pos + removed, QTextCursor::KeepAnchor);
qDebug() << "Removed: " << removed << " (" << c.selectedText() << ")";
redo();
}
if(added > 0){
QTextCursor c(textCursor());
c.setPosition(pos);
c.setPosition(pos + added, QTextCursor::KeepAnchor);
qDebug() << "Added: " << added << " (" << c.selectedText() << ")";
}
}
I see 2 possible solutions:
Store the contents of the document after every contents change, so in every next change you will have access to the previous contents and you will be able to extract the value using position and charsRemoved.
Pros: It's isolated mechanism and will not interfere with any other signals or slots.
Cons: It implies significant memory and CPU footprint (every text change causes full string copy).
(a better one in my opinion) In the slot function implementation use the undo() and redo() methods of the QPlainTextEdit to restore previous contents for the time of querying charsRemoved. Note, that calling undo() and redo() will not trigger the contentsChange() signal (I just tested it), so it's as simple as that.
Pros: Doesn't cause additional memory footprint. Not sure about the CPU footprint, but I think it's also better in that case.
Cons: This will only work with Undo/Redo mechanism enabled (which it is by default) and it also might affect any undo/redo code that you use or override (usually it's not the case).
To be clear, a sample code snipped for solution 2:
void MainWindow::textChanged(int pos, int rem, int add)
{
ui->plainTextEdit->undo();
qDebug() << ui->plainTextEdit->document()->toPlainText().mid(pos, rem); // <- text removed
ui->plainTextEdit->redo();
qDebug() << ui->plainTextEdit->document()->toPlainText().mid(pos, add); // <- text added
}
Related
I've just noticed something when using QNetworkReply that I was unable to find the slightest hint in the Qt documentation for QIODevice::readAll() (which the QNetworkReply inherits this method from).
Here is what the documentation states:
Reads all remaining data from the device, and returns it as a byte
array.
This function has no way of reporting errors; returning an empty
QByteArray can mean either that no data was currently available for
reading, or that an error occurred.
Let's say I have the following connection:
connect(this->reply, &QIODevice::readyRead, this, &MyApp::readyReadRequest);
Ths readyReadRequest() slot looks like this:
void MyApp::readyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
LOG(INFO) << "Data contents:\n" << QString(this->reply->readAll());
this->bufferReply = this->reply->readAll();
}
The surprise came after I called this->bufferReply (which a QByteArray class member of MyApp). I passed it to a QXmlStreamReader and did:
while (!reader.atEnd())
{
LOG(DEBUG) << "Reading next XML element";
reader.readNext();
LOG(DEBUG) << reader.tokenString();
}
if (reader.hasError())
{
LOG(ERROR) << "Encountered error while parsing XML data:" << reader.errorString();
}
Imagine my surprise when I got the following output:
2017-10-17 16:12:18,591 DEBUG [default] [void MyApp::processReply()][...] Reading next XML element
2017-10-17 16:12:18,591 DEBUG [default] [void MyApp::processReply()] [...] Invalid
2017-10-17 16:12:18,591 ERROR [default] Encountered error while parsing XML data: Premature end of document
Through debugging I got that my bufferReply at this point is empty. I looked in the docs again but couldn't find anything that hints removing the data from the device (in my case the network reply) after reading it all.
Removing the line where I print the byte array or simply moving it after this->bufferReply = this->reply->readAll(); and then printing the contents of the class member fixed the issue:
void MyApp::readyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
this->bufferReply = this->reply->readAll();
LOG(INFO) << "Data contents:\n" << QString(this->bufferReply);
}
However I would like to know if I'm doing something wrong or is the documentation indeed incomplete.
Since readAll() doesn't report errors or that data is not available at the given point in time returning an empty byte array is the only thing that hints towards the fact that something didn't work as intended.
Yes. When you call QIODevice::readAll() 2 times, it is normal that the 2nd time you get nothing. Everything has been read, there is nothing more to be read.
This behavior is standard in IO read functions: each call to a read() function returns the next piece of data. Since readAll() reads to the end, further calls return nothing.
However, this does not necessarily means that the data has been flushed. For instance when you read a file, it just moves a "cursor" around and you can go back to the start of the file with QIODevice::seek(0). For QNetworkReply, I'd guess that the data is just discarded.
I was wondering what is the most efficient performant way to output a new line to console. Please explain why one technique is more efficient. Efficient in terms of performance.
For example:
cout << endl;
cout << "\n";
puts("");
printf("\n");
The motivation for this question is that I find my self writing loops with outputs and I need to output a new line after all iterations of the loop. I'm trying to find out what's the most efficient way to do this assuming nothing else matters. This assumption that nothing else matters is probably wrong.
putchar('\n') is the most simple and probably fastest. cout and printf with string "\n" work with null terminated string and this is slower because you process 2 bytes (0A 00). By the way, carriage return is \r = 13 (0x0D). \n code is Line Feed (LF).
You don't specify whether you are demanding that the update to the screen is immediate or deferred until the next flush. Therefore:
if you're using iostream io:
cout.put('\n');
if you're using stdio io:
std::putchar('\n');
The answer to this question is really "it depends".
In isolation - if all you're measuring is the performance of writing a '\n' character to the standard output device, not tweaking the device, not changing what buffering occurs - then it will be hard to beat options like
putchar('\n');
fputchar('\n', stdout);
std::cout.put('\n');
The problem is that this doesn't achieve much - all it does (assuming the output is to a screen or visible application window) is move the cursor down the screen, and move previous output up. Not exactly a entertaining or otherwise valuable experience for a user of your program. So you won't do this in isolation.
But what comes into play to affect performance (however you measure that) if we don't output newlines in isolation? Let's see;
Output of stdout (or std::cout) is buffered by default. For the output to be visible, options include turning off buffering or for the code to periodically flush the buffer. It is also possible to use stderr (or std::cerr) since that is not buffered by default - assuming stderr is also directed to the console, and output to it has the same performance characteristics as stdout.
stdout and std::cout are formally synchronised by default (e.g. look up std::ios_base::sync_with_stdio) to allow mixing of output to stdout and std::cout (same goes for stderr and std::cerr)
If your code outputs more than a set of newline characters, there is the processing (accessing or reading data that the output is based on, by whatever means) to produce those other outputs, the handling of those by output functions, etc.
There are different measures of performance, and therefore different means of improving efficiency based on each one. For example, there might be CPU cycles, total time for output to appear on the console, memory usage, etc etc
The console might be a physical screen, it might be a window created by the application (e.g. hosted in X, windows). Performance will be affected by choice of hardware, implementation of windowing/GUI subsystems, the operating system, etc etc.
The above is just a selection, but there are numerous factors that determine what might be considered more or less performance.
On Ubuntu 15.10, g++ v5.2.1 (and an older vxWorks, and OSE)
It is easy to demonstrate that
std::cout << std::endl;
puts a new line char into the output buffer, and then flushes the buffer to the device.
But
std::cout << "\n";
puts a new line char into the output buffer, and does not output to the device. Some future action will be needed to trigger the output of the newline char in the buffer to the device.
Two such actions are:
std::cout << std::flush; // will output the buffer'd new line char
std::cout << std::endl; // will output 2 new line chars
There are also several other actions that can trigger the flush of the std::cout buffering.
#include <unistd.h> // for Linux
void msDelay (int ms) { usleep(ms * 1000); }
int main(int, char**)
{
std::cout << "with endl and no delay " << std::endl;
std::cout << "with newline and 3 sec delay " << std::flush << "\n";
msDelay(3000);
std::cout << std::endl << " 2 newlines";
return(0);
}
And, per comment by someone who knows (sorry, I don't know how to copy his name here), there are exceptions for some environments.
It's actually OS/Compiler implementation dependent.
The most efficient, least side effect guaranteed way to output a '\n' newline character is to use std::ostream::write() (and for some systems requires std::ostream was opened in std::ios_base::binary mode):
static const char newline = '\n';
std::cout.write(&newline,sizeof(newline));
I would suggest to use:
std::cout << '\n'; /* Use std::ios_base::sync_with_stdio(false) if applicable */
or
fputc('\n', stdout);
And turn the optimization on and let the compiler decide what is best way to do this trivial job.
Well if you want to change the line I'd like to add the simplest and the most common way which is using (endl), which has the added perk of flushing the stream, unlike cout << '\n'; on its own.
Example:
cout << "So i want a new line" << endl;
cout << "Here is your new line";
Output:
So i want a new line
Here is your new line
This can be done for as much new lines you want. Allow me to show an example using 2 new lines, it'll definitely clear all of your doubts,
Example:
cout << "This is the first line" << endl;
cout << "This is the second line" << endl;
cout << "This is the third line";
Output:
This is the first line
This is the second line
This is the third line
The last line will just have a semicolon to close since no newline is needed. (endl) is also chain-able if needed, as an example, cout << endl << endl; would be a valid sequence.
For some reason, every time I use std::cout, the entire content (sort of, difficult to explain) of the console is re-printed, unless I << endl;. To provide some context, I am using glfw to back my Window class, which has higher level std::function callbacks. My compiler is MinGW 3.21, using what pieces of C++11 MinGW 3.21 actually implements. What is going on?
void Window::setTextCallback(std::function<void(char text)> callback) {
textCallback = callback;
auto onText = [](GLFWwindow* window, unsigned int text, int mods) {
Window* win = reinterpret_cast<Window*>(glfwGetWindowUserPointer(window));
win->textCallback(static_cast<char>(text));
};
glfwSetCharModsCallback(window, onText);
}
And then in main.cpp...
Window w;
w.setTextCallback([](char text){
cout << text;
}
When the window is open, lets say I type "asdf". The output is "aasasdasdf". In slow motion, it goes: "a", "aas", "aasasd", aasasdasdf".
However, if I change main.cpp to:
Window w;
w.setTextCallback([](char text){
cout << text << endl;
}
The output is:
"a
s
d
f"
As expected.
No other threads are using cout and I know that because I don't have any other threads. This behavior does not happen elsewhere.
cout is a buffer. So each time you << to it, you're just adding text and it's holding on. endl is a way to flush the buffer. If you want to print only the last bit of text you push into it, you need to start with it empty. Try this post and then I'm sure your googling can finish any questions you might have about this.
As part of a bigger application I am working on a class for reading input from a text file for use in the initialization of the program. Now I am myself fairly new to programming, and I only started to learn C++ in December, so I would be very grateful for some hints and ideas on how to get started! I apologise in advance for a rather long wall of text.
The text file format is "keyword-driven" in the following way:
There are a rather small number of main/section keywords (currently 8) that need to be written in a given order. Some of them are optional, but if they are included they should adhere to the given ordering.
Example:
Suppose there are 3 potential keywords ordered like as follows:
"KEY1" (required)
"KEY2" (optional)
"KEY3" (required)
If the input file only includes the required ones, the ordering should be:
"KEY1"
"KEY3"
Otherwise it should be:
"KEY1"
"KEY2"
"KEY3"
If all the required keywords are present, and the total ordering is ok, the program should proceed by reading each section in the sequence given by the ordering.
Each section will include a (possibly large) amount of subkeywords, some of which are optional and some of which are not, but here the order does NOT matter.
Lines starting with characters '*' or '--' signify commented lines, and they should be ignored (as well as empty lines).
A line containing a keyword should (preferably) include nothing else than the keyword. At the very least, the keyword must be the first word appearing there.
I have already implemented parts of the framework, but I feel my approach so far has been rather ad-hoc. Currently I have manually created one method per section/main keyword , and the first task of the program is to scan the file for to locate these keywords and pass the necessary information on to the methods.
I first scan through the file using an std::ifstream object, removing empty and/or commented lines and storing the remaining lines in an object of type std::vector<std::string>.
Do you think this is an ok approach?
Moreover, I store the indices where each of the keywords start and stop (in two integer arrays) in this vector. This is the input to the above-mentioned methods, and it would look something like this:
bool readMAINKEY(int start, int stop);
Now I have already done this, and even though I do not find it very elegant, I guess I can keep it for the time being.
However, I feel that I need a better approach for handling the reading inside of each section, and my main issue is how should I store the keywords here? Should they be stored as arrays within a local namespace in the input class or maybe as static variables in the class? Or should they be defined locally inside relevant functions? Should I use enums? The questions are many!
Now I've started by defining the sub-keywords locally inside each readMAINKEY() method, but I found this to be less than optimal. Ideally I want to reuse as much code as possible inside each of these methods, calling upon a common readSECTION() method, and my current approach seems to lead to much code duplication and potential for error in programming. I guess the smartest thing to do would simply be to remove all the (currently 8) different readMAINKEY() methods, and use the same function for handling all kinds of keywords. There is also the possibility for having sub-sub-keywords etc. as well (i.e. a more general nested approach), so I think maybe this is the way to go, but I am unsure on how it would be best to implement it?
Once I've processed a keyword at the "bottom level", the program will expect a particular format of the following lines depending on the actual keyword. In principle each keyword will be handled differently, but here there is also potential for some code reuse by defining different "types" of keywords depending on what the program expects to do after triggering the reading of it. Common task include e.g. parsing an integer or a double array, but in principle it could be anything!
If a keyword for some reason cannot be correctly processed, the program should attempt as far as possible to use default values instead of terminating the program (if reasonable), but an error message should be written to a logfile. For optional keywords, default values will of course also be used.
In order to summarise, therefore, my main questions are the following:
1. Do you think think my approach of storing the relevant lines in a std::vector<std::string> to be reasonable?
This will of course require me to do a lot of "indexing work" to keep track of where in the vector the different keywords are located. Or should I work more "directly" with the original std::ifstream object? Or something else?
2. Given such a vector storing the lines of the text file, how I can I best go about detecting the keywords and start reading the information following them?
Here I will need to take account of possible ordering and whether a keyword is required or not. Also, I need to check if the lines following each "bottom level" keyword is in the format expected in each case.
One idea I've had is to store the keywords in different containers depending on whether they are optional or not (or maybe use object(s) of type std::map<std::string,bool>), and then remove them from the container(s) if correctly processed, but I am not sure exactly how I should go about it..
I guess there is really a thousand different ways one could answer these questions, but I would be grateful if someone more experienced could share some ideas on how to proceed. Is there e.g. a "standard" way of doing such things? Of course, a lot of details will also depend on the concrete application, but I think the general format indicated here can be used in a lot of different applications without a lot of tinkering if programmed in a good way!
UPDATE
Ok, so let my try to be more concrete. My current application is supposed to be a reservoir simulator, so as part of the input I need information about the grid/mesh, about rock and fluid properties, about wells/boundary conditions throughout the simulation and so on. At the moment I've been thinking about using (almost) the same set-up as the commercial Eclipse simulator when it comes to input, for details see
http://petrofaq.org/wiki/Eclipse_Input_Data.
However, I will probably change things a bit, so nothing is set in stone. Also, I am interested in making a more general "KeywordReader" class that with slight modifications can be adapted for use in other applications as well, at least it can be done in a reasonable amount of time.
As an example, I can post the current code that does the initial scan of the text file and locates the positions of the main keywords. As I said, I don't really like my solution very much, but it seems to work for what it needs to do.
At the top of the .cpp file I have the following namespace:
//Keywords used for reading input:
namespace KEYWORDS{
/*
* Main keywords and corresponding boolean values to signify whether or not they are required as input.
*/
enum MKEY{RUNSPEC = 0, GRID = 1, EDIT = 2, PROPS = 3, REGIONS = 4, SOLUTION = 5, SUMMARY =6, SCHEDULE = 7};
std::string mainKeywords[] = {std::string("RUNSPEC"), std::string("GRID"), std::string("EDIT"), std::string("PROPS"),
std::string("REGIONS"), std::string("SOLUTION"), std::string("SUMMARY"), std::string("SCHEDULE")};
bool required[] = {true,true,false,true,false,true,false,true};
const int n_key = 8;
}//end KEYWORDS namespace
Then further down I have the following function. I am not sure how understandable it is though..
bool InputReader::scanForMainKeywords(){
logfile << "Opening file.." << std::endl;
std::ifstream infile(filename);
//Test if file was opened. If not, write error message:
if(!infile.is_open()){
logfile << "ERROR: Could not open file! Unable to proceed!" << std::endl;
std::cout << "ERROR: Could not open file! Unable to proceed!" << std::endl;
return false;
}
else{
logfile << "Scanning for main keywords..." << std::endl;
int nkey = KEYWORDS::n_key;
//Initially no keywords have been found:
startIndex = std::vector<int>(nkey, -1);
stopIndex = std::vector<int>(nkey, -1);
//Variable used to control that the keywords are written in the correct order:
int foundIndex = -1;
//STATISTICS:
int lineCount = 0;//number of non-comment lines in text file
int commentCount = 0;//number of commented lines in text file
int emptyCount = 0;//number of empty lines in text file
//Create lines vector:
lines = std::vector<std::string>();
//Remove comments and empty lines from text file and store the result in the variable file_lines:
std::string str;
while(std::getline(infile,str)){
if(str.size()>=1 && str.at(0)=='*'){
commentCount++;
}
else if(str.size()>=2 && str.at(0)=='-' && str.at(1)=='-'){
commentCount++;
}
else if(str.size()==0){
emptyCount++;
}
else{
//Found a non-empty, non-comment line.
lines.push_back(str);//store in std::vector
//Start by checking if the first word of the line is one of the main keywords. If so, store the location of the keyword:
std::string fw = IO::getFirstWord(str);
for(int i=0;i<nkey;i++){
if(fw.compare(KEYWORDS::mainKeywords[i])==0){
if(i > foundIndex){
//Found a valid keyword!
foundIndex = i;
startIndex[i] = lineCount;//store where the keyword was found!
//logfile << "Keyword " << fw << " found at line " << lineCount << " in lines array!" << std::endl;
//std::cout << "Keyword " << fw << " found at line " << lineCount << " in lines array!" << std::endl;
break;//fw cannot equal several different keywords at the same time!
}
else{
//we have found a keyword, but in the wrong order... Terminate program:
std::cout << "ERROR: Keywords have been entered in the wrong order or been repeated! Cannot continue initialisation!" << std::endl;
logfile << "ERROR: Keywords have been entered in the wrong order or been repeated! Cannot continue initialisation!" << std::endl;
return false;
}
}
}//end for loop
lineCount++;
}//end else (found non-comment, non-empty line)
}//end while (reading ifstream)
logfile << "\n";
logfile << "FILE STATISTICS:" << std::endl;
logfile << "Number of commented lines: " << commentCount << std::endl;
logfile << "Number of non-commented lines: " << lineCount << std::endl;
logfile << "Number of empty lines: " << emptyCount << std::endl;
logfile << "\n";
/*
Print lines vector to screen:
for(int i=0;i<lines.size();i++){
std:: cout << "Line nr. " << i << " : " << lines[i] << std::endl;
}*/
/*
* So far, no keywords have been entered in the wrong order, but have all the necessary ones been found?
* Otherwise return false.
*/
for(int i=0;i<nkey;i++){
if(KEYWORDS::required[i] && startIndex[i] == -1){
logfile << "ERROR: Incorrect input of required keywords! At least " << KEYWORDS::mainKeywords[i] << " is missing!" << std::endl;;
logfile << "Cannot proceed with initialisation!" << std::endl;
std::cout << "ERROR: Incorrect input of required keywords! At least " << KEYWORDS::mainKeywords[i] << " is missing!" << std::endl;
std::cout << "Cannot proceed with initialisation!" << std::endl;
return false;
}
}
//If everything is in order, we also initialise the stopIndex array correctly:
int counter = 0;
//Find first existing keyword:
while(counter < nkey && startIndex[counter] == -1){
//Keyword doesn't exist. Leave stopindex at -1!
counter++;
}
//Store stop index of each keyword:
while(counter<nkey){
int offset = 1;
//Find next existing keyword:
while(counter+offset < nkey && startIndex[counter+offset] == -1){
offset++;
}
if(counter+offset < nkey){
stopIndex[counter] = startIndex[counter+offset]-1;
}
else{
//reached the end of array!
stopIndex[counter] = lines.size()-1;
}
counter += offset;
}//end while
/*
//Print out start/stop-index arrays to screen:
for(int i=0;i<nkey;i++){
std::cout << "Start index of " << KEYWORDS::mainKeywords[i] << " is : " << startIndex[i] << std::endl;
std::cout << "Stop index of " << KEYWORDS::mainKeywords[i] << " is : " << stopIndex[i] << std::endl;
}
*/
return true;
}//end else (file opened properly)
}//end scanForMainKeywords()
You say your purpose is to read initialization data from a text file.
Seems you need to parse (syntax analyze) this file and store the data under the right keys.
If the syntax is fixed and each construction starts with a keyword, you could write a recursive descent (LL1) parser creating a tree (each node is a stl vector of sub-branches) to store your data.
If the syntax is free, you might pick JSON or XML and use an existing parsing library.
I'm working on adding a new feature to an existing program. It's basically a save/load workspace feature, where a user can save the positions of their windows, and then load said positions whenever they want to by selecting a menu item. In order to implement this, I have created code which extracts the screen coordinates of the window and writes them to a file (below) :
void CMainFrame::SaveWorkspace()
{
RECT ctrlsize;
m_pDialog->GetWindowRect((LPRECT)&ctrlsize); //obtains location for window
ofstream Workspace("saveone", ios::out);
Workspace << ctrlsize.left << "," << ctrlsize.top << "," << ctrlsize.right << "," << ctrlsize.bottom;
}
And this (is supposed to) loads the workspace:
void CMainFrame::LoadWorkspace()
{
//Read in the data from the file
int data[3][4];
int r=0;
int a=0;
int b=0;
ifstream infile;
infile.open("saveone");
for(a = 0; a< 3; a++)
{
for(b = 0;b<4;b++)
{
infile >> data[a][b];
cout << data[a][b];
}
}
infile.close();
//Now, assign the extracted values
RECT ctrlset;
ctrlset.top = data[0][1];
ctrlset.left = data[0][0];
ctrlset.right = data[2][0];
ctrlset.bottom = data[0][3];
// Finally, reassign the window positions
m_pDialog->SetWindowPos(NULL, ctrlset.left, ctrlset.top, (ctrlset.right - ctrlset.left), (ctrlset.bottom - ctrlset.top), SWP_SHOWWINDOW);
}
Problems:
1) the SaveWorkspace function works sporadically; more often than not, it doesn't create a file.
2) the LoadWorkspace function doesn't work. Specifically, only the data[0][0] coordinate gets saved to the array (the first value in the file).
This seems like a fairly easy thing to do, I'm a bit embarrassed that it's giving me so much trouble...
EDIT: I've fixed problem #1. Now I just need to figure out why my array isn't working.
You have at least two problems in the reading.
Your array definition is wrong. It is :
data[2][3];
This has only 6 values.
However, in the loop you are reading 12 values out.
You have the "," values in the file. You are not getting rid of them.
Maybe as an easy solution, you could add a new line after each entry when you write them.
Or you could enter the details of a single rectangle on one line, then read the full line and parse for the individual components yourself.