reading rows and columns from a textfile QT GUI C++ - c++

I want to read the size and the values of my matrix from a text file.
an example of a text file
graphe.txt
4 (the size of the matrix)
1 0 1 0
1 1 1 1
0 1 1 1
0 0 0 1
I tried a code but unfortunately it didn't work .I got this errors:
error: 'class MainWindow' has no member named 'display' this->display->setText(val);
error: cannot convert 'QString' to 'int' in assignment
matrice[ligne][i]=val;
void MainWindow::remplir_matrice(int taille_mat,int matrice[][50] )
{
QFile file("/home/yosra/degré/degré/graphe.txt");
if (file.open(QIODevice::ReadOnly))
{
QTextStream in(&file);
int i=1;
int ligne=1;
while ((!in.atEnd())&&(ligne<=taille_mat))
{
ligne++;
QString line = in.readLine();
QStringList list = line.split(" ");
QString val = list.at(i);
this->display->setText(val);
val.toInt();
matrice[ligne][i]=val;
i++;
}
file.close();
}
}
void MainWindow::afficher(int matrice[][50],int taille_mat)
{
qDebug()<<" les elements de matrice";
for(int i=0;i<taille_mat;i++)
{
for(int j=0;j<taille_mat;j++)
qDebug()<<"M "<<matrice[i][j]<<endl;
}
}
void MainWindow::parourir_fichier(int matrice[50][50],int taille_mat)
{
QFile file("/home/y/degré/classement/graphe.txt");
if (file.open(QIODevice::ReadOnly))
{
QTextStream in(&file);
QStringList list;
QString line = in.readLine();
QString val = list.at(0);
this->display->setText(val);
val.toInt();
taille_mat=val;
qDebug() << "taille_mat=" << taille_mat<<endl;
file.close();
}
remplir_matrice(taille_mat,matrice);
afficher(matrice,taille_mat);
}
this is my MainWindow's header
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void remplir_matrice(int taille_mat,int matrice[][50] );
void parourir_fichier(int matrice[][50],int taille_mat);
void afficher(int matrice[][50],int taille_mat);
private:
Ui::MainWindow *ui;
int matrice[50][50];
int taille_mat;
};

Could it be that you mean:
ui->display->setText(val);
The MainWindow class does not have a pointer to the display object. Perhaps the display object was created with Qt Creator editor as a TextEdit field?
Update
If you just want to see a value while you are still developing your code, you are probably better off using qDebug() (documentation here). You will need to include to make this work. The output will be shown in the output pane when you run the application from Qt Creator.
#include <QDebug>
// ...further down in your code:
qDebug() << "Output of val:" << val;

The second error message is pretty clear, isn't it? A QString cannot be automatically converted to an int. I don't really know Qt, but a quick Google search reveals the existence of a toInt member function, so the following probably works:
matrice[ligne][i]=val.toInt();
As for the first error message, this->display supposes the existence of a member variable in MainWindow. If display is a member function (it certainly sounds like one), then you need parentheses: this->display(). If there is no member function of that name either, then we cannot help you much with the code that you have posted.

Related

How can I set custom text to a QLabel in C++?

I'm working on a C++/Qt simulator which integrates a parameter's page. At the end of the parameters, a QLabel notifies the user whether the entered data is valid or not.
This text should appear with a custom color, so I implemented this:
ParametersDialog.h
#include <iostream>
#include <QtWidgets>
using namespace std;
class ParametersDialog: public QDialog {
Q_OBJECT
public:
ParametersDialog(QWidget *parent = nullptr);
~ParametersDialog();
...
private:
QLabel *notificationLabel = new QLabel;
...
void notify(string message, string color);
};
ParametersDialog.cpp
#include "<<src_path>>/ParametersDialog.h"
ParametersDialog::ParametersDialog(QWidget *parent): QDialog(parent) {
...
notify("TEST TEST 1 2 1 2", "green");
}
...
void ParametersDialog::notify(string message, string color = "red") {
notificationLabel->setText("<font color=" + color + ">" + message + "</font>");
}
I don't understand why it gives me this error:
D:\dev\_MyCode\SM_Streamer\<<src_path>>\ParametersDialog.cpp:65:79: error: no matching function for call to 'QLabel::setText(std::__cxx11::basic_string<char>)'
notificationLabel->setText("<font color=" + color + ">" + message + "</font>");
^
I understand that my string concatenation has created a basic_string<char> element that cannot be set as a QLabel text.
What could be the simplest implementation of my notify method?
the problem is that std::string and QString are not straight forward concatenable...
a trick can be done:
QString mx = "<font color=%1>%2</font>";
notificationLabel->setText(mx.arg(color.c_str(), message.c_str()));

