Adding Text in GraphicsView in Qt - c++

I am implementing a code to add different entities on a button click.I am getting a problem to add Text in graphics view. Following is the code snippet that I am implementing. What needs to be done?
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPaintEvent>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPainter>
#include "line.h"
#include "circle.h"
#include "ellipse.h"
#include "point.h"
#include "arc.h"
#include "text.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void wheelEvent(QWheelEvent* event);
private:
Ui::MainWindow *ui;
bool mFirstClick;
bool mPaintFlag;
int mStartX;
int mStartY;
int mEndX;
int mEndY;
QGraphicsScene *scene;
QPainter *painter;
point *item;
line *item1;
circle *item2;
ellipse *item3;
arc *item4;
private slots:
void drawPoint();
void drawLine();
void drawCircle();
void drawEllipse();
void drawArc();
void drawText();
void on_actionSave_triggered();
void on_actionOpen_triggered();
void on_actionQuit_2_triggered();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMouseEvent>
#include<QFileDialog>
#include<QMessageBox>
#include<QTextEdit>
#include<QString>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle(tr("GD CAD"));
scene = new QGraphicsScene;
for(int x = 0; x <= ui->graphicsView->width(); x += 10){
scene->addLine(x,0,x,ui->graphicsView->height(),QPen(Qt::green));
}
for(int y = 0; y <= ui->graphicsView->height(); y += 10){
scene->addLine(0,y,ui->graphicsView->width(),y,QPen(Qt::green));
}
for(int x = 10; x <= ui->graphicsView->width(); x += 100){
scene->addLine(x,0,x,ui->graphicsView->height(),QPen(Qt::darkGreen));
}
for(int y = 10; y <= ui->graphicsView->height(); y += 100){
scene->addLine(0,y,ui->graphicsView->width(),y,QPen(Qt::darkGreen));
}
ui->graphicsView->setScene(scene);
connect(ui->pointButton, SIGNAL(clicked()), this, SLOT(drawPoint()));
connect(ui->lineButton, SIGNAL(clicked()), this, SLOT(drawLine()));
connect(ui->circleButton, SIGNAL(clicked()), this, SLOT(drawCircle()));
connect(ui->ellipseButton, SIGNAL(clicked()), this, SLOT(drawEllipse()));
connect(ui->arcButton, SIGNAL(clicked()),this, SLOT(drawArc()));
connect(ui->text,SIGNAL(clicked()),this,SLOT(drawText()));
connect(ui->actionPoints, SIGNAL(triggered()), this, SLOT(drawPoint()));
connect(ui->actionLine, SIGNAL(triggered()), this, SLOT(drawLine()));
connect(ui->actionCircle, SIGNAL(triggered()), this, SLOT(drawCircle()));
connect(ui->actionEllipse, SIGNAL(triggered()), this, SLOT(drawEllipse()));
}
void MainWindow::drawPoint(){
ui->graphicsView->setScene(scene);
item = new point;
scene->addItem(item);
qDebug() << "Point Created";
connect(item, SIGNAL(DrawFinished()), this, SLOT(drawPoint()));
}
void MainWindow::drawLine(){
ui->graphicsView->setScene(scene);
item1 = new line;
scene->addItem(item1);
qDebug() << "Line Created";
connect(item1, SIGNAL(DrawFinished()), this, SLOT(drawLine()));
}
void MainWindow::drawCircle(){
ui->graphicsView->setScene(scene);
item2 = new circle;
scene->addItem(item2);
qDebug() << "Circle Created";
connect(item2, SIGNAL(DrawFinished()), this, SLOT(drawCircle()));
}
void MainWindow::drawArc(){
ui->graphicsView->setScene(scene);
item4 = new arc;
scene->addItem(item4);
qDebug() << "Circle Created";
connect(item4, SIGNAL(DrawFinished()), this, SLOT(drawArc()));
}
void MainWindow::drawEllipse(){
ui->graphicsView->setScene(scene);
item3 = new ellipse;
scene->addItem(item3);
qDebug() << "Ellipse Created";
connect(item3, SIGNAL(DrawFinished()), this, SLOT(drawEllipse()));
}
void MainWindow::wheelEvent(QWheelEvent* event) {
ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
// Scale the view / do the zoom
double scaleFactor = 1.15;
if(event->delta() > 0) {
// Zoom in
ui->graphicsView->scale(scaleFactor, scaleFactor);
} else {
// Zooming out
ui->graphicsView->scale(1.0 / scaleFactor, 1.0 / scaleFactor);
}
}
void MainWindow::on_actionOpen_triggered()
{
QString filename=QFileDialog::getOpenFileName(
this,
tr("Open File"),
QString(),
tr("file Name(*.dwg|*.DWG|*.dxf)")
);
if (!filename.isEmpty()) {
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::critical(this,
tr("Error"), tr("Could not open file"));
return;
}
}
}
void MainWindow::on_actionSave_triggered()
{
QString filename=QFileDialog::getSaveFileName(
this,
tr("Save File"),
QString(),
tr("file Name(*.txt)")
);
if(!filename.isEmpty()) {
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::critical(this, tr("Error"), tr("Could not open file"));
return;
} else {
QTextStream stream(&file);
QTextEdit *textEdit;
stream << textEdit->toPlainText();
stream.flush();
file.close();
}
}
}
void MainWindow::on_actionQuit_2_triggered(){
MainWindow *window;
window->close();
}
void MainWindow::drawText(){
ui->graphicsView->setScene(scene);
text *item5 = new text;
scene->addItem(item5);
qDebug() << "text created";
connect(item5, SIGNAL(DrawFinished()), this, SLOT(drawText()));
}
MainWindow::~MainWindow()
{
delete ui;
}
text.h
#ifndef TEXT_H
#define TEXT_H
#include <QGraphicsTextItem>
#include <QPen>
#include<QFocusEvent>
#include<QGraphicsItem>
#include<QGraphicsScene>
#include<QGraphicsSceneMouseEvent>
#include<QGraphicsTextItem>
#include<QFont>
#include"mainwindow.h"
#include"ui_mainwindow.h"
class text:public QGraphicsTextItem
{
Q_OBJECT
public:
enum { Type = UserType + 3 };
enum Mode { InsertText };
void setFont(const QFont &font);
text(QGraphicsItem *parent = 0);
int type() const { return Type; }
public slots:
void setMode(Mode mode);
signals:
void lostFocus(text *item);
void selectedChange(QGraphicsItem *item);
void textInserted(QGraphicsTextItem *item);
void itemSelected(QGraphicsItem *item);
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
void focusOutEvent(QFocusEvent *event);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
private:
Mode myMode;
QFont myFont;};
#endif // TEXT_H
text.cpp
#include "text.h"
#include"mainwindow.h"
#include"ui_mainwindow.h"
text::text(QGraphicsItem *parent):QGraphicsTextItem(parent)
{
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemIsSelectable);
}
QVariant text::itemChange(GraphicsItemChange change,
const QVariant &value)
{
if (change == QGraphicsItem::ItemSelectedHasChanged)
emit selectedChange(this);
return value;
}
void text::focusOutEvent(QFocusEvent *event)
{
setTextInteractionFlags(Qt::NoTextInteraction);
emit lostFocus(this);
QGraphicsTextItem::focusOutEvent(event);
}
void text::setMode(Mode mode)
{
myMode = mode;
}
void text::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if (textInteractionFlags() == Qt::NoTextInteraction)
setTextInteractionFlags(Qt::TextEditorInteraction);
QGraphicsTextItem::mouseDoubleClickEvent(event);
}
void text::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;
switch(myMode){
case InsertText:
text *textItem;
textItem = new text();
// textItem->setFont(myFont);
textItem->setPlainText("hello");
textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
textItem->setZValue(1000.0);
connect(textItem, SIGNAL(lostFocus(DiagramTextItem*)),
this, SLOT(editorLostFocus(DiagramTextItem*)));
connect(textItem, SIGNAL(selectedChange(QGraphicsItem*)),
this, SIGNAL(itemSelected(QGraphicsItem*)));
//addItem(textItem);
// textItem->setDefaultTextColor(myTextColor);
textItem->setPos(mouseEvent->scenePos());
emit textInserted(textItem);
//! [8] //! [9]
default:
;
}
// QGraphicsScene::mousePressEvent(mouseEvent);
}

