Catch a signal from a parent class and send your own same named signal?
-
Hey there everybody, I'm new to this community!
I did a lot of research, but haven't found any help or similar problems.First of all I'm gonna try to give you a short explanation:
I wrote my own class, called ComboTable, which inherits QComboBox. Its' aim is to be a QComboBox with a QTableView, instead of a QListView. Because I wanna simply replace QComboBox widgets in a pretty big software (without rewriting parts of it) there are some functions I had to overwrite, which worked out pretty good. The main problem seems to be the calculation of the items' index. QComboBox just returns QModelIndex::row(), which does not work for me, because I got columns, too.
And that's where my bigger problems start:
There are some signals, using the index (e.g. activated ( int ) )
QComboBox returns the activated items' row.My (dismissed) ideas:
I don't want to implement an own (other named) signal, because (as I wrote a few lines ago) I wanna replace QComboBox Widgets without changing the softwares' code. Another problem is, that I cannot set a signal private, so you could use a not working signal, which is no option to me.
Overwriting activated ( int ), catching activated ( QString ) and giving out activated ( int ) with the recalculated index, does not work, because there could be more than one item using the same QString.
My last idea was to write my own signal activated ( int ), catch the parent class' activated(int) and connect it to a function which emits my own activated ( int ) with the right index.
@QComboBox pBox = dynamic_cast <QComboBox> (this);@
DID NOT WORK! :(
Here is my simplified code:
ComboTable::ComboTable(QWidget *parent) :
QComboBox(parent)
{
view = new QTableView();
updateTable();
this->setView(view);
view->horizontalHeader()->hide();
view->verticalHeader()->hide();
view->setSelectionMode(QAbstractItemView::SingleSelection);actLock=false; QComboBox *pBox = dynamic_cast <QComboBox*> (this); connect( pBox, SIGNAL(activated(int)), this, SLOT(emitActivated()));
}
void ComboTable::emitActivated()
{if(actLock) return; actLock=true; qDebug() << "TEXT" << currentText() << endl; emit activated(currentText()); emit activated(currentIndex()); actLock=false;
}
class ComboTable : public QComboBox
{
Q_OBJECT
public:explicit ComboTable(QWidget *parent = 0);
signals:
void itemsEdited(); void activated ( int index );
void activated ( QString text )
protected:
void wheelEvent(QWheelEvent *e);
// void _q_emitCurrentIndexChanged(const QModelIndex &index);
// void _q_emitHighlighted(const QModelIndex &index);protected slots:
void emitActivated();
private:
static QList <Item> items; QTableView *view; int columns; bool returnZero; QModelIndex currentIndex_; TableModel *model; QLineEdit *line; void insertSeparator(int index){} bool actLock;
}; @
Is there any chance to handle the parent class' signal and my own (same named) signal on each own?
Thank you for thinking about my problem and your ideas in front! :)
Steff
-
don't use dynamic_cast. Try qobject_cast.
-
Does not work as well! :(
-
hi, can you post more complete test program, many parts missing in your code above. Then someone can try further for solution
-
The code is pretty long, that's why I didn't post it.
I can only post 6000 characters, the whole code got about 12000.It's not about a bug I wanna fix, it's about a basic problem I don't know how to handle.
@
#ifndef COMBOTABLE_H
#define COMBOTABLE_H#include <QWidget>
#include <QComboBox>
#include <QAbstractTableModel>
#include "combotable.h"
#include <QDebug>
#include <QTableView>
#include <QLabel>struct Item {QIcon icon; QString text;QVariant userData;};
class TableModel;
class QAbstractItemView;
class QLineEdit;
class QComboBoxPrivate;
class QCompleter;//---------------Combo Table--------------------------------
class ComboTable : public QComboBox
{
Q_OBJECT
public:explicit ComboTable(QWidget *parent = 0); void addItem(const QString &text, const QVariant &userData = QVariant()); void addItem(const QIcon & icon, const QString & text, const QVariant & userData = QVariant()); void addItems(const QList<QString> &list); int count(); int currentIndex() const; int findData(const QVariant &data, int role = Qt::UserRole, Qt::MatchFlags flags = Qt::MatchExactly|Qt::MatchCaseSensitive) const; int findText(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly|Qt::MatchCaseSensitive) const; void insertItem(int index, const QString &text, const QVariant &userData = QVariant()); void insertItem(int index,const QIcon & icon, const QString &text, const QVariant &userData = QVariant()); void insertItems(int index, const QList <QString> &list); QVariant itemData(int index, int role = Qt::UserRole ) const; QIcon itemIcon (int index) const; QString itemText(int index) const; void removeItem(int index); void setCurrentIndex(int index); void setItemData( int index, const QVariant & value, int role = Qt::UserRole ); void setItemIcon(int index, const QIcon &icon); void setItemText(int index, const QString &text); static QList <Item>& getItems(){return items;} void setColumnWidth ( int column, int width ); void setRowHeight ( int row, int height ); QList <QString> toString(); Item& getItem(int index);
signals:
void itemsEdited(); //void activated ( int index );
public slots:
void clear(); void shout(int index); void shout(const QString& text);
protected:
void wheelEvent(QWheelEvent *e); void _q_emitCurrentIndexChanged(const QModelIndex &index); void _q_emitHighlighted(const QModelIndex &index);
protected slots:
void giveOut(); void updateTable(); void emitActivated();
private:
static QList <Item> items; QTableView *view; int columns; bool returnZero; QModelIndex currentIndex_; TableModel *model; QLineEdit *line; void insertSeparator(int index){} bool actLock;
};
//-------------------------Table Model ----------------------------------------
class TableModel : public QAbstractTableModel
{
Q_OBJECTpublic:
TableModel(int rows, int cols, QObject* parent = 0): QAbstractTableModel(parent), rows(rows), cols(cols){}
int rowCount(const QModelIndex& parent = QModelIndex()) const { return rows;}
int columnCount(const QModelIndex& parent = QModelIndex()) const { return cols; }
void setRows(int i){rows=i;}
void setCols(int i) {cols=i;}QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
private:
int rows;
int cols;};
#endif // COMBOTABLE_H
@
-
@
//combotable.cpp PART 1
#include "combotable.h"
#include <QTableView>
#include <QComboBox>
#include <QTableView>
#include <QHeaderView>
#include <QVariant>
#include <QDebug>
#include <math.h>
#include <QPushButton>
#include <QGridLayout>
#include <QLineEdit>
#include <QWheelEvent>//----------------------------------TableModel----------------------------------------------------
QVariant TableModel::data(const QModelIndex& index, int role) const
{if (index.isValid() && role == Qt::DecorationRole && index.row()*cols+index.column()< ComboTable::getItems().length()) { return QIcon(ComboTable::getItems().at(index.row()*cols+index.column()).icon); qDebug() << "Icon"; } if (index.isValid() && role == Qt::DisplayRole && index.row()*cols+index.column()< ComboTable::getItems().length()) { return QString(ComboTable::getItems().at(index.row()*cols+index.column()).text); qDebug() << "Text"; } if (index.isValid() && role == Qt::UserRole && index.row()*cols+index.column()< ComboTable::getItems().length()) { return QVariant(ComboTable::getItems().at(index.row()*cols+index.column()).userData); qDebug() << "UserData"; } return QVariant();
}
//-----------------------------ComboTable--------------------------------------
QList <Item> ComboTable::items;
ComboTable::ComboTable(QWidget *parent) :
QComboBox(parent)
{
view = new QTableView();
updateTable();
this->setView(view);
view->horizontalHeader()->hide();
view->verticalHeader()->hide();
view->setSelectionMode(QAbstractItemView::SingleSelection);QPushButton *btn = new QPushButton(); btn->show(); actLock=false; QComboBox *pBox = dynamic_cast <QComboBox*> (this); connect( btn, SIGNAL(clicked()), this, SLOT(giveOut()) ); connect( pBox, SIGNAL(activated(int)), this, SLOT(emitActivated())); connect(this,SIGNAL(activated(int)),this,SLOT(shout(int))); connect(this,SIGNAL(itemsEdited()),this,SLOT(updateTable()));
}
void ComboTable::insertItem(int index, const QString &text, const QVariant &userData)
{
Item item;
item.text=text;if(userData.isValid()) item.userData=userData; items.insert(index,item); emit itemsEdited();
}
void ComboTable::insertItem(int index,const QIcon & icon, const QString &text, const QVariant &userData)
{
Item item;
item.text=text;
item.icon=icon;if(userData.isValid()) item.userData=userData; items.insert(index,item); emit itemsEdited();
}
void ComboTable::insertItems(int index, const QList <QString> &list)
{
for(int i=0; i< list.length(); i++)
{
Item item;
item.text=list.at(i);
items.insert(index+i,item);
}emit itemsEdited();
}
Item& ComboTable::getItem(int index)
{
if(index>items.length()-1)
{
Item emptyItem;
return emptyItem;
}
else
return items[index];
}void ComboTable::addItem(const QString &text, const QVariant &userData)
{
Item item;
item.text=text;if(userData.isValid()) item.userData=userData; items.append(item); emit itemsEdited();
}
void ComboTable::addItem(const QIcon & icon, const QString & text, const QVariant & userData)
{
Item item;
item.text=text;item.icon=icon; if(userData.isValid()) item.userData=userData; items.append(item); emit itemsEdited();
}
void ComboTable::addItems(const QList <QString> &list)
{
for(int i=0; i< list.length(); i++)
{
Item item;
item.text=list.at(i);
items.append(item);
}
emit itemsEdited();
}void ComboTable::removeItem(int index)
{
items.removeAt(index);
emit itemsEdited();
}void ComboTable::updateTable()
{
int tableWidth=0;int size(ceil(sqrt(items.length()))); qDebug() << "Length and SIZE SQRT" << items.length() << "and" << size << endl; model = new TableModel(size,size, this); columns = size; this->setModel(model); for(int i=0; i<size;i++) { tableWidth += view->columnWidth(i); qDebug() << "ColumWidth" << view->columnWidth(i); } qDebug() << "TABLEWIDTH" << tableWidth << endl; view->setMinimumWidth(tableWidth);
}
void ComboTable::setColumnWidth ( int column, int width )
{
view->setColumnWidth(column,width);
emit itemsEdited();
}void ComboTable::setRowHeight ( int row, int height )
{
view->setRowHeight (row,height);
emit itemsEdited();
}int ComboTable::count()
{
return items.length();
}QList <QString> ComboTable::toString()
{
QList <QString> list;for (int i=0; i<items.length(); i++) { list.append(items.at(i).text); } return list;
}
void ComboTable::giveOut() //------------Tests currentText and currentIndex functions--------------------
{
qDebug() << "GIVEOUT" << this->currentText() << "INDEX"<< this->currentIndex();
}int ComboTable::currentIndex() const
{
if(view->selectionModel()->currentIndex().column()== -1)
return 0;int ret = view->selectionModel()->currentIndex().row()*columns+view->selectionModel()->currentIndex().column(); return ret;
}
@ -
@
// combotable.cpp PART 2
QVariant ComboTable::itemData(int index, int role) const
{
if(items.at(index).userData.isValid() && role ==Qt::DisplayRole)
return items.at(currentIndex()).text;if(items.at(index).userData.isValid() && role ==Qt::DecorationRole) return items.at(currentIndex()).icon; if(items.at(index).userData.isValid() && role ==Qt::UserRole) return items.at(currentIndex()).userData; return QVariant::Invalid;
}
void ComboTable::setItemData(int index, const QVariant & value, int role)
{
if (value.isValid() && role == Qt::DisplayRole )
items[index].text= value.toString();if (value.isValid() && role == Qt::DecorationRole) items[index].icon= value.value<QIcon>(); if (value.isValid() && role == Qt::UserRole ) items[index].userData= value; return;
}
void ComboTable::clear()
{
items.clear();
emit itemsEdited();
}void ComboTable::setCurrentIndex(int index)
{
int rowTmp=floor(index/columns);
int colTmp=index%columns;QModelIndex mi = model->index(rowTmp, colTmp); view->selectionModel()->setCurrentIndex(mi,QItemSelectionModel::SelectCurrent); this->setModelColumn(colTmp); QComboBox::setCurrentIndex(rowTmp); emit currentIndexChanged (index);
}
QIcon ComboTable::itemIcon (int index) const
{
return items.at(index).icon;
}void ComboTable::setItemIcon(int index, const QIcon &icon)
{
items[index].icon=icon;
}QString ComboTable::itemText(int index) const
{
return items.at(index).text;
}void ComboTable::setItemText(int index, const QString &text)
{
items[index].text=text;
}int ComboTable::findData(const QVariant &data, int role, Qt::MatchFlags flags) const
{
QModelIndexList result;for(int i=0; i<columns; i++) { QModelIndex start = model->index(0,i); result.append(model->match(start, role, data, 1, flags)); } if (result.isEmpty()) return -1; return result.first().row()*columns+result.first().column();
}
int ComboTable::findText(const QString &text,Qt::MatchFlags flags) const
{
QVariant data = text;
return findData(data,Qt::DisplayRole,flags);
}void ComboTable::wheelEvent(QWheelEvent *e)
{int newIndex = currentIndex(); if (e->delta() > 0) newIndex--; else newIndex++; if((newIndex<items.length()) && newIndex >= 0) { setCurrentIndex(newIndex); emit activated(currentIndex());
// emit activated(currentText());
}e->accept();
}
void ComboTable::emitActivated()
{
qDebug() << "TEXT before lock" << currentText() << " l: " << actLock << endl;if(actLock) return; actLock=true; qDebug() << "TEXT" << currentText() << endl;
// emit activated(currentText());
emit activated(currentIndex());actLock=false;
}
void ComboTable::_q_emitCurrentIndexChanged(const QModelIndex &index)
{
if (!index.isValid())
return;QString text(index.data().toString()); emit currentIndexChanged(index.row()*columns+index.column()); emit currentIndexChanged(text);
}
void ComboTable::_q_emitHighlighted(const QModelIndex &index)
{
if (!index.isValid())
return;QString text(index.data().toString()); emit highlighted(index.row()*columns+index.column()); emit highlighted(text);
}
void ComboTable::shout(int index)
{
qDebug() << "SHOUT" << items.at(index).text << endl ;
}void ComboTable::shout(const QString& text)
{
qDebug() << "SHOUT TEXT" << text<< endl ;
}@
-
I haven't been looking into this code specifically, but it is possibly not a good idea to declare and emit signals that have the same name and signature as a signal that is available in one of the parent classes. Signals are protected functions and you will obscure these if you follow this path. Also, the connect() function always takes QObject as arguments, so there is no telling what will happen. (It will probably connect to the QComboBox signals.)
If you insist on keeping these signal names, you could consider wrapping the QComboBox up into a widget of your own and propagating most of the useful interface. It will be more predictable what is going to happen.
So consider:
@ class MyWidget : public QWidget
{
Q_OBJECT
public:...
signals:
void activated(int);
void activated(QString);private:
QComboBox *combo;
};@instead of
@ class MyCombo : public QComboBox
{
Q_OBJECT
public:
...
signals:
void activated(int);
void activated(QString);
};@You'll loose the polymorphism to a QComboBox. If you don't want that, get some different names for your signals.
-
If you want to use the QTableView with QComboBox, isn't that possible with the QCompleter? I have used that with QLineEdit a lot, and it works fine with tables. Take a look to QComboBox::setCompleter(..).
-
bq. You can use QCompleter to provide auto completions in any Qt widget, such as QLineEdit and QComboBox. When the user starts typing a word, QCompleter suggests possible ways of completing the word, based on a word list.
Sounds like something different! :-/
I'm gonna try Franzk solution. I think I can keep a lot of Code.
The only thing i don't know how to deal with are Events.
How can I ignore QComboBox's (e.g.) wheel event?EDIT : SORRY, found it myself. Using an EventHandler is the right solution! :)