How to display graphics objects behind or foreground of text inside QTextEdit in Qt?

I would like to display a rectangle behind a word I selected like Qt Creator does here:
I am experimenting with the example of QSyntaxHighlighter. I am able to change styles based on keyword patterns. I would like to have graphics or widgets for custom autocompletion lists.
For autocompletion follow the Custom Completer Example or the Completer Example.
The code below follows the first one, which I blatantly, unashamedly copied and integrated into the BackgroundHighlighter class and main.cpp.
This answer will contain five files within a project along with a Qt Resource File.
highlighter.h (Highlighter Class for Syntax)
highlighter.cpp
backgroundHighlighter.h (BackgroundHighlighter Class)
backgroundHighlighter.cpp
main.cpp
res.qrc (optional, not needed, you can hardcode your text)
res (directory) (optional)
|- symbols.txt (optional, you can set your own default text)
|- wordlist.txt (optional, copied from example but you could use your own line-delimited word list and set this in main.cpp with a QStringListModel)
Note that the implementation of the Highlighter class for (1) and (2) can be found in the Qt Syntax Highlighter Example. I will leave its implementation as an exercise for the reader.
In calling the BackgroundHighlighter class, one can pass it a file name to load text from a file. (This wasn't in the OP's specification, but was convenient to implement due to the large amount of text I wanted to test.)
Also note that I integrated the Custom Completer Example into the class.
Here's backgroundHighlighter.h (3) (~45 lines, ~60 lines with completer):
#ifndef BACKGROUNDHIGHLIGHTER_H
#define BACKGROUNDHIGHLIGHTER_H
#include <QtWidgets>
#include <QtGui>
// this is the file to your highlighter
#include "myhighlighter.h"
class BackgroundHighlighter : public QTextEdit
{
Q_OBJECT
public:
BackgroundHighlighter(const QString &fileName = QString(), QWidget *parent = nullptr);
void loadFile(const QString &fileName);
void setCompleter(QCompleter *completer);
QCompleter *completer() const;
protected:
void keyPressEvent(QKeyEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
public slots:
void onCursorPositionChanged();
private slots:
void insertCompletion(const QString &completion);
private:
// this is your syntax highlighter
Highlighter *syntaxHighlighter;
// stores the symbol being highlighted
QString highlightSymbol;
// stores the position (front of selection) where the cursor was originally placed
int mainHighlightPosition;
// stores character formats to be used
QTextCharFormat mainFmt; // refers to format block directly under the cursor
QTextCharFormat subsidiaryFmt; // refers to the formatting blocks on matching words
QTextCharFormat defaultFmt; // refers to the default format of the **entire** document which will be used in resetting the format
void setWordFormat(const int &position, const QTextCharFormat &format);
void runHighlight();
void clearHighlights();
void highlightMatchingSymbols(const QString &symbol);
// completer, copied from example
QString textUnderCursor() const;
QCompleter *c;
};
#endif // BACKGROUNDHIGHLIGHTER_H
And here's backgroundHighlighter.cpp (4) (~160 lines, ~250 lines with completer):
#include "backgroundhighlighter.h"
#include <QDebug>
// constructor
BackgroundHighlighter::BackgroundHighlighter(const QString &fileName, QWidget *parent) :
QTextEdit(parent)
{
// I like Monaco
setFont(QFont("Monaco"));
setMinimumSize(QSize(500, 200));
// load initial text from a file OR from a hardcoded default
if (!fileName.isEmpty())
loadFile(fileName);
else
{
QString defaultText = "This is a default text implemented by "
"a stackoverflow user. Please upvote the answer "
"at https://stackoverflow.com/a/53351512/10239789.";
setPlainText(defaultText);
}
// set the highlighter here
QTextDocument *doc = document();
syntaxHighlighter = new Highlighter(doc);
// TODO change brush/colours to match theme
mainFmt.setBackground(Qt::yellow);
subsidiaryFmt.setBackground(Qt::lightGray);
defaultFmt.setBackground(Qt::white);
// connect the signal to our handler
connect(this, &QTextEdit::cursorPositionChanged, this, &BackgroundHighlighter::onCursorPositionChanged);
}
// convenience function for reading a file
void BackgroundHighlighter::loadFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return;
// the file could be in Plain Text OR Html
setText(file.readAll());
}
void BackgroundHighlighter::setCompleter(QCompleter *completer)
{
if (c)
QObject::disconnect(c, 0, this, 0);
c = completer;
if (!c)
return;
c->setWidget(this);
c->setCompletionMode(QCompleter::PopupCompletion);
c->setCaseSensitivity(Qt::CaseInsensitive);
QObject::connect(c, SIGNAL(activated(QString)),
this, SLOT(insertCompletion(QString)));
}
QCompleter *BackgroundHighlighter::completer() const
{
return c;
}
void BackgroundHighlighter::keyPressEvent(QKeyEvent *e)
{
if (c && c->popup()->isVisible()) {
// The following keys are forwarded by the completer to the widget
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
e->ignore();
return; // let the completer do default behavior
default:
break;
}
}
bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E
if (!c || !isShortcut) // do not process the shortcut when we have a completer
QTextEdit::keyPressEvent(e);
const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
if (!c || (ctrlOrShift && e->text().isEmpty()))
return;
static QString eow("~!##$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3
|| eow.contains(e->text().right(1)))) {
c->popup()->hide();
return;
}
if (completionPrefix != c->completionPrefix()) {
c->setCompletionPrefix(completionPrefix);
c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
}
QRect cr = cursorRect();
cr.setWidth(c->popup()->sizeHintForColumn(0)
+ c->popup()->verticalScrollBar()->sizeHint().width());
c->complete(cr); // pop it up!
}
void BackgroundHighlighter::focusInEvent(QFocusEvent *e)
{
if (c)
c->setWidget(this);
QTextEdit::focusInEvent(e);
}
// convenience function for setting a `charFmt` at a `position`
void BackgroundHighlighter::setWordFormat(const int &position, const QTextCharFormat &charFmt)
{
QTextCursor cursor = textCursor();
cursor.setPosition(position);
cursor.select(QTextCursor::WordUnderCursor);
cursor.setCharFormat(charFmt);
}
// this will handle the `QTextEdit::cursorPositionChanged()` signal
void BackgroundHighlighter::onCursorPositionChanged()
{
// if cursor landed on different format, the `currentCharFormat` will be changed
// we need to change it back to white
setCurrentCharFormat(defaultFmt);
// this is the function you're looking for
runHighlight();
}
void BackgroundHighlighter::insertCompletion(const QString &completion)
{
if (c->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - c->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
}
QString BackgroundHighlighter::textUnderCursor() const
{
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
return tc.selectedText();
}
/**
* BRIEF
* Check if new highlighting is needed
* Clear previous highlights
* Check if the word under the cursor is a symbol (i.e. matches ^[A-Za-z0-9_]+$)
* Highlight all relevant symbols
*/
void BackgroundHighlighter::runHighlight()
{
// retrieve cursor
QTextCursor cursor = textCursor();
// retrieve word under cursor
cursor.select(QTextCursor::WordUnderCursor);
QString wordUnder = cursor.selectedText();
qDebug() << "Word Under Cursor:" << wordUnder;
// get front of cursor, used later for storing in `highlightPositions` or `mainHighlightPosition`
int cursorFront = cursor.selectionStart();
// if the word under cursor is the same, then save time
// by skipping the process
if (wordUnder == highlightSymbol)
{
// switch formats
setWordFormat(mainHighlightPosition, subsidiaryFmt); // change previous main to subsidiary
setWordFormat(cursorFront, mainFmt); // change position under cursor to main
// update main position
mainHighlightPosition = cursorFront;
// jump the gun
return;
}
// clear previous highlights
if (mainHighlightPosition != -1)
clearHighlights();
// check if selected word is a symbol
if (!wordUnder.contains(QRegularExpression("^[A-Za-z0-9_]+$")))
{
qDebug() << wordUnder << "is not a symbol!";
return;
}
// set the highlight symbol
highlightSymbol = wordUnder;
// store the cursor position to check later
mainHighlightPosition = cursorFront;
// highlight all relevant symbols
highlightMatchingSymbols(wordUnder);
qDebug() << "Highlight done\n\n";
}
// clear previously highlights
void BackgroundHighlighter::clearHighlights()
{
QTextCursor cursor = textCursor();
// wipe the ENTIRE document with the default background, this should be REALLY fast
// WARNING: this may have unintended consequences if you have other backgrounds you want to keep
cursor.select(QTextCursor::Document);
cursor.setCharFormat(defaultFmt);
// reset variables
mainHighlightPosition = -1;
highlightSymbol.clear();
}
// highlight all matching symbols
void BackgroundHighlighter::highlightMatchingSymbols(const QString &symbol)
{
// highlight background of congruent symbols
QString docText = toPlainText();
// use a regex with \\b to look for standalone symbols
QRegularExpression regexp("\\b" + symbol + "\\b");
// loop through all matches in the text
int matchPosition = docText.indexOf(regexp);
while (matchPosition != -1)
{
// if the position
setWordFormat(matchPosition, matchPosition == mainHighlightPosition ? mainFmt : subsidiaryFmt);
// find next match
matchPosition = docText.indexOf(regexp, matchPosition + 1);
}
}
Finally, here's main.cpp (5) (~10 lines, ~45 lines with completer)
#include <QApplication>
#include <backgroundhighlighter.h>
QAbstractItemModel *modelFromFile(const QString& fileName, QCompleter *completer)
{
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return new QStringListModel(completer);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
QStringList words;
while (!file.atEnd()) {
QByteArray line = file.readLine();
if (!line.isEmpty())
words << line.trimmed();
}
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return new QStringListModel(words, completer);
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
BackgroundHighlighter bh(":/res/symbols.txt");
QCompleter *completer = new QCompleter();
completer->setModel(modelFromFile(":/res/wordlist.txt", completer));
// use this and comment the above if you don't have or don't want to use wordlist.txt
// QStringListModel *model = new QStringListModel(QStringList() << "aaaaaaa" << "aaaaab" << "aaaabb" << "aaacccc",
completer);
// completer->setModel(model);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setWrapAround(false);
bh.setCompleter(completer);
bh.show();
return a.exec();
}
In res.qrc add a / prefix and add files (res/symbols.txt, res/wordlist.txt) from the res/ subdirectory.
I have tested with a symbols.txt file resembling
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
symbol1 symbol2 symbol3 symbol4 symbol5
// ... ditto 500 lines
It takes about 1 second, which probably isn't ideal (100ms is probably more ideal).
However, you might want to watch over for the line count as it grows. With the same text file at 1000 lines, the program will start to take approx. 3 seconds for highlighting.
Note that... I haven't optimised it entirely. There could possibly be a better implementation which formats only when the symbol scrolls into the user's view. This is just a suggestion. How to implement it I don't know.
Notes
For reference, I've attached symbols.txt and wordlist.txt on github.
If you want to change the background colour of formatting, go to lines 27 to 29 of backgroundhighlighter.cpp. There, you can see that I centralised the formatting.
BackgroundHighlighter::clearHighlights() might clear away any background highlights originally added as it sets the ENTIRE document's character background to the default format. This may be an unintended consequence of the result.

