I want to link two QListWidget, but I don't know how to do with code. Here is what I did :
We can see two QListWidget. With the left QListWidget, I add (by example : "Bonjour", "Hello", "Tag") three QListWidgetItem. I want that if I click on one of three QListWidgetItem of the left QListWidget that I can add QListWidgetItem with the right QListWidget (by example, for "Bonjour" : "Tu", "Vas", "Bien"). If I don't click on one of three QListWidgetItem, I can't add QListWidgetItem with the right QListWidget. If I did for "Bonjour" : "Tu", "Vas", "Bien" and I click on "Hello" (obviously, "Hello" contains nothing), there is nothing in the right QListWidget. That's just an example of what I want to do. Below, I write my code if it's helpful :
- secondwindow.cpp -
#include "secondwindow.h"
#include "ui_secondwindow.h"
#include "thirdwindow.h"
#include "ui_thirdwindow.h"
SecondWindow::SecondWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::SecondWindow)
{
ui->setupUi(this);
ui->button_1->setIcon(QIcon(":/Images/Images/Haut.png"));
ui->button_2->setIcon(QIcon(":/Images/Images/Bas.png"));
ui->button_5->setIcon(QIcon(":/Images/Images/Haut.png"));
ui->button_6->setIcon(QIcon(":/Images/Images/Bas.png"));
connect(ui->button_1, SIGNAL(clicked()), this, SLOT(UpForLeft()));
connect(ui->button_2, SIGNAL(clicked()), this, SLOT(DownForLeft()));
connect(ui->button_3, SIGNAL(clicked()), this, SLOT(DeleteForLeft()));
connect(ui->button_4, SIGNAL(clicked()), this, SLOT(AddForLeft()));
connect(ui->button_9, SIGNAL(clicked()), this, SLOT(ShowThirdWindow()));
connect(ui->button_10, SIGNAL(clicked()), this, SLOT(close()));
connect(ui->table_1, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
this, SLOT(EditForLeft(QListWidgetItem *)));
}
SecondWindow::~SecondWindow()
{
delete ui;
}
void SecondWindow::ShowThirdWindow()
{
ThirdWindow *window = new ThirdWindow;
window->setWindowTitle("B");
window->setWindowIcon(QIcon(":/Images/Images/Bouclier.png"));
window->setFixedSize(820, 440);
window->show();
}
void SecondWindow::UpForLeft()
{
QListWidgetItem *item;
int i;
i = ui->table_1->currentRow();
item = ui->table_1->takeItem(i);
ui->table_1->insertItem(i - 1, item);
ui->table_1->setCurrentRow(i - 1);
}
void SecondWindow::DownForLeft()
{
QListWidgetItem *item;
int i;
i = ui->table_1->currentRow();
item = ui->table_1->takeItem(i);
ui->table_1->insertItem(i + 1, item);
ui->table_1->setCurrentRow(i + 1);
}
void SecondWindow::UpForRight()
{
QListWidgetItem *item;
int i;
i = ui->table_2->currentRow();
item = ui->table_2->takeItem(i);
ui->table_1->insertItem(i - 1, item);
ui->table_1->setCurrentRow(i - 1);
}
void SecondWindow::DownForRight()
{
QListWidgetItem *item;
int i;
i = ui->table_2->currentRow();
item = ui->table_2->takeItem(i);
ui->table_1->insertItem(i + 1, item);
ui->table_1->setCurrentRow(i + 1);
}
void SecondWindow::AddForLeft()
{
QString string;
string = ui->line_1->text();
ui->table_1->addItem(string);
ui->line_1->clear();
}
void SecondWindow::DeleteForLeft()
{
QListWidgetItem *item;
int i;
i = ui->table_1->currentRow();
item = ui->table_1->takeItem(i);
delete item;
}
void SecondWindow::EditForLeft(QListWidgetItem *item)
{
item->setFlags(item->flags() | Qt::ItemIsEditable);
item = ui->table_1->currentItem();
ui->table_1->editItem(item);
}
- secondwindow.h -
#ifndef SECONDWINDOW_H
#define SECONDWINDOW_H
#include <QListWidgetItem>
#include <QWidget>
#include <QString>
#include <QIcon>
#include "thirdwindow.h"
#include "ui_thirdwindow.h"
namespace Ui {
class SecondWindow;
}
class SecondWindow : public QWidget
{
Q_OBJECT
public:
explicit SecondWindow(QWidget *parent = 0);
~SecondWindow();
public slots:
void ShowThirdWindow();
void UpForLeft();
void DownForLeft();
void UpForRight();
void DownForRight();
void AddForLeft();
void DeleteForLeft();
void EditForLeft(QListWidgetItem *item);
private:
Ui::SecondWindow *ui;
ThirdWindow *window;
};
#endif // SECONDWINDOW_H
Thank you for help.
A QListWidget is a convenience widget that mixes a model with a view. This makes things a bit harder than necessary as you need to keep replenishing the models with data from your top-level model that represents the tree-structured data.
Instead, you could use a QStandardItemModel, and expose it via QListView. The view only shows one column at a given level of the tree, without any children. To view the children, select an appropriate root index.
A key missing feature of QStandardItemModel is moveRows, needed to move items up/down. That's easy enough to remedy with a limited implementation that supports moving a single item within the same parent. Thus the views can be completely model-agnostic by using moveRow. We shall implement moveRows first:
// https://github.com/KubaO/stackoverflown/tree/master/questions/list-widgets-40403640
#include <QtWidgets>
class StandardItemModel : public QStandardItemModel {
bool moveRows(const QModelIndex &srcParent, int srcRow, int count,
const QModelIndex &dstParent, int dstRow) override {
if (count == 0) return true;
if (count != 1 || srcParent != dstParent) return false;
if (srcRow == dstRow) return true;
if (abs(srcRow - dstRow) != 1) return false;
auto root = srcParent.isValid() ? itemFromIndex(srcParent) : invisibleRootItem();
if (!root) return false;
auto srcItem = root->takeChild(srcRow);
auto dstItem = root->takeChild(dstRow);
if (!srcItem || !dstItem) return false;
root->setChild(srcRow, dstItem);
root->setChild(dstRow, srcItem);
return true;
}
public:
using QStandardItemModel::QStandardItemModel;
};
Subsequently, a ListUi widget implements the view, without any knowledge of how the model might work. For the purpose of this example, new items are edited in-place, instead of using a separate control.
class ListUi : public QWidget {
Q_OBJECT
QGridLayout m_layout{this};
QVBoxLayout m_column;
QPushButton m_up{"⬆"}, m_down{"⬇"}, m_remove{"−"}, m_add{"+"};
QLabel m_caption;
QListView m_list;
inline QAbstractItemModel * model() const { return m_list.model(); }
inline QModelIndex root() const { return m_list.rootIndex(); }
inline QModelIndex index(int row) const { return model()->index(row, 0, root()); }
public:
ListUi(const QString & caption, QWidget * parent = nullptr) :
QWidget{parent},
m_caption{caption}
{
m_layout.addWidget(&m_up, 0, 0);
m_layout.addWidget(&m_down, 1, 0, 1, 1, Qt::AlignTop);
m_layout.addLayout(&m_column, 0, 1, 3, 1);
m_column.addWidget(&m_caption);
m_column.addWidget(&m_list);
m_layout.addWidget(&m_remove, 0, 2);
m_layout.addWidget(&m_add, 2, 2);
m_caption.setAlignment(Qt::AlignCenter);
connect(&m_add, &QPushButton::clicked, [this]{
int row = model()->rowCount(root());
if (model()->columnCount(root()) == 0)
model()->insertColumn(0, root());
if (model()->insertRow(row, root())) {
m_list.setCurrentIndex(index(row));
m_list.edit(index(row));
}
});
connect(&m_remove, &QPushButton::clicked, [this]{
if (m_list.currentIndex().isValid())
model()->removeRow(m_list.currentIndex().row(), root());
});
connect(&m_up, &QPushButton::clicked, [this]{
auto row = m_list.currentIndex().row();
if (row > 0 && model()->moveRow(root(), row, root(), row - 1))
m_list.setCurrentIndex(index(row-1));
});
connect(&m_down, &QPushButton::clicked, [this]{
auto row = m_list.currentIndex().row();
if (row >= 0 && row < (model()->rowCount(root()) - 1) &&
model()->moveRow(root(), row, root(), row + 1))
m_list.setCurrentIndex(index(row+1));
});
}
void setModel(QAbstractItemModel * model) {
m_list.setModel(model);
connect(m_list.selectionModel(), &QItemSelectionModel::currentChanged, this, &ListUi::currentIndexChanged);
}
void setRootIndex(const QModelIndex & index) {
m_list.setRootIndex(index);
}
Q_SIGNAL void currentIndexChanged(const QModelIndex &);
};
A simple test harness instantiates two ListUis, a StandardItemModel, populates the model from a JSON source, and links them to obtain the desired functionality.
class Window : public QWidget {
Q_OBJECT
QGridLayout m_layout{this};
ListUi m_programs{"Liste des programmes"};
ListUi m_sessions{"Liste des sessions"};
QPushButton m_generate{"Générer les données"};
StandardItemModel m_model;
public:
explicit Window(QWidget * parent = nullptr) : QWidget{parent}
{
m_layout.addWidget(&m_programs, 0, 0);
m_layout.addWidget(&m_sessions, 0, 1);
m_layout.addWidget(&m_generate, 1, 1, 1, 1, Qt::AlignRight);
m_programs.setModel(&m_model);
m_sessions.setModel(&m_model);
m_sessions.setDisabled(true);
connect(&m_programs, &ListUi::currentIndexChanged, [this](const QModelIndex & root){
m_sessions.setEnabled(true);
m_sessions.setRootIndex(root);
});
connect(&m_generate, &QPushButton::clicked, this, &Window::generateData);
}
Q_SIGNAL void generateData();
void setJson(const QJsonDocument & doc) {
m_model.clear();
auto object = doc.object();
for (auto it = object.begin(); it != object.end(); ++it) {
if (!m_model.columnCount()) m_model.appendColumn({});
auto root = new QStandardItem(it.key());
m_model.appendRow(root);
if (it.value().isArray()) {
auto array = it.value().toArray();
for (auto const & value : array) {
if (!root->columnCount()) root->appendColumn({});
root->appendRow(new QStandardItem{value.toString()});
}
}
}
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
auto json = R"--({
"Foo":["Foo-1", "Foo-2", "Foo-3"],
"Bar":["Bar-1", "Bar-2"]
})--";
Window ui;
ui.connect(&ui, &Window::generateData, [&]{ ui.setJson(QJsonDocument::fromJson(json)); });
ui.show();
return app.exec();
}
#include "main.moc"
It is a simple matter to iterate the standard item model to regenerate the JSON representation.
This concludes the example.
Related
I have the following problem, I have a QTableWidget to which I have added some QDoubleSpinBox to some cells, all good so far my question is how I can do to select all the content of the spinbox when changing the focus, I mean when pressing the key tab and when they receive the focus, I will appreciate any suggestions.
I leave my code:
void MainClass::setData(double val1, double val2,int _rowCount)
{
for (int i=0;i<= _rowCount; i++) {
int rowCount=ui->tableWidget_4->rowCount();
// ui->tableWidget_4->setRowCount(rowCount);
//primera columna
ui->tableWidget_4->insertRow(rowCount);
// qDebug()<<rowCount;
QTableWidgetItem *item=new QTableWidgetItem(QString("v %1").arg(v));
ui->tableWidget_4->setItem(rowCount,V,item);
//segunda columna columna
// ui->tableWidget_4->insertRow(rowCount);
QTableWidgetItem *item1=new QTableWidgetItem(0);
ui->tableWidget_4->setItem(rowCount,Velocidad,item1);
ui->tableWidget_4->setCellWidget(rowCount,Velocidad,new QDoubleSpinBox);
QDoubleSpinBox *sb=qobject_cast<QDoubleSpinBox *>(
ui->tableWidget_4->cellWidget(rowCount,Velocidad));
sb->setValue(val1);
// qDebug()<<item1->data(Qt::DisplayRole).toString();
//tercera columna columna
// ui->tableWidget_4->insertRow(rowCount);
QTableWidgetItem *item2=new QTableWidgetItem(QString("h %1").arg(h));
ui->tableWidget_4->setItem(rowCount,H,item2);
//cuarta columna columna
// ui->tableWidget_4->insertRow(rowCount);
QTableWidgetItem *item3=new QTableWidgetItem(val2);
ui->tableWidget_4->setItem(rowCount,Profundidad,item3);
ui->tableWidget_4->setCellWidget(rowCount,Profundidad,new QDoubleSpinBox);
QDoubleSpinBox *sb2=qobject_cast<QDoubleSpinBox *>(
ui->tableWidget_4->cellWidget(rowCount,Profundidad));
sb2->setValue(val2);
v++;
h++;
}
here the form
Note that when the spinboxes are in focus, only the cursor blinks but not all the content is selected.
note:
in the assignment of the column number in setItem I use an enumeration instead of the column numbers.
enum ColNames{V,Velocidad,H,Profundidad};
One possible solution is to override the focusInEvent method of the QDoubleSpinBox that is invoked when it receives the focus, and then select all the text.
class DoubleSpinBox: public QDoubleSpinBox{
public:
using QDoubleSpinBox::QDoubleSpinBox;
protected:
void focusInEvent(QFocusEvent *event){
QDoubleSpinBox::focusInEvent(event);
selectAll();
}
};
And then set it using the setCellWidget method.
You can also avoid using the setCellWidget method and use the delegate:
#include <QtWidgets>
#include <random>
class Helper: public QObject{
public:
Helper(QDoubleSpinBox *ds): QObject(ds), m_ds(ds){
m_ds->installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event){
if(watched == m_ds && event->type() == QEvent::FocusIn){
m_ds->selectAll();
}
return QObject::eventFilter(watched, event);
}
private:
QDoubleSpinBox* m_ds;
};
class Delegate: public QStyledItemDelegate{
public:
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index);
if(QDoubleSpinBox *ds = qobject_cast<QDoubleSpinBox *>(editor)){
new Helper(ds);
}
return editor;
}
};
class MainWindow: public QMainWindow{
public:
MainWindow(QWidget *parent=nullptr):
QMainWindow(parent), table(new QTableWidget)
{
table->setColumnCount(4);
table->setItemDelegate(new Delegate);
setCentralWidget(table);
}
void append(double value1, double value2){
int number_of_rows = table->rowCount();
QTableWidgetItem *item1 = new QTableWidgetItem;
item1->setText(QString("v %1").arg(number_of_rows));
QTableWidgetItem *item2 = new QTableWidgetItem;
item2->setData(Qt::DisplayRole, value1);
QTableWidgetItem *item3 = new QTableWidgetItem;
item3->setText(QString("h %1").arg(number_of_rows));
QTableWidgetItem *item4 = new QTableWidgetItem;
item4->setData(Qt::DisplayRole, value2);
table->insertRow(number_of_rows);
table->setItem(number_of_rows, 0, item1);
table->setItem(number_of_rows, 1, item2);
table->setItem(number_of_rows, 2, item3);
table->setItem(number_of_rows, 3, item4);
table->openPersistentEditor(item2);
table->openPersistentEditor(item4);
}
private:
QTableWidget *table;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyle("fusion");
MainWindow w;
std::uniform_real_distribution<> dist(1.0, 2.0);
for(int i=0; i< 4; i++){
double val1 = dist(*QRandomGenerator::global());
double val2 = dist(*QRandomGenerator::global());
w.append(val1, val2);
}
w.resize(640, 480);
w.show();
return a.exec();
}
in my application I try to connect nodes with lines. I use a QGraphicsView with a QGraphicsScene and my own QGraphicsItems. Now if I click on an item I want to draw a line to another node. To give a visual feedback, the goal should change color if the mouse hovers over the goal. The basics works so far, but my problem is that if I drag a line with the mouse (via mouseMoveEvent), I do not get any hoverEvents any more. I replicated the behaviour with this code:
Header File:
#pragma once
#include <QtWidgets/Qwidget>
#include <QGraphicsItem>
#include <QGraphicsScene>
class HaggiLearnsQt : public QWidget
{
Q_OBJECT
public:
HaggiLearnsQt(QWidget *parent = Q_NULLPTR);
};
class MyScene : public QGraphicsScene
{
public:
MyScene(QObject* parent = 0);
void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent);
};
class MyItem : public QGraphicsItem
{
public:
MyItem(QGraphicsItem* parent = Q_NULLPTR);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
bool mouseOverItem;
};
Implementation:
#include "HaggiLearnsQt.h"
#include <QMessageBox>
#include <QFrame>
#include <QHBoxLayout>
#include <QGraphicsView>
MyScene::MyScene(QObject* parent)
{}
void MyScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
MyItem::MyItem(QGraphicsItem* parent) : mouseOverItem(false)
{
setAcceptHoverEvents(true);
}
QRectF MyItem::boundingRect() const
{
return QRectF(-50, -50, 50, 50);
}
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QBrush b = QBrush(Qt::black);
if(mouseOverItem)
b = QBrush(Qt::yellow);
painter->setBrush(b);
painter->drawRect(boundingRect());
}
void MyItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
mouseOverItem = true;
QGraphicsItem::hoverEnterEvent(event);
}
void MyItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
mouseOverItem = false;
QGraphicsItem::hoverLeaveEvent(event);
}
HaggiLearnsQt::HaggiLearnsQt(QWidget *parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
MyScene* graphicsScene = new MyScene();
QGraphicsView* graphicsView = new QGraphicsView();
graphicsView->setRenderHint(QPainter::RenderHint::Antialiasing, true);
graphicsView->setScene(graphicsScene);
layout->addWidget(graphicsView);
graphicsView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
graphicsView->setMinimumHeight(200);
graphicsView->setMinimumWidth(200);
graphicsView->setStyleSheet("background-color : gray");
MyItem* myitem = new MyItem();
myitem->setPos(50, 50);
graphicsScene->addItem(myitem);
}
And the default main.cpp:
#include "HaggiLearnsQt.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
HaggiLearnsQt w;
w.show();
return a.exec();
}
If you run the code, a box appears in the middle of the window. If you hover over the box, it changes color. Now try to klick outside the box and drag wiht pressed button into the box. The box does not receive a hover and does not change color.
So my question is: Can I somehow change the item while I move the mouse with a pressed button?
You can get the hovered item passing mouseEvent->scenePos() to the QGraphicsScene::itemAt method inside the scene mouse move event handler.
Have a pointer to a MyItem instance, in MyScene:
class MyScene : public QGraphicsScene
{
MyItem * hovered;
//...
initialize it to zero in MyScene constructor:
MyScene::MyScene(QObject* parent)
{
hovered = 0;
}
then use it to track the current highlighted item (if there's one):
void MyScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if(mouseEvent->buttons())
{
QGraphicsItem * item = itemAt(mouseEvent->scenePos(), QTransform());
MyItem * my = dynamic_cast<MyItem*>(item);
if(my != 0)
{
qDebug() << mouseEvent->scenePos();
if(!my->mouseOverItem)
{
my->mouseOverItem = true;
my->update();
hovered = my;
}
}
else
{
if(hovered != 0)
{
hovered->mouseOverItem = false;
hovered->update();
hovered = 0;
}
}
}
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
The line if(mouseEvent->buttons()) at the beginning prevents the check to be performed if no mouse button is held.
Don't forget to initialize mouseOverItem to false in MyItem constructor:
MyItem::MyItem(QGraphicsItem* parent) : mouseOverItem(false)
{
setAcceptHoverEvents(true);
mouseOverItem = false;
}
I want to create a scrollable widget which contains a collection of child widgets managed by QBoxLayout, and it must be easy to add and remove widgets from this collection. But when I add child widgets to it, viewport widget did not expand its size (remain initial size), instead children are overlapping on themselves. I do not know what to do to fix this.
Here is the code:
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QtGui/QScrollArea>
#include <QResizeEvent>
#include <QDebug>
class CMainWidget : public QScrollArea
{
Q_OBJECT
public:
CMainWidget(QWidget *parent = 0);
~CMainWidget();
protected:
virtual void resizeEvent(QResizeEvent *pEvent);
virtual void keyPressEvent(QKeyEvent *pEvent);
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include "rootitem.h"
CMainWidget::CMainWidget(QWidget *parent)
: QScrollArea(parent)
{
QWidget* pViewport = new QWidget();
QBoxLayout* pLayout = new QBoxLayout(QBoxLayout::TopToBottom);
pLayout->setSizeConstraint(QLayout::SetNoConstraint);
for (int iWidgetIndex = 0; iWidgetIndex < 20; iWidgetIndex++)
pLayout->addWidget(new CRootItem());
pViewport->setLayout(pLayout);
pViewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
setWidget(pViewport);
}
CMainWidget::~CMainWidget()
{
}
void CMainWidget::resizeEvent(QResizeEvent *pEvent)
{
QWidget* pViewport = widget();
int iHeight = pViewport->height();
int iWidth = pEvent->size().width();
pViewport->resize(iWidth, iHeight);
}
void CMainWidget::keyPressEvent(QKeyEvent *pEvent)
{
QWidget* pViewport = widget();
QBoxLayout* pLayout = (QBoxLayout*)pViewport->layout();
if (pEvent->key() == Qt::Key_Space)
{
pLayout->addWidget(new CRootItem());
qDebug() << "adding...";
}
if (pEvent->key() == Qt::Key_C)
{
if (!pLayout->isEmpty())
{
QLayoutItem* pItem = pLayout->itemAt(0);
pLayout->removeItem(pItem);
delete pItem->widget();
delete pItem;
qDebug() << "removing...";
}
}
}
Here is the image which shows child widgets overlapping after inserting a couple of new items:
EDIT
Just solved my problem using dirty approach: subtraction and adding item fixed height from viewport's. Is there more fashion way to handle this problem?
Code:
void CMainWidget::keyPressEvent(QKeyEvent *pEvent)
{
QWidget* pViewport = widget();
QBoxLayout* pLayout = (QBoxLayout*)pViewport->layout();
if (pEvent->key() == Qt::Key_Space)
{
QWidget* pItem = new CRootItem();
pLayout->addWidget(pItem);
QSize Size = pViewport->size();
Size.rheight() += pItem->height() + pLayout->spacing();
pViewport->resize(Size);
qDebug() << "adding...";
}
if (pEvent->key() == Qt::Key_C)
{
if (!pLayout->isEmpty())
{
QLayoutItem* pItem = pLayout->itemAt(0);
pLayout->removeItem(pItem);
QSize Size = pViewport->size();
Size.rheight() -= pItem->widget()->height() + pLayout->spacing();
pViewport->resize(Size);
delete pItem->widget();
delete pItem;
qDebug() << "removing...";
}
}
}
Comment out your resizeEvent. You rarely need to set the explicit size or position of a widget unless it's a top level window. Let the layout do the work.
I'm creating a Calendar program on my PC, but I've encountered a very weird problem while creating a Widget to show Day-details.
What it should show is the selected date at the top with a previous and next button, and under that a list with QPushButton-subclasses at a 1 hour interval, but for some reason I only can get the selected date + the previous button + the next button when i'll try to run it.
It seems to be a problem that the widget-pointers are placed in a list, but it seems to be weird for me that that is a problem. But still when I tested to not use the list and hard-coding everything it worked, so why is it a problem that I place those pointers in a list?
GUICalendarDay::GUICalendarDay(QDate date)
{
m_date = date;
//creating buttons
m_prevDay = new QPushButton(QString("<"));
m_nextDay = new QPushButton(QString(">"));
connect(m_nextDay, SIGNAL(clicked()), this, SLOT(nextDay()));
connect(m_prevDay, SIGNAL(clicked()), this, SLOT(prevDay()));
//creating the cells
for(int i = 0; i++; (i<TOTALHOURS))
{
m_shedule[i] = new GUICalendarCell(QTime(i, 0, 0), QTime(i, 59, 59));//creating 1-hour intervals
connect(m_shedule[i], SIGNAL(clickedRange(QTime,QTime)), this, SLOT(selectedRange(QTime,QTime)));
}
//creating the labels
m_dateLabel = new QLabel(m_date.toString(QString("dd/MM/yyyy")));
QLabel *hourLabel[TOTALHOURS];
for(int i = 0; i++; (i<TOTALHOURS))
{
hourLabel[i] = new QLabel(QTime(i,0,0).toString(QString("hh:mm")));
}
//placing everything in a layout
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(m_prevDay, 0, 1);
mainLayout->addWidget(m_dateLabel, 0, 2);
mainLayout->addWidget(m_nextDay, 0, 3);
for(int i = 0; i++; (i<TOTALHOURS))
{
mainLayout->addWidget(hourLabel[i], i+1, 0);
hourLabel[i]->show();
mainLayout->addWidget(m_shedule[i], i+1, 1, 3, 1);
m_shedule[i]->show();
}
setLayout(mainLayout);
}
It's not a really big problem, but I would like to keep my code clean and I also would like to know why this does not work.
You've switched the loop increment with the loop test in the last for loop. It should read for (int i = 0; i < TOTALHOURS; ++).
Your code has several other style issues.
Use container classes, not raw arrays.
You can put all the members into the class, instead of explicitly storing them on the heap.
You can use a PIMPL to make the header not include implementation details.
The example below requires C++11 and Qt 5. It could be adapted to Qt 4 and C++9x through the use of Q_PRIVATE_SLOT.
// Interface (header file)
#include <QWidget>
class QDate;
class GUICalendarDayPrivate;
class GUICalendarDay : public QWidget {
Q_OBJECT
Q_DECLARE_PRIVATE(GUICalendarDay)
QScopedPointer<GUICalendarDayPrivate> const d_ptr;
public:
explicit GUICalendarDay(const QDate & date, QWidget * parent = 0);
~GUICalendarDay();
};
// Implementation (.cpp file)
#include <QGridLayout>
#include <QPushButton>
#include <QLabel>
#include <QDate>
// dummy to make the example compile
class GUICalendarCell : public QWidget {
Q_OBJECT
public:
explicit GUICalendarCell(const QTime &, const QTime &, QWidget * parent = 0) : QWidget(parent) {}
Q_SIGNAL void clickedRange(const QTime &, const QTime &);
};
class GUICalendarDayPrivate {
public:
QDate date;
QGridLayout layout;
QPushButton prevDay, nextDay;
QLabel dateLabel;
QList<GUICalendarCell*> schedule;
QList<QLabel*> hourLabels;
explicit GUICalendarDayPrivate(const QDate &, QWidget * parent);
void onNextDay() {}
void onPrevDay() {}
void selectedRange(const QTime &, const QTime &) {}
};
GUICalendarDay::GUICalendarDay(const QDate &date, QWidget *parent) : QWidget(parent),
d_ptr(new GUICalendarDayPrivate(date, this))
{}
GUICalendarDay::~GUICalendarDay() {}
GUICalendarDayPrivate::GUICalendarDayPrivate(const QDate & date_, QWidget * parent) :
date(date_),
layout(parent),
prevDay("<"), nextDay(">"), dateLabel(date.toString(QString("dd/MM/yyyy")))
{
const int TOTALHOURS = 24;
QObject::connect(&nextDay, &QAbstractButton::clicked, [this]{ onNextDay(); });
QObject::connect(&prevDay, &QAbstractButton::clicked, [this]{ onPrevDay(); });
layout.addWidget(&prevDay, 0, 1);
layout.addWidget(&dateLabel, 0, 2);
layout.addWidget(&nextDay, 0, 3);
for(int i = 0, row = layout.rowCount(); i<TOTALHOURS; ++i, ++row) {
auto from = QTime(i, 0, 0), to = QTime(i, 59, 59);
auto cell = new GUICalendarCell(from, to);
auto hourLabel = new QLabel(from.toString(QString("hh:mm")));
QObject::connect(cell, &GUICalendarCell::clickedRange,
[this](const QTime & from, const QTime & to){
selectedRange(from, to);
});
schedule << cell;
hourLabels << hourLabel;
layout.addWidget(hourLabel, row, 0);
layout.addWidget(cell, row, 1, 3, 1);
}
}
// main.cpp
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GUICalendarDay day(QDate::currentDate());
day.show();
return a.exec();
}
// Only needed if all of the example is in a single file
#include "main.moc"
I developing some kind of builder for our project. I want to use both drag and drop support and context menu in my application. Currently I use drag and drop support but no luck with context menu.
Left side my gui is toolbox. I am draging and droping widgets to the right side(QGraphicsScene) and I also want to use context menu inside QGraphicsScene. I use context menu inside graphics scene before. But before I did not use drap & drop operations. I write proper code but it does not work. What is missing?(Is it related drag & drop). Below is my code files.
//#dragwidget.cpp - Toolbox widgets
#include "dragwidget.h"
DragWidget::DragWidget(void)
{
}
DragWidget::~DragWidget(void)
{
}
void DragWidget::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
QMimeData *data = new QMimeData;
data->setProperty("type",property("type"));
QDrag *drag = new QDrag(this);
drag->setMimeData(data);
drag->start();
}
}
//#view.cpp - Wrapper class for QGraphicsView
#include "view.h"
View::View(Scene *scene,QWidget *parent)
:QGraphicsView(scene,parent)
{
}
View::~View()
{
}
//#scene.cpp - Wrapper class for QGraphicsScene which catch drop events
#include "scene.h"
Scene::Scene(QObject *parent)
:QGraphicsScene(parent)
{
}
Scene::~Scene(void)
{
}
void Scene::setSceneRect(qreal x, qreal y, qreal w, qreal h)
{
QGraphicsScene::setSceneRect(x,y,w,h);
}
void Scene::dragEnterEvent(QGraphicsSceneDragDropEvent *e)
{
if(e->mimeData()->hasFormat("text/plain"));
e->acceptProposedAction();
}
void Scene::dragMoveEvent(QGraphicsSceneDragDropEvent *e)
{
}
void Scene::dropEvent(QGraphicsSceneDragDropEvent *e)
{
e->acceptProposedAction();
int itemType = e->mimeData()->property("type").toInt();
switch(itemType)
{
case BlockSegment:
drawStandartBlockSegment(e->scenePos());
break;
case Switch:
drawStandartSwitch(e->scenePos());
break;
case Signal:
drawStandartSignal(e->scenePos());
break;
}
}
void Scene::drawStandartBlockSegment(QPointF p)
{
BlockSegmentItem *blockSegment = new BlockSegmentItem(p.x(),p.y(),p.x()+100,p.y());
addItem(blockSegment);
}
void Scene::drawStandartSwitch(QPointF p)
{
SwitchItem *switchItem = new SwitchItem(":app/SS.svg");
switchItem->setPos(p);
addItem(switchItem);
}
void Scene::drawStandartSignal(QPointF p)
{
SignalItem *signalItem = new SignalItem(":app/sgs3lr.svg");
signalItem->setPos(p);
addItem(signalItem);
}
//#item.cpp - Base class for my custom graphics scene items
#include "item.h"
Item::Item(ItemType itemType, QGraphicsItem *parent)
:QGraphicsWidget(parent),m_type(itemType)
{
}
Item::~Item()
{
}
int Item::type()
{
return m_type;
}
QRectF Item::boundingRect() const
{
return QRectF(0,0,0,0);
}
QPainterPath Item::shape() const
{
QPainterPath path;
return path;
}
void Item::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(painter);
Q_UNUSED(option);
Q_UNUSED(widget);
}
void Item::deleteItem()
{
}
void Item::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
Q_UNUSED(event);
}
//#switchitem.cpp - my custom switch item.
#include "switchitem.h"
SwitchItem::SwitchItem(const QString& fileName,QGraphicsItem *parent /* = 0 */)
:Item(Switch,parent)
{
svg = new QGraphicsSvgItem(fileName,this);
createActions();
createContextMenu();
}
SwitchItem::~SwitchItem(void)
{
}
QRectF SwitchItem::boundingRect() const
{
return QRectF(pos().x(),pos().y(),svg->boundingRect().width(),
svg->boundingRect().height());
}
QPainterPath SwitchItem::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
void SwitchItem::createActions()
{
deleteAct = new QAction("Sil",this);
deleteAct->setIcon(QIcon(":app/delete.png"));
connect(deleteAct,SIGNAL(triggered()),this,SLOT(deleteItem()));
}
void SwitchItem::createContextMenu()
{
contextMenu = new QMenu("SwitchContextMenu");
contextMenu->addAction(deleteAct);
}
void SwitchItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *e)
{
contextMenu->exec(e->screenPos());
}
//#app.cpp - Application class
#include "app.h"
#include "dragwidget.h"
App::App(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
Q_INIT_RESOURCE(app);
ui.setupUi(this);
canvasScene = new Scene(this);
canvasScene->setSceneRect(0,0,5000,5000);
canvasView = new View(canvasScene);
canvasView->setBackgroundBrush(Qt::black);
canvasView->ensureVisible(0,0,canvasView->geometry().width(),canvasView->geometry().height());
QHBoxLayout *layout = new QHBoxLayout;
toolBox = new QFrame(this);
layout->addWidget(toolBox,1);
layout->addWidget(canvasView,7);
ui.centralWidget->setLayout(layout);
createToolBox();
}
App::~App()
{
}
void App::createToolBox()
{
QVBoxLayout *layout = new QVBoxLayout;
QLabel *blockSegmentLabel = new QLabel("Block Segment");
DragWidget *blockSegmentWidget = new DragWidget;
blockSegmentWidget->setProperty("type",BlockSegment);
blockSegmentWidget->setPixmap(QPixmap(":app/blocksegment.png"));
QLabel *switchLabel = new QLabel("Switch");
DragWidget *switchWidget = new DragWidget;
switchWidget->setProperty("type",Switch);
switchWidget->setPixmap(QPixmap(":app/SS.png"));
QLabel *signalLabel = new QLabel("Signal");
DragWidget *signalWidget = new DragWidget;
signalWidget->setProperty("type",Signal);
signalWidget->setPixmap(QPixmap(":app/sgs3lr.png"));
layout->addWidget(blockSegmentLabel);
layout->addWidget(blockSegmentWidget);
layout->addWidget(switchLabel);
layout->addWidget(switchWidget);
layout->addWidget(signalLabel);
layout->addWidget(signalWidget);
toolBox->setLayout(layout);
}
void DragWidget::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
QMimeData *data = new QMimeData;
data->setProperty("type",property("type"));
QDrag *drag = new QDrag(this);
drag->setMimeData(data);
drag->start();
}
}
Problem is here. You've "eaten" right button :) Try fall back into base implementation of mousePressEvent