Hm, if i am not mistaken, it does look like you are setting all kinds of properties of the text item, like Interaction-flags, z-value, color and so on -- everything but not the actual text. The text would be empty and therefore your QGraphicsTextItem will be invisible.
Use setPlainText(), setHtml() or setDocument() on your text item.
There are two places where text-items are created; one is in the mainwindow.cpp:
void MainWindow::drawText(){
ui->graphicsView->setScene(scene);
text *item5 = new text;
scene->addItem(item5);
qDebug() << "text created";
connect(item5, SIGNAL(DrawFinished()), this, SLOT(drawText()));
}
The text is created and added to the scene (with addItem()), but it does not have an actual content; set its content:
item5->setPlainText ("Mickey");
The second text-item is created from inside the text (which is a subclass of a QGraphicsTextItem) class itself:
void text::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
...
switch(myMode){
case InsertText:
text *textItem;
textItem = new text();
// textItem->setFont(myFont);
textItem->setPlainText("hello");
It is created correctly, and given a text, but not added to the scene. Add it to the scene, for instance by making it a child of the current text item:
textItem->setParentItem (this);

Related

How to visualize a .txt or .csv file on a QTableView after triggering currentRowChanged(int currentRow) on QListWidget?

Problem: How to visualize a .txt or .csv file on a QTableView after triggering currentRowChanged(int currentRow) on QListWidget?
I have a very simple interface that I designed as a minimal verifiable example that look like below with a QPushButton a QLineEdit a QListWidget and a QTableView.
If I upload a .csv file using the QPushButton there are no problem and the content of the file is correctly shown on a QTableView:
However, and here is the problem:
When I drag and drop (functionality correctly implemented) multiple files and I click on one of them I have problem visualizing the file because the content does not change. It seems that the slot currentRowChanged(int currentRow) in the QListWidget is never triggered despite the files exists.
Below is the desired result:
Here is the example I implemented. If you copy/paste on your computer it will work no problem:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QListWidgetItem>
#include <QStandardItemModel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
enum fileFormat {
CSV,
TXT,
OTHER
};
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void showFilesDetails(QListWidgetItem *item);
void readFile(QString path, fileFormat format);
void setValueAt(int ix, int jx, const QString &value);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
private slots:
void on_listWidget_currentRowChanged(int currentRow);
void on_loadBtn_clicked();
private:
Ui::MainWindow *ui;
fileFormat *format;
QStandardItemModel *model;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDragEnterEvent>
#include <QDragLeaveEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QDebug>
#include <QFileDialog>
namespace constants
{
const QStringList HEADERS = {
"tax_id", "Org_name", "GeneID", "CurrentID", "Status"};
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setAcceptDrops(true);
model = new QStandardItemModel(this);
ui->tableView->setModel(model);
model->setHorizontalHeaderLabels(constants::HEADERS);
model->setColumnCount(constants::HEADERS.length());
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);
QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, ui->tableView);
horizontal->setSectionsClickable(true);
horizontal->setHighlightSections(true);
ui->tableView->setHorizontalHeader(horizontal);
}
MainWindow::~MainWindow(){ delete ui; }
void MainWindow::dragEnterEvent(QDragEnterEvent *event) { event->accept(); }
void MainWindow::dragLeaveEvent(QDragLeaveEvent *event) { event->accept(); }
void MainWindow::dragMoveEvent(QDragMoveEvent *event) { event->accept(); }
void MainWindow::dropEvent(QDropEvent *event)
{
QString name;
QList<QUrl> urls;
QList<QUrl>::Iterator i;
urls = event->mimeData()->urls();
for(i = urls.begin(); i != urls.end(); ++i) {
name = i->path();
ui->listWidget->addItem(name);
}
}
void MainWindow::on_listWidget_currentRowChanged(int currentRow)
{
QListWidgetItem *item = nullptr;
if(ui->listWidget->count() > 0)
ui->listWidget->setCurrentRow(currentRow);
showFilesDetails(item);
}
void MainWindow::readFile(QString path, fileFormat format)
{
QListWidgetItem *item = nullptr;
QFile file(path);
if(!file.exists()){
qDebug() << "File does not exist "<<path;
}else{
qDebug() << path<< "File exist...";
}
if (file.fileName().endsWith(".txt", Qt::CaseInsensitive))
{
qDebug() << "File does not exist "<<file.fileName();
} else if(file.fileName().endsWith(".csv", Qt::CaseInsensitive))
{
qDebug() << "File does not exist "<<file.fileName();
}
QString line;
if (file.open(QIODevice::ReadOnly | QIODevice::Text)){
QTextStream stream(&file);
while (!stream.atEnd()){
line = stream.readLine();
ui->listWidget->setCurrentItem(item);
qDebug() << "line: "<<line;
}
}
file.close();
}
void MainWindow::showFilesDetails(QListWidgetItem *item)
{
QString path;
fileFormat frmt;
readFile(path, frmt);
ui->listWidget->setCurrentItem(item);
}
void MainWindow::on_loadBtn_clicked()
{
auto filename = QFileDialog::getOpenFileName(this, "Open", QDir::rootPath(), "CSV file (*.csv)");
if(filename.isEmpty()) {
ui->statusbar->showMessage("File empty");
return;
}
QFile file(filename);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
ui->statusbar->showMessage("File did not open");
return;
}
QTextStream xin(&file);
int ix = 0;
while (!xin.atEnd()) {
model->setRowCount(ix);
auto line = xin.readLine();
auto values = line.split(",");
const int colCount = values.size();
model->setColumnCount(colCount);
for(int jx = 0; jx < colCount; ++jx) {
setValueAt(ix, jx, values.at(jx));
}
++ix;
ui->lineEdit->setText(filename);
ui->statusbar->showMessage(filename);
}
ui->listWidget->addItem(filename);
file.close();
}
void MainWindow::setValueAt(int ix, int jx, const QString &value)
{
if (!model->item(ix, jx)) {
model->setItem(ix, jx, new QStandardItem(value));
} else {
model->item(ix, jx)->setText(value);
}
}
In order to solve the problem I read very useful sources and in particular I came across this one, this one and also I found this post useful. However, I am trying to figure out where the problem might be.
The drag/drop functionality is properly implemented but how do I upload the files in the QTableView and properly trigger the QListWidget event?
Thank to anyone who would have time to read and point to a potential solution.
afaik,funtion on_listWidget_currentRowChanged triggered properly.
function showFilesDetails passes an empty string to readFile,this may be the problem.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDragEnterEvent>
#include <QDragLeaveEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QDebug>
#include <QFileDialog>
namespace constants
{
const QStringList HEADERS = {
"tax_id", "Org_name", "GeneID", "CurrentID", "Status"};
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setAcceptDrops(true);
model = new QStandardItemModel(this);
ui->tableView->setModel(model);
model->setHorizontalHeaderLabels(constants::HEADERS);
model->setColumnCount(constants::HEADERS.length());
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);
QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, ui->tableView);
horizontal->setSectionsClickable(true);
horizontal->setHighlightSections(true);
ui->tableView->setHorizontalHeader(horizontal);
}
MainWindow::~MainWindow(){ delete ui; }
void MainWindow::dragEnterEvent(QDragEnterEvent *event) { event->accept(); }
void MainWindow::dragLeaveEvent(QDragLeaveEvent *event) { event->accept(); }
void MainWindow::dragMoveEvent(QDragMoveEvent *event) { event->accept(); }
void MainWindow::dropEvent(QDropEvent *event)
{
QString name;
QList<QUrl> urls;
QList<QUrl>::Iterator i;
urls = event->mimeData()->urls();
for(i = urls.begin(); i != urls.end(); ++i) {
name = i->toLocalFile();
ui->listWidget->addItem(name);
}
}
void MainWindow::on_listWidget_currentRowChanged(int currentRow)
{
QListWidgetItem *item = nullptr;
if(ui->listWidget->count() > 0)
ui->listWidget->setCurrentRow(currentRow);
showFilesDetails(item);
}
void MainWindow::readFile(QString path, fileFormat format)
{
QListWidgetItem *item = nullptr;
QFile file(path);
if(!file.exists()){
qDebug() << "File does not exist "<<path;
}else{
qDebug() << path<< "File exist...";
}
if (file.fileName().endsWith(".txt", Qt::CaseInsensitive))
{
qDebug() << "File does not exist "<<file.fileName();
} else if(file.fileName().endsWith(".csv", Qt::CaseInsensitive))
{
qDebug() << "File does not exist "<<file.fileName();
}file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream xin(&file);
int ix = 0;
while (!xin.atEnd()) {
model->setRowCount(ix);
auto line = xin.readLine();
auto values = line.split(",");
const int colCount = values.size();
model->setColumnCount(colCount);
for(int jx = 0; jx < colCount; ++jx) {
setValueAt(ix, jx, values.at(jx));
}
++ix;
ui->lineEdit->setText(path);
ui->statusbar->showMessage(path);
}
file.close();
}
void MainWindow::showFilesDetails(QListWidgetItem *item)
{
item=ui->listWidget->currentItem();
QString path=
item->data(Qt::DisplayRole).toString();;
fileFormat frmt;
readFile(path, frmt);
ui->listWidget->setCurrentItem(item);
}
void MainWindow::on_loadBtn_clicked()
{
auto filename = QFileDialog::getOpenFileName(this, "Open", QDir::rootPath(), "CSV file (*.csv)");
if(filename.isEmpty()) {
ui->statusbar->showMessage("File empty");
return;
}
QFile file(filename);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
ui->statusbar->showMessage("File did not open");
return;
}
QTextStream xin(&file);
int ix = 0;
while (!xin.atEnd()) {
model->setRowCount(ix);
auto line = xin.readLine();
auto values = line.split(",");
const int colCount = values.size();
model->setColumnCount(colCount);
for(int jx = 0; jx < colCount; ++jx) {
setValueAt(ix, jx, values.at(jx));
}
++ix;
ui->lineEdit->setText(filename);
ui->statusbar->showMessage(filename);
}
ui->listWidget->addItem(filename);
file.close();
}
void MainWindow::setValueAt(int ix, int jx, const QString &value)
{
if (!model->item(ix, jx)) {
model->setItem(ix, jx, new QStandardItem(value));
} else {
model->item(ix, jx)->setText(value);
}
}

