Implementing a shift-click modifier to a specific form widget? - c++

If I use Qt Designer for my application's main window -- and the application contains many different widgets inside of it -- how do I further customize those widgets sepcifically? For example, I have a QTableView widget inside of my main application. Upon debugging the application, a ui_myapplication.h file gets created from the Qt Designer .ui form. Suppose I wanted to add some extra things to the widgets defined in that file. How would I if it's created at runtime?
Example:
In myApplication.cpp, I have this block of code:
void myApplication::mousePressEvent(QMouseEvent* event) {
if(event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
ui->tableView->setSortingEnabled(false);
}
}
}
This is similar to what I want, but not exactly. The idea is to have the QTableView widget named tableView (which is contained within the main application that I created in Qt Designer) to disable the table's sorting functionality when I hold Shift and left-click a column header. (the end goal is to make it so that Shift+clicking will disable column sorting for a short while so I can select all items in the column instead of having it sort the column).
The code above will only work if I Shift+Click the very bottom of the main application (in the space where there are no other widgets). That makes sense. But how do I make it so that doing a Shift+click inside the tableView widget will trigger the qDebug() << "shift modifier"; line?
I'd want something similar to this: (pseudocode):
void myApplication::mousePressEvent(QMouseEvent* event) {
if(target == ui->tableView->horizontalheader() && event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
ui->tableView->setSortingEnabled(false);
}
}
}
How can I do this?
EDIT: research had led me to believe I can do this with an event filter that could target a specific widget. Am I on the right track?
EDIT 2: Thanks to goug's answer below, I was able to accomplish what I needed by subclassing QTableView and then promoting my existing form's QTableView to the new class. See below:
mytableview.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include "mytableview.h"
#include <QTableView>
class MyTableView : public QTableView
{
Q_OBJECT
public:
explicit MyTableView(QWidget * parent = 0);
~MyTableView();
protected:
void mousePressEvent(QMouseEvent *event);
};
#endif // MYTABLEVIEW_H
mytableview.cpp
#include "mytableview.h"
#include <QDebug>
MyTableView::MyTableView(QWidget* parent)
{
}
MyTableView::~MyTableView()
{
}
void MyTableView::mousePressEvent(QMouseEvent* event) {
if(event->modifiers() & Qt::ShiftModifier) {
if(event->button() == Qt::LeftButton) {
qDebug() << "shift modifier";
setSortingEnabled(false);
}
}
}

There's a couple of different approaches you could use here. You could subclass QTableView, and then in Qt Designer, you place a QTableView as normal, but then promote it to your derived class. The generated code creates an instance of your class rather than QTableView. In Qt Designer, right-click on the table view and select from the Promote options. You'll have to enter your class details the first time. I'd be inclined to go this route especially if there's other custom behavior you want to implement on the table view.
Another option is to create a new class and install it as an event filter on your QTableView. Your new class then gets the events before they go to the QTableView and you can act accordingly on them. Look up installEventFilter in Qt Assistant, and that'll get you to the details of how to do this.

Related

Qt Model for non-table custom widget

I created a custom widget composed of QPushButtons placed on a broken layout since I need to place them on specific places with no geometric pattern. I've previously used QAbstractTableModel for QTableView with success and I would like to have the same ability of letting a model be in charge of the front-end updating. The buttons will mostly provide visual feedback changing text and color based on backend data changes. I included a link (end of post) that has something of use for me but I'm unable to see how I would achieve what you get by, for example, reemplementing Model::data() when role is Qt::DisplayRole. How would QModelIndex apply in something like this?
The first thing that comes to mind with a rough pseudocode:
class MyModel : QAbstractItemModel
{
QMap<int, BtnData> m_btnDataMap;
QVariant data(QModelIndex mIdx, int role) {
if (role == DisplayRole) {
return m_btnDataMap[mIdx.row()]; // returns a BtnData item
}
}
void updateButtonData(int btnKey, BtnData d) {
m_btnDataMap[btnKey] = d;
Q_EMIT dataChanged(btnKey);
}
};
class RandomlyPlacedButtonsWidget : public QWidget
{
QMap<int, QPushButton*> m_btnMap;
MyModel m_myModel;
RandomlyPlacedButtonsWidget() {
connect(m_myModel, dataChanged(int btnKey), this, updateButtonVisual(int btnKey));
}
void updateButtonVisual(int btnKey) {
BtnData data = m_myModel->data(index, DisplayRole);
m_btnMap[index]->text = data.text;
m_btnMap[index]->color = data.color;
}
};
Is this a proper way of using the dataChanged() signal on the widget side? Would something like this make sense to do? It would be ignoring columns, would that conflict with it? Would QAbstractListModel be better because of that?
I don't need specific code, I'm more in need of a way to properly approach this while keeping things clean and easy to maintain.
Using Qt Model/View with non-table like data and non-table/list UI?

QTableView does not detect doubleClick [duplicate]

My application starts with an empty table, and I want to imlement different methods to add items. One should be by double-clicking the table's unused area (or "background") that is not occupied by any cells. When a cell is double-clicked, I want the default behavior.
I've found way to do this by re-implementing QAbstractScrollArea::mouseDoubleClickEvent() method in my TestTable class:
#include <QMouseEvent>
#include <QTableView>
class TestTable : public QTableView
{
Q_OBJECT
signals:
void backgroundDoubleClickEvent(void);
protected:
void mouseDoubleClickEvent (QMouseEvent* e)
{
if (indexAt(e->pos()).isValid())
{
QTableView::mouseDoubleClickEvent(e);
}
else
{
e->accept();
emit backgroundDoubleClickEvent();
}
}
};
This works, but is there a more elegant way of doing this without subclassing QTableView?
I'm not aware of any limitations of my current implementation. Are there obvious caveats?
If you don't want to subclass QTableView, try installEventFilter

Calling function each time qt windows gain focus

I have a mainwindow which in there is a Qtableview by clicking on insert record I go to other modal windows to add record when I add record and close the second windows I come back to main windows but the qtableview doesn't show the new record that is added. The record is in database.
I already make this somehow work with :
void MainWindow::showEvent( QShowEvent* event ) {
QWidget::showEvent( event );
updTbl();
}
But it only works when windows get minimized.
QMainWindow has also two event handler from QWidget
void QWidget::focusInEvent(QFocusEvent *event)
void QWidget::focusOutEvent(QFocusEvent *event)
If you use QtCreator, go to your mainwindow.h and search the line "class MainWindow : public QMainWindow". Right click on QMainWindow -> Refactoring -> Insert virtual function. That's an easy way to find which virtual functions exist and that can be overloaded, you can select focusInEvent and focusOutEvent from there.
Handling activate/deactivate events as follows will give you the desired behaviour
// overloading event(QEvent*) method of QMainWindow
bool MainWindow::event(QEvent* e)
{
switch (e->type())
{
case QEvent::WindowActivate:
// gained focus
//Update Table
break;
case QEvent::WindowDeactivate:
// lost focus
break;
};
return QMainWindow::event(e);
}
Ref: https://gist.github.com/01walid/2276009

What is the best way to propagate event to top most Widget/QMainWindow in QT

I have a layered kind of architecture, in which one Widget is inside another.
Top most object is QMainWindow, and there can be any numbers of internal widgets (one inside another).
example:- mainwindow is parent of widget1, widget1 is parent of widget2..so on..
Now I need to handle the close event of the latest child in the top most window.
I want to know the best way of achieving it in QT 5.7.
You may want to subclass the QMainWindow and override the childEvent function:
http://doc.qt.io/qt-5/qobject.html#childEvent
Would allow you to do something like:
class TarunsMainWindow : QMainWindow {
protected:
void childEvent(QChildEvent event) override {
if (event->type() == QEvent::ChildRemoved) {
// do something special
}
QMainWindow::childEvent(event);
}
}

Why do I not see the drop indicator in a QTableView?

I use drag and drop in my QTableView (works). However, I do not see any drop indicator. I should see a line where the drop is supposed to be inserted, shouldn't I? At least here they say so.
My init is pretty much standard.
// see model for implementing logic of drag
this->viewport()->setAcceptDrops(allowDrop);
this->setDragEnabled(allowDrag);
this->setDropIndicatorShown(true);
this->m_model->allowDrop(allowDrop);
I have no idea why I do not see the indicator. A style sheet is used with the views, could that be the reason. However, I have disabled the stylesheet and still do not see it.
The view uses entire rows for selection, not sure if this causes an issue. So any hint is appreciated.
-- Edit --
As of the comment below, tried all selection modes: single, multi or extended, no visual effect. Also tried cell instead of row selection, again no improvement.
-- Edit 2 --
Currently evaluating another style proxy example, similar to the one below, originally referenced here
-- Related --
QTreeView draw drop indicator
How to highlight the entire row on mouse hover in QTableWidget: Qt5
https://forum.qt.io/topic/12794/mousehover-entire-row-selection-in-qtableview/7
https://stackoverflow.com/a/23111484/356726
I faced the same problem, I tried two options which both worked for me. IIRC, the help came from an answer on SO.
if you are subclassing QTreeView, you can override its paintEvent() method. It is calling by default the drawTree() method and the paintDropIndicator() one (the latter being part of QAbstractItemView private class).
You can call drawTree() from your paintEvent(), and it should override the default drag and drop indicator as well :
class MyTreeView : public QTreeView
{
public:
explicit MyTreeView(QWidget* parent = 0) : QTreeView(parent) {}
void paintEvent(QPaintEvent * event)
{
QPainter painter(viewport());
drawTree(&painter, event->region());
}
};
the other method is to subclass QProxyStyle and overriding the drawPrimitive() method. When you get the element QStyle::PE_IndicatorItemViewItemDrop as a parameter, you can paint it your own way.
The code will look like this:
class MyOwnStyle : public QProxyStyle
{
public:
MyOwnStyle(QStyle* style = 0) : QProxyStyle(style) {}
void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
{
if (element == QStyle::PE_IndicatorItemViewItemDrop)
{
//custom paint here, you can do nothing as well
QColor c(Qt::white);
QPen pen(c);
pen.setWidth(1);
painter->setPen(pen);
if (!option->rect.isNull())
painter->drawLine(option->rect.topLeft(), option->rect.topRight());
}
else
{
// the default style is applied
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
};