I wrote a simple combobox with checkboxes as its items. When I select an item and press button, it shows what item I selected. But I would like to do something like this: I have an item called "all" - when I select it, all other items should be selected, and when I unselect it, all others items should be unselected. Any ideas?
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStandardItemModel>
#include <QComboBox>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QStandardItemModel *model;
private slots:
void buttonclicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->model = new QStandardItemModel(4, 1); // 4 rows, 1 col
for (int r = 0; r < 4; ++r)
{
QStandardItem* item;
if(r == 0)
item = new QStandardItem(QString("All"));
else
item = new QStandardItem(QString("Item %0").arg(r));
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setData(Qt::Unchecked, Qt::CheckStateRole);
model->setItem(r, 0, item);
}
ui->comboBox->setModel(model);
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(buttonclicked()));
}
void MainWindow::buttonclicked()
{
unsigned int i;
for(i=0; i<ui->comboBox->count(); i++)
{
QModelIndex index = ui->comboBox->model()->index(i, 0);
QVariant v = index.data(Qt::CheckStateRole);
int j = v.toInt();
if(j == 2)
{
QModelIndex ii = ui->comboBox->model()->index(i, 0);
QString text = ii.data(Qt::DisplayRole).toString();
qDebug() << text;
}
}
qDebug()<<"";
}
MainWindow::~MainWindow()
{
delete ui;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
ui file
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QComboBox" name="comboBox">
<property name="geometry">
<rect>
<x>100</x>
<y>50</y>
<width>201</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>110</x>
<y>100</y>
<width>181</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Check what was selected</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
It looks like this:
Implement a qtslot for this button such that when it is clicked/triggered you will iterate over all of the checkboxes within the desired combobox and edit their checked state.
I have been learning how to use model subclassing while trying replicate the checkbox list using your code and have found a way to trigger a signal when checked status in any of the checkboxes populating the list changes.
Declaring a public slot in MainWindow.h:
public slots:
void itemCheck(QModelIndex a, QModelIndex b);
Implementing the slot in MainWindow.cpp:
void MainWindow::void itemCheck(QModelIndex a, QModelIndex b)
{
qDebug() << "check event";
}
And connecting this new slot to the model's dataChanged signal:
connect(ui->comboBox->model(), SIGNAL(dataChanged(QModelIndex, QModelIndex, QVector<int>)), this, SLOT(itemCheck(QModelIndex, QModelIndex)));
I think, it is possible to filter the event for the "All" checkbox and set the other ones appropriately.
Managed to get it working:
void MainWindow::itemCheck(QModelIndex a, QModelIndex b)
{
qint16 i;
QModelIndex cycleIndex;
// if "All" checked
if (a.row() == 0)
{
// blocking new dataChanged singnal while managing other checkboxes
ui->comboBox->model()->blockSignals(true);
// setting other checkboxes appropriately
if (a.data(Qt::CheckStateRole).toBool())
{
for (i = 1; i<ui->comboBox->count(); i++)
{
cycleIndex = ui->comboBox->model()->index(i, 0);
ui->comboBox->model()->setData(cycleIndex, Qt::Checked, Qt::CheckStateRole);
}
} else
{
for (i = 1; i<ui->comboBox->count(); i++)
{
cycleIndex = ui->comboBox->model()->index(i, 0);
ui->comboBox->model()->setData(cycleIndex, Qt::Unchecked, Qt::CheckStateRole);
}
}
ui->comboBox->model()->blockSignals(false);
}
}
Hope this will help someone learning Qt from scratch.
Related
I have this Qt Project where I can capture an Image from WebCam and afterwards draw on it using OpenCV MouseCallbacks (I perform the drawing on the imshow, not the QGraphicsView).
I can capture the Image and display it using my pushbutton, but I can't draw anything (it even crashes after I click on the Image).
Codes:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPixmap>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QTimer>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <vector>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
static void DrawScanPoints(int event, int x, int y, int flag, void* param);
void DrawScanPoints(int event, int x, int y);
private slots:
void on_pbt_Capture_clicked();
void on_pbt_Scan_clicked();
public slots:
void UpdateFrame();
private:
Ui::MainWindow *ui;
cv::VideoCapture videoCap;
cv::Mat liveImage, inputImage;
bool camRun = true;
};
#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);
videoCap.open(0);
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(UpdateFrame()));
timer->start(20);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::UpdateFrame()
{
if(camRun)
{
videoCap.read(liveImage);
QImage image = QImage(liveImage.data, liveImage.cols, liveImage.rows, QImage::Format_RGB888).rgbSwapped();
QGraphicsScene* scene = new QGraphicsScene(this);
scene->addPixmap(QPixmap::fromImage(image));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
}
}
void MainWindow::DrawScanPoints(int event, int x, int y, int flag, void* param)
{
MainWindow* mw = reinterpret_cast<MainWindow*>(param);
mw->DrawScanPoints(event, x, y);
}
void MainWindow::DrawScanPoints(int event, int x, int y)
{
if(event & cv::EVENT_LBUTTONDOWN)
{
cv::Point pt = cv::Point(x, y);
cv::circle(inputImage, pt, 10, cv::Scalar(0, 255, 0), 1, cv::LINE_AA);
}
}
void MainWindow::on_pbt_Capture_clicked()
{
camRun = false;
inputImage = liveImage;
cv::namedWindow("Capture");
cv::setMouseCallback("Capture", DrawScanPoints, this);
while(1)
{
cv::imshow("Capture", inputImage);
cv::waitKey(0);
cv::destroyAllWindows();
}
}
void MainWindow::on_pbt_Scan_clicked()
{
}
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.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>608</width>
<height>440</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QGraphicsView" name="graphicsView">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>501</width>
<height>391</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="pbt_Capture">
<property name="geometry">
<rect>
<x>520</x>
<y>10</y>
<width>81</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Capture</string>
</property>
</widget>
<widget class="QPushButton" name="pbt_Scan">
<property name="geometry">
<rect>
<x>520</x>
<y>40</y>
<width>80</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>Scan</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>608</width>
<height>17</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
I'm not able to reproduce this locally (I'm missing some of the source files, e.g. main and the UI) but probably you should pass this in setMouseCallback as the last parameter. See How should I pass a pointer-to-member-function to setMouseCallback in OpenCV?
Edit after question was updated:
Remove cv::destroyAllWindows from on_pbt_Capture_clicked; replace cv::waitKey(0); with cv::waitKey(30);:
void MainWindow::on_pbt_Capture_clicked()
{
camRun = false;
inputImage = liveImage;
cv::namedWindow("Capture");
cv::setMouseCallback("Capture", DrawScanPoints, this);
while(1)
{
cv::imshow("Capture", inputImage);
cv::waitKey(30);
}
}
Reason: you've never updated the "capture" window: waitKey(0) waits forever (for any key input) - so your window will be updated with circles only after you press any key on the keyboard. So if you change to waitKey(30) the updates will be happening every 30 [ms] automatically (timeout after which waitKey exits).
No need to destroy and recreate window every time hence removing destroyAllWindows. If you want to be smart about when to update the window in on_pbt_Capture_clicked, use conditional variable. WAit for it inside while(1) and notify_one it from inside DrawScanPoints.
I'm new to Qt. How can i create such a color picker control? I need to place it on another widget or dialog. Selected color should be rounded by white rectangle or made distinguishable in another way.
Which controls i need to use to create such a widget.
You can use QtColorPicker
: The QtColorPicker class provides a widget for selecting colors from a popup color grid.
Users can invoke the color picker by clicking on it, or by navigating to it and pressing Space. They can use the mouse or arrow keys to navigate between colors on the grid, and select a color by clicking or by pressing Enter or Space. The colorChanged() signal is emitted whenever the color picker's color changes.
References:
http://doc.qt.io/qt-4.8/qcolordialog.html
http://docs.huihoo.com/qt/solutions/4/qtcolorpicker/qtcolorpicker.html
Code that generates such a layout can roughly look like:
QWidget w;
QGridLayout *grid = new QGridLayout(&w);
std::vector<QString> colors{ "red", "blue", "green", "yellow",
"magenta", "darkblue", "darkgreen", "black" };
static const int buttonsPerRow = 5;
for (int i = 0; i < 15; ++i)
{
QPushButton *btn = new QPushButton(&w);
// Customize the colour button
btn->setMaximumSize(16, 16);
QString qss = QString("background-color: %1").arg(colors[i % colors.size()]);
btn->setStyleSheet(qss);
grid->addWidget(btn, i / buttonsPerRow, i % buttonsPerRow);
}
w.show();
Not the best aproach, but it is only to show the idea with custom buttons, grid layout and stylesheet. Better will be to create custom Widget and paint inside it yourself.
mainwindow.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>30</x>
<y>20</y>
<width>331</width>
<height>201</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout"/>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>19</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
mainwindow.h
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "custombutton.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->centralWidget->setStyleSheet("background-color: black;");
QList<CustomButton *> * btnlist = new QList<CustomButton *>();
CustomButton * btn = new CustomButton(Qt::green, btnlist);
CustomButton * btn1 = new CustomButton(Qt::yellow, btnlist);
CustomButton * btn2 = new CustomButton(Qt::red, btnlist);
CustomButton * btn3 = new CustomButton(QColor(100,50,240), btnlist);
CustomButton * btn4 = new CustomButton(QColor(50,50,240), btnlist);
CustomButton * btn5 = new CustomButton(QColor(100,155,240), btnlist);
CustomButton * btn6 = new CustomButton(QColor(200,50,240), btnlist);
CustomButton * btn7 = new CustomButton(QColor(0,50,240), btnlist);
btnlist->append(btn);
btnlist->append(btn1);
btnlist->append(btn2);
btnlist->append(btn3);
btnlist->append(btn4);
btnlist->append(btn5);
btnlist->append(btn6);
btnlist->append(btn7);
this->ui->gridLayout->addWidget(btn,1,1);
this->ui->gridLayout->addWidget(btn1,1,2);
this->ui->gridLayout->addWidget(btn2,1,3);
this->ui->gridLayout->addWidget(btn3,1,4);
this->ui->gridLayout->addWidget(btn4,1,5);
this->ui->gridLayout->addWidget(btn5,2,1);
this->ui->gridLayout->addWidget(btn6,2,2);
this->ui->gridLayout->addWidget(btn7,2,3);
}
custombutton.h
#ifndef CUSTOMBUTTON_H
#define CUSTOMBUTTON_H
#include <QWidget>
#include <QPushButton>
#include <QColor>
class CustomButton : public QPushButton
{
public:
CustomButton(QColor color, QList<CustomButton *> *);
public slots:
void release(bool);
private:
QList<CustomButton *> * btn_list;
};
#endif // CUSTOMBUTTON_H
custombutton.cpp
#include "custombutton.h"
CustomButton::CustomButton(QColor color, QList<CustomButton *> * list)
{
this->btn_list = list;
QString StyleSheet = QString("QPushButton:checked { border: 2px solid white; background-color: %1; } QPushButton { border: 0px solid white; background-color: %1; }").arg(color.name());
this->setCheckable(true);
this->setStyleSheet(StyleSheet);
this->setFixedSize(56, 56);
this->show();
connect(this, &CustomButton::toggled, this, &CustomButton::release );
}
void CustomButton::release(bool checked)
{
int ind = this->btn_list->indexOf(this);
CustomButton * tmp = this->btn_list->takeAt(ind);
if(checked)
{
this->setChecked(true);
for(int i = 0; i < this->btn_list->count(); i++)
{
this->btn_list->at(i)->setChecked(false);
}
}
this->btn_list->append(this);
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
It is a working draft. Do not use this in release :) It is only to show an idea how this can be done.
I have created a widget using Qt Creator such a way that it has two sub windows inside a main window and some push buttons to load, save images, set pen width and color to paint on the window.
But when i start to paint it gives me error saying
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setPen: Painter not active
QPainter::drawPoints: Painter not active
Does anyone know what mistake i am doing, i checked threads related to this topic but could not find suitable solution.
I am also new to c++, so please help me to find a solution
This is my code below
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtCore>
#include <QImage>
#include <QColor>
#include <QPoint>
#include <QtGui>
#include <QPainter>
#include <QMainWindow>
#include <QFileDialog>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
bool isModified() const { return modified; }
QColor penColor() const { return newPenColor; }
int penWidth() const { return newPenWidth; }
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
private slots:
void on_open_clicked();
void on_save_clicked();
void on_penWidth_clicked();
void on_penColor_clicked();
private:
Ui::Widget *ui;
QImage image;
QPixmap imageobject;
int newPenWidth;
QColor newPenColor;
bool modified;
bool scribbling;
QPoint firstPoint, secondPoint;
void drawFirstPoint(const QPoint);
void drawSecondPoint(const QPoint);
};
#endif // WIDGET_H
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QtWidgets>
#ifndef QT_NO_PRINTER
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>
#endif
#include <QLabel>
#include <QWidget>
Widget::Widget(QWidget *parent) : QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setAttribute(Qt::WA_StaticContents);
modified = false;
scribbling = false;
newPenWidth = 1;
newPenColor = Qt::blue;
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_open_clicked()
{
QString filename = QFileDialog::getOpenFileName(this, tr("choose"), "", tr("Image(*.png *.jpg *.jpeg *.bmp *.gif)"));
if (QString::compare(filename, QString())!=0)
{
QImage image;
bool valid = image.load(filename);
if (valid)
{
image = image.scaledToWidth(ui->inputWindow->width(), Qt::SmoothTransformation);
ui->inputWindow->setPixmap(QPixmap::fromImage(image));
image = image.scaledToWidth(ui->outputWindow->width(), Qt::SmoothTransformation);
ui->outputWindow->setPixmap(QPixmap::fromImage(image));
}
else
{
//Error handling
}
}
}
void Widget::on_save_clicked()
{
QString filename = QFileDialog::getSaveFileName(this,tr("choose"), "", tr("PNG (*.png);; JPEG (*.jpg *.jpeg);; BMP(*.bmp);; GIF(*.gif)"));
QImage imageobject = image;
imageobject.save(filename);
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QRect dirtyRect = event->rect();
painter.drawImage(dirtyRect, image, dirtyRect);
}
void Widget::mousePressEvent(QMouseEvent *event)
{
scribbling = true;
if (event->button() == Qt::LeftButton && scribbling)
{
firstPoint = event->pos();
drawFirstPoint(firstPoint);
}
else if (event->button() == Qt::RightButton && scribbling) {
secondPoint = event->pos();
drawSecondPoint(secondPoint);
}
}
void Widget::drawFirstPoint(const QPoint)
{
QPainter painter(this);
painter.setPen(QPen(newPenColor, newPenWidth, Qt::SolidLine, Qt::RoundCap,
Qt::RoundJoin));
painter.drawPoint(firstPoint);
modified = true;
int rad = (newPenWidth / 2) + 2;
update(QRect(firstPoint, firstPoint).normalized().adjusted(-rad, -rad, +rad, +rad));
}
void Widget::drawSecondPoint(const QPoint)
{
QPainter painter(this);
painter.setPen(QPen(newPenColor, newPenWidth, Qt::SolidLine, Qt::RoundCap,
Qt::RoundJoin));
painter.drawPoint(secondPoint);
modified = true;
int rad = (newPenWidth / 2) + 2;
update(QRect(secondPoint, secondPoint).normalized().adjusted(-rad, -rad, +rad, +rad));
}
void Widget::on_penWidth_clicked()
{
bool ok;
int newWidth = QInputDialog::getInt(this, tr("Scribble"),
tr("Select pen width:"),
this->penWidth(), 1, 50, 1, &ok);
if (ok)
newPenWidth = newWidth;
}
void Widget::on_penColor_clicked()
{
QColor newColor = QColorDialog::getColor(this->penColor());
if (newColor.isValid())
newPenColor = newColor;
}
widget.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1300</width>
<height>700</height>
</rect>
</property>
<property name="windowTitle">
<string>Test window</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<widget class="QLabel" name="inputWindow">
<property name="geometry">
<rect>
<x>40</x>
<y>120</y>
<width>600</width>
<height>500</height>
</rect>
</property>
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
<property name="cursor">
<cursorShape>CrossCursor</cursorShape>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="QLabel" name="outputWindow">
<property name="geometry">
<rect>
<x>660</x>
<y>120</y>
<width>600</width>
<height>500</height>
</rect>
</property>
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>170</x>
<y>10</y>
<width>651</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="open">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string>OPEN</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="save">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string>SAVE</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="penWidth">
<property name="text">
<string>Pen Width</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="penColor">
<property name="text">
<string>Pen Color</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
All painting on a widget must happen in the paintEvent() function, and you are trying to paint outside of it - that won't work.
You must either find a way to put all your drawing calls inside the paintEvent() function, or draw on a buffer, for example a QPixmap and then draw that pixmap onto the widget in the paintEvent() When you draw on a buffer you can draw from everywhere, the limitation is only for widget drawing. For pixmaps you (usually) must draw from the main thread, if you want to draw from another thread, use QImage instead.
How is it possible to move a QGraphicsscene inside a View?
I have a Scene filling the entire View. The Scene is containing one (will be more later) Item (QImage for now), which is updated every few seconds.
My goal is to move the content of the item with mouseMoveEvent. While the item is beeing updated (lasts a few seconds) i want to move the whole Scene and reset the movement after the item is fully updated.
For now I tried overriding the mouseMoveEvent of QGraphicsScene and emit a signal to PPI-Class containintg the QGraphicsScene Objet. The onMouseMoveEvent-slot then tries to set the sceneRect. But this does not work, I get a StackOverflow-Exception.
How can i get this to work? Thank you!
main.cpp
#include "ppi.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PPI w;
w.show();
return a.exec();
}
ppi.ui (just added a graphicsview with size of 1024x1024 and name gVPPI to the mainwindow)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PPIClass</class>
<widget class="QMainWindow" name="PPIClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1079</width>
<height>1113</height>
</rect>
</property>
<property name="windowTitle">
<string>PPI</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QGraphicsView" name="gVPPI">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>1024</width>
<height>1024</height>
</rect>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1079</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
<include location="ppi.qrc"/>
</resources>
<connections/>
</ui>
ppi.h
#ifndef PPI_H
#define PPI_H
#include <QtGui/QMainWindow>
#include "ui_ppi.h"
#include "ppiscene.h"
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QDebug>
class PPI : public QMainWindow
{
Q_OBJECT
public:
PPI(QWidget *parent = 0, Qt::WFlags flags = 0);
~PPI();
private:
Ui::PPIClass ui;
PPIScene* ppiScene;
QGraphicsPixmapItem *videoPixmap;
QImage newImage();
bool moveReset;
QPointF moveCoordinates;
QPointF startMoveCoordinates;
protected slots:
void onMouseMoved(QPointF coordinates, QGraphicsSceneMouseEvent *event);
};
#endif // PPI_H
ppi.cpp
#include "ppi.h"
PPI::PPI(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
ppiScene = new PPIScene(this);
ppiScene->setSceneRect(0,0,1024,1024);
ui.gVPPI->setScene(ppiScene);
ui.gVPPI->setMouseTracking(true);
videoPixmap = ppiScene->addPixmap(QPixmap::fromImage(newImage()));
startMoveCoordinates = QPointF(0,0);
connect(ppiScene, SIGNAL(mouseMoved(QPointF, QGraphicsSceneMouseEvent*)), this, SLOT(onMouseMoved(QPointF, QGraphicsSceneMouseEvent*)));
}
PPI::~PPI()
{
}
void PPI::onMouseMoved(QPointF coordinates, QGraphicsSceneMouseEvent *event)
{
//move view
if(event->modifiers() & Qt::ControlModifier)
{
moveCoordinates = coordinates;
//ui.gVPPI->translate(moveCoordinates.x() - startMoveCoordinates.x(), moveCoordinates.y() - startMoveCoordinates.y());
ppiScene->setSceneRect(moveCoordinates.x() - startMoveCoordinates.x(), moveCoordinates.y() - startMoveCoordinates.y(), 1024, 1024);
qDebug() << coordinates;
qDebug() << "dx" << moveCoordinates.x() - startMoveCoordinates.x();
qDebug() << "dy" << moveCoordinates.y() - startMoveCoordinates.y();
qDebug() << "";
}
}
QImage PPI::newImage() {
QImage image = QImage(1024, 1024, QImage::Format_Indexed8);
image.setColorCount(256);
for (int i = 0; i < 256; ++i)
image.setColor(i, qRgb(i, 0, 0));
image.fill(200);
return image;
}
ppiscene.h
#ifndef PPISCENE_H
#define PPISCENE_H
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
class PPIScene : public QGraphicsScene
{
Q_OBJECT
public:
PPIScene(QObject *parent);
~PPIScene();
QPointF mouseCoordinates;
private:
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
signals:
void mouseMoved(QPointF coordinates, QGraphicsSceneMouseEvent *event);
};
#endif // PPISCENE_H
ppiscene.cpp
#include "ppiscene.h"
PPIScene::PPIScene(QObject *parent)
: QGraphicsScene(parent)
{
}
PPIScene::~PPIScene()
{
}
void PPIScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
mouseCoordinates = event->scenePos();
emit mouseMoved(mouseCoordinates, event);
QGraphicsScene::mouseMoveEvent(event);
}
I want to use my global qss stylesheet with a derived class. I understand I have to override the paintEvent (style sheet reference , or here).
void CustomWidget::paintEvent(QPaintEvent *) {
QStyleOption opt;
opt.init(this); // tried initFrom too, same result=>not working
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
However, it does not seem to work. With CDerived:QWidget and the following style sheet lines I face:
CDerived { background-color: black; } // no effect
QWidget { background-color: black; } // works
CDerived implements paintEvent as above. Anything else I need to do?
-- Edit / Solution --
Thanks to JK's hint I have figured it out. My above example is actually not correctly reflecting my scenario. My real class resides in a C++ namespace (my mistake I have missed that). So I have to write MyNamespace--CDerived in the qss. See "Widgets inside C++ namespaces"
After I have tried JK's simple example here, I suddenly realized my mistake!
Correct one:
MyNamespace--CDerived { background-color: black; } // works, use -- for ::
Remarks: Relateds SO question (a,b), but with no answer to this particular question. My derived class resides in a C++ namespace.
It is strange....it works fine for me:
untitled.pro:
#-------------------------------------------------
#
# Project created by QtCreator 2014-10-07T11:34:54
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = untitled
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp \
mywidget.cpp
HEADERS += mainwindow.h \
mywidget.h
FORMS += mainwindow.ui
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#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);
}
MainWindow::~MainWindow()
{
delete ui;
}
mainwindow.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="MyWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>70</x>
<y>30</y>
<width>201</width>
<height>121</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>30</x>
<y>20</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>PushButton</string>
</property>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>MyWidget</class>
<extends>QWidget</extends>
<header>mywidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
mywidget.h:
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *e);
};
#endif // MYWIDGET_H
mywidget.cpp:
#include "mywidget.h"
#include <QStyleOption>
#include <QPainter>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
}
void MyWidget::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e)
QStyleOption opt;
opt.init(this); // tried initFrom too, same result=>not working
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("MyWidget { background-color: red; }");
MainWindow w;
w.show();
return a.exec();
}