Using QPainter to draw a line between QWidgets - c++

I'm working on an application where I need to be able to draw a line between two QWidget objects. I have tried quite a few things, but my current attempt (which I think is in the right direction I just think I'm missing something) is to have the containing widget (which I called DrawWidget and which holds the QGridLayout that the QWidget objects are added to) override the paintEvent method and call the QPainter::drawLine() function.
The issues I'm having are that:
No matter how I try to get the position of the widgets, the endpoints of the line are always in the wrong place
Whenever I try to draw a second line, the first line that I drew gets erased.
Here is the paintEvent function of the containing widget:
void paintEvent(QPaintEvent *)
{
if (!drewSinceUpdate){
drewSinceUpdate = true;
QPainter painter(this);
painter.setPen(QPen(Qt::black));
painter.drawLine(start->geometry().center(), end->geometry().center());
}
}
I have tried many different ways to get the correct position of the widgets in the last line of paintEvent, which I will post some of the ways (I can't remember all of them):
painter.drawLine(start->pos(), end->pos());
painter.drawLine(start->mapToGlobal(start->geometry().center()), end->mapToGlobal(end->geometry().center()));
painter.drawLine(this->mapToGlobal(start->geometry().center()), this->mapToGlobal(end->geometry().center()));
painter.drawLine(start->mapTo(this, start->pos()), end->mapTo(this, end->pos()));
painter.drawLine(this->mapFrom(start, start->pos()), this->mapFrom(end, end->pos()));
And just to make my question clear, here is an example of what I am looking for, taken from QT Diagram Scene Example:
But this is what I end up getting:
Thank you for any help you can provide.
NOTE:
-start and end are both QWidget objects which I passed in using another method
-The hierarchy relevant to DrawWidget is:
QMainWindow
->QScrollArea
->DrawWidget
->QGridLayout
->Items <-- These are the things I want to connect
EDIT: To make a Complete and Verifiable example, here is the entirety of the relevant code.
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QPushButton>
#include <QScrollBar>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// Setting up the relevant hierarchy
ui->setupUi(this);
scrollArea = new QScrollArea();
setCentralWidget(scrollArea);
drawWidget = new DrawWidget();
gridLayout = new QGridLayout();
gridLayout->setSpacing(300);
drawWidget->setLayout(gridLayout);
scrollArea->setWidget(drawWidget);
scrollArea->setWidgetResizable(true);
AddItemSlot();
QApplication::connect(scrollArea->horizontalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(scrollHorizontal()));
}
// This is just creating a single one of the example widgets which I want to connect
QWidget* MainWindow::CreateNewItem(){
QWidget* itemWidget = new QWidget();
itemWidget->setStyleSheet("background-color: lightgray");
QHBoxLayout* singleItemLayout = new QHBoxLayout();
itemWidget->setLayout(singleItemLayout);
QTextEdit* textEdit = new QTextEdit(std::to_string(counter++).c_str());
textEdit->setStyleSheet("background-color:white;");
singleItemLayout->addWidget(textEdit);
QVBoxLayout* rightSidePanel = new QVBoxLayout();
rightSidePanel->setAlignment(Qt::AlignTop);
QPushButton* button1 = new QPushButton("Top Button");
QApplication::connect(button1, SIGNAL(clicked(bool)), this, SLOT(AddItemSlot()));
rightSidePanel->addWidget(button1);
QWidget* rightPanelWidget = new QWidget();
rightSidePanel->setMargin(0);
rightPanelWidget->setLayout(rightSidePanel);
singleItemLayout->addWidget(rightPanelWidget);
itemWidget->setLayout(singleItemLayout);
itemWidget->setMinimumWidth(400);
itemWidget->setFixedSize(400,200);
return itemWidget;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::scrollHorizontal()
{
scrollArea->ensureWidgetVisible(noteItems.back());
}
void MainWindow::AddItemSlot()
{
QWidget* w = CreateNewItem();
gridLayout->addWidget(w,currRow, currCol++);
if (!noteItems.empty()){
drawWidget->updateEndpoints(noteItems.back(), w);
}
noteItems.push_back(w);
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QGridLayout>
#include <QWidget>
#include <QMainWindow>
#include <QScrollArea>
#include <drawwidget.h>
#include "drawscrollarea.h"
#include <vector>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void scrollHorizontal();
void AddItemSlot();
private:
Ui::MainWindow *ui;
QWidget* CreateNewItem();
int counter = 0, currCol = 0, currRow = 0;
std::vector<QWidget*> noteItems;
QScrollArea* scrollArea;
DrawWidget* drawWidget;
QGridLayout* gridLayout;
};
#endif // MAINWINDOW_H
DrawWidget.cpp:
#include "drawwidget.h"
#include <QDebug>
#include <QRect>
DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
}
void DrawWidget::paintEvent(QPaintEvent *)
{
if (!drewSinceUpdate){
drewSinceUpdate = true;
QPainter painter(this);
painter.setPen(QPen(Qt::black));
for (ConnectedPair pair : items){
const QWidget* from = pair.from;
const QWidget* to =pair.to;
QPoint start = from->mapToGlobal(from->rect().topRight() + QPoint(0, from->height()/2));
QPoint end = to->mapToGlobal(to->rect().topLeft() + QPoint(0, to->height()/2));
painter.drawLine(mapFromGlobal(start), mapFromGlobal(end));
}
}
}
void DrawWidget::updateEndpoints(QWidget* startIn, QWidget* endIn){
drewSinceUpdate = false;
items.push_back(ConnectedPair{startIn, endIn});
}
DrawWidget.h
#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H
#include <QWidget>
#include <QPainter>
#include <QtCore>
#include <vector>
class DrawWidget : public QWidget
{
Q_OBJECT
public:
explicit DrawWidget(QWidget *parent = nullptr);
void updateEndpoints(QWidget* startIn, QWidget* endIn);
virtual void paintEvent(QPaintEvent *);
signals:
private:
struct ConnectedPair {
const QWidget* from;
const QWidget* to;
};
std::vector<ConnectedPair> items;
bool drewSinceUpdate = true;
};
#endif // DRAWWIDGET_H

