QToolButton prevent from moving - qt

Is there a way to prevent QToolButton from being "pressed in" when clicked? I read somewhere that setting
button->setCheckable(false);
should do the trick, but it doesn't.

There is a way to do it via a QProxyStyle:
class ButtonProxyStyle : public QProxyStyle
{
public:
const int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0)
{
int ret = 0;
switch (metric)
{
case QStyle::PM_ButtonShiftHorizontal:
case QStyle::PM_ButtonShiftVertical:
ret = 0;
break;
default:
ret = QProxyStyle::pixelMetric(metric, option, widget);
break;
}
return ret;
}
};
And then, with your button:
myToolButton->setStyle(new ButtonProxyStyle);

Add QAction to the toolbar and use it to controll your tool button
// button action
QAction * poBtnAction = poToolbar->addWidget(button);
// disable button
poBtnAction->setEnabled(false);

Related

QSystemTrayIcon notification message with custom icon

QSystemTrayIcon has a function :
void showMessage(const QString &title, const QString &msg,
MessageIcon icon = Information, int msecs = 10000);
is there a way to change it to custom icon , for example like this -
void showIconMessage(const QString &title, const QString &msg,
QIcon icon = QIcon(), int msecs = 10000);
without modifying the Qt sources
I know that showMessage (d is instance of QSystemTrayIconPrivate and is called with Q_D(QSystemTrayIcon) macro)
void QSystemTrayIcon::showMessage(const QString& title, const QString& msg,
QSystemTrayIcon::MessageIcon icon, int msecs)
{
Q_D(QSystemTrayIcon);
if (d->visible)
d->showMessage_sys(title, msg, icon, msecs);
}
calls showMessage_sys from QSystemTrayIconPrivate where in turn all the magic with icon happens:
void QSystemTrayIconPrivate::showMessage_sys(const QString &message,
const QString &title,
QSystemTrayIcon::MessageIcon icon,
int msecs)
{
if (!qpa_sys)
return;
QIcon notificationIcon;
switch (icon) {
case QSystemTrayIcon::Information:
notificationIcon = QApplication::style()- >standardIcon(QStyle::SP_MessageBoxInformation);
break;
case QSystemTrayIcon::Warning:
notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
break;
case QSystemTrayIcon::Critical:
notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
break;
default:
break;
}
qpa_sys->showMessage(message, title, notificationIcon,
static_cast<QPlatformSystemTrayIcon::MessageIcon>(icon), msecs);
}
Now, it seems, that I need to re-implement these two functions in two classes and i'm ready to go, but.. It seems that QSystemTrayIcon is closely tied to QSystemTrayIconPrivate. Instance of QSystemTrayIconPrivate is created only in QSystemTrayIcon constructor (which I can't really change if I plan to create classes that inherit both QSystemTrayIcon and QSystemTrayIconPrivate and re-implement showMessage functions):
QSystemTrayIcon::QSystemTrayIcon(QObject *parent)
: QObject(*new QSystemTrayIconPrivate(), parent)
{
}
QSystemTrayIcon::QSystemTrayIcon(const QIcon &icon, QObject *parent)
: QObject(*new QSystemTrayIconPrivate(), parent)
{
setIcon(icon);
}
So is there anything I am missing? Or is there another way to simply show notification message with custom icon?
What you could try (not sure if it will work for system tray) is do the same as described in this answer and override the SP_MessageBoxWarning / SP_MessageBoxCritical / SP_MessageBoxInformation icons, but as I said I'm not sure if the system tray just uses a downscaled version of the message box icons or if the system tray icons are separate. In the case of the latter, I guess you will have to patch QT sources, maybe add a new item to the QSystemTrayIcon and patch the switch to call some function provided by you to return the needed icon. Something like:
void QSystemTrayIconPrivate::showMessage_sys(const QString &message,
const QString &title,
QSystemTrayIcon::MessageIcon icon,
int msecs)
{
if (!qpa_sys)
return;
QIcon notificationIcon;
switch (icon) {
case QSystemTrayIcon::Information:
notificationIcon = QApplication::style()- >standardIcon(QStyle::SP_MessageBoxInformation);
break;
case QSystemTrayIcon::Warning:
notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
break;
case QSystemTrayIcon::Critical:
notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
break;
case QSystemTrayIcon::Custom:
// Call a function that will fetch the needed icon and assign it to notificationIcon
break;
default:
break;
}
qpa_sys->showMessage(message, title, notificationIcon,
static_cast<QPlatformSystemTrayIcon::MessageIcon>(icon), msecs);
}

QT Custom QStyle for QTabBar and QIcon

I have customize a Qtabbar's QMdiarea to get horizontal tab instead of vertical tab like this :
http://www.qtcentre.org/wiki/index.php?title=Customizing_QTabWidget%27s_QTabBar
But i want to insert icon in my QTabBar.
If I apply my custom style to my QTabBar my icons doesn't appear.
If I don't apply, my icon appear.
here my custom style :
class CustomTabStyle : public QPlastiqueStyle
{
Q_OBJECT
public:
QSize sizeFromContents(ContentsType type, const QStyleOption *option,const QSize &size, const QWidget *widget) const
{
QSize s = QPlastiqueStyle::sizeFromContents(type, option, size, widget);
if (type == QStyle::CT_TabBarTab)
s.transpose();
return s;
}
void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
if (element == CE_TabBarTabLabel)
{
if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
{
QStyleOptionTab opt(*tab);
opt.shape = QTabBar::RoundedNorth;
QPlastiqueStyle::drawControl(element, &opt, painter, widget);
return;
}
}
QPlastiqueStyle::drawControl(element, option, painter, widget);
}
};
and i apply my style like this :
mMdiAreaDock=aMdiArea;
m_pMdiAreaTabBar = NULL;
QObjectList listChildren = mMdiAreaDock->children();
for (QObjectList::Iterator i = listChildren.begin(); i != listChildren.end(); ++i)
{
if (QString((*i)->metaObject()->className()) == "QTabBar")
{
m_pMdiAreaTabBar = dynamic_cast<QTabBar*>(*i);
break;
}
}
m_pMdiAreaTabBar->setStyle(new CustomTabStyle());
return 0;
where mMdiAreaDock is an QMdiArea
and m_pMdiAreaTabBar is a QTabBar
Look at this code:
const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option));
QStyleOptionTab opt(*tab);
option is an instance of QStyleOptionTabV2.
When you create a new object QStyleOptionTab opt with copy constructor, you lose some important data which extended QStyleOptionTabV2 contains including information about an icon.
Use this code instead:
if (const QStyleOptionTabV2 *tab = qstyleoption_cast<const QStyleOptionTabV2 *>(option))
{
QStyleOptionTabV2 opt(*tab);
opt.shape = QTabBar::RoundedNorth;
QPlastiqueStyle::drawControl(element, &opt, painter, widget);
return;
}
ps. I used this code to assign an icon for a specific tab:
m_pMdiAreaTabBar->setTabIcon(0, icon);

QStandardItemModel header with widget and text

i need to use checkbox with text, like this "Check all":
in header of QStanndardItemModel. I tried like this
QStandardItem* item0 = new QStandardItem("some text");
item0->setCheckable(true);
item0->setCheckState(Qt::Checked);
item0->setText("some text");
_model->setHorizontalHeaderItem(1, item0);
This way only works for items not for header, i mean for items if i use
_model->setItem(new QStandardItem(some_item);
I heard about writing my own class which inherit QHeaderView but i dont know if this can help in my problem. I would ask if there is a easy way to achieve this?
Regards
i did something like this:
QStandardItemModel *model = new QStandardItemModel(this);
ui->tableView->setModel(model);
QtCheckHeaderView *header = new QtCheckHeaderView(Qt::Horizontal, ui->tableView);
QtCheckHeaderView *vheader = new QtCheckHeaderView(Qt::Vertical, ui->tableView);
ui->tableView->setHorizontalHeader(header);
ui->tableView->setVerticalHeader(vheader);
QStandardItem *root = model->invisibleRootItem();
QList<QList<QStandardItem *> > items;
for (int i = 0; i < 10; ++i)
{
QList<QStandardItem *> res;
for (int j = 0; j < 1000; ++j)
{
res.append(new QStandardItem(QString("%1;%2").arg(j).arg(i)));
vheader->addCheckable(j);
}
items.append(res);
root->appendColumn(res);
header->addCheckable(i);
}
this works great. Draws checkbox with text in vertical and horizontal header.
But this works great only if i put this code to MainWindow constructor. If i put this code to for example pushbutton function this wont work. Datas are fine but headers are invisible.
ui->tableView->repaint();
wont work. Someone maybe knows the answer why this is happening and/or how to solve this?
thanks for answer
Qt models don't support item flags for headers. Setting item flags on items that are used as headers has no effect. QHeaderView doesn't support drawing checkboxes. I'm afraid subclassing QHeaderView is the simpliest way.
I wrote an example of how it can be implemented based on this FAQ page. This class assumes that you use QStandardItemModel and uses flags of its header items. If someone would want to use other model class, subclassing QAbstractItemModel and implementing missing interface and changing headerview implementation would be needed.
class MyHeader : public QHeaderView
{
public:
MyHeader(Qt::Orientation orientation, QWidget * parent = 0);
protected:
QStandardItemModel* standard_model;
virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
virtual void mousePressEvent(QMouseEvent *event);
virtual void setModel(QAbstractItemModel* model);
};
MyHeader::MyHeader(Qt::Orientation orientation, QWidget *parent) : QHeaderView(orientation, parent)
{
standard_model = 0;
}
void MyHeader::paintSection(QPainter *painter, const QRect &rect, int logical_index) const {
painter->save();
QHeaderView::paintSection(painter, rect, logical_index);
painter->restore();
if (standard_model && orientation() == Qt::Horizontal) {
QStandardItem* item = standard_model->horizontalHeaderItem(logical_index);
if (item && item->isCheckable()) {
int offset = (height() - style()->pixelMetric(QStyle::PM_IndicatorHeight))/2;
int pos = sectionViewportPosition(logical_index);
QStyleOptionButton option;
option.rect = QRect(offset + pos, offset,
style()->pixelMetric(QStyle::PM_IndicatorWidth),
style()->pixelMetric(QStyle::PM_IndicatorHeight));
if (item->checkState() == Qt::Checked) {
option.state = QStyle::State_On;
} else {
option.state = QStyle::State_Off;
}
option.state |= QStyle::State_Enabled;
style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter);
}
}
}
void MyHeader::mousePressEvent(QMouseEvent *event) {
int offset = (height() - style()->pixelMetric(QStyle::PM_IndicatorHeight))/2;
if (standard_model && orientation() == Qt::Horizontal) {
for(int logical_index = 0; logical_index < count(); logical_index++) {
int pos = sectionViewportPosition(logical_index);
QRect rect(offset + pos, offset,
style()->pixelMetric(QStyle::PM_IndicatorWidth),
style()->pixelMetric(QStyle::PM_IndicatorHeight));
if (rect.contains(event->pos())) {
QStandardItem* item = standard_model->horizontalHeaderItem(logical_index);
if (item && item->isCheckable()) {
item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
return;
}
}
}
}
QHeaderView::mousePressEvent(event);
}
void MyHeader::setModel(QAbstractItemModel *model) {
QHeaderView::setModel(model);
standard_model = qobject_cast<QStandardItemModel*>(model);
}
//usage
QTableView view;
QStandardItemModel model;
model.setColumnCount(5);
QStandardItem* item0 = new QStandardItem("some text");
item0->setCheckable(true);
item0->setCheckState(Qt::Checked);
item0->setText("some text");
model.setHorizontalHeaderItem(0, item0);
view.setModel(&model);
MyHeader *myHeader = new MyHeader(Qt::Horizontal, &view);
view.setHorizontalHeader(myHeader);
view.show();

QListView and delegate display unintended item

I've a problem with my QListView, it paint an unintended item on the top left of the QListView :
http://s4.postimage.org/64orbk5kd/Screen_Shot_2013_02_14_at_20_23_14.png
I use a QStyledItemDelegate in my QListView :
m_stringList.push_back("FIRST");
m_stringList.push_back("SECOND");
m_stringList.push_back("THIRD");
m_model.setStringList(m_stringList);
ui->processesListView->setFlow(QListView::LeftToRight);
ui->processesListView->setModel(&m_model);
ui->processesListView->setItemDelegate(new ProcessItemDelegate(this, ui->processesListView));
The delegate (ProcessItemDelegate) paint method use a custom QWidget to display the information :
void ProcessItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex &inIndex ) const
{
_listItem->setContent(_listView->model()->data(inIndex).toString());
painter->save();
painter->translate(option.rect.center());
_listItem->render(painter);
painter->restore();
}
The setContent method of the QWidget is very simple :
void ProcessItem::setContent(const QString &s)
{
ui->processId->setText(s);
}
I have another way to add a widget to some list using a QListWidget.
For example knowing that ui->historyView is a QListWidget element and HistoryElementView a subclass of QWidget.
void View::onHistoryChanged(const QList<HistoryElement> &history)
{
clearHistory();
foreach(HistoryElement elt, history)
{
HistoryElementView *historyViewElement = new HistoryElementView(elt.getDateTime("dd/MM/yyyy - hh:mm"), elt.getFilename());
QListWidgetItem *item = new QListWidgetItem();
ui->historyView->addItem(item);
ui->historyView->setItemWidget(item, historyViewElement);
}
}
void View::clearHistory()
{
QListWidgetItem *item;
while (ui->historyView->count() != 0)
{
item = ui->historyView->takeItem(0);
delete item;
}
}
You do not need to delete the widgets inside your QListWidgetItem, it will be handle by Qt.
Once your widgets are inside the list, you can retrieve them using :
// Using index
QListWidgetItem *item = ui->historyView->item(0);
HistoryElementView *elt = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
// Using position
QListWidgetItem *item = ui->historyView->itemAt(pos);
HistoryElementView *historyElement = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
Hope it helps.

How to identify when the current tab is changing in a QTabWidget?

I'm using a QTabWidget and I need a way to handle the change of the current tab before it actually happens, and possibly cancel it if certain conditions are met. The QTabWidget::currentChanged signal is received after the current tab has changed, but is there a QTabWidget::currentChanging signal or another way to achieve the behavior I need?
In my case, I connect SIGNAL and SLOT like this:
//check if user clicked at a tab
connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSelected()));
and in tabSelected() function, I check current Tab Index:
void MainWindow::tabSelected(){
if(ui->tabWidget->currentIndex()==0){
// Do something here when user clicked at tab1
}
if(ui->tabWidget->currentIndex()==3){
// Do something here when user clicked at tab4
}
}
This is how I solved it
void MainWindow::on_tabWidget_currentChanged(int index)
{
if (lockTabs) ui->tabWidget->setCurrentIndex(lockedTab);
}
On click of a button, I set lockTabs to true and save the current tab index to lockedTab (int). No matter what tab you click it will just throw you back to the locked tab.
I do agree with the first comment that disabling tabs is the better way tho. This is my solution for disabling tabs:
void MainWindow::lockTabs(int except){
for (int i=0; i<ui->tabWidget->count(); i++) {
if (i!=except) ui->tabWidget->setTabEnabled(i, false);
}
}
void MainWindow::unlockTabs() {
for (int i=0; i<ui->tabWidget->count(); i++) {
ui->tabWidget->setTabEnabled(i, true);
}
}
In your header, declare:
QWidget *saveTab
Create a routine tabChanged have the slot for the currentChanged() signal. Then:
void pkgName::tabChanged
//"ask your question"
if "bad reply"
// This is where you'll "set back to your old tab"
ui->tabWidget->setCurrentWidget(savedWidget)
end if
savedWidget = ui->tabWidget-> getCurrentWidget()
// Process
Using a regular QTabWidget and flipping back to previous tab after currentChanged was emitted if change was forbidden does not look right for user as the new tab is made visible before the previous one is re-selected, this is due to QTabWidget informing the tab "changed", not "is about to change".
One option is to create your own QTabWidget. Thanks to QTabBar, this is pretty obvious.
You'll also need to create QTabWidget like function to use it "like" a QTabWidget, but there's not so many function you'll need.
Here is an example of QTabWidget like class with a aboutToChangeTab signal being emitted informing that tab is about to be changed, one may set allowed to false to forbid tab change.
#pragma once
#include <QWidget>
class QTabBar;
class QStackedWidget;
class SmartTabWidget : public QWidget
{
Q_OBJECT
typedef QWidget baseClass;
public:
SmartTabWidget( QWidget* parent );
int addTab(QWidget* page, const QString& label);
int addTab(QWidget* page, const QIcon& icon, const QString& label);
int currentIndex() const;
QWidget* widget( int index );
signals:
void aboutToChangeTab( QWidget* curTab, QWidget* nextTab, bool* allowed );
private slots:
void tryToChangeTab( int index );
private:
QTabBar* m_tab;
QStackedWidget* m_stack;
};
And:
#include "smart_tab_widget.h"
#include <QTabBar>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QIcon>
SmartTabWidget::SmartTabWidget( QWidget* widget ) :
baseClass( widget )
{
new QVBoxLayout( this );
layout()->setContentsMargins( 0,0,0,0 );
layout()->addWidget( m_tab = new QTabBar(this) );
layout()->addWidget( m_stack = new QStackedWidget(this) );
connect(m_tab, SIGNAL(currentChanged(int)), this, SLOT(tryToChangeTab(int)));
}
int SmartTabWidget::addTab(QWidget* page, const QString& label)
{
return addTab( page, QIcon(), label );
}
int SmartTabWidget::addTab(QWidget* page, const QIcon& icon, const QString & label)
{
m_stack->addWidget( page );
int index = m_tab->addTab( icon, label );
assert( m_stack->count() == index+1 );
return index;
}
int SmartTabWidget::currentIndex() const
{
return m_tab->currentIndex();
}
QWidget* SmartTabWidget::widget( int index )
{
return m_stack->widget( index );
}
void SmartTabWidget::tryToChangeTab( int index )
{
int currentIndex = m_stack->currentIndex();
bool canChange = true;
emit aboutToChangeTab( m_stack->widget( currentIndex ),
m_stack->widget( index ),
&canChange );
if ( canChange )
{
m_stack->setCurrentIndex( index );
}
else
{
// prevent this function to be called again
bool blocked = m_tab->blockSignals( true );
// unselect requested tab as change is not allowed
m_tab->setCurrentIndex( currentIndex );
m_tab->blockSignals( blocked );
}
}
One can connect aboutToChangeTab to a slot (allowTabChange) and do something like:
void MyWidget::allowTabChange( QWidget* curTab, QWidget* nextTab, bool* allowed )
{
if ( !canChange( curTab, nextTab ) )
*allowed = false;
}
There is a simple solution with event filters that doesn't require to subclass QTabWidget. In my case I needed to disable switching to a particular tab
ui->tabWidget->tabBar()->installEventFilter(this);
then:
bool MainWindow::eventFilter(QObject* watched, QEvent* event)
{
if(watched == ui->tabWidget->tabBar())
{
if(event->type() == QEvent::MouseButtonPress)// || event->type() == QEvent::MouseButtonRelease)
{
auto pos = dynamic_cast<QMouseEvent*>(event)->pos();
auto index = ui->tabWidget->tabBar()->tabAt(pos);
if(ui->tabWidget->widget(index) == ui->addButtonTab)
return true; // cancell event
}
}
return QMainWindow::eventFilter(watched, event);
}
At the stage of mouse click it is possible to retrieve index of a currently selected tab and prepare it for being switched(or cancel switching as done in my example).

Resources