PDA

View Full Version : How to use Checkboxes in QMenu?



Stravinsky
11th June 2015, 13:29
Hi Everyone.

I have problem with my program in Qt5. I use QSpreadsheetHeaderView class which is subclass of QHeaderView.

.cpp:


#include <QPainter>
#include <QCursor>
#include <QHoverEvent>
#include <QMenu>
#include <QRegExp>
#include <QApplication>
#include <QSignalMapper>
#include <QWidgetAction>
#include "QSpreadsheetHeaderView.h"
#include "mainwindow.h"

QSpreadsheetHeaderView::QSpreadsheetHeaderView(Qt: :Orientation orientation, QWidget * parent)
: QHeaderView(orientation, parent)
{
// Required to refresh button menu when the mouse leave the header.
setAttribute(Qt::WA_Hover, true);
}

void QSpreadsheetHeaderView::mousePressEvent ( QMouseEvent * event )
{
QHeaderView::mousePressEvent(event);

int logicalIndex = logicalIndexAt(event->pos());

if (buttonMenuRect(logicalIndex).contains(event->pos())) {
QMenu menu(this);
QAction *hideCol = menu.addAction("Hide");
QAction *sortAZ = menu.addAction("Sort A->Z");
QAction *sortZA = menu.addAction("Sort Z->A");
QMenu * format= menu.addMenu(tr("&Filter"));
QAction *all = format->addAction("All");
format->addSeparator();

QSignalMapper *mapper = new QSignalMapper(this);
for(int i = 0; i < model()->columnCount(); i++)
{
QAction *others = format->addAction(model()->headerData(i, Qt::Horizontal).toString());
format->addAction(others);

QWidgetAction * addition = new QWidgetAction(&menu);
checkBox = new QCheckBox(&menu);
checkBox->setText(model()->headerData(i, Qt::Horizontal).toString());
addition->setDefaultWidget(checkBox);
menu.addAction(addition);
mapper->setMapping(addition, i);
}

// Disable hide column if only one column remains. Otherwise
// the gui is no more available to show them back.
hideCol->setEnabled(hiddenSectionCount() < count() - 1);

QAction *res = menu.exec(mapToGlobal(event->pos()));

if (res == hideCol) {
hideSection(logicalIndex);
updateSection(logicalIndex-1);
}
if (res == sortAZ)
model()->sort(logicalIndex, Qt::AscendingOrder);
if (res == sortZA)
model()->sort(logicalIndex, Qt::DescendingOrder);
}

// Catch previous arrow mouse click.
if (prevRect(logicalIndex).contains(event->pos())) {
showSection(logicalIndex - 1);
updateSection(logicalIndex - 2);
}

// Catch next arrow mouse click.
if (nextRect(logicalIndex).contains(event->pos())) {
showSection(logicalIndex + 1);
updateSection(logicalIndex + 2);
}
}

void QSpreadsheetHeaderView::mouseMoveEvent(QMouseEvent * event)
{
QHeaderView::mouseMoveEvent(event);

// Required to refresh the button menu enable/disable state.
int logicalIndex = logicalIndexAt(event->pos());
updateSection(logicalIndex);
}

void QSpreadsheetHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
painter->save();

QHeaderView::paintSection(painter, rect, logicalIndex);

painter->restore();

if (!rect.isValid() || isSortIndicatorShown())
return;

if (isSectionHidden(logicalIndex - 1)) {
drawPrevButton(painter, logicalIndex);
}

if (isSectionHidden(logicalIndex + 1)) {
drawNextButton(painter, logicalIndex);
}

QPoint pos = mapFromGlobal(QCursor::pos());
if (rect.contains(pos)) {
drawMenuButton(painter, logicalIndex, buttonMenuRect(logicalIndex).contains(pos));
}
}

QRect QSpreadsheetHeaderView::sectionRect(int logicalIndex) const
{
return QRect(sectionViewportPosition(logicalIndex), 0, sectionSize(logicalIndex), height());
}

QRect QSpreadsheetHeaderView::buttonMenuRect(int logicalIndex) const
{
QRect sr = sectionRect(logicalIndex);

return QRect(sr.right() - 5 - 13, sr.center().y() - 6, 13, 13);
}