For this case we use the function mapToGlobal() and mapfromGlobal(), since pos() returns a position with respect to the parent and this can cause problems if the widget has different parents.
drawwidget.h
#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H
#include <QWidget>
class DrawWidget : public QWidget
{
Q_OBJECT
public:
explicit DrawWidget(QWidget *parent = nullptr);
void addWidgets(const QWidget *from, const QWidget *to);
protected:
void paintEvent(QPaintEvent *);
private:
struct WidgetsConnected {
const QWidget* from;
const QWidget* to;
};
QList<WidgetsConnected> list;
};
#endif // DRAWWIDGET_H
drawwidget.cpp
#include "drawwidget.h"
#include <QPainter>
DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
}
void DrawWidget::addWidgets(const QWidget * from, const QWidget * to)
{
list.append(WidgetsConnected{from , to});
update();
}
void DrawWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
for(const WidgetsConnected el: list){
const QWidget* from = el.from;
const QWidget* to = el.to;
QPoint start = from->mapToGlobal(from->rect().topRight() + QPoint(0, from->height()/2));
QPoint end = to->mapToGlobal(to->rect().topLeft() + QPoint(0, to->height()/2));
painter.drawLine(mapFromGlobal(start), mapFromGlobal(end));
}
}
The complete example can be found here.

Related

Qt widget takes a long time until it starts to receive mouse events in full screen mode on macOS

