I am looking for an easy way to link hand coded design to the UI for widgets applications in Qt. I plan to use the UI builder to easily adjust the layouts and obtain proper spacing, which I find hard to do without the UI builder.
I want to create a 3x3 grid of buttons for which I plan to use QVector< QVector<QPushButton*> > (I am not sure how I would do this in UI builder.)
Here is what I have tried, Why are the buttons not displayed even when I set the parent of each button to the widget ?
main.cpp
#include "window.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
#include <QPushButton>
namespace Ui {
class Window;
}
class Window : public QWidget
{
Q_OBJECT
public:
explicit Window(QWidget *parent = 0);
~Window();
private:
Ui::Window *ui;
static const int tiles = 50, height = 600, width = 500;
QVector< QVector<QPushButton*> > cells;
};
#endif // WINDOW_H
window.cpp
#include "window.h"
#include "ui_window.h"
Window::Window(QWidget *parent) :
QWidget(parent),
ui(new Ui::Window)
{
ui->setupUi(this);
this->resize(width, height);
int j = 0;
for(auto &row : cells)
{
int i = 0;
for(auto &col : row)
{
col = new QPushButton(this);
col->setGeometry(i, j, tiles, tiles);
i += tiles;
}
j += tiles;
}
}
Window::~Window()
{
delete ui;
for(auto &row : cells)
{
for(auto &col : row)
{
delete col;
}
}
}
Any help would be appreciated.
The vectors are empty, so you iterate over nothing. Instead of managing these manually, you could leverage the grid layout.
Alas, you're complicating things unnecessarily with all the manual memory management and geometry management. It's unnecessary. All you need to do is to add widgets to the layout you allude to. And even then, I don't see how relegating the layout to a .ui file helps you since there the layout must be empty. So yes: you can set spacings, but you won't see them until you run the code. So it seems like a pointless exercise, unless you have other elements you're not telling us about (why aren't you - you went so far already).
Below is a minimum example that simplifies it as much as practicable, but see this answer and the links therein for more idea.
// https://github.com/KubaO/stackoverflown/tree/master/questions/button-grid-43214317
#include <QtWidgets>
namespace Ui { class Window {
public:
// Approximate uic output
QGridLayout *layout;
void setupUi(QWidget * widget) {
layout = new QGridLayout(widget);
}
}; }
class Window : public QWidget
{
Q_OBJECT
Ui::Window ui;
QPushButton * buttonAt(int row, int column) {
auto item = ui.layout->itemAtPosition(row, column);
return item ? qobject_cast<QPushButton*>(item->widget()) : nullptr;
}
public:
explicit Window(QWidget *parent = {});
};
Window::Window(QWidget *parent) : QWidget(parent) {
ui.setupUi(this);
for (int i = 0; i < 5; ++i)
for (int j = 0; j < 6; ++j)
{
auto b = new QPushButton(QStringLiteral("%1,%2").arg(i).arg(j));
ui.layout->addWidget(b, i, j);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
#include "main.moc"
Related
I'm using a custom QTableView with a custom QAbstractTableModel and a QItemDelegate. I'd need to access the contents of the delegate's editor while the user is editing it, and after several attempts, I couldn't find anything satisfying.
Indeed, I've tried several things.
First: trying to access the delegate's current input (created through createEditor) through a property defined in QItemDelegate but... it seems that none exists. That's why I tried to add a QWidget* editor property and setting it in the createEditor.
Unfortunately, QItemDelegate's createEditor is supposed to be const, which makes me unable to set my property there (and since I don't control what calls createEditor, I can't do it before or after).
I don't really know what to do here. Actually, I also needed to know when the user started (or stopped) editing the cell content, which I eventually achieved by creating two const signals (editingStarted and editingStopped). I could probably create a const editorOpened(QWidget*) signal but it just feels bad and ugly...
I can't believe nothing "official" exists to achieve what I'm trying to do, hence this question. If I have everything wrong from the beginning, I'd be glad to know. If you have any other ideas, please suggest.
EDIT: Here is a minimal working example
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QTableView>
#include "mytableview.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
auto tableView = new MyTableView(this);
setCentralWidget(tableView);
}
MainWindow::~MainWindow()
{
}
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QItemDelegate>
#include <QLineEdit>
#include <QStandardItemModel>
class MyItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual void onCloseEditor();
virtual ~MyItemDelegate() = default;
signals:
// Const signals trick
void editingStarted() const;
void editingFinished() const;
void editorOpened(const QWidget*) const;
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
MyItemDelegate::MyItemDelegate(QObject* parent) : QItemDelegate(parent)
{
connect(this, &QItemDelegate::closeEditor, this, &MyItemDelegate::onCloseEditor);
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
emit editingStarted();
emit editorOpened(lineEdit);
return lineEdit;
}
void MyItemDelegate::onCloseEditor()
{
emit editingFinished();
}
MyTableView.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include <QTableView>
#include <QDebug>
#include "myitemdelegate.h"
class MyTableView : public QTableView
{
Q_OBJECT
public:
explicit MyTableView(QWidget *parent = nullptr);
signals:
public slots:
};
#endif // MYTABLEVIEW_H
MyTableView.cpp
#include "mytableview.h"
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::editingStarted, []() {
qDebug() << "Editing started";
});
connect(delegate, &MyItemDelegate::editingFinished, []() {
qDebug() << "Editing finished";
});
connect(delegate, &MyItemDelegate::editorOpened, [](const QWidget* editor) {
auto lineEdit = qobject_cast<const QLineEdit*>(editor);
connect(lineEdit, &QLineEdit::textChanged, [](const QString& text) {
qDebug() << text;
});
});
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
The following solution maybe fits your needs. I just defined a new signal inside the delegate and connected to it inside the class owning the delegate.
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QStyledItemDelegate>
class MyItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual ~MyItemDelegate() = default;
signals:
void valueChanged(const QString&);
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
#include <QLineEdit>
MyItemDelegate::MyItemDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
connect(lineEdit, &QLineEdit::textChanged, this, &MyItemDelegate::valueChanged);
return lineEdit;
}
MyTableView.cpp
#include "mytableview.h"
#include <QStandardItemModel>
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::valueChanged, [](auto v) { qDebug() << v; });
}
From Qvector is set the number of buttons and their names (text). After cliking on the button I need to reseave text on it and display in lineEdit.
The header file:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QWidget>
#include <QVector>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void Buttons(QVector<QString>&);
private:
QVector<QPushButton*>button;
QString mline;
QLineEdit *line;
QVBoxLayout *layout;
QAction *Clicked;
};
#endif // DIALOG_H
The source file:
#include "dialog.h"
Dialog::Dialog(QWidget *parent): QDialog(parent)
{
this->setFixedSize(this->minimumSize());
line = new QLineEdit(this);
layout = new QVBoxLayout(this);
layout->addWidget(line);
QVector<QString>v;
v.append("a");
v.append("b");
v.append("c");
v.append("5");
v.append("45");
Buttons(v);
for(int i=0;i<button.size();i++)
layout->addWidget(button[i]);
setLayout(layout);
}
Dialog::~Dialog(){}
void Dialog::Buttons(QVector<QString>&vec)
{
if(!button.isEmpty())
button.clear();
for(int i=0; i<vec.size();i++)
{
button.append(new QPushButton(this));
button[i]->setText(vec[i]);
button[i]->show();
}
}
For it I'm not using user interface (ui), and couldn't make slots for all the buttons. How can it be done?
You can use QButtonGroup to id the buttons and consolidate the signals from all the buttons to a single slot
Example
QButtonGroup myButtongroup;
connect(&myButtonGroup,SIGNAL(buttonClicked(QAbstractButton*),this, SLOT(myButtonClicked (QAbstractButton*)));
for(int i=0; i<vec.size();i++)
{
QString buttonName = vec[i];
myButtonGroup.addButton(new QPushButton(buttonName,this),i);
//Get the button using myButtonGroup.button(i) to add to your layout
// You can add the buttons to the layout right here to elimate one more loop
layout.addWidget(myButtonGroup.button(i));
}
//Slot for button clicked
void Dialog::myButtonClicked(QAbstractButton *myButton)
{
line->setText(myButton->text());// Adding the button name to line edit
}
For more QButtonGroup signals refer the documentation
You can use signal/slot in your case. The signal/slot is related to QObject. It does not matter whether you use QT Designer or not. In your code,
for(int i=0; i<vec.size();i++)
{
button.append(new QPushButton(this));
connect(button[i], SIGNAL( clicked() ), this, SLOT(OnButtonClicked()));
button[i]->setText(vec[i]);
button[i]->show();
}
Maintain a QList of QPushButton references that you add and use "signals and slots" to register the clicked() signal of each button to a single slot. Inside the function, iterate the QList of QPushButton by comparing with QObject::sender() and identify the source.
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
class TestWidget: public QWidget
{
Q_OBJECT
private:
QLabel *label;
QVBoxLayout *mainLayout;
QList<QPushButton*> mButtonList;
public:
TestWidget(QWidget *parent=nullptr) : QWidget(parent)
{
mainLayout = new QVBoxLayout(this);
label = new QLabel;
mainLayout->addWidget(label);
this->setLayout(mainLayout);
}
void addButton(QString buttonName)
{
QPushButton *button = new QPushButton(buttonName);
QObject::connect(button, SIGNAL(clicked(bool)), this, SLOT(buttonClicked(bool)));
mButtonList.append(button);
mainLayout->addWidget(button);
}
public slots:
void buttonClicked(bool event)
{
Q_UNUSED(event)
static_cast<QPushButton*>(QObject::sender())->setFocus();
for (int i = 0; i < mButtonList.size(); ++i)
{
if (mButtonList.at(i) == sender())
{
label->setText(QString(mButtonList.at(i)->text()));
break;
}
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestWidget wid;
wid.addButton("First Button");
wid.addButton("Second Button");
wid.addButton("Third Button");
wid.addButton("Fourth Button");
wid.show();
return a.exec();
}
#include "main.moc"
The above sample code along with *.pro file is available in github
My problem is that I just can't seem to make my QSlider work with double values instead of integer, because I need to make it return double values to a QLineEdit and also set it's own value when I put some value in the edit.
When I was a Qt beginner I started with this tutorial. It is a little bit old (it refers to Qt4.1), but it was good enough to get me started!
I have put together a simple example application that can show you where to start... Maybe you can find it helpful!
#include <QApplication>
#include <QtGui>
#include <QVBoxLayout>
#include <QSlider>
#include <QLabel>
class DoubleSlider : public QSlider {
Q_OBJECT
public:
DoubleSlider(QWidget *parent = 0) : QSlider(parent) {
connect(this, SIGNAL(valueChanged(int)),
this, SLOT(notifyValueChanged(int)));
}
signals:
void doubleValueChanged(double value);
public slots:
void notifyValueChanged(int value) {
double doubleValue = value / 10.0;
emit doubleValueChanged(doubleValue);
}
};
class Test : public QWidget {
Q_OBJECT
public:
Test(QWidget *parent = 0) : QWidget(parent),
m_slider(new DoubleSlider()),
m_label(new QLabel())
{
m_slider->setOrientation(Qt::Horizontal);
m_slider->setRange(0, 100);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(m_slider);
layout->addWidget(m_label);
connect(m_slider, SIGNAL(doubleValueChanged(double)),
this, SLOT(updateLabelValue(double)));
updateLabelValue(m_slider->value());
}
public slots:
void updateLabelValue(double value) {
m_label->setText(QString::number(value, 'f', 2));
}
private:
QSlider *m_slider;
QLabel *m_label;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Test *wid = new Test();
wid->show();
return a.exec();
}
#include "main.moc"
You can simply divide slider value on some constant. For example:
const int dpi = 10; // Any constant 10^n
int slideVal = 57; // Integer value from slider
double realVal = double( slideVal / dpi ); // float value
Consider a QWidget, normally a child in some Layout.
Supposed I want to make it fullScreen for a while, then have it return to it's old spot.
QWidget::setFullScreen() requires that the widget needs to be an independent window - any ideas how to work it out?
The simplest way I can see is to reparent to 0. Something like this:
#include <QApplication>
#include <QPushButton>
class MyButton : public QPushButton
{
public:
MyButton(QWidget* parent) : QPushButton(parent) {}
void mousePressEvent(QMouseEvent*) {
this->setParent(0);
this->showMaximized();
this->show();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget mainWidget;
MyButton button(&mainWidget);
mainWidget.show();
return a.exec();
}
I have modified the previous example. The previous example never goes back to normal screen.
Just copy paste the code and it will run.
#include <QApplication>
#include <QPushButton>
class MyButton : public QPushButton
{
public:
MyButton(QWidget* parent) : QPushButton(parent) {
m_pParent = parent;
maxMode = false;
}
QWidget * m_pParent;
bool maxMode;
Qt::WindowFlags m_enOrigWindowFlags;
QSize m_pSize;
void mousePressEvent(QMouseEvent*) {
if (maxMode== false)
{
m_enOrigWindowFlags = this->windowFlags();
m_pSize = this->size();
this->setParent(0);
this->setWindowFlags( Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
this->showMaximized();
maxMode = true;
}
else
{
this->setParent(m_pParent);
this ->resize(m_pSize);
this->overrideWindowFlags(m_enOrigWindowFlags);
this->show();
maxMode = false;
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget mainWidget;
MyButton button(&mainWidget);
mainWidget.show();
return a.exec();
}
I have the following problem I've been struggeling with. I have a list of toolbuttons placed in a scroll area. I want to make a second scroll area with push buttons that duplicate the text of the tool buttons, but only for the toolbuttons that are selected. For example let's select toolbuttons 1,2 and 3. Then when I push an "update" button, a scrollarea with pushbuttons 1,2 and three should appear. This is my code:
CPP-file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtGui>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scrollArea.setParent(ui->centralWidget);
scrollArea.setGeometry(50,50,200,300);
scrollArea.setWidget(&viewport);
viewport.setLayout(&layout);
scrollArea2.setParent(ui->centralWidget);
scrollArea2.setGeometry(350,50,200,300);
scrollArea2.setWidget(&viewport2);
viewport2.setLayout(&layout2);
update.setParent(ui->centralWidget);
update.setGeometry(50,400,100,50);
update.setText("update");
addButtons();
connect(&update,SIGNAL(clicked()),this,SLOT(refreshScrollArea()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::addButtons()
{
for(int i=0; i<5;i++)
{
QToolButton *button = new QToolButton;
button->setCheckable(true);
buttons<<button;
buttons[i]->setText(QString::number(i+1));
buttons[i]->setMinimumSize(200,50);
layout.addWidget(buttons[i]);
}
viewport.resize(200,5*50);
}
void MainWindow::refreshScrollArea()
{
while (!layout2.isEmpty())
{
delete layout2.takeAt(0);
}
int selected_index=0;
for(int i=0; i<5;i++)
{
if (buttons[i]->isChecked())
{
QPushButton * button = new QPushButton;
button->setText(buttons[i]->text());
layout2.addWidget(button);
selected_index++;
}
}
viewport2.resize(150,50*selected_index);
}
header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtGui>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void addButtons();
void refreshScrollArea();
private:
Ui::MainWindow *ui;
QVector<QToolButton*> buttons;
QScrollArea scrollArea;
QScrollArea scrollArea2;
QVBoxLayout layout;
QVBoxLayout layout2;
QWidget viewport;
QWidget viewport2;
QPushButton update;
};
#endif // MAINWINDOW_H
Main:
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
When I push the update-button once, it works perfectly, but when I select different tool buttons and push update again it seems like the buttons are being stacked as if the layout is not empty. However when I select no toolbutton and push update there are no buttons, so the layout is emptied. Can anybody see what I´m doing wrong?
An example of the 'update' code:
// you clear your duplicate layout
int num = layout2->count();
for (int i = 0; i < num; ++i
{
delete layout2->itemAt(0);
}
// you create duplicate buttons for checked initial buttons
for (int i = 0; layout1->count(); ++i)
{
QPushButton* btn = (QPushButton*)layout1->itemAt(i);
if (btn->isChecked())
{
QPushButton* newBtn = new QPushButton;
newBtn->setText(btn->text());
layout2->addWidget(newBtn);
}
}
If you don't need to create and delete new buttons again and again, you can have constant set of buttons and just show and hide them.