Segfault on clicking on QFrame

This topic might be little lengthy since I have to explain the premise before procceding with the probleme at hand.Firstly my main goal is to have this application in which the user is capable of drag-n-dropping commands from a toolbar in order to form workflows which are send and executed on a remote server.Currently i am working on the client part in qt and it is driving me nuts.
This is my code:
draglabel.h
#ifndef DRAGLABEL_H
#define DRAGLABEL_H
#include <QLabel>
class QDragEnterEvent;
class QDragMoveEvent;
class QFrame;
class DragLabel : public QLabel
{
public:
DragLabel(const QString &text, QWidget *parent);
QString labelText() const;
private:
QString m_labelText;
};
#endif // DRAGLABEL_H
draglabel.c
#include "draglabel.h"
#include <QtWidgets>
DragLabel::DragLabel(const QString &text, QWidget *parent)
: QLabel(parent)
{
QFontMetrics metric(font());
QSize size = metric.size(Qt::TextSingleLine, text);
QImage image(size.width() + 12, size.height() + 12, QImage::Format_ARGB32_Premultiplied);
image.fill(qRgba(0, 0, 0, 0));
QFont font;
font.setStyleStrategy(QFont::ForceOutline);
QLinearGradient gradient(0, 0, 0, image.height()-1);
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(0.2, QColor(200, 200, 255));
gradient.setColorAt(0.8, QColor(200, 200, 255));
gradient.setColorAt(1.0, QColor(127, 127, 200));
QPainter painter;
painter.begin(&image);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(gradient);
painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1),
25, 25, Qt::RelativeSize);
painter.setFont(font);
painter.setBrush(Qt::black);
painter.drawText(QRect(QPoint(6, 6), size), Qt::AlignCenter, text);
painter.end();
setPixmap(QPixmap::fromImage(image));
m_labelText = text;
}
QString DragLabel::labelText() const
{
return m_labelText;
}
dragwidget.h
#ifndef DRAGWIDGET_H
#define DRAGWIDGET_H
#include <QWidget>
#include <QFrame>
#include <vector>
#include <set>
#include "draglabel.h"
using namespace std;
class QDragEnterEvent;
class QDropEvent;
class DragWidget : public QFrame
{
public:
DragWidget(QWidget *parent = nullptr);
void setMode(int desiredMode);
void changePairingMode();
void showAvailableCommands();
void initDrawingLayout();
vector<tuple<QString,QString>> actCommands;
vector<tuple<QString,QString>> execCommands;
vector<pair<int,int>>waitingForPair;
int pairingMode=0;
QFrame*drawingCon;
private:
int widgetMode=1;
protected:
void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE;
void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE;
void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
};
#endif // DRAGWIDGET_H
dragwidget.cpp
#include "draglabel.h"
#include "dragwidget.h"
#include "arrowhead.h"
#include <QtWidgets>
#include <QWidget>
#include <QFrame>
#include <QColor>
#include <tuple>
using namespace std;
static inline QString dndProcMimeType() { return QStringLiteral("application/x-fridgemagnet"); }
DragWidget::DragWidget(QWidget *parent)
: QFrame(parent)
{
drawingCon=new QFrame(this);
QPalette newPalette = palette();
newPalette.setColor(QPalette::Window, Qt::white);
setPalette(newPalette);
setWindowTitle(tr("Drag-and-Drop"));
setMinimumSize(300,300);
setAcceptDrops(true);
setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
drawingCon->setPalette(newPalette);
drawingCon->setWindowTitle(tr("Drag-and-Drop"));
drawingCon->setMinimumSize(350,350);
drawingCon->setAcceptDrops(false);
drawingCon->show();
}
void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat(dndProcMimeType())) {
if (children().contains(event->source())) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
} else if (event->mimeData()->hasText()) {
event->acceptProposedAction();
} else {
event->ignore();
}
}
void DragWidget::dragMoveEvent(QDragMoveEvent *event)
{
if (event->mimeData()->hasFormat(dndProcMimeType())) {
if (children().contains(event->source())) {
if(widgetMode==1)
{
event->setDropAction(Qt::MoveAction);
event->accept();
}
else {
event->ignore();
}
} else {
if(widgetMode==1)
{
event->acceptProposedAction();
}
else
{
if(widgetMode==1)
{
event->accept();
}
else {
event->ignore();
}
}
}
} else if (event->mimeData()->hasText()) {
event->acceptProposedAction();
} else {
event->ignore();
}
}
void DragWidget::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasFormat(dndProcMimeType())) {
const QMimeData *mime = event->mimeData();
QByteArray itemData = mime->data(dndProcMimeType());
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
QString text;
QPoint offset;
dataStream >> text >> offset;
DragLabel *newLabel = new DragLabel(text, this);
newLabel->move(event->pos() - offset);
newLabel->show();
newLabel->setAttribute(Qt::WA_DeleteOnClose);
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
tuple<QString,QString> addTest;
addTest=make_tuple(text,"");
actCommands.push_back(make_tuple(text,""));
for(auto it:actCommands)
qDebug()<<get<0>(it)<<" "<<get<1>(it);
event->acceptProposedAction();
}
} else {if (event->mimeData()->hasText()) {
if(widgetMode==1)
{
event->accept();
}
else {
event->ignore();
}
event->acceptProposedAction();
}
}
}
void DragWidget::mousePressEvent(QMouseEvent *event)
{
DragLabel *child = static_cast<DragLabel*>(childAt(event->pos()));
if(!pairingMode){
if (!child)
return;
QPoint hotSpot = event->pos() - child->pos();
if(widgetMode==1)
qDebug()<<child->labelText();
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << child->labelText() << QPoint(hotSpot);
QMimeData *mimeData = new QMimeData;
mimeData->setData(dndProcMimeType(), itemData);
mimeData->setText(child->labelText());
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(*child->pixmap());
drag->setHotSpot(hotSpot);
child->hide();
if (drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction) == Qt::MoveAction)
child->close();
else {
child->show();
}
}
else {
if(widgetMode==1)
{
DragLabel *child = static_cast<DragLabel*>(childAt(event->pos()));
if (!child)
return;
qDebug()<<"Facem pair cu:"<<child->labelText();
waitingForPair.push_back(make_pair(child->x(),child->y()));
if(waitingForPair.size()==2) {
ArrowHead *line=new ArrowHead(waitingForPair.at(0).first,waitingForPair.at(0).second,waitingForPair.at(1).first,waitingForPair.at(1).second,drawingCon);
line->show();
waitingForPair.erase(waitingForPair.begin(),waitingForPair.begin()+1);
qDebug()<<"Tragem linie";
}
}
}
}
void DragWidget::setMode(int desiredMode)
{
widgetMode=desiredMode;
}
void DragWidget::showAvailableCommands()
{
DragLabel*grep=new DragLabel("grep",this);
grep->move(this->x(),this->y());
grep->show();
DragLabel*cat=new DragLabel("cat",this);
grep->move(this->x()+40,this->y());
cat->show();
DragLabel*wc=new DragLabel("wc",this);
wc->move(this->x()+90,this->y());
wc->show();
}
void DragWidget::changePairingMode()
{
if(pairingMode==1)
pairingMode=0;
else {
pairingMode=1;
}
}
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QTextEdit>
#include "dragwidget.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
protected:
virtual void closeEvent(QCloseEvent *event) override;
private slots:
void handleButton();
void closeAppButton();
void pairButton();
private:
QPushButton *executeCode;
QPushButton *pairCommands;
QPushButton *closeApp;
QTextEdit *inputUser;
QTextEdit *outputServer;
DragWidget * commandLayout=new DragWidget();
DragWidget * availableLayout=new DragWidget();
};
#endif // MAINWINDOW_H
mainWindow.cpp
#include "mainwindow.h"
#include "draglabel.h"
#include "dragwidget.h"
#include <QCoreApplication>
#include <QApplication>
#include <QHBoxLayout>
#include <QPushButton>
#include <QCloseEvent>
#include <QTextEdit>
#include <QFrame>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
executeCode=new QPushButton("Execute");
closeApp=new QPushButton("Close");
pairCommands=new QPushButton("Pair");
connect(closeApp, SIGNAL (released()), this, SLOT (closeAppButton()));
connect(pairCommands, SIGNAL (released()), this, SLOT (pairButton()));
void pairButton();
QHBoxLayout * horizontalLayout=new QHBoxLayout();
commandLayout->setMode(1);
availableLayout->setMode(2);
horizontalLayout->addWidget(commandLayout);
horizontalLayout->addWidget(availableLayout);
availableLayout->showAvailableCommands();
QVBoxLayout*inputBoxes=new QVBoxLayout();
inputUser=new QTextEdit();
outputServer=new QTextEdit();
inputBoxes->addWidget(inputUser);
inputBoxes->addWidget(outputServer);
horizontalLayout->addLayout(inputBoxes);
QVBoxLayout*withButtons=new QVBoxLayout();
withButtons->addLayout(horizontalLayout);
withButtons->addWidget(pairCommands);
withButtons->addWidget(executeCode);
withButtons->addWidget(closeApp);
withButtons->addWidget(new QFrame());
setCentralWidget(new QWidget);
centralWidget()->setLayout(withButtons);
}
void MainWindow::handleButton()
{
}
void MainWindow::closeEvent(QCloseEvent *event)
{
event->accept();
}
void MainWindow::closeAppButton()
{
exit(EXIT_SUCCESS);
}
void MainWindow::pairButton()
{
commandLayout->changePairingMode();
qDebug()<<commandLayout->pairingMode;
}
Note:It might seem idiotic but i have the same class for the "toolbar",from where you're supposed to drag commands and also for part where you are supposed to drag commands and pair them.
This is mostly modified code of the fridge-magnets example on the qt website.
The problem that is giving headaches is drawing lines between dragwidget, I have tried drawing everything in the same QFrame but that proved to be disastrous since the whole pixelMap of the instance dragWidget is overwritten at every draw.The solution with which i came up is to overlay a supplimentary QFrame over my dragWidget in order to draw lines there and everyone to be happy,but as always misfortune strikes at every step.When i am trying to click on the command widget everything's fine but clicking on anything other than a DragLabel results in a segfault due to clicking on the QFrame due to childAt() returning the address of the QFrame overlayed on the first instance of dragWdiget();
My main question is: How can i overcome this obstacle
You should use qobject_cast instead of static_cast.
Add Q_OBJECT macro in each class declaration:
class DragLabel : public QLabel
{
Q_OBJECT
public:
//... class declaration ...
}
class DragWidget : public QFrame
{
Q_OBJECT
public:
//... class declaration ...
}
Then use qobject_cast instead of static_cast for childAt(), for example :
DragLabel *child = qobject_cast<DragLabel*>(childAt(event->pos()));
if(!child){
....
}