In normal screen mode, Qt widgets receive mouse events immediately after creation. In full-screen mode on macOS, however, it takes a while until the widget starts receiving those events. From my tests, it takes ~700ms on average until a widget receives its first mouse event versus ~30ms in normal screen mode. Why does it take so long for mouse events to stream in when using full-screen mode? Is there a way to force Qt to receive those events earlier? See my test in the GIF animation below.
The source code for my test follows below.
/**
* main.cpp
*/
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return QApplication::exec();
}
/**
* mainwindow.h
*/
#pragma once
#include "popupwindow.h"
#include <QWidget>
#include <QPushButton>
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
private slots:
void handleFullScreenButton();
void handleNormalScreenButton();
private:
PopupWindow* m_fullScreenWindow;
QPushButton* m_fullScreenButton;
QPushButton* m_normalScreenButton;
PopupWindow* m_normalScreenWindow;
};
/**
* popupwindow.h
*/
#pragma once
#include <QWidget>
#include <QLabel>
class PopupWindow : public QWidget
{
Q_OBJECT
public:
explicit PopupWindow(QWidget* parent = nullptr);
protected:
void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
private:
void showText(const QString &text);
QLabel* m_text;
qint64 m_creationTimestamp;
bool m_eventReceived;
};
/**
* mainwindow.cpp
*/
#include "mainwindow.h"
#include <QDebug>
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: m_normalScreenWindow(nullptr)
, m_fullScreenWindow(nullptr)
{
auto *layout = new QVBoxLayout(this);
m_normalScreenButton = new QPushButton("Popup screen");
m_fullScreenButton = new QPushButton("Popup full screen");
connect(m_normalScreenButton, &QPushButton::released, this, &MainWindow::handleNormalScreenButton);
connect(m_fullScreenButton, &QPushButton::released, this, &MainWindow::handleFullScreenButton);
layout->addWidget(m_normalScreenButton);
layout->addWidget(m_fullScreenButton);
}
void MainWindow::handleNormalScreenButton() {
m_normalScreenWindow = new PopupWindow(this);
m_normalScreenWindow->show();
m_normalScreenWindow->activateWindow();
m_normalScreenWindow->raise();
}
void MainWindow::handleFullScreenButton() {
m_fullScreenWindow = new PopupWindow(this);
m_fullScreenWindow->showFullScreen();
m_fullScreenWindow->activateWindow();
m_fullScreenWindow->raise();
}
/**
* popupwindow.cpp
*/
#include "popupwindow.h"
#include <QDateTime>
#include <QDebug>
#include <QHBoxLayout>
PopupWindow::PopupWindow(QWidget *parent)
: m_creationTimestamp(QDateTime::currentMSecsSinceEpoch())
, m_eventReceived(false)
, m_text(nullptr)
{
setAttribute(Qt::WA_DeleteOnClose);
setMouseTracking(true);
}
void PopupWindow::mouseMoveEvent(QMouseEvent *event) {
if (m_eventReceived) {
return;
}
auto deltaTimeMs = QDateTime::currentMSecsSinceEpoch() - m_creationTimestamp;
showText(QString("First mouse event received after %1 ms.").arg(deltaTimeMs));
m_eventReceived = true;
}
void PopupWindow::mousePressEvent(QMouseEvent *event) {
showNormal();
close();
}
void PopupWindow::showText(const QString &text) {
m_text = new QLabel(text);
m_text->setStyleSheet("font: 26pt;");
auto *layout = new QVBoxLayout(this);
layout->addWidget(m_text, Qt::AlignCenter);
}

How to display QLabel inside qwidget from active QMdiAreaSubwindow in statusbar?

