I have a small minimal example of a user interface for visualizing images (both .tif, .tiff, .jpg etc) composed of:
1) N.1 QLabel (used to show the image)
2) N.1 Pushbutton (used to upload a folder)
3) N.1 QLineEdit (used to visualize the path)
4) N.2 QToolbuttons (used as left and right to look through images)
I am trying to look through images using the left and the right QToolbuttons but something is not correct and I am not able to see any image. I was looking at this source as an example in order to develop my own implementation and use it for other projects I am developing.
mainwindow.h
private slots:
void on_imageCroppedABtn_clicked();
void on_leftArrowCroppedA_clicked();
void on_rightArrowCroppedA_clicked();
private:
Ui::MainWindow *ui;
QString camADir;
QString fileCamA;
int croppedIndexA;
QStringList croppedFilenamesA;
QDir croppedA;
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
croppedIndexA = 0;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_imageCroppedABtn_clicked()
{
QString cdir = QFileDialog::getExistingDirectory(this, tr("Choose an image directory to load"),
fileCamA, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if((cdir.isEmpty()) || (cdir.isNull()))
return;
croppedA.setPath(cdir);
croppedFilenamesA = croppedA.entryList(QStringList() << "*.tiff" << "*.TIFF" << "*.tif" << "*.TIF", QDir::Files);
croppedIndexA = 0;
ui->lineEditfolderA->setText(croppedA.path());
}
void MainWindow::on_leftArrowCroppedA_clicked()
{
croppedIndexA--;
if(croppedIndexA < 0)
croppedIndexA = croppedFilenamesA.size()-1;
if(croppedFilenamesA.size() > 0)
{
ui->labelCroppedA->setScaledContents(true);
ui->labelCroppedA->setPixmap(QPixmap::fromImage(QImage(croppedFilenamesA[croppedIndexA])));
ui->labelCroppedA->show();
}
}
void MainWindow::on_rightArrowCroppedA_clicked()
{
croppedIndexA++;
if(croppedIndexA >= croppedFilenamesA.size())
croppedIndexA = 0;
if(croppedFilenamesA.size() > 0)
{
ui->labelCroppedA->setScaledContents(true);
ui->labelCroppedA->setPixmap(QPixmap::fromImage(QImage(croppedFilenamesA[croppedIndexA])));
ui->labelCroppedA->show();
}
}
I have been trying to change the implementation in many different ways but I always am not able to see images. Can anyone shed a little bit of light on this issue?
QImage ctor requires the full path to an image which is read. You can store a result of calling getExistingDirectory in data member cdir. When you call entryList all files in the passed directory are listed. While creating QImage you need to concatenate dir name with file name from this dir. So you can call:
ui->labelCroppedA->setPixmap(
QPixmap::fromImage(QImage(cdir + "/" + croppedFilenamesA[croppedIndexA])));
^ add directory separator
Related
I am writing a program in C++ using QT. Task: The user can specify a directory that consists of other folders, and files are located inside these folders. It is necessary to archive the folders with the file and place the archives in another directory. I am working with an additional thread for processing (I created a Worker class, etc.). I did everything according to the example from the QT documentation. The problem is the following: Everything works on my computer and on computers with Windows 10. But on Windows 7 computers, after processing one large folder, the program crashes. Error: The program does not work, code 0xc0000005. What to do? Is this a problem in the code or do I need to install something additionally? Net Framework, Visual C++, etc. updated, but nothing helped. The program code is attached.
P.S. If you don't use an additional thread, then everything works correctly. But an additional thread is needed so that the processing progress can be displayed, that is, how many directories have been processed
#include "renamewidget.h"
#include "ui_renamewidget.h"
#include <private/qzipwriter_p.h>
QString RenameWidget::filePath; //static field
QString RenameWidget::directoryPath; //static field
QString RenameWidget::resultPath; //static field
RenameWidget::RenameWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::RenameWidget)
{
ui->setupUi(this);
filePath = "C:/Users/user/Desktop/1.xlsx";
directoryPath = "C:/Users/user/Desktop/2";
resultPath = "C:/Users/user/Desktop/3";
connect(this, &RenameWidget::doWork, &worker, &Worker::doWork);
connect(&worker, &Worker::workProgress, this, &RenameWidget::setText);
worker.moveToThread(&thread);
thread.start();
}
void RenameWidget::setText(QString message)
{
ui->lableLoad->setText(message); // setProgress
}
void RenameWidget::on_pb_Rename_clicked()
{
emit doWork();
}
void Worker::doWork()
{
emit workProgress("");
QDir dir;
dir.setPath(RenameWidget::getDirectoryPath());
QStringList listDir = dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
for (int i = 0; i<listDir.size();i++)
{
QString article = QString::number(i);
dir.setPath(RenameWidget::getDirectoryPath() + "/" + listDir.at(i));
QStringList tempstr = dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
QString temppath = RenameWidget::getDirectoryPath() + "/" + listDir.at(i) + "/" + tempstr .at(0);
QString tempres = RenameWidget::getResultPath() + "/" + "archives" + "/" + article;
dir.mkpath(tempres);
doArchive(temppath + "/", tempres + "/" + article + ".zip");
emit workProgress("Обработано каталогов: " + QString::number(i+1) + "/" + QString::number(listDir.size()));
}
emit workProgress("Готово");
}
void Worker::doArchive(QString path, QString zippath)
{
QZipWriter zip(zippath);
zip.setCompressionPolicy(QZipWriter::AutoCompress);
QDirIterator it(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString file_path = it.next();
if (it.fileInfo().isFile())
{
QFile file(file_path);
if (!file.open(QIODevice::ReadOnly))
continue;
zip.setCreationPermissions(QFile::permissions(file_path));
QByteArray ba = file.readAll();
zip.addFile(file_path.remove(path), ba);
}
}
zip.close();
}
I am trying to build a command log on a user interface. Meaning, when the user click a button, check a box, upload some images etc, basically every time the user interacts with the user interface the action is recorded inside a QListWidget Command Log shown below. Basically this is how the ui looks as soon as the user run it:
And this is what I am try to achieve everytime the user interacts with the ui:
Below snippets of code from the constructor:
mainwindow.h
private:
QListWidget *mNewTextSQLLog;
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mDockWidget_A = new QDockWidget(QLatin1String("Command Log"));
mDockWidget_A->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
mDockWidget_A->setMinimumHeight(30);
// Adding object to the DockWidget
mNewText = new QListWidget;
mNewText->setStyleSheet("background-color: light grey;");
mNewText->setMinimumHeight(50);
mNewText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mDockWidget_A->setWidget(mNewText);
addDockWidget(Qt::BottomDockWidgetArea, mDockWidget_A);
resizeDocks({mDockWidget_A}, {200}, Qt::Horizontal);
}
And then some command of the ui, for example here is when the user upload images using a QPushButton and images are also shown on a QLabel:
void MainWindow::imageOriginlUploadB()
{
dir_Original_B = QFileDialog::getExistingDirectory(this, tr("Choose an image directory to load"),
filesListRight, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if(dir_Original_B.length() > 0){
QImage image;
QDir dirBObj(dir_Original_B);
QStringList filesListRight = dirBObj.entryList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
ui->labelOrigImageB->setPixmap(QPixmap::fromImage(image.scaled(125,125,Qt::KeepAspectRatio,Qt::SmoothTransformation)));
for ( int i = 0 ; i < filesListRight.size() ; i++ )
{
ui->listWidgetOriginalImgB->addItem(filesListRight.at(i));
}
ui->listWidgetOriginalImgB->update();
ui->labelOrigImageB->show();
}
}
void MainWindow::on_originalmgB_clicked()
{
imageOriginlUploadB();
}
or here is resizing the QGraphicsView using a QPushButton:
void MainWindow::on_fitViewBtn_clicked()
{
ui->graphicsViewLX->fitInView(mLeftScene->sceneRect(), Qt::KeepAspectRatio);
ui->graphicsViewRX->fitInView(mRightScene->sceneRect(), Qt::KeepAspectRatio);
}
And this is the activation of a QCheckBox:
void MainWindow::on_checkBoxScreen_A_toggled(bool checked)
{
if(ui->checkBoxScreen_A->isEnabled()) {
if(checked)
{
ui->checkBoxScreen_A->setText("Active");
ui->saveToFile_A->setEnabled(true);
ui->saveToFile_A->setStyleSheet("QPushButton{ background-color: green }");
}
else {
ui->checkBoxScreen_A->setText("Inactive");
ui->saveToFile_A->setEnabled(false);
ui->saveToFile_A->setStyleSheet("QPushButton{ background-color: grey }");
}
}
}
How to achieve that?
Thank you very much for pointing in the right direction
I think QListWidget isn't quite the right widget to use for a Command Log -- you probably want to use either a QPlainTextEdit or a QTextEdit instead. (The main difference between the two is that QPlainTextEdit is optimized for displaying large amounts of text, at the expense of not supporting some of the fancier text-formatting features provided by QTextEdit)
Once you've created one of those two widgets, adding text to the bottom of log is just a matter of calling appendPlainText() (or append()) on the widget each time you want to add another line of log-text.
Unless you want to allow the user to edit the text in the Command Log, calling setReadOnly(true) on the widget is also a good idea.
(If you also want the log-view to automatically scroll to the bottom so that the newly-added text will be visible, you can also call myCommandLogWidget->verticalScrollBar()->setValue(myCommandLogWidget->verticalScrollBar()->maximum()); after adding the text)
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.
I am trying to make a Widget that contains multiple widgets with each a QCustomplot and some Plot Data (QLabel and QPushbuttons). This widget is open from a mainwindow with parent=0.
I add the subwidgets with a function, but if it is used more than one time it throws an error:
malloc.c:3757: _int_malloc: Assertion `(unsigned long) (size) >=
(unsigned long) (nb)' failed.
So heres the code, that leads to this:
Opening the widget, that should contain the subwidgets:
Zeitdatenfenster = new Plotfenster();
Zeitdatenfenster->getZeitData(ausgewaehlteKanaele, *messdaten, double(Anfangszeiteingabe->value()), double(Endzeiteingabe->value()));
Zeitdatenfenster->show();
Zeitdatenfenster is in the MainWindow class header : Plotfenster* Zeitdatenfenster;
Constructor:
Plotfenster::Plotfenster(QWidget *parent) :
QWidget(parent),
ui(new Ui::Plotfenster)
{
ui->setupUi(this);
counter=0;
}
getZeitData( ...) -function:
void Plotfenster::getZeitData(const std::vector<bool>& Kanalauswahl, const Messdatenverwaltung& messdaten, double Anfangszeit, double Endzeit){
setWindowTitle("Time Data");
int zahler=0;
for(int i = 0; i<Kanalauswahl.size();i++){
if(Kanalauswahl[i]){
Ploteigenschaften* kanalzeit = new Ploteigenschaften();
std::cout << kanalzeit << std::endl;
geplotteteDatenInhalt.push_back(kanalzeit);
geplotteteDatenInhalt[zahler]->setZeitData(messdaten, i,Anfangszeit,Endzeit);
addPlot(geplotteteDatenInhalt[zahler],zahler);
zahler++;
}
}
}
geplotteteDatenInhalt is a std::vector<Ploteigenschaften*>,
addPlot() is the function where the subwidgets are created. Ploteigenschaften is just a class that stores the data that is plottet and functions to change it.(with the two Qvectors added to the qcustomplot)
AddPlot function:
void Plotfenster::addPlot(Ploteigenschaften* plotDaten, int zahler ){
Plot* aktuellerPlot = new Plot(plotDaten, this);
geplottetes.push_back(aktuellerPlot);
ui->plotcontainer->addWidget(geplottetes[zahler],counter);
counter++;
}
Plot is the subwiget that contains the constuctor:
Plot::Plot(Ploteigenschaften* plotDatens, QWidget *parent) :
QWidget(parent),
ui(new Ui::Plot)
{
ui->setupUi(this);
ui->plotGrid->setColumnStretch(1, 3);
ui->plotGrid->setHorizontalSpacing(20);
derPlot = new QCustomPlot(this);
this->plotDaten = plotDatens;
this->Datensetzten();
}
The programm crashes at the QCustomplot constructor.
In Datensetzten the plot is specified:
void Plot::Datensetzten(){
this->setInfoText();
this->setPlot();
this->setSaveButton();
}
void Plot::setPlot(){
derPlot->addGraph();
derPlot->graph(0)->setData(plotDaten->xDaten,plotDaten->yDaten);
derPlot->xAxis->setRange(plotDaten->xUntereGrenze,plotDaten->xObereGrenze);
derPlot->yAxis->setRange(plotDaten->yMinimum-std::abs(plotDaten->yMinimum)*0.01,plotDaten->yMaximum+std::abs(plotDaten->yMaximum)*0.01);
derPlot->xAxis->setLabel(plotDaten->BeschriftungxAchse);
derPlot->yAxis->setLabel(plotDaten->BeschriftungyAchse);
derPlot->xAxis2->setVisible(true);
derPlot->xAxis2->setTickLabels(false);
derPlot->yAxis2->setVisible(true);
derPlot->yAxis2->setTickLabels(false);
ui->plotGrid->addWidget(derPlot,0,1,2,1);
derPlot->show();
}
Sorry for the long post, but I have no idea where the error is located and would be very glad for som ideas.
Thanks!
In my screenshot taking project the QPixmap.save() function returns false meaning fails every time. However when I copy the example project from Qt page http://qt-project.org/doc/qt-5/qtwidgets-desktop-screenshot-example.html, it works. Thus it rules out Windows 7 issue with file permissions.
So I wonder why it fails?
widget.h file:
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void updateTimer();
void startScreenshots();
void stopScreenshots();
void takeScreenshot();
private:
Ui::Widget *ui;
QString initialPath;
QPixmap currentScreenshot;
QSpinBox * delaySpinBox;
QPushButton * startButton;
QPushButton * stopButton;
QHBoxLayout * hboxLayout;
QGroupBox * groupBox;
QTimer * timer;
void setInitialPath();
void addStuff();
};
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setInitialPath();
addStuff();
}
Widget::~Widget()
{
delete ui;
}
void Widget::updateTimer()
{
timer->stop();
int milisecs = delaySpinBox->value() *1000;
timer->start( milisecs );
}
void Widget::startScreenshots()
{
timer->start( delaySpinBox->value() * 1000 );
}
void Widget::stopScreenshots()
{
timer->stop();
}
void Widget::takeScreenshot()
{
//take screenshot
currentScreenshot = QPixmap::grabWindow(QApplication::desktop()->winId());
//save screenshot
QString format = "png";
QDateTime local( QDateTime::currentDateTime() );
QString date = local.toString();
QString fileName = initialPath + date;
if(!currentScreenshot.save(fileName, format.toLatin1().constData()) )
{
qDebug() << "didnt save\n";
QMessageBox::information(this,"failed to save","failed to save");
}
}
void Widget::setInitialPath()
{
initialPath = QFileDialog::getExistingDirectory(this, tr("Open Directory"),
"/home",
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
}
void Widget::addStuff()
{
timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(takeScreenshot()) );
delaySpinBox = new QSpinBox(this);
delaySpinBox->setValue(60);
delaySpinBox->setSuffix(tr(" s"));
connect( delaySpinBox,SIGNAL(valueChanged(int)),this,SLOT(updateTimer()) );
startButton = new QPushButton(this);
startButton->setText("start");
connect(startButton,SIGNAL(clicked()),this,SLOT(startScreenshots()) );
stopButton = new QPushButton(this);
stopButton->setText("stop");
connect(stopButton,SIGNAL(clicked()),this,SLOT(stopScreenshots()) );
hboxLayout = new QHBoxLayout(this);
hboxLayout->addWidget(startButton);
hboxLayout->addWidget(stopButton);
hboxLayout->addWidget(delaySpinBox);
groupBox = new QGroupBox(tr("Options"));
groupBox->setLayout(hboxLayout);
setLayout(hboxLayout);
}
QDateTime local( QDateTime::currentDateTime() )
probably contains symbols which Windows doesn't allow. (there are few symbols). That's why you cannot save it.
Solution: fist of all, try to remove dateTime from filename and see is it work.
If you want use dateTime try format it without forbidden symbols
Forbidden symbols in Windows for example:
< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)
QDateTime always return string which contains colon, but it is forbidden and you can't use it, you should replace it.
It just works under linux. In addition to #Chernobyl's answer, AFAIK QPixmap::save doesn't add the suffix automatically, so you need to change
QString fileName = initialPath + date;
to
QString fileName = initialPath + date.replace(":", "-") + ".png";
(The .replace(":", "-") part is for escaping forbidden ":" symbol in the file name)
When you are constructing the filename and its path as follows:
QString fileName = initialPath + date;
You will have a path similar to
C:/Users/YourName/Pictures
for your initial path.
While your date will be in the format of
Sun Sep 7 11:35:46 2014
So during your concatenation, you would end up with something like
C:/Users/YourName/PicturesSun Sep 7 11:35:46 2014
If you look closely, there are quite a few problems here:
You are missing an "/" at the end of your initial path
Your date String contains characters that Windows does not allow for file names
Once saved, the file will be missing its extension, this will need to be added to the end of your filename String as well.
Fixes required:
You need change the date format to something acceptable for Windows by using
QString date = local.toString("A Valid QDateTime format here for windows")
QString fileName = initialPath + "/" + date + "." + format;