using QProcess->setReadChannelMode(QProcess::MergedChannels) and using QProcess->readall()

It's my first time to ask question on stackoverflow. And I'm a chinese girl, if my description about this problem has so much grammar error that you can't understand it easyly, I'm so sorry.
Below is my question:
headerfile:
class AdbDriver : public QObject
{
Q_OBJECT
private:
QString PnPutilPath_;
QProcess *process_;
public:
explicit AdbDriver(QObject *parent = 0);
~AdbDriver();
void installDriver();
};
AdbDriver::AdbDriver(QObject *parent):QObject(parent){
PnPutilPath_ = qgetenv("WINDIR") + "\\sysnative\\pnputil.exe";
process_ = new QProcess();
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
}
sourcefile:
AdbDriver::~AdbDriver(){
delete process_;
}
void AdbDriver::installDriver(){
QFile file(PnPutilPath_);
if(file.exists()){
qDebug()<<"pnputil.exe exist";
QString generaladbDriver = "E:/driver_androidusb/generaladb.inf";
qDebug()<<"the programming include driver:"<<generaladbDriver;
QFile file(generaladbDriver);
if(file.exists()){
qDebug()<<"yes, the driver is right in bihu package";
}
else{
qDebug()<<"loss driver in bihu package";
}
QStringList arguments;
arguments<<"-i"<<"-a"<<generaladbDriver;
process_->start(PnPutilPath_, arguments);
while(!process_->waitForStarted()){
qDebug()<<"wait";
}
qDebug()<<"while out";
process_->waitForReadyRead();
qDebug()<<"start";
// qDebug()<<process_->readAll();
process_->close();
}
else{
qDebug()<<"sorry, your computer has no tool pnputil.exe.";
}
}
when i Commented out code
qDebug()<<process_->readAll();
and use
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
it works properly.But if i use
qDebug()<<process_->readAll();
instead of
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
it will be wrong. what's the reason?
According to Qt document, both setReadChannelMode and setStandardOutputFile have to be called before QProcess::start to take effect, so replacing
qDebug() << process_->readAll();
with
process_->setReadChannelMode(QProcess::MergedChannels);
process_->setStandardOutputFile("E:/log.txt");
is same as just commenting out qDebug() << process_->readAll();.
So I guess the child process does not output anything, so process_->readAll() will block, and the program halts.
(你看一下E:/log.txt有没有内容,估计是process_->readAll()阻塞了)