I have a app that uses QMdiArea.
I want the text in the statusbar to update when another QMdiAreaSubwindow becomes active.
So the text in the statusbar should become the same as the Qlabel text inside the QWidget which is been displayed inside the QMdiAreaSubwindow.
But i can't find a way to do this. Right now the statusbar only shows the text from latest created QMdiAreaSubwindow. But it won't update the text in the statusbar(With qlabel from the qwidget) when another QMdiAreaSubwindow is selected.
As you can see in the screenshot, the text in the statusbar keeps saying "test2", but I want it to change to "text" from the active QMdiAreaSubwindow.
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <newwindow.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void NewSubWindow(QString name);
void createStatusBar(QString name);
private slots:
void on_actionNew_triggered();
void on_mdiArea_subWindowActivated(QMdiSubWindow *arg1);
private:
Ui::MainWindow *ui;
NewWindow *nDialog;
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mdisubwidget.h"
#include "newwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
nDialog = new NewWindow();
connect(nDialog,&NewWindow::transmit,this,&MainWindow::NewSubWindow);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::NewSubWindow(QString name) {
// new Widget to add to mdiareasubwindow
mdisubwidget *mdiwidget = new mdisubwidget();
mdiwidget->addName(name);
mdiwidget->setWindowTitle(name);
// Create new mdiAreaSubWindow
ui->mdiArea->addSubWindow(mdiwidget);
// Show mdiArea
mdiwidget->show();
}
void MainWindow::on_actionNew_triggered()
{
nDialog->show();
}
void MainWindow::on_mdiArea_subWindowActivated(QMdiSubWindow *arg1)
{
mdisubwidget *mdiwidget = new mdisubwidget(arg1->widget());
qDebug() << "name" << mdiwidget->returnName();
createStatusBar(mdiwidget->returnName());
}
void MainWindow::createStatusBar(QString name)
{
statusBar()->showMessage("chart = "+name);
}
mdisubwidget.h
#ifndef MDISUBWIDGET_H
#define MDISUBWIDGET_H
#include <QWidget>
namespace Ui {
class mdisubwidget;
}
class mdisubwidget : public QWidget
{
Q_OBJECT
public:
explicit mdisubwidget(QWidget *parent = nullptr);
void addName(QString name);
QString returnName();
~mdisubwidget();
private:
Ui::mdisubwidget *ui;
};
#endif // MDISUBWIDGET_H
mdisubwidget.cpp
#include "mdisubwidget.h"
#include "ui_mdisubwidget.h"
#include "mainwindow.h"
QString currentName;
mdisubwidget::mdisubwidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::mdisubwidget)
{
ui->setupUi(this);
}
void mdisubwidget::addName(QString name) {
ui->label_2->setText(name);
currentName = name;
}
QString mdisubwidget::returnName() {
return currentName;
}
mdisubwidget::~mdisubwidget()
{
delete ui;
}
NewWindow.h:
#ifndef NEWWINDOW_H
#define NEWWINDOW_H
#include <QWidget>
namespace Ui {
class NewWindow;
}
class NewWindow : public QWidget
{
Q_OBJECT
public:
explicit NewWindow(QWidget *parent = nullptr);
~NewWindow();
signals:
void transmit(QString name);
private slots:
void on_pushButton_clicked();
private:
Ui::NewWindow *ui;
};
#endif // NEWWINDOW_H
NewWindow.cpp:
#include "newwindow.h"
#include "ui_newwindow.h"
#include "mainwindow.h"
NewWindow::NewWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::NewWindow)
{
ui->setupUi(this);
}
NewWindow::~NewWindow()
{
delete ui;
}
void NewWindow::on_pushButton_clicked()
{
QString name = ui->lineEdit->text();
emit transmit(name);
}
ok you're using Qt Designer to connect the signal of subWindowActivated to the slot of on_mdiArea_subWindowActivated of your MainWindow, double check with qDebug in your on_mdiArea_subWindowActivated function if the name of your selected sub window appears on the console as you tried to change your current mdi sub window so follow my code snippets to find your way:
connect(ui->mdiArea, &QMdiArea::subWindowActivated, this, &DesignerWindow::activeViewChanged);
activeViewChanged():
void DesignerWindow::activeViewChanged(QMdiSubWindow *activeSubWindow)
{
// checks if there is no active sub window defined or the number of subwindows
// are zero then return
if (!activeSubWindow)
return;
if (ui->mdiArea->subWindowList().count() == 0) {
ui->itemsTree->clear();
return;
}
// defines the current Midi, View and graphical Scene when current sub window changes
currentMidi = reinterpret_cast<MidiWindow*>(activeSubWindow->widget());
currentView = reinterpret_cast<HMIView*>(currentMidi->internalView());
currentScene = reinterpret_cast<HMIScene*>(currentMidi->internalScene());
ItemsToolBar::ItemType currentType = currentScene->itemType();
itemsToolBar->selectItemType(currentType);
// updates the widgets and labels in status bar related to current midi sub window
updateScale(currentView->zoomFactor() * 100);
updateSelected();
updateItemsTree();
updateRendererType();
}
for example for updating the label in the status bar that holds the zooming factor of the current mdiSubWindow I wrote the updateScale procedure as below:
void DesignerWindow::updateScale(double _scale)
{
scale = static_cast<int>(_scale);
scaleLbl->setText(QString("%1%").arg(scale));
}
and finally I've noticed that your creating a label in status bar every time that you try to update the text in it, please avoid such a procedure and create a QLabel object and add it to your status bar as a permanent widget like below:
scaleLbl = new QLabel(this);
scaleLbl->setFrameStyle(QFrame::Sunken | QFrame::Panel);
scaleLbl->setMinimumWidth(50);
statusBar()->addPermanentWidget(scaleLbl);

Layout mismanagement issue in Qt Application

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.

Connect QPushButton between two classes