Draggable pixmaps inside a QGraphicsScene of graphic items

I have a scene with a 12*4 grid with blocks of QGraphicsItems ,when i right click on the blocks I have a contexmenu that
can add icons inside the blocks my proplem is that
I can't fingure out how can I make those icons draggable to the other blocks inside the graphic scene ,I know there is the "Draggable Icons Example" but how can I implement that code to a graphic scene.
this is the mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsPathItem>
class QGraphicsSceneMouseEvent;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
bool eventFilter(QObject *, QEvent *);
~MainWindow();
private slots:
void showContextMenu(const QPoint&);
void addPixBlock();
private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
QGraphicsItem *itemAt(const QPointF&);
int x;
int y;
QMenu *Menu;
QMenu *Submenu;
QAction *Picture;
QGraphicsPixmapItem* pix;
};
#endif // MAINWINDOW_H
the mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "block.h"
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QPainter>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scene = new QGraphicsScene(this) ;
for(int row=-4;row<8;++row)
for(int column=0;column<4;++column)
{
Block *b = new Block;
scene->addItem(b);
b->setPos(row* 95,column*85);
}
ui->graphicsView->setScene(scene);
scene->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
QGraphicsItem* MainWindow::itemAt(const QPointF &pos)
{
QList<QGraphicsItem*> items = scene->items(QRectF(pos - QPointF(1,1),
QSize(3,3)));
foreach(QGraphicsItem *item, items)
if (item->type() > QGraphicsItem::UserType)
return item;
return 0;
}
bool MainWindow::eventFilter(QObject *o, QEvent *e)
{
QGraphicsSceneMouseEvent *me = (QGraphicsSceneMouseEvent*) e;
switch ((int) e->type()){
case QEvent::GraphicsSceneMousePress:{
switch ((int) me->button()){
case Qt::RightButton:{
QGraphicsItem *item = itemAt(me->scenePos());
if (item && item->type() == Block::Type){
x=item->scenePos().x();
y=item->scenePos().y();
showContextMenu(item->scenePos().toPoint());
}
break;
}
}
break;
}
}
return QObject::eventFilter(o, e);
}
void MainWindow::showContextMenu(const QPoint &pos)
{
Menu= new QMenu("Menu");
Submenu=Menu->addMenu(QIcon(":/img/pix.png"),"Pix");
Picture =Submenu->addAction(QIcon(":/img/pix.png"),"Pix");
connect(Picture, SIGNAL(triggered()), this, SLOT(addPixBlock()));
Menu->exec(QCursor::pos());
}
void MainWindow::addPixBlock()
{
QPixmap pixmap(":/img/pix.png");
pix = scene->addPixmap(pixmap.scaled(70,50));
pix->setPos(x,y);
}
the block.h
#ifndef BLOCK_H
#define BLOCK_H
#include <QGraphicsPathItem>
class QGraphicsSceneMouseEvent;
class Block : public QGraphicsPathItem
{
public:
enum { Type = QGraphicsItem::UserType + 3 };
int type() const { return Type; }
Block(QGraphicsItem *parent = 0);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget
*widget);
bool eventFilter(QObject *, QEvent *);
};
#endif // BLOCK_H
the Block.cpp
#include "block.h"
#include <QPainter>
#include <QtWidgets>
class QGraphicsSceneMouseEvent;
Block::Block(QGraphicsItem *parent)
: QGraphicsPathItem(parent)
{
QPainterPath p;
//<->,|,<->,|,roundness
p.addRoundedRect(0,0,80,50, 5, 5);
setPath(p);
setAcceptDrops(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
void Block::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
painter->setPen(QPen(QColor(67, 141, 220)));
painter->setBrush(QColor(67, 141, 220,100));
painter->drawPath(path());
}
First of all if you want to place a QGraphicsPixmapItem on top of another item, a better option is to set it as your parentItem.
On the other hand we can use an event filter but a better option in this case is to implement a custom QGraphicsScene, and when pressing with the left key it allows to drag the item, for that we use QDrag and we pass the data of the item, then we overwrite the event dropEvent where we will obtain the item and establish a new parent.
graphicsscene.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
class QMenu;
class QAction;
class GraphicsScene : public QGraphicsScene
{
public:
using QGraphicsScene::QGraphicsScene;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void dropEvent(QGraphicsSceneDragDropEvent *event) override;
private:
QGraphicsPixmapItem *findPixmapItem(QGraphicsItem *item);
void createDrag(const QPointF &pos, QWidget *widget, QGraphicsItem *item);
void showContextMenu(const QPointF &pos);
void addPixBlock(QGraphicsItem *item);
QMenu *menu;
QMenu *submenu;
QAction *picture;
QGraphicsPixmapItem *pix;
};
#endif // GRAPHICSSCENE_H
graphicsscene.cpp
#include "graphicsscene.h"
#include <QDrag>
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
#include <QMimeData>
#include <QWidget>
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
auto its = items(QRectF(event->scenePos() - QPointF(1,1), QSize(3,3)));
auto val = std::find_if(its.constBegin(), its.constEnd(), [](auto const& it){
return it->type() > QGraphicsItem::UserType;
});
if(val == its.constEnd())
return;
if(event->button() == Qt::RightButton){
showContextMenu(event->scenePos());
}
else{
createDrag(event->scenePos(), event->widget(), *val);
}
}
void GraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
QByteArray byteArray = event->mimeData()->data("Item");
QGraphicsPixmapItem * item = *reinterpret_cast<QGraphicsPixmapItem**>(byteArray.data());
QGraphicsItem *item_parent = itemAt(event->scenePos(), QTransform());
item->setParentItem(item_parent);
}
QGraphicsPixmapItem *GraphicsScene::findPixmapItem(QGraphicsItem *item){
auto chs = item->childItems();
auto val = std::find_if(chs.constBegin(), chs.constEnd(), [](auto const& it){
return static_cast<QGraphicsPixmapItem *>(it) != Q_NULLPTR;
});
return val == chs.constEnd() ? Q_NULLPTR : static_cast<QGraphicsPixmapItem *>(*val);
}
void GraphicsScene::createDrag(const QPointF &pos, QWidget *widget, QGraphicsItem *item){
QGraphicsPixmapItem *pix = findPixmapItem(item);
if(pix == Q_NULLPTR)
return;
QByteArray byteArray(reinterpret_cast<char*>(&pix),sizeof(QGraphicsPixmapItem*));
QDrag *drag = new QDrag(widget);
QMimeData * mimeData = new QMimeData;
mimeData->setData("Item",byteArray);
drag->setMimeData(mimeData);
drag->setHotSpot(pos.toPoint()-pix->scenePos().toPoint());
drag->setPixmap(pix->pixmap());
drag->start();
}
void GraphicsScene::showContextMenu(const QPointF &pos)
{
QGraphicsItem *item = itemAt(pos, QTransform());
menu= new QMenu("Menu");
submenu = menu->addMenu(QIcon(":/img/pix.png"),"Pix");
picture = submenu->addAction(QIcon(":/img/pix.png"),"Pix");
connect(picture, &QAction::triggered, [item, this](){
addPixBlock(item);
});
menu->exec(QCursor::pos());
}
void GraphicsScene::addPixBlock(QGraphicsItem *item)
{
if(findPixmapItem(item))
return;
QPixmap pixmap(":/img/pix.png");
pix = addPixmap(pixmap.scaled(70,50));
if(pix->parentItem() != item)
pix->setParentItem(item);
}
Then we establish that new scene and add the Blocks.
The complete example can be found in the following link