Set renderer of QGraphicsSvgItem on preprocessed svg xml document very slow

I am using QGraphicsSvgItem subclass, that reads some content from a file, places content into a QDomDocument, does some initial processing, then sets the processed DOM onto a renderer.
During program processing, additional changes are required on a copy of pre-processed DOM, so the DOM is stored in class. After changes, the DOM is placed on renderer.
class MyGraphicsSvgItem : public QGraphicsSvgItem
{
public:
MyGraphicsSvgItem (QGraphicsItem *parent = 0):
QGraphicsSvgItem(parent),
_svgXML() {}
~MyGraphicsSvgItem () { delete renderer(); }
void CheckAndChangeSomeThings() {}
void LoadStuff (QString fileName)
{
QFile file(fileName);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&file);
QString svgContent = in.readAll();
file.close();
_svgXML.setContent(svgContent);
CheckAndChangeSomeThings(); // this modifies _svgXML
QByteArray _data = _svgXML.toByteArray();
setSharedRenderer(new QSvgRenderer(_data)); // very slow
}
void ChangeThingslater();
void ChangeSomeThingslater()
{
ChangeThingslater(); // this modifies _svgXML
renderer()->load(_svgXML.toByteArray()); // very slow - no file involved
}
protected:
QDomDocument _svgXML;
};
There seems to be a significant slow processing during the lines that assign the DOM to the renderer.
QByteArray _data = _svgXML.toByteArray();
setSharedRenderer(new QSvgRenderer(_data));
If I skip the DOM processing - if I set the renderer to the file - the code speeds up considerably:
Leaving all the code in, but replacing
setSharedRenderer(new QSvgRenderer(_data)); // VERY SLOW
with
setSharedRenderer(new QSvgRenderer(fileName)); // FAST
So it seems the bottleneck is loading the svg renderer from QByteArray.
I looked for alternatives... there is no mention of performance in documentation
QSvgRenderer::QSvgRenderer(const QString & filename, QObject * parent
= 0)
Constructs a new renderer with the given parent and loads the contents of the SVG file with the specified filename.
QSvgRenderer::QSvgRenderer(const QByteArray & contents, QObject *
parent = 0)
Constructs a new renderer with the given parent and loads
the SVG data from the byte array specified by contents.
QSvgRenderer::QSvgRenderer(QXmlStreamReader * contents, QObject *
parent = 0)
Constructs a new renderer with the given parent and loads
the SVG data using the stream reader specified by contents.
Looking in QXmlStreamReader Class, I find that its constructors are similar ! In addition, it says
In some cases it might also be a faster and more convenient alternative for use in applications that would otherwise use a DOM tree
I seem to be going in circles, and even though having already a well formed xml in the DOM, it seems I cannot take advantage of it !
What are my alternatives, to either loading the renderer from the pre-processed DOM, or to a different way of pre-processing the xml - using something other than DOM that the renderer can read fast ?
qt 4.8. c++
You can do all the DOM processing in a worker method that's executed on the thread queue using QtConcurrent::run.
You can use the QSvgRenderer directly in your item. Initialize it in the worker method, and load from QByteArray and not a file. You can then pass the renderer to the GUI thread and use it to render the graphics item by setting it on the QGraphicsSvgItem.
Caveats:
Since you create the renderer in a worker thread, you must move it to a null thread after you've done using it in the worker thread. Conversely, you must move it to the GUI thread once it has been received by the GUI thread.
Recall that moveToThread can only be called from the object's current thread, or any thread if thread() == 0.
In Qt 4.8, there's a bug in QGraphicsSvgItem::setSharedRenderer: it doesn't properly connect the renderer's repaintNeeded signal to its update method. The work around this to connect the signal manually to your own update slot.
This will prevent the GUI from getting blocked by the long processing.
Reentering the event loop, as you do, from within the item is a source of bugs and just a very bad idea from the design standpoint. Use the non-blocking API of the file dialog instead.
Below is an example that demonstrates this technique. It also displays a small spinner when the item is being loaded/processed. There's a simulated delay for this purpose.
#include <QGraphicsView>
#include <QGraphicsSvgItem>
#include <QGraphicsSceneMouseEvent>
#include <QFileDialog>
#include <QSvgRenderer>
#include <QDomDocument>
#include <QtConcurrentRun>
#include <QFutureWatcher>
#include <QThread>
#include <QApplication>
struct Thread : public QThread { using QThread::sleep; }; // Needed for Qt 4 only
class RendererGenerator {
QString m_fileName;
void process(QDomDocument &) {
Thread::sleep(3); /* let's pretend we process the DOM for a long time here */
}
QByteArray generate(const QByteArray & data) {
QDomDocument dom;
dom.setContent(data);
process(dom);
return dom.toByteArray();
}
public:
typedef QSvgRenderer * result_type;
RendererGenerator(const QString & fileName) : m_fileName(fileName) {}
QSvgRenderer * operator()() {
QFile file(m_fileName);
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
QScopedPointer<QSvgRenderer> renderer(new QSvgRenderer);
renderer->load(generate(data));
renderer->moveToThread(0);
return renderer.take();
}
return 0;
}
};
class UserSvgItem : public QGraphicsSvgItem {
Q_OBJECT
QSvgRenderer m_spinRenderer, * m_lastRenderer;
QScopedPointer<QSvgRenderer> m_renderer;
QFuture<QSvgRenderer*> m_future;
QFutureWatcher<QSvgRenderer*> m_watcher;
QGraphicsView * aView() const {
QList<QGraphicsView*> views = scene()->views();
return views.isEmpty() ? 0 : views.first();
}
Q_SLOT void update() { QGraphicsSvgItem::update(); }
void mousePressEvent(QGraphicsSceneMouseEvent * event) {
if (event->button() == Qt::LeftButton) askForFile();
}
void setRenderer(QSvgRenderer * renderer) {
if (m_lastRenderer) disconnect(m_lastRenderer, SIGNAL(repaintNeeded()), this, SLOT(update()));
setSharedRenderer(renderer);
m_lastRenderer = renderer;
connect(renderer, SIGNAL(repaintNeeded()), SLOT(update()));
if (aView()) aView()->centerOn(this);
}
void askForFile() {
QFileDialog * dialog = new QFileDialog(aView());
connect(dialog, SIGNAL(fileSelected(QString)), SLOT(loadFile(QString)));
dialog->setAcceptMode(QFileDialog::AcceptOpen);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
}
Q_SLOT void loadFile(const QString & file) {
if (m_future.isRunning()) return;
setRenderer(&m_spinRenderer);
m_future = QtConcurrent::run(RendererGenerator(file));
m_watcher.setFuture(m_future);
}
Q_SLOT void rendererReady() {
m_renderer.reset(m_future.result());
m_renderer->moveToThread(thread());
setRenderer(m_renderer.data());
}
public:
UserSvgItem(const QString & fileName = QString(), QGraphicsItem *parent = 0) :
QGraphicsSvgItem(fileName, parent), m_lastRenderer(0) {
connect(&m_watcher, SIGNAL(finished()), SLOT(rendererReady()));
setFlags(QGraphicsItem::ItemClipsToShape);
setCacheMode(QGraphicsItem::NoCache);
}
void setWaitAnimation(const QByteArray & data) { m_spinRenderer.load(data); }
};
namespace {
const char svgCircle[] =
"<svg height=\"100\" width=\"100\"><circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" /></svg>";
const char svgRectangle[] =
"<svg width=\"400\" height=\"110\"><rect width=\"300\" height=\"100\" style=\"fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)\"></svg>";
const char svgThrobber[] =
"<svg width=\"16\" height=\"16\" viewBox=\"0 0 300 300\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"><path d=\"M 150,0 a 150,150 0 0,1 106.066,256.066 l -35.355,-35.355 a -100,-100 0 0,0 -70.711,-170.711 z\" fill=\"#3d7fe6\"><animateTransform attributeName=\"transform\" attributeType=\"XML\" type=\"rotate\" from=\"0 150 150\" to=\"360 150 150\" begin=\"0s\" dur=\"1s\" fill=\"freeze\" repeatCount=\"indefinite\" /></path></svg>";
void write(const char * str, const QString & fileName) {
QFile out(fileName);
if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) out.write(str);
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
write(svgRectangle, "rectangle.svg"); // Put svg resources into the working directory
write(svgCircle, "circle.svg");
QGraphicsScene scene;
UserSvgItem item("circle.svg");
QGraphicsView view(&scene);
scene.addItem(&item);
item.setWaitAnimation(QByteArray::fromRawData(svgThrobber, sizeof(svgThrobber)-1));
view.show();
return app.exec();
}
#include "main.moc"