I have a QListWidget that is in a QGridLayout in my constructor of the Draw class.QGridLAyout contains other elements.
Draw::Draw(QWidget *parent):QDialog(parent){
gridlayout=new QGridLayout;
gridlayout->addLayout(hbox,0,0);
gridlayout->addLayout(hbox1,1,0);
gridlayout->addLayout(hbox2,2,0);
gridlayout->addWidget(listWidget,3,0);
gridlayout->addLayout(hbox4,4,0);
this->setLayout(gridlayout);
}
When clicking on a QPushButton. I'm calling a slot that filled the QListWidget.
//SLOT
void Draw::fillListWidget(){
//item is QListWidgetItem
item=new QListWidgetItem;
item->setSizeHint(QSize(0,50));
listWidget->addItem(item);
//WidgetfillListWidget is an other class with parameters for the QListWidget
listWidget->setItemWidget(item,new WidgetfillListWidget(label1,path));
}
In my other class I build the new QWidget to fill the QlistWidget. The QListWidget contains a label, a string and a QPushButton.
WidgetfillListWidget::WidgetfillListWidget(QString label, QString p){
instanceDraw=new Draw(this);
QString name = label;
QString path = p;
name1=new QLabel(name,this);
path1=new QLineEdit(path,this);
remove=new QPushButton("remove",this);
hboxlist=new QHBoxLayout;
hboxlist->addWidget(name1);
hboxlist->addWidget(path1);
hboxlist->addWidget(remove);
setLayout(hboxlist);
connect(remove,SIGNAL(clicked()),instanceDraw,SLOT(removeItem()));
}
In the Draw class I have the slot that has to delete an item but it does not work
void Draw::removeItem(){
listWidget->takeItem(listWidget->row(item));
}
Deleting the item does not work. I think it comes from my Draw object in the connect. But I do not understand how to solve the problem. Does someone have an idea?
The problem is because the Draw created in WidgetfillListWidget is different from the original Draw, they are 2 different objects.
In this case the solution is to create a clicked signal in WidgetfillListWidget that is issued when the QPushButton is pressed.
Then that signal is connected to the removeItem() method. In it we must obtain the item, but that is not simple, one way to do it is through the geometric position as I show below:
widgetfilllistwidget.h
#ifndef WIDGETFILLLISTWIDGET_H
#define WIDGETFILLLISTWIDGET_H
#include <QWidget>
class QLabel;
class QPushButton;
class QHBoxLayout;
class QLineEdit;
class WidgetfillListWidget : public QWidget
{
Q_OBJECT
public:
explicit WidgetfillListWidget(const QString & name, const QString &path, QWidget *parent = nullptr);
signals:
void clicked();
private:
QLabel *lname;
QLineEdit *lpath;
QPushButton *premove;
QHBoxLayout *hboxlist;
};
#endif // WIDGETFILLLISTWIDGET_H
widgetfilllistwidget.cpp
#include "widgetfilllistwidget.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
WidgetfillListWidget::WidgetfillListWidget(const QString &name, const QString & path, QWidget *parent) : QWidget(parent)
{
lname=new QLabel(name);
lpath=new QLineEdit(path);
premove=new QPushButton("remove");
hboxlist=new QHBoxLayout(this);
hboxlist->addWidget(lname);
hboxlist->addWidget(lpath);
hboxlist->addWidget(premove);
connect(premove, &QPushButton::clicked, this, &WidgetfillListWidget::clicked);
}
draw.h
#ifndef DRAW_H
#define DRAW_H
#include <QDialog>
class QGridLayout;
class QListWidget;
class QPushButton;
class Draw : public QDialog
{
Q_OBJECT
public:
Draw(QWidget *parent = 0);
~Draw();
private:
void fillListWidget();
void removeItem();
QGridLayout *gridlayout;
QListWidget *listWidget;
QPushButton *button;
};
#endif // DRAW_H
draw.cpp
#include "draw.h"
#include "widgetfilllistwidget.h"
#include <QGridLayout>
#include <QListWidget>
#include <QPushButton>
Draw::Draw(QWidget *parent)
: QDialog(parent)
{
button = new QPushButton("Press Me");
listWidget = new QListWidget;
gridlayout=new QGridLayout(this);
gridlayout->addWidget(listWidget, 0, 0);
gridlayout->addWidget(button, 1, 0);
connect(button, &QPushButton::clicked, this, &Draw::fillListWidget);
}
void Draw::fillListWidget()
{
QListWidgetItem *item=new QListWidgetItem;
item->setSizeHint(QSize(0,50));
listWidget->addItem(item);
//WidgetfillListWidget is an other class with parameters for the QListWidget
QString label = "label1";
QString path = "label2";
WidgetfillListWidget *widget = new WidgetfillListWidget(label,path);
listWidget->setItemWidget(item, widget);
connect(widget, &WidgetfillListWidget::clicked, this, &Draw::removeItem);
}
void Draw::removeItem()
{
WidgetfillListWidget *widget = qobject_cast<WidgetfillListWidget *>(sender());
if(widget){
QPoint gp = widget->mapToGlobal(QPoint());
QPoint p = listWidget->viewport()->mapFromGlobal(gp);
QListWidgetItem *item = listWidget->itemAt(p);
item = listWidget->takeItem(listWidget->row(item));
delete item;
}
}
Draw::~Draw()
{
}

Pulse LED on button toggle in QT

I am learning Qt and i came up with an idea to switch on and off an LED with buttons, i got it working but i couldn't figure out how to implement the same with a toggle button. I will be posting the code below, please take a look and let me know how i can implement the above stated behavior.
pulseledwidget.h
#ifndef PULSINGLEDWIDGET_H
#define PULSINGLEDWIDGET_H
#include <QLabel>
#include <QPixmap>
#include <QTimer>
#include <QObject>
#include <QDebug>
class pulsingLedWidget : public QLabel
{
Q_OBJECT
public:
explicit pulsingLedWidget(QWidget *parent = 0);
public slots:
void startPulsing();
void extinguish();
private:
QPixmap onPixmap,offPixmap;
QTimer timer;
bool state;
};
#endif // PULSINGLEDWIDGET_H
pulseledwidget.cpp
#include "pulsingledwidget.h"
pulsingLedWidget::pulsingLedWidget(QWidget *parent) : QLabel(parent)
{
onPixmap.load(":/ledon.png");
offPixmap.load(":/ledoff.png");
state = true;
setPixmap(offPixmap);
timer.setInterval(200);
connect(&timer,SIGNAL(timeout()),this,SLOT(startPulsing()));
}
void pulsingLedWidget::startPulsing()
{
timer.start();
if(state)
{
setPixmap(onPixmap);
state = false;
}
else
{
setPixmap(offPixmap);
state = true;
}
}
void pulsingLedWidget::extinguish()
{
timer.stop();
setPixmap(offPixmap);
}
leddialog.h
#ifndef LEDDIALOG_H
#define LEDDIALOG_H
#include <QDialog>
#include <QVBoxLayout>
#include <QPushButton>
#include "pulsingledwidget.h"
class ledDialog : public QDialog
{
Q_OBJECT
public:
explicit ledDialog(QWidget *parent = 0);
private:
QPushButton *onbutton;
QPushButton *offbutton;
QVBoxLayout *layout;
pulsingLedWidget *ledwidget;
};
#endif // LEDDIALOG_H
leddialog.cpp
#include "leddialog.h"
ledDialog::ledDialog(QWidget *parent) : QDialog(parent)
{
QPushButton *onbutton = new QPushButton("On");
QPushButton *offbutton = new QPushButton("Off");
QVBoxLayout *layout = new QVBoxLayout(this);
pulsingLedWidget *ledwidget = new pulsingLedWidget(this);
ledwidget->setAlignment(Qt::AlignCenter);
layout->addWidget(ledwidget);
layout->addWidget(onbutton);
layout->addWidget(offbutton);
resize(150,200);
connect(onbutton,SIGNAL(clicked(bool)),ledwidget,SLOT(startPulsing()));
connect(offbutton,SIGNAL(clicked(bool)),ledwidget,SLOT(extinguish()));
}
What i have tried :
leddialog.h
#ifndef LEDDIALOG_H
#define LEDDIALOG_H
#include <QDialog>
#include <QVBoxLayout>
#include <QPushButton>
#include "pulsingledwidget.h"
class ledDialog : public QDialog
{
Q_OBJECT
public:
explicit ledDialog(QWidget *parent = 0);
private slots:
void makeDecision(bool state);
private:
QPushButton *onbutton;
QPushButton *offbutton;
QVBoxLayout *layout;
pulsingLedWidget *ledwidget;
};
#endif // LEDDIALOG_H
leddialog.cpp
#include "leddialog.h"
ledDialog::ledDialog(QWidget *parent) : QDialog(parent)
{
QPushButton *onbutton = new QPushButton("On");
QPushButton *offbutton = new QPushButton("Off");
QVBoxLayout *layout = new QVBoxLayout(this);
pulsingLedWidget *ledwidget = new pulsingLedWidget(this);
ledwidget->setAlignment(Qt::AlignCenter);
onbutton->setCheckable(true);
layout->addWidget(ledwidget);
layout->addWidget(onbutton);
layout->addWidget(offbutton);
resize(150,200);
connect(onbutton,SIGNAL(toggled(bool)),this,SLOT(makeDecision(bool)));
}
void ledDialog::makeDecision(bool state)
{
if(state)
{
ledwidget->startPulsing();
}
else
{
ledwidget->extinguish();
}
}
but the above procedure is not working. I tried to run it in debug mode to know the mistake that i have made, it is showing "Segmentation fault" error.