Transferring data from one form to another in Qt?

there was such problem. I have a main and auxiliary form. With the second form, I take two values: the index and the color. I'm transferring to the main form. I get the values ​​in the "setParametr" method. But I can not get them in the "paintEvent" method. QDebug show that the method "setParametr" got the values. When I check the values ​​in "on_pushButton_clicked()" values is 0. What could be the problem. Just started to study Qt
dialog.h
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void setcolor(int);
int getcolor();
void setindex(int);
int getindex();
private slots:
void ColorIndex();
void on_pushButton_clicked();
private:
Ui::Dialog *ui;
int squareColor;
int Index;
};
dialog.cpp
#include "ui_dialog.h"
#include "dialog.h"
#include "mainwindow.h"
#include "QDebug"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(ColorIndex()));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::setcolor(int color)
{
squareColor = color;
}
int Dialog::getcolor()
{
return squareColor;
}
void Dialog::setindex(int index)
{
Index = index;
}
int Dialog::getindex()
{
return Index;
}
void Dialog::on_pushButton_clicked()
{
MainWindow *main = new MainWindow();
if(ui->radioButton->isChecked()){
setcolor(1);
}
if(ui->radioButton_2->isChecked()){
setcolor(2);
}
if(ui->radioButton_3->isChecked()){
setcolor(3);
}
if(ui->comboBox->currentIndex()==0)
{
setindex(1);
}
if(ui->comboBox->currentIndex()==1)
{
setindex(2);
}
if(ui->comboBox->currentIndex()==2)
{
setindex(3);
}
if(ui->comboBox->currentIndex()==3)
{
setindex(4);
}
qDebug() << QString::number(getcolor());
qDebug() << QString::number(getindex());
main->SetParametr(getindex(),getcolor());
}
mainwindow.h
#include <QWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QPainterPath>
#include <QColor>
#include <QColorDialog>
#include <QPushButton>
#include <QMainWindow>
#include "dialog.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setColor(int);
int getColor();
void setIndex(int);
int getIndex();
private:
Dialog *dial = new Dialog();
int fIndex;
int fColor;
private slots:
void on_actionMy_Dialog_triggered();
void paintEvent(QPaintEvent *event);
void on_pushButton_clicked();
signals:
void SetParametr(int index, int color);
private:
Ui::MainWindow *ui;
void onInit();
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QDebug"
#include "dialog.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
fColor = 0;
fIndex =0;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::SetParametr(int index, int color)
{
fIndex = index;
fColor = color;
qDebug() << QString::number(fColor) + "s";
qDebug() << QString::number(fIndex);
}
/*void MainWindow::setColor(int _color)
{
fColor = _color;
}
int MainWindow::getColor()
{
return fColor;
}
void MainWindow::setIndex(int _index)
{
fIndex = _index;
}
int MainWindow::getIndex()
{
return fIndex;
}
*/
void MainWindow::paintEvent(QPaintEvent *event)
{
QColor red1;
if(fColor==1){
red1 = Qt::red;
}
if(fColor == 2){
red1 = Qt::blue;
}
if(fColor == 3){
red1 = Qt::green;
}
int index1;
if(fIndex == 0)
{
index1 = 0;
}
if(fIndex == 1)
{
index1 = 1;
}
if(fIndex == 2)
{
index1 = 2;
}
if(fIndex == 3)
{
index1 = 3;
}
Q_UNUSED(event);
QPainter painter(this);
if(index1 == 0)
{
painter.setBrush(QBrush(red1, Qt::SolidPattern));
painter.drawEllipse(100, 50, 150, 150);
}
if(index1 == 1)
{
painter.setBrush(QBrush(red1, Qt::SolidPattern));
painter.drawRect(50, 50, 250, 150);
}
if(index1 == 2)
{
painter.setBrush(QBrush(red1, Qt::SolidPattern));
QPolygon polygon;
polygon<<QPoint(175,50)<<QPoint(50,200)<<QPoint(300,200);
painter.drawPolygon(polygon);
}
if(index1 == 3)
{
painter.setBrush(QBrush(red1, Qt::SolidPattern));
painter.drawRect(100, 50, 150, 150);
}
}
void MainWindow::on_actionMy_Dialog_triggered()
{
dial->show();
}
void MainWindow::on_pushButton_clicked()
{
qDebug() << QString::number(fColor);
qDebug() << QString::number(fIndex);
}