An issue when creating a pop-up menu w.r.t to a parametr via loop

I am trying to create pop-up menu depending on a variable as follows:
QMenu menu(widget);
for(int i = 1; i <= kmean.getK(); i++)
{
stringstream ss;
ss << i;
string str = ss.str();
string i_str = "Merge with " + str;
QString i_Qstr = QString::fromStdString(i_str);
menu.addAction(i_Qstr, this, SLOT(mergeWith1()));
}
menu.exec(position);
where:
kmean.get(K) returns an int value,
mergeWith1() is some `SLOT()` which works fine
Issue:
The loop creates an action on menu only for i=1 case, and ignores other values of i.
Additional information
When doing the same loop with casual int values (without convert) everything works fine. e.g. if I do in loop only menu.addAction(i, this, SLOT(...))) and my K=4, a menu will be created with four actions in it, named 1, 2, 3, 4 correspondingly.
What can be the problem caused by
I think the issue is in convert part, when I convert i to string using stringstream and after to QString. May be the value is somehow lost. I am not sure.
QESTION:
How to make the loop accept the convert part?
What do I do wrong in convert part?
In Qt code, you shouldn't be using std::stringstream or std::string. It's pointless.
You have a crashing bug by having the menu on the stack and giving it a parent. It'll be double-destructed.
Don't use the synchronous blocking methods like exec(). Show the menu asynchronously using popup().
In order to react to the actions, connect a slot to the menu's triggered(QAction*) signal. That way you can deal with arbitrary number of automatically generated actions.
You can use the Qt property system to mark actions with custom attributes. QAction is a QObject after all, with all the benefits. For example, you can store your index in an "index" property. It's a dynamic property, created on the fly.
Here's a complete example of how to do it.
main.cpp
#include <QApplication>
#include <QAction>
#include <QMenu>
#include <QDebug>
#include <QPushButton>
struct KMean {
int getK() const { return 3; }
};
class Widget : public QPushButton
{
Q_OBJECT
KMean kmean;
Q_SLOT void triggered(QAction* an) {
const QVariant index(an->property("index"));
if (!index.isValid()) return;
const int i = index.toInt();
setText(QString("Clicked %1").arg(i));
}
Q_SLOT void on_clicked() {
QMenu * menu = new QMenu();
int last = kmean.getK();
for(int i = 1; i <= last; i++)
{
QAction * action = new QAction(QString("Merge with %1").arg(i), menu);
action->setProperty("index", i);
menu->addAction(action);
}
connect(menu, SIGNAL(triggered(QAction*)), SLOT(triggered(QAction*)));
menu->popup(mapToGlobal(rect().bottomRight()));
}
public:
Widget(QWidget *parent = 0) : QPushButton("Show Menu ...", parent) {
connect(this, SIGNAL(clicked()), SLOT(on_clicked()));
}
};
int main (int argc, char **argv)
{
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}
#include "main.moc"