QRect QSpreadsheetHeaderView::prevRect(int logicalIndex) const
{
if (isSectionHidden(logicalIndex))
return QRect();

QRect sr = sectionRect(logicalIndex);

return QRect(sr.left() + 1, sr.center().y() - 6, 13, 13);
}

QRect QSpreadsheetHeaderView::nextRect(int logicalIndex) const
{
if (isSectionHidden(logicalIndex))
return QRect();

QRect sr = sectionRect(logicalIndex);

return QRect(sr.right() - 13, sr.center().y() - 6, 13, 13);
}

void QSpreadsheetHeaderView::drawMenuButton(QPainter *painter, int logicalIndex, bool enabled) const
{
QRect brect = buttonMenuRect(logicalIndex);

painter->setPen(enabled ? QColor(186,186,186) : QColor(223, 223, 223));
painter->setBrush(QColor(246,246,246));
painter->drawRect(brect.adjusted(0,0,-1,-1));

painter->setPen(enabled ? QColor(71,71,71) : QColor(193, 193, 193));
painter->drawLine(brect.left()+3, brect.top()+5, brect.right()-3, brect.top()+5);
painter->drawLine(brect.left()+4, brect.top()+6, brect.right()-4, brect.top()+6);
painter->drawLine(brect.left()+5, brect.top()+7, brect.right()-5, brect.top()+7);
painter->drawPoint(brect.left()+6, brect.top()+8);
}

void QSpreadsheetHeaderView::drawPrevButton(QPainter *painter, int logicalIndex) const
{
QRect rect = prevRect(logicalIndex);

painter->setPen(QColor(71,71,71));
painter->drawLine(rect.left()+1, rect.center().y() - 3, rect.left()+1, rect.center().y() + 3);
painter->drawLine(rect.left()+2, rect.center().y() - 2, rect.left()+2, rect.center().y() + 2);
painter->drawLine(rect.left()+3, rect.center().y() - 1, rect.left()+3, rect.center().y() + 1);
painter->drawPoint(rect.left()+4, rect.center().y());
}

void QSpreadsheetHeaderView::drawNextButton(QPainter *painter, int logicalIndex) const
{
QRect rect = nextRect(logicalIndex);

painter->setPen(QColor(71,71,71));
painter->drawLine(rect.right()-2, rect.center().y() - 3, rect.right()-2, rect.center().y() + 3);
painter->drawLine(rect.right()-3, rect.center().y() - 2, rect.right()-3, rect.center().y() + 2);
painter->drawLine(rect.right()-4, rect.center().y() - 1, rect.right()-4, rect.center().y() + 1);
painter->drawPoint(rect.right()-5, rect.center().y());
}



.h


#ifndef QSPREADSHEETHEADERVIEW
#define QSPREADSHEETHEADERVIEW

#include <QHeaderView>
#include <QCheckBox>

/*!
\class QSpreadsheetHeaderView
\brief The QSpreadsheetHeaderView class is a special QHeaderView that mimic Google Spreadsheet header.
version 1.0
\sa QHeaderView
*/
class QSpreadsheetHeaderView : public QHeaderView
{
Q_OBJECT

public:
QSpreadsheetHeaderView(Qt::Orientation orientation, QWidget * parent = 0);
QCheckBox *checkBox;
//protected:
void mousePressEvent(QMouseEvent * event);
void mouseMoveEvent(QMouseEvent * event);
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;

QRect sectionRect(int logicalIndex) const;
QRect buttonMenuRect(int logicalIndex) const;
QRect prevRect(int logicalIndex) const;
QRect nextRect(int logicalIndex) const;

void drawMenuButton(QPainter *painter, int logicalIndex, bool enabled) const;
void drawPrevButton(QPainter *painter, int logicalIndex) const;
void drawNextButton(QPainter *painter, int logicalIndex) const;
private:

};

#endif // QSPREADSHEETHEADERVIEW



I add to the menu checkboxes that represent column of the table. I know that every time I use function checboxes are creating once again. I tried signal/slots, implementation of checkboxes array, but my knowledge does not allow to solve this problem. I would like to save value of checkboxes after leaving menu. Also i need to connect this checkboxes with hiding colums. Can someone help me?

anda_skoa
12th June 2015, 13:41
Any reason you are not using a checkable action?

Cheers,
_

Stravinsky
15th June 2015, 17:19
I already implement menu. I have problem with staying on top of the menu when I want to check more than one checkable action. When i use QCheckBox i dont have this problem. Do you know how to select multiple actions without hiding the menu?.

Stravinsky
18th June 2015, 05:13
I fixed this problem.

I used 2 signal&slots. One for non checkable action and second for checkable. The second one have menu->show() in slot to keep menu on top.




void QSpreadsheetHeaderView::mousePressEvent ( QMouseEvent * event )
{
QHeaderView::mousePressEvent(event);

logicalIndex = logicalIndexAt(event->pos());

if (buttonMenuRect(logicalIndex).contains(event->pos())) {

if(set_checkbox==false)
{
menu = new QMenu(this);

sortAZ = menu->addAction("Sortuj A->Z");
sortZA = menu->addAction("Sortuj Z->A");
clear_sort = menu->addAction("Anuluj");
menu->addSeparator();
hideCol = menu->addAction("Ukryj kolumnÄ™");
clear_hideCol = menu->addAction("Odkryj wszystkie");

for(int i = 0; i <model()->columnCount(); i++)
{
actions[i] = new QAction(menu);
actions[i] = menu->addAction(model()->headerData(i, Qt::Horizontal).toString());
actions[i]->setCheckable(true);
connect(actions[i],SIGNAL(triggered(bool)),this,SLOT(hide_selected_c olumn(bool)));
if(i<columns_min)actions[i]->setChecked(true);

}

set_checkbox=true;
connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(action(QAction*)));
}

// Disable hide column if only one column remains. Otherwise
// the gui is no more available to show them back.
hideCol->setEnabled(hiddenSectionCount() < count() - 1);
if(hideCol->isEnabled()==false)
{
for(int i = 0; i <model()->columnCount(); i++)
{
if(actions[i]->isChecked()==true)
{
previous_sender=actions[i];
freeze=true;
actions[i]->setEnabled(false);
break;
}
}
}

menu->popup(mapToGlobal(event->pos()));
}

// Catch previous arrow mouse click.
else if (prevRect(logicalIndex).contains(event->pos()) && set_checkbox==true) {
showSection(logicalIndex - 1);
actions[logicalIndex - 1]->setChecked(true);
updateSection(logicalIndex - 2);
if(freeze==true) { previous_sender->setEnabled(true); freeze=false;}
}

// Catch next arrow mouse click.
else if (nextRect(logicalIndex).contains(event->pos()) && set_checkbox==true) {
showSection(logicalIndex + 1);
actions[logicalIndex + 1]->setChecked(true);
updateSection(logicalIndex + 2);
if(freeze==true) { previous_sender->setEnabled(true); freeze=false; }
}


}



Slot for non checkable:



void QSpreadsheetHeaderView::action(QAction *action) {


if (action == hideCol)
{
hideSection(logicalIndex);
actions[logicalIndex]->setChecked(false);
updateSection(logicalIndex-1);
}

if (action == sortAZ)
model()->sort(logicalIndex, Qt::AscendingOrder);

if (action == sortZA)
model()->sort(logicalIndex, Qt::DescendingOrder);

if (action == clear_hideCol){
for(int i = 0; i <model()->columnCount(); i++)
{
showSection(i);
actions[i]->setChecked(true);
actions[i]->setEnabled(true);
if(freeze==true) { previous_sender->setEnabled(true); freeze=false;}
}
}

if (action == clear_sort){

}

}


and checkable:


void QSpreadsheetHeaderView::hide_selected_column(bool checked)
{
menu->show();
QAction * senderCHECK = qobject_cast<QAction *>(this->sender());

for(int i=0;i<23;i++)
{
if(senderCHECK==actions[i])
{
if(checked==false && freeze==false)
{
hideSection(i);
actions[i]->setChecked(false);
}

else if(checked==true && freeze==true)
{
hideCol->setEnabled(true);
previous_sender->setEnabled(true);
showSection(i);
actions[i]->setChecked(true);
freeze=false;

}

else if(checked==true)
{
showSection(i);
actions[i]->setChecked(true);
}
}
}

if(count()-hiddenSectionCount()==1)
{
freeze=true;
hideCol->setEnabled(false);
for(int i=0;i<23;i++)
{
if(actions[i]->isChecked()==true)
{
previous_sender=actions[i];
actions[i]->setEnabled(false);
break;
}
}
}
}