Designing a Qt Creator's SideBar in C++

I have created a class for making a sidebar just like in Qt Creator (one to the left). I am having no idea now to make it look exactly like the one in Qt creator as mine looks ugly!
The sidebar.h:
#ifndef _SIDEBAR_H_
#define _SIDEBAR_H_
#include <QVector>
#include <QString>
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QPixmap>
#include <iostream>
class SideBar : public QWidget
{
public:
SideBar(QWidget *parent=nullptr);
void addIcon(const char *name);
void addIcon(QString &name);
private:
QVBoxLayout *_layout;
};
#endif // SIDEBAR_H
The sidebar.cpp
#include "sidebar.h"
#include <QPushButton>
#include <QIcon>
SideBar::SideBar(QWidget *parent) : QWidget(parent)
{
_layout = new QVBoxLayout(this);
setLayout(_layout);
}
void SideBar::addIcon(const char *name)
{
QString str(name);
addIcon(str);
}
void SideBar::addIcon(QString &file)
{
QPushButton *button = new QPushButton(this);
QPixmap pixmap(file);
QIcon buttonIcon(pixmap);
button->setIcon(buttonIcon);
// button->setIconSize(pixmap.rect().size());
_layout->addWidget(button);
}
This is one i want:
And this is one i got:
A possible solution is to use QAction to handle the clicks and icons, overwriting the methods paintEvent, mousePressEvent, mouseMoveEvent, leaveEvent, changing the colors regarding the state in which the widget is.
sidebar.h
#ifndef SIDEBAR_H
#define SIDEBAR_H
#include <QAction>
#include <QWidget>
class SideBar : public QWidget
{
Q_OBJECT
public:
explicit SideBar(QWidget *parent = nullptr);
void addAction(QAction *action);
QAction *addAction(const QString &text, const QIcon &icon = QIcon());
QSize minimumSizeHint() const;
signals:
public slots:
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void leaveEvent(QEvent * event);
QAction *actionAt(const QPoint &at);
private:
QList<QAction *> mActions;
QAction *mCheckedAction;
QAction *mOverAction;
};
#endif // SIDEBAR_H
sidebar.cpp
#include "sidebar.h"
#include <QPaintEvent>
#include <QPainter>
#include <QDebug>
#include <QEvent>
#define action_height 90
SideBar::SideBar(QWidget *parent) :
QWidget(parent), mCheckedAction(NULL), mOverAction(NULL)
{
setMouseTracking(true);
}
void SideBar::paintEvent(QPaintEvent *event)
{
QPainter p(this);
QFont fontText(p.font());
fontText.setFamily("Helvetica Neue");
p.setFont(fontText);
int action_y = 0;
p.fillRect(rect(), QColor(100, 100, 100));
for(auto action: mActions)
{
QRect actionRect(0, action_y, event->rect().width(), action_height);
if(action->isChecked())
{
p.fillRect(actionRect, QColor(35, 35, 35));
}
if(action == mOverAction){
p.fillRect(actionRect, QColor(150, 150, 150));
}
p.setPen(QColor(255, 255, 255));
QSize size = p.fontMetrics().size(Qt::TextSingleLine, action->text());
QRect actionTextRect(QPoint(actionRect.width()/2 - size.width()/2, actionRect.bottom()-size.height()-5), size);
p.drawText(actionTextRect, Qt::AlignCenter, action->text());
QRect actionIconRect(0, action_y + 10, actionRect.width(), actionRect.height()-2*actionTextRect.height()-10);
QIcon actionIcon(action->icon());
actionIcon.paint(&p, actionIconRect);
action_y += actionRect.height();
}
}
QSize SideBar::minimumSizeHint() const
{
return action_height*QSize(1, mActions.size());
}
void SideBar::addAction(QAction *action)
{
mActions.push_back(action);
action->setCheckable(true);
update();
}
QAction *SideBar::addAction(const QString &text, const QIcon &icon)
{
QAction *action = new QAction(icon, text, this);
action->setCheckable(true);
mActions.push_back(action);
update();
return action;
}
void SideBar::mousePressEvent(QMouseEvent *event)
{
QAction* tempAction = actionAt(event->pos());
if(tempAction == NULL || tempAction->isChecked())
return;
qDebug()<<"clicked";
if(mCheckedAction)
mCheckedAction->setChecked(false);
if(mOverAction == tempAction)
mOverAction = NULL;
mCheckedAction = tempAction;
tempAction->setChecked(true);
update();
QWidget::mousePressEvent(event);
}
void SideBar::mouseMoveEvent(QMouseEvent *event)
{
QAction* tempAction = actionAt(event->pos());
if(tempAction == NULL){
mOverAction = NULL;
update();
return;
}
if(tempAction->isChecked() || mOverAction == tempAction)
return;
mOverAction = tempAction;
update();
QWidget::mouseMoveEvent(event);
}
void SideBar::leaveEvent(QEvent * event)
{
mOverAction = NULL;
update();
QWidget::leaveEvent(event);
}
QAction* SideBar::actionAt(const QPoint &at)
{
int action_y = 0;
for(auto action: mActions)
{
QRect actionRect(0, action_y, rect().width(), action_height);
if(actionRect.contains(at))
return action;
action_y += actionRect.height();
}
return NULL;
}
#undef action_height
The sample code is here.
Screenshots: