0

I have a specific string on a QLineEdit, this string is passed to a QListView via QPushButton. Those strings are choices of a QComboBox and they are very specific:

1) "[ INFO] Minimum Distance: 5",

2) "[ INFO] Minimum Distance: 10" and

3) "[ INFO] Minimum Distance: 15"

Here a perfectly working minimal verifiable example if you need to test it.

Currently I can successfully change the color of the QGraphicsView but by clicking or double-clicking on the QString entered in the QListView.

The problem: How can I detect the specific QString content inside a QListView in order to change the background color of a QGraphicsView? What I mean I don't want to click or double-click on the entry of the QListView but I would like the QListView to see that there is a string "[ INFO] Minimum Distance: 5" and therefore change the color of the QGraphicsView automatically without me clicking or double-clicking on the QListView entry.

After I "Go to slot" my choices are the following below:

go_to_slot

Below the MVE working code, you can copy /paste on your machine and it will work:

mainwindow.h

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsTextItem>
#include <QStringListModel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void changeColorDetection();
    void updateListView();
    void updateListView(const QString & message);

private slots:
    void on_pushButton_clicked();
    void on_listView_entered(const QModelIndex &index);
    void on_listView_activated(const QModelIndex &index);

private:
    Ui::MainWindow *ui;
    QGraphicsView *mView;
    QGraphicsScene *mScene;
    QGraphicsTextItem *mText;
    StringList *newString;
    QStringListModel *model;

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mView = new QGraphicsView();
    mScene = new QGraphicsScene();
    ui->graphicsView->setScene(mScene);

    QFont font;
    font.setPixelSize(10);
    font.setBold(false);
    font.setFamily("Calibri");

    mText = new QGraphicsTextItem;
    mText->setPos(150,70);
    mScene->addText(tr("Boat outside alarm area"))->setDefaultTextColor(Qt::black);

    model = new QStringListModel();
    ui->listView->setModel(model);
    ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);

    emptyIndex();

    connect(ui->listView, SIGNAL(loggingUpdated()), this, SLOT(updateListView(const QString &)));
    connect(ui->graphicsView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(on_listView_activated(const QModelIndex &index)));
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::updateListView(const QString & message)
{
    if(model->insertRow(model->rowCount())) {
        QModelIndex index = model->index(model->rowCount() - 1, 0);
        model->setData(index, message);
        ui->listView->scrollTo(index);
    }
}

void MainWindow::on_pushButton_clicked()
{
    QString str = ui->lineEdit->text();
    model->insertRow(model->rowCount());
    QModelIndex index = model->index(model->rowCount()-1);
    model->setData(index, str);
    ui->listView->scrollToBottom();
}

void MainWindow::on_comboBox_currentIndexChanged(const QString &arg1)
{
    QString list = ui->comboBox->currentText();
    ui->lineEdit->setText(list);
    Q_UNUSED(arg1)
}

void MainWindow::on_listView_activated(const QModelIndex &index)
{
    QStringList allStrings = model->stringList();
    QString last = allStrings.last();
    if(last.startsWith("[ INFO] Minimum Distance: 5"))
    {
        ui->graphicsView->setBackgroundBrush(QColor(Qt::red));
    }
    else if(last.startsWith("[ INFO] Minimum Distance: 10"))
    {
        ui->graphicsView->setBackgroundBrush(QColor(Qt::yellow));
    }
    else if(last.startsWith("[ INFO] Minimum Distance: 15"))
    {
        ui->graphicsView->setBackgroundBrush(QColor(Qt::green));
    }
    Q_UNUSED(index)
}

EDIT 2

mainwindow.h

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsTextItem>
#include <QStringListModel>
#include "listview.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE


class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void setGraphicViewColor(QColor c);

private:
    Ui::MainWindow *ui;
    QGraphicsView *mView;
    QGraphicsScene *mScene;
    QGraphicsTextItem *mText;
    QStringListModel *model;

    ListView *myListView;

};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mView = new QGraphicsView();
    mScene = new QGraphicsScene();
    ui->graphicsView->setScene(mScene);

    QFont font;
    font.setPixelSize(10);
    font.setBold(false);
    font.setFamily("Calibri");

    mText = new QGraphicsTextItem;
    mText->setPos(150,70);
    mScene->addText(tr("Boat outside alarm area"))->setDefaultTextColor(Qt::black);

    model = new QStringListModel();
    ui->listView->setModel(model);
    ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);


    connect(ui->listView, SIGNAL(changeColor(QColor)), this, SLOT(setGraphicViewColor(QColor)));
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::setGraphicViewColor(QColor c)
{
    qDebug() << "Update your graphicsView backgroundBrush" << c;
    ui->graphicsView->setBackgroundBrush(Qt::green);
}

listview.h

#ifndef LISTVIEW_H
#define LISTVIEW_H

#include <QListView>
#include <QStringListModel>

class ListView : public QListView
{
    Q_OBJECT
public:
    ListView(QWidget *parent = nullptr);

signals:
    void changeColor(QColor c);

protected:
    void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()) override;

};

#endif // LISTVIEW_H

listview.cpp

#include "listview.h"

ListView::ListView(QWidget *parent)
    : QListView(parent)
{}

void ListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
    QListView::dataChanged(topLeft, bottomRight, roles);
    /**
     * Assuming that you have just one item changed
     * So topLeft == bottomRight
     */
    if (topLeft.row() == model()->rowCount()-1){
        QString last = topLeft.data().toString();
        if(last.startsWith("[ INFO] Minimum Distance: 5")) {
            emit changeColor(Qt::red);
        } else if(last.startsWith("[ INFO] Minimum Distance: 10")) {
            emit changeColor(Qt::yellow);
        } else if(last.startsWith("[ INFO] Minimum Distance: 15")) {
            emit changeColor(Qt::green);
        }
    }
}

Below the error and a screenshot of the ui that does not detect the change event:

error

The output of the .ui:

list_view

What I have done so far:

I have been doinf a lot of research about this problem and came across this source which was useful but could not solve the problem, but in addition it seems to use a QModelIndex and I am not sure this is exactly what I need for this small project.

Also I read this source which was useful to establish and capture the specific and unique string but in terms of changing colors I could not solve that.

Thank you very much for pointing in the right direction for solving this issue.

Emanuele
  • 2,194
  • 6
  • 32
  • 71
  • Subclass the listView and reimplement [dataChanged()](https://doc.qt.io/qt-5/qlistview.html#dataChanged) – thibsc Apr 28 '20 at 19:36
  • That is a great idea! Thanks I will try it and get back to you with the solution as soon as I have it. – Emanuele Apr 28 '20 at 20:44
  • @thibsc, I am having trouble with reimplementing `dataChanged()`, could you please give me a hand and propetly subclass the ListView if you have time? Thank you very much! :) – Emanuele Apr 29 '20 at 21:49

1 Answers1

1

If I well understand, you want to change the backgroundBrush of your graphicsView if the last item of your QStringListModel starts with your specific strings.

To detect this, you can subclass QListView:
listview.h:

#ifndef LISTVIEW_H
#define LISTVIEW_H

#include <QListView>
#include <QStringListModel>

class ListView : public QListView
{
    Q_OBJECT
public:
    ListView(QWidget *parent = nullptr);

signals:
    void changeColor(QColor c);

protected:
    void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()) override;

};

#endif // LISTVIEW_H

listview.cpp:

#include "listview.h"

ListView::ListView(QWidget *parent)
    : QListView(parent)
{

}

void ListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
    QListView::dataChanged(topLeft, bottomRight, roles);
    /**
     * Assuming that you have just one item changed
     * So topLeft == bottomRight
     */
    if (topLeft.row() == model()->rowCount()-1){
        QString last = topLeft.data().toString();
        if(last.startsWith("[ INFO] Minimum Distance: 5")) {
            emit changeColor(Qt::red);
        } else if(last.startsWith("[ INFO] Minimum Distance: 10")) {
            emit changeColor(Qt::yellow);
        } else if(last.startsWith("[ INFO] Minimum Distance: 15")) {
            emit changeColor(Qt::green);
        }
    }
}

Now you have all what you need, but you need to add a slot to connect the signal of your custom ListView with your QGraphicsView::brush

// Add this in your mainwindows.h
public slots:
    void setGraphicViewColor(QColor c);

// This in the ctor of your MainWindow:
connect(ui->listView, SIGNAL(changeColor(QColor)), this, SLOT(setGraphicViewColor(QColor)));

// And the implementation of your custom slot in mainwindows.cpp
void MainWindow::setGraphicViewColor(QColor c)
{
    qDebug() << "Update your graphicsView backgroundBrush" << c;
    //ui->graphicsView->setBackgroundBrush(c);
}
thibsc
  • 3,747
  • 2
  • 18
  • 38
  • Thanks for your help. I have been trying your suggestion until now but there is something that is not working 100% in fact nothing happens. [Here](https://i.imgur.com/NlHRbdL.png) a print screen of the error. I added **EDIT 2** on the question with your suggestion and I cleaned all not necessary code leave the minimal. – Emanuele Apr 30 '20 at 17:51
  • On my `.ui` file I already have a `QListView` component called `listView`. I think I am confused by the new introduction of the subclassed components `ListView`. I included it in the `mainwindow.h` but on the `mainwindow.cpp` of the constructor I still have the old component. What am I missing? Thank you so far for your help and time! It is very much appreciated! – Emanuele Apr 30 '20 at 17:53
  • @Emanuele, If you are using the designer, it's because you don't have promote your `QListView` to a `ListView` that is the error. So on the designer right click on your list and click promote, fill the class filename and it will be ok – thibsc Apr 30 '20 at 17:56