I created a small MVCE example to reproduce aspect ratio issue with an image on QGraphicsView.
Building on this i realized the image scaling calculates the aspect ratio correctly but every time i resize QGraphicsView it ignores my heightForWidth() value (which actually returns the right height).
ImageView.h
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QResizeEvent>
#include <QWidget>
#include <QImage>
#include <QPixmap>
class ImageView : public QGraphicsView
{
Q_OBJECT
public:
ImageView(QWidget* parent = nullptr);
void setImage(const QImage& image);
virtual int heightForWidth(int width) const override;
protected:
virtual void resizeEvent(QResizeEvent *event) override;
private:
QGraphicsScene* m_scene;
QImage m_image;
QPixmap m_pixImage;
int m_rows;
int m_cols;
};
ImageView.cpp
#include "imageview.h"
#include <QDebug>
ImageView::ImageView(QWidget *parent) :
QGraphicsView{parent},
m_scene{new QGraphicsScene{this}},
m_rows{0},
m_cols{0}
{
setScene(m_scene);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QSizePolicy p(QSizePolicy::Minimum,QSizePolicy::Minimum);
p.setHeightForWidth(true);
setSizePolicy(p);
setFrameStyle(0);
setMouseTracking(true);
}
void ImageView::setImage(const QImage &image)
{
m_image = image;
m_cols = image.width();
m_rows = image.height();
m_pixImage = QPixmap::fromImage(image);
m_scene->addPixmap(m_pixImage);
fitInView(m_scene->itemsBoundingRect(),Qt::KeepAspectRatio);
}
/* virtual */ int ImageView::heightForWidth(int width) const
{
int height = (m_cols != 0) ? width * m_rows / m_cols : width;
qDebug() << "Height for width: " << height;
return height;
}
void ImageView::resizeEvent(QResizeEvent *event)
{
m_scene->clear();
qDebug() << "Graphics View: " << event->size();
QPixmap pxImg = m_pixImage.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
if(!pxImg.isNull())
{
qDebug() << "Image: " << pxImg.size();
qDebug() << "Graphics View: " << event->size();
m_scene->addPixmap(pxImg);
}
fitInView(m_scene->itemsBoundingRect(),Qt::KeepAspectRatio);
QGraphicsView::resizeEvent(event);
}
MainWindow.h
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->graphicsView->setImage(QImage("656780.png"));
}
MainWindow::~MainWindow()
{
delete ui;
}
Output:
Height for width: 586 // ideal height for the width to maintain aspect ratio (width should be 781)
Graphics View: QSize(782, 544) // instead height is set to 544 (width should be 725)
Image: QSize(725, 544) // scaling the image to the resize event size with aspect ratio
// turns out 725 is right for a width of 544
Graphics View: QSize(782, 544) // same here
As a result of the output my image is never scaled to the exact size of the QGraphicsView on resizing.
How do i solve this? Is my calculation wrong? Or am i missing something?
Okay after struggling a lot i realized i need not scale the image manually in QGraphicsView. fitInView() actually does this automatically. To answer my question, KeepAspectRatioByExpanding is the answer.
void ImageView::resizeEvent(QResizeEvent *event)
{
fitInView(m_scene->itemsBoundingRect(),Qt::KeepAspectRatioByExpanding);
QGraphicsView::resizeEvent(event);
}
This scales the image along with the size of QGraphicsView. One must keep in mind to allow scrollbars to show up if the image is upscaled or shrinked below its original size.
Related
I am working on Falling ball application,
About the application: the balls keep on falling at regular intervals, and there is a basket that catches the balls. If caught then we earn a point if not then lose one life.
For this application, I am using qt and CPP.
I have created one class Sprite with specifications regarding the initial position of the ball(x and y) and speed (dx and dy).
I am using one timer for the speed of the ball, but I need to another timer for ball creation at regular intervals. Could anyone help me with how to implement the ball creation at regular intervals?
sprite.h
--------
#ifndef SPRITE_H
#define SPRITE_H
#include <QTimer>
#include <QPainter>
#include <QWidget>
/**
* Header class for Sprite
*/
class Sprite : public QTimer
{
public:
Sprite(QWidget *parent);
//draw() to draw a sprite
void draw(QPainter &painter);
protected:
//This timerEvent will be called after certain time prescribed.
virtual void timerEvent(QTimerEvent *e) override;
int x;//position of sprite in x-direction.
int y;//position of sprite in y-direction.
int dx;//difference in x-direction position.
int dy;//difference in y-direction position.
int x1;//position of the basket in x-direction
int y1;//position of the basket in y-direction.
QWidget *parent;//parent class for all widgets.
};
#endif // SPRITE_H
sprite.cpp
-----------
#include "sprite.h"
#include <QDebug>
Sprite::Sprite(QWidget *parent):parent(parent)
{
QRect rct = parent->rect();
x = rand() % rct.width();//randomly initialize the x-position for the sprite.
y=rct.height()*0.05;//start position for the sprite is about 5% after the top of the menu bar.
dx = rand() % 10;//the speed is randomly set in x-direction.
// dy = rand() % 10;//the speed is randomly set in y-direction.
dy = 4;
x1=rct.width()/2;
y1 = rct.height()-80;
start(10);
}
void Sprite::draw(QPainter &painter)
{
qDebug() <<"Sprite::draw() called";
painter.drawEllipse(x, y, 15, 15);//ball
painter.drawRect( x1, y1, 80, 30);//basket
}
void Sprite::timerEvent(QTimerEvent *)
{
qDebug("timerEvent called");
QRect rct = parent->rect();
if ( x > rct.width() || x < 0)
dx *= -1;
if ( y > rct.height() || y < 0){}
x += dx;
y += dy;
parent->update();
}
mainwindow.cpp
-----------------
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() <<"mainWindow constructor";
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, this,&MainWindow::onTimer);
timer.start(1000);
}
MainWindow::~MainWindow()
{
qDebug() <<"mainWindow destructor";
delete ui;
}
void MainWindow::onTimer()
{
std::cout << "Tick!-----------------------------------------------------------------------------" << std::endl;
}
void MainWindow::paintEvent(QPaintEvent *)
{
qDebug() <<"painEvent() called";
QPainter painter(this);
painter.fillRect(rect(), QBrush(QColor(Qt::white)));
painter.setPen(Qt::black);
painter.setBrush(QBrush(QColor(Qt::darkBlue)));
emit draw(painter);
}
MainWindow.h
-----------
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "sprite.h"
#include <balls.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void draw(QPainter &painter);
void move(int nDirection);
protected:
//This event handler captures all the paint events request to repaint all or part of the widget.
void paintEvent(QPaintEvent* event)override;
public slots:
void onTimer();
private slots:
void on_actionStart_triggered();
void on_actionStop_triggered();
private:
Ui::MainWindow *ui;
Sprite *sprite;
};
#endif // MAINWINDOW_H
How can I create balls at regular intervals, any sudo code will help.
Or any better way to implement this is also welcome.
Calling QObject::startTimer twice, will run two timers simultaneously:
int timerId1 = startTimer(2000);
int timerId2 = startTimer(10);
void MyObject::timerEvent(QTimerEvent *event)
{
if (event->timerId() == timerId1)
; // 2000ms timer is fired
else if (event->timerId() == timerId2)
; // 10ms timer is fired
}
Also the Qt documentation itself contains a clear example of this use case.
An alternative is to use two QTimer objects, and connect a slot to the timeout signal of each QTimer object.
I have an application which shows some images in a grid and rest of the widgets in their respective positions. Something like this
Drawing inspiration from this i managed to solve keeping the aspect ratio of an image while resizing.
What are the issues:
The QLabel widgets(image widgets) overlap if QMainWindow is shrinked. Check the image below
The whole application is apparently not suited for different screens. Running this application on a laptop, layout is completely messed up.
What have i done:
Here is the MVCE code i have created
//ImageWidget.h
#ifndef IMAGEWIDGET_H
#define IMAGEWIDGET_H
#include <QLabel>
#include <QResizeEvent>
#include <QWidget>
class ImageWidget : public QLabel
{
Q_OBJECT
public:
explicit ImageWidget(QWidget* parent = nullptr);
virtual QSize sizeHint() const;
QPixmap scaledPixmap() const;
virtual int widthForHeight(int height) const;
public slots:
void setPixmap ( const QPixmap& p);
void resizeEvent(QResizeEvent* ev);
private:
QPixmap pix;
};
#endif // IMAGEWIDGET_H
ImageWidget.cpp
#include "imagewidget.h"
ImageWidget::ImageWidget(QWidget* parent) :
QLabel(parent)
{
setStyleSheet("QLabel{margin-left: 10px; border-radius: 25px; background: white; color: #4A0C46;}");
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy.setWidthForHeight(true);
setSizePolicy(sizePolicy);
setMinimumSize(sizeHint());
}
void ImageWidget::setPixmap (const QPixmap& p)
{
pix = p;
QLabel::setPixmap(scaledPixmap());
}
/* virtual */ int ImageWidget::widthForHeight(int height) const
{
return pix.isNull() ? height * pix.height() / pix.width() : height;
}
QSize ImageWidget::sizeHint() const
{
if(pix.width() != 0)
{
int h = this->height();
return QSize(widthForHeight(h), h);
}
else
{
return QSize(300, 300);
}
}
QPixmap ImageWidget::scaledPixmap() const
{
auto scaled = pix.scaled(this->size() * devicePixelRatioF(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
scaled.setDevicePixelRatio(devicePixelRatioF());
return scaled;
}
void ImageWidget::resizeEvent(QResizeEvent* )
{
if (!pix.isNull())
{
QLabel::setPixmap(scaledPixmap());
}
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QTableWidget>
#include "imagewidget.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
virtual void resizeEvent(QResizeEvent* event) override;
private:
Ui::MainWindow *ui;
ImageWidget* lbl1;
ImageWidget* lbl2;
ImageWidget* lbl3;
ImageWidget* lbl4;
QPushButton* btn1;
QPushButton* btn2;
QPushButton* btn3;
QPushButton* btn4;
QTableWidget* tableWidget;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QTabWidget>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QTabBar>
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVBoxLayout* mainLayout = new QVBoxLayout;
QVBoxLayout* tabLay = new QVBoxLayout;
QHBoxLayout* buttonLay = new QHBoxLayout;
QGridLayout* gridLay = new QGridLayout;
QHBoxLayout* dockLay = new QHBoxLayout;
btn1 = new QPushButton(this);
btn1->setText("Button1");
btn2 = new QPushButton(this);
btn2->setText("Button2");
btn3 = new QPushButton(this);
btn3->setText("Button3");
btn4 = new QPushButton(this);
btn4->setText("Button4");
QTabWidget* tabView = new QTabWidget(this);
tabView->addTab(new QWidget(), "Table");
tabView->setMinimumSize(500, 300);
tabView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
tableWidget = new QTableWidget(this);
tableWidget->setFixedHeight(200);
lbl1 = new ImageWidget(this);
lbl2 = new ImageWidget(this);
lbl3 = new ImageWidget(this);
lbl4 = new ImageWidget(this);
QPixmap lbl1Pix("1.png");
QPixmap lbl2Pix("2.png");
QPixmap lbl3Pix("3.png");
QPixmap lbl4Pix("4.png");
lbl1->setPixmap(lbl1Pix);
lbl1->show();
lbl2->setPixmap(lbl2Pix);
lbl2->show();
lbl3->setPixmap(lbl3Pix);
lbl3->show();
lbl4->setPixmap(lbl4Pix);
lbl4->show();
buttonLay->addWidget(btn1);
buttonLay->addWidget(btn2);
buttonLay->addWidget(btn3);
buttonLay->addWidget(btn4);
tabLay->addWidget(tabView);
gridLay->addWidget(lbl1, 0, 0);
gridLay->addWidget(lbl2, 0, 1);
gridLay->addWidget(lbl3, 1, 0);
gridLay->addWidget(lbl4, 1, 1);
dockLay->addLayout(gridLay);
dockLay->addLayout(tabLay);
mainLayout->addLayout(dockLay);
mainLayout->addLayout(buttonLay);
mainLayout->addWidget(tableWidget);
centralWidget()->setLayout(mainLayout);
setMinimumSize(200,200);
show();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::resizeEvent(QResizeEvent * /*event*/)
{
// get label dimensions
int h = lbl1->height();
int w = lbl1->widthForHeight(h);
lbl1->setFixedWidth(w);
lbl2->setFixedWidth(w);
lbl3->setFixedWidth(w);
lbl4->setFixedWidth(w);
}
Main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
What do i want:
Set a minimum size to the whole application so that the imagewidgets do not overlap.
Make the whole app work on any PC or laptop screen with different resolutions.
The imagewidgets should take up available space while keeping the aspect ratio of the images and the QTabWidget on the right should have a fixed size.
Maybe there is an easy solution but i am bit confused with Qt Layout management system.
EDIT1: Added the image with overlapping widgets
Here is what I think is happening. When you set the pixmap like here:
QLabel::setPixmap(scaledPixmap());
The label will set the size of the images as the minimum size. And from that point on the label can not be resized any smaller.
The solution I have found around this is to set the following resize flags for the QLabel in the constructor:
QSizePolicy sizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
setSizePolicy(sizePolicy);
This way the QLabel will be resizable at all times. Or you might have to adapt the minimum width.
I am not sure if that is even it, but maybe it is a good start. Let me know what you make of this.
I'm trying to create a simple frame in Qt with a tick and some text. I made two new label implementations because I wanted the labels to dynamically fill all the available space but when I resize the window the sizes are off, as shown by the qDebug output, which represents the size of the image label:
Resized: 244 , 244 <-- Window first created
Resized: 305 , 305 <-- Window maximized
Resized: 135 , 135 <-- Window restored to original size
As you can see, when the window is restored to its original size the image is not. The last size should be 244, 244.
The code which describes the behaviour of the two widgets is the following:
"widgets.h":
/*
* This file includes many custom widgets.
*/
#ifndef APOCRYPHA_WIDGETS
#define APOCRYPHA_WIDGETS
#include <QWidget>
#include <QLabel>
#include <QTimer>
#include <QPixmap>
#include <QResizeEvent>
#include <QPaintEvent>
class AutoTextLabel : public QLabel {
Q_OBJECT
public:
explicit AutoTextLabel(QWidget* parent);
AutoTextLabel(QWidget* parent, QString text);
protected:
void resizeEvent(QResizeEvent* event) override;
private:
QTimer* resizeTimer;
private slots:
void onResizeEnd();
};
class AutoImageLabel : public QLabel {
Q_OBJECT
public:
explicit AutoImageLabel(QWidget* parent);
AutoImageLabel(QWidget* parent, const QPixmap& pixmap);
void setFillOrientation(int orientation);
QSize sizeHint() const override;
public slots:
void setPixmap(const QPixmap &newPix);
void resizeEvent(QResizeEvent* event) override;
protected:
// void paintEvent(QPaintEvent* event) override;
private:
int fillOrientation;
int widthForHeight(int h) const;
int heightForWidth(int w) const override;
QPixmap scaledPixmap() const;
QPixmap labelPixmap;
};
#endif //APOCRYPHA_WIDGETS
"widgets.cpp":
/*
* This file includes many custom widgets.
*/
#include "widgets.h"
#include <QPainter>
#include <QDebug>
AutoTextLabel::AutoTextLabel(QWidget *parent, QString text) : QLabel(text, parent){
// Enable antialiasing
QFont aaFont(font());
aaFont.setStyleStrategy(QFont::PreferAntialias);
setFont(aaFont);
// This timer is used to fire a slot when a window is resized
resizeTimer = new QTimer();
resizeTimer->setSingleShot(true);
connect(resizeTimer, SIGNAL(timeout()), SLOT(onResizeEnd()));
}
AutoTextLabel::AutoTextLabel(QWidget *parent) : AutoTextLabel(parent, "") {}
void AutoTextLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
// Only fire when 25ms have passed since the last resize.
resizeTimer->start(25);
}
void AutoTextLabel::onResizeEnd() {
QFont updatedFont(font());
// Resize Text
if (!text().isEmpty()){
int fontSize = 1;
updatedFont.setPixelSize(fontSize);
QRect boundingRectangle;
// Update bounding rectangle
if (wordWrap())
boundingRectangle = QFontMetrics(updatedFont).boundingRect(contentsRect(), Qt::TextWordWrap, text());
else
boundingRectangle = QFontMetrics(updatedFont).boundingRect(text());
while (boundingRectangle.height() <= contentsRect().height()) {
fontSize++;
updatedFont.setPixelSize(fontSize);
// Update bounding rectangle
if (wordWrap())
boundingRectangle = QFontMetrics(updatedFont).boundingRect(contentsRect(), Qt::TextWordWrap, text());
else
boundingRectangle = QFontMetrics(updatedFont).boundingRect(text());
}
updatedFont.setPixelSize(fontSize - 1);
setFont(updatedFont);
}
}
/* Auto Image Label */
AutoImageLabel::AutoImageLabel(QWidget *parent, const QPixmap &pixmap) : QLabel(parent) {
setMinimumSize(1, 1);
setScaledContents(false);
setPixmap(pixmap);
}
AutoImageLabel::AutoImageLabel(QWidget *parent) : QLabel(parent) {
setScaledContents(false);
}
void AutoImageLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
if(!labelPixmap.isNull())
QLabel::setPixmap(scaledPixmap());
qDebug() << "Resized: " << scaledPixmap().width() << ", " << scaledPixmap().height();
}
int AutoImageLabel::widthForHeight(int h) const {
return labelPixmap.isNull() ? width() : (labelPixmap.width() * h) / labelPixmap.height();
}
int AutoImageLabel::heightForWidth(int w) const {
return labelPixmap.isNull() ? height() : (labelPixmap.height() * w) / labelPixmap.width();
}
void AutoImageLabel::setFillOrientation(int orientation) {
this->fillOrientation = orientation;
}
QSize AutoImageLabel::sizeHint() const {
if (fillOrientation == Qt::Horizontal)
return QSize(width(), heightForWidth(width()));
else
return QSize(widthForHeight(height()), height());
}
QPixmap AutoImageLabel::scaledPixmap() const {
return labelPixmap.scaled(sizeHint(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
void AutoImageLabel::setPixmap(const QPixmap &newPix) {
labelPixmap = newPix;
QLabel::setPixmap(scaledPixmap());
}
"other_frames.h":
//
// Created by Riccardo on 18/09/2017.
//
#ifndef APOCRYPHA_OTHER_FRAMES_H
#define APOCRYPHA_OTHER_FRAMES_H
#include <QFrame>
#include <QLabel>
#include <QGridLayout>
#include <QWidget>
#include <QResizeEvent>
#include <QPixmap>
#include <QTimer>
#include "widgets.h"
class ConfirmationFrame : public QFrame {
Q_OBJECT
public:
explicit ConfirmationFrame(QWidget* parent);
ConfirmationFrame(QWidget* parent, const QString& text);
private:
QGridLayout* layout;
AutoImageLabel* imageLabel;
AutoTextLabel* textLabel;
};
#endif //APOCRYPHA_OTHER_FRAMES_H
"other_frames.cpp":
//
// Created by Riccardo on 18/09/2017.
//
#include "other_frames.h"
#include <QDebug>
ConfirmationFrame::ConfirmationFrame(QWidget* parent, const QString &text) : QFrame(parent) {
textLabel = new AutoTextLabel(this, text);
QPixmap pix(":/images/check-tick.png");
imageLabel = new AutoImageLabel(this, pix);
textLabel->setAlignment(Qt::AlignCenter);
imageLabel->setAlignment(Qt::AlignCenter);
textLabel->setWordWrap(true);
// Green Background
setStyleSheet("background-color: rgba(106, 242, 94, 1);");
layout = new QGridLayout();
layout->setSpacing(0);
layout->setContentsMargins(32, 32, 32, 32);
layout->setRowStretch(0, 1);
layout->setRowStretch(1, 1);
layout->addWidget(imageLabel, 0, 1);
layout->addWidget(textLabel, 1, 1);
setLayout(layout);
}
ConfirmationFrame::ConfirmationFrame(QWidget *parent) : ConfirmationFrame(parent, "") {
}
"window_main.h":
#ifndef WINDOW_MAIN_H
#define WINDOW_MAIN_H
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QGridLayout>
#include <QFrame>
#include <QScreen>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
QFrame *mainFrame;
void center(QScreen* screen);
void autoSetSize(QScreen* screen);
private:
void createMenu();
// Components
QGridLayout *mainLayout;
QMenuBar *menuBar;
QMenu *fileMenu;
};
#endif // WINDOW_MAIN
"window_main.cpp":
#include "window_main.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
mainFrame = new QFrame();
mainLayout = new QGridLayout();
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
createMenu();
mainFrame->setStyleSheet("background-color: red;");
mainFrame->setLayout(mainLayout);
setCentralWidget(mainFrame);
}
void MainWindow::createMenu(){
menuBar = new QMenuBar;
fileMenu = new QMenu(tr("&File"), this);
menuBar->addMenu(fileMenu);
setMenuBar(menuBar);
}
void MainWindow::center(QScreen *screen) {
QSize size = screen->availableSize();
int x = size.width() / 2 - width() / 2;
int y = size.height() / 2 - height() / 2;
move(x, y);
}
void MainWindow::autoSetSize(QScreen *screen) {
QSize screenSize = screen->availableSize();
// TODO Math.round
setMinimumSize(QSize((int)(screenSize.width() / 1.25), (int)(screenSize.height() / 1.25)));
}
"main.cpp":
#include <QApplication>
#include <iostream>
#include <QFile>
#include "quiz/choice.h"
#include "quiz/question.h"
#include "quiz/quizmaker.h"
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#include <src/user_interface/other_frames.h>
#include "user_interface/window_main.h"
#include <QScreen>
#include <QFontDatabase>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// Set Application Parameters
QCoreApplication::setOrganizationName("Riccardo Fagiolo");
QCoreApplication::setOrganizationDomain("kopharex.me");
QCoreApplication::setApplicationName("Apocrypha");
// Set application font
const int id = QFontDatabase::addApplicationFont(":/fonts/montserrat/Montserrat-Regular.otf");
QString family = QFontDatabase::applicationFontFamilies(id).at(0);
QFont font(family);
font.setStyleStrategy(QFont::PreferAntialias);
a.setFont(font);
// App Settings
QSettings settings;
settings.setValue("data_dir", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
// Create UI
auto* window = new MainWindow();
ConfirmationFrame* cframe = new ConfirmationFrame(window, "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?");
window->mainFrame->layout()->addWidget(cframe);
window->autoSetSize(a.primaryScreen());
//cframe->updateTextLabel();
window->show();
window->center(a.primaryScreen());
// [...] - Nothing related to user interface.
return a.exec();
}
Here is a screenshot of the current MainWindow and ConfirmationFrame to give you an idea of what i'm trying to accomplish:
Window Screenshot
All comments regarding the code are welcome.
Thanks for any help,
Riccardo
Hello I tried to fix the resizing issue with an hack.
Before starting the timer to resize the text, just reduce its font to a 1 pixel font:
void AutoTextLabel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
// set a very small font, then start the timer
QFont updatedFont(font());
updatedFont.setPixelSize(1);
setFont(updatedFont);
// Only fire when 25ms have passed since the last resize.
resizeTimer->start(25);
}
Can the effect be acceptable in your opinion?
I'm quite new on qt. I'm trying to create a tab on my ui that can give the option to count how many usb cameras are connected to computer and list then using OPENCV. After that, I want to select one of then and start streaming a video into a custom widget, using paintEvent. My main difficulties are: how to start and stop a streaming when a button is pressed. Below i'm posting my code.
mainwindow.h
//includes...
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void on_CheckCamerasButton_clicked();
void on_StartStreamingButton_clicked();
private:
Ui::MainWindow *ui;
cameraimage camera;
};
#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;
}
void MainWindow::on_StartStreamingButton_clicked(){
camera.startStreaming();
}
void MainWindow::on_CheckCamerasButton_clicked(){
camera.stopStreaming();
}
cameraimage.h
//includes...
class cameraimage : public QWidget
{
Q_OBJECT
public:
explicit cameraimage(QWidget *parent = nullptr);
private:
QPoint mPoint;
QTimer *timer;
cv::VideoCapture captureVideo;
public slots:
void paintEvent(QPaintEvent * event);
void startStreaming();
void stopStreaming();
};
#endif // CAMERAIMAGE_H
cameraimage.cpp
#include "cameraimage.h"
cameraimage::cameraimage(QWidget *parent) : QWidget(parent)
{
setMouseTracking(true);
}
void cameraimage::startStreaming(){
qDebug() << "Starting Streaming";
captureVideo.open(-1);
if (captureVideo.isOpened() == false){
qDebug() << "Camera can't open";
return;
}
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1);
}
void cameraimage::stopStreaming(){
captureVideo.release();
timer->stop();
}
void cameraimage::paintEvent(QPaintEvent *){
cv::Mat tmpImage;
cv::Mat image;
captureVideo.read(tmpImage);
if (tmpImage.empty() == true){
qDebug() << "EMPTY!";
return;
}
cv::cvtColor(tmpImage, image, CV_BGR2RGB);
QImage img((const unsigned char*)(image.data), image.cols, image.rows, QImage::Format_RGB888);
QPixmap pixmap = QPixmap::fromImage(img);
QPainter painter(this);
float comprimento = 1.0*width()/pixmap.width();
float altura = 1.0*height()/pixmap.height();
float ratio = 0.;
if (comprimento<=altura)
ratio = comprimento;
else
ratio = altura;
QSize size = ratio*pixmap.size();
size.setHeight(size.height()-10);
QPoint p;
p.setX(0 + (width()-size.width())/2);
p.setY(5);
painter.drawPixmap(QRect(p, size), pixmap.scaled(size, Qt::KeepAspectRatio));
}
I've got the following output after clicked on StartStreamingButton on MainWindow:
Starting Streaming...
EMPTY!
EMPTY!
EMPTY!
...
Can someone help me please?
Best regards :D
I had a properly working Qt code with custom widget. Then I refractored a signal name and undo the action immediately. Since then I'm getting -
mainwindow.obj:-1: error: LNK2001: unresolved external symbol "protected: virtual void
__thiscall QWidget::mouseDoubleClicked(class QMouseEvent *)"
?mouseDoubleClicked#QWidget##MAEXPAVQMouseEvent###Z)
mouseDoubleClicked is the method which wmits the refractored signal. Although I undo the refractoring action but still it's giving this error. The same code did run bofe that.
I've tried cleaning and running qMake but no success. Even created a new project and copy pasted the file contents but still got the same error.
These are the files-
mainwindow.h
#pragma once;
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
myqtimagewidget.h
#pragma once
#include <QWidget>
#include <QImage>
#include <QPainter>
#include <QtGui/QMouseEvent>
#include "opencv2/opencv.hpp"
class MyQtImageWidget : public QWidget
{
Q_OBJECT
protected:
QImage qimage;
cv::Mat temp;
void paintEvent(QPaintEvent*);
void mouseDoubleClicked(QMouseEvent* event);
// void mousePressEvent(QMouseEvent *event);
public:
explicit MyQtImageWidget(QWidget *parent = 0) : QWidget(parent) {}
QSize sizeHint() const {return qimage.size(); }
QSize minimumSizeHint() const {return qimage.size(); }
signals:
void signalMouseMoved(QString);
public slots:
void showImage(const cv::Mat &image);
};
myqtimagewidget.cpp
#include "myqtimagewidget.h"
#include "opencv2/opencv.hpp"
using namespace cv;
void MyQtImageWidget::showImage(const Mat &image)
{
//resize image to fit widget. Maintains aspect ratio
int width = this->maximumWidth();
int hight = this->maximumHeight();
Mat rescaledImg;
if(image.cols > width || image.rows > hight)
{
// debug info --v
QString data = "Original dimention: " + QString::number(image.cols) + "x" + QString::number(image.rows);
qDebug(data.toStdString().c_str());
// debug info --^
double aspectRatio = (double)image.cols / (double)image.rows; // width / height
cv::Size2i newSize;
// debug info --v
qDebug(std::to_string(width).c_str());
qDebug(std::to_string(hight).c_str());
// debug info --^
if(image.cols >= image.rows)
{
newSize = cv::Size(width, width / aspectRatio);
}
else
{
newSize = cv::Size(hight * aspectRatio, hight);
}
cv::resize(image, rescaledImg, newSize, 1, 1);
// debug info --v
data = "Resized dimention: " + QString::number(rescaledImg.cols) + "x" + QString::number(rescaledImg.rows);
qDebug(data.toStdString().c_str());
// debug info --^
}
else
rescaledImg = image;
// Convert the image to the RGB888 format
switch (rescaledImg.type())
{
case CV_8UC1:
cvtColor(rescaledImg, temp, CV_GRAY2RGB);
break;
case CV_8UC3:
cvtColor(rescaledImg, temp, CV_BGR2RGB);
break;
}
// QImage needs the data to be stored continuously in memory
assert(temp.isContinuous());
// Assign OpenCV's image buffer to the QImage. Note that the bytesPerLine parameter
// (http://qt-project.org/doc/qt-4.8/qimage.html#QImage-6) is 3*width because each pixel
// has three bytes.
qimage = QImage(temp.data, temp.cols, temp.rows, temp.cols*3, QImage::Format_RGB888);
repaint();
}
void MyQtImageWidget::paintEvent(QPaintEvent *event)
{
// Display the image
QPainter painter(this);
painter.drawImage(QPoint(0,0), qimage);
painter.end();
}
void MyQtImageWidget::mouseDoubleClicked(QMouseEvent *event)
{
int x = event->pos().x(), y = event->pos().y();
QString data = "x: " + QString::number(x) + ", y: " + QString::number(y);
emit MyQtImageWidget::signalMouseMoved(data);
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "opencv2/opencv.hpp"
#include "myqtimagewidget.h"
using namespace cv;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QString imageSrc = "E:\\Images\\Panorama\\Harshil3578_stitch.jpg";
Mat image = imread(imageSrc.toStdString(), IMREAD_COLOR);
}
MainWindow::~MainWindow()
{
delete ui;
}
In the GUI I have a widget promoted to MyQtImageWidget and connected to a QLabel.
Consider what the linker is telling you:
QWidget has a virtual method named mouseDoubleClicked.
That method is not implemented.
The problem is that QWidget does not have a virtual method named mouseDoubleClicked. My best guess is that during your refactoring, you somehow modified the header file for QWidget, renaming an existing virtual function to mouseDoubleClicked.
If that's the case, getting a fresh copy of qwidget.h (or re-installing Qt) should resolve the problem.