I have the following problem with the QTableWidget or QTableWidgetItem:
I would like to analyze the text in the cell during its editing/typing,
for example as a reaction on KeyReleaseEvent.
However the QTableWidgetItem::text() property is changed only AFTER the
cell editing is finished (focus has left the cell).
How can I overcome such behavior? Of course, it is possible to analyze the
button keys in the KeyReleaseEvent, but with the text() property it would be much easier...
One possible solution is to establish a custom QLineEdit as editor through the delegate:
#include <QtWidgets>
class LineEdit: public QLineEdit{
public:
using QLineEdit::QLineEdit;
protected:
void keyReleaseEvent(QKeyEvent *event) {
QLineEdit::keyPressEvent(event);
qDebug() << text();
}
};
class StyledItemDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const{
LineEdit *editor = new LineEdit(parent);
return editor;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTableWidget w(10, 10);
w.setItemDelegate(new StyledItemDelegate(&w));
w.show();
return a.exec();
}
Related
I have a QListWidget that contains filenames. Right-clicking on an item brings up a menu which contains a Rename... item. Clicking on Rename... lets the user edit the filename. So far so good.
But in the Qt Creator Projects window, right-clicking on (say) foo.cpp and selecting Rename... presents the user with a field in which foo is selected, and .cpp isn't. So the default action is to rename the foo part without changing the .cpp extension.
This is exactly what I need, but I don't see a way to achieve it using Qt's public API. What do I have to do? Custom delegates, I suppose; but this is surely a common requirement, so I wondered if anybody had some ready-to-use C++ code that I could snarf?
A possible solution is to create a QObject that intercepts the editor's show event and then change the selection:
#include <QtWidgets>
class Helper: public QObject{
public:
Helper(QLineEdit *le): QObject(le), m_le(le){
m_le->installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event){
if(watched == m_le && event->type() == QEvent::Show){
QString filename = m_le->text();
QFileInfo fi(filename);
QString base = fi.baseName();
m_le->setSelection(0, base.length());
}
return QObject::eventFilter(watched, event);
}
private:
QLineEdit* m_le;
};
class StyledItemDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex &index) const{
QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index);
if(QLineEdit *le = qobject_cast<QLineEdit *>(editor)){
new Helper(le);
}
return editor;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeWidget w;
w.setItemDelegate(new StyledItemDelegate);
for(const QString & filename: {"foo.txt", "foo.tar.gz", "foo.cpp"}){
auto item = new QTreeWidgetItem({filename});
item->setFlags(item->flags() | Qt::ItemIsEditable);
w.addTopLevelItem(item);
}
w.show();
return a.exec();
}
I have a ComboBox and set it to be edited.
QComboBox *myCombo = new QComboBox(this);
myCombo->setEditable(true);
myCombo->setStyleSheet("QComboBox::down-arrow{image: url(:/bulb.png);}");
myCombo->setCursor( QCursor( Qt::PointingHandCursor ) );
So now when i click onto the editing field, nothing happen. But what I need is, when I click onto the bulb (which is the down-arrow), something (like a table or a dialog....) should be appeared. How can I recognize this click event in this case? I looked at the list of signals for combo box but could not find any signal for that.
By overwriting the mousePressEvent() method you must use hitTestComplexControl() method to know that QStyle::SubControl has been pressed by issuing a signal if it is QStyle::SC_ComboBoxArrow.
#include <QtWidgets>
class ComboBox: public QComboBox
{
Q_OBJECT
public:
using QComboBox::QComboBox;
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent *event) override{
QComboBox::mousePressEvent(event);
QStyleOptionComboBox opt;
initStyleOption(&opt);
QStyle::SubControl sc = style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, event->pos(), this);
if(sc == QStyle::SC_ComboBoxArrow)
emit clicked();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ComboBox w;
w.setEditable(true);
w.setStyleSheet("QComboBox::down-arrow{image: url(:/bulb.png);}");
QObject::connect(&w, &ComboBox::clicked, [](){
qDebug()<<"clicked";
});
w.show();
return a.exec();
}
#include "main.moc"
Although showPopup() is a possible option this can be called directly without the down-arrow being pressed, for example by calling it directly: myCombo->showPopup() so it is not the correct option.
A possible solution is to subclass QComboBox and reimplement showPopup() virtual method:
.h:
#ifndef COMBOBOXDROPDOWN_H
#define COMBOBOXDROPDOWN_H
#include <QComboBox>
#include <QDebug>
class ComboBoxDropDown : public QComboBox
{
public:
ComboBoxDropDown(QWidget *parent = nullptr);
void showPopup() override;
};
#endif // COMBOBOXDROPDOWN_H
.cpp:
#include "comboboxdropdown.h"
ComboBoxDropDown::ComboBoxDropDown(QWidget *parent)
: QComboBox (parent)
{
}
void ComboBoxDropDown::showPopup()
{
//QComboBox::showPopup();
qDebug() << "Do something";
}
I am creating object from QGraphicsRectItem and adding to the Qgraphicscene(scene).
I want to get every movement(pos) of object(qgraphicsrectitem) so that I subclassed Qgraphicsrectitem. But in this class I am getting some errors.
How can I get object position changed in scene ?
error:
'staticMetaObject' is not a member of 'QGraphicsRectItem'
{ &QGraphicsRectItem::staticMetaObject, qt_meta_stringdata_ItemHandler.data
ItemHandler.h
#include <QObject>
#include <QGraphicsRectItem>
class ItemHandler : public QGraphicsRectItem, public QObject
{
Q_OBJECT
public:
ItemHandler(QGraphicsItem *parent = 0 );
~ItemHandler();
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
signals:
void objectHandlePosChanged(QPointF value);
};
ItemHandler.cpp
#include "itemhandler.h"
ItemHandler::ItemHandler(QGraphicsItem *parent) : QGraphicsRectItem(parent)
{
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
}
ItemHandler::~ItemHandler()
{
}
QVariant ItemHandler::itemChange(QGraphicsItem::GraphicsItemChange change,
const QVariant &value)
{
QPointF newPos = value.toPointF();
emit objectHandlePosChanged(newPos);
}
In you header file, inherit first from QObject, as follows:
#include <QObject>
#include <QGraphicsRectItem>
class ItemHandler : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
ItemHandler(QGraphicsItem *parent = 0 );
~ItemHandler();
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value);
signals:
void objectHandlePosChanged(QPointF value);
};
And this is my test main function (sorry it's just a sandbox):
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow* mywindow = new MainWindow();
QGraphicsScene scene;
ItemHandler *item = new ItemHandler;
item->setRect(10.0, 10.0, 10.0, 10.0);
scene.addItem(item);
QApplication::connect(item, SIGNAL(objectHandlePosChanged(QPointF)), mywindow, SLOT(moved(QPointF)));
QGraphicsView view(&scene);
view.setFixedSize(250, 250);
view.setWindowTitle("QGraphicsItem Test");
item->setPos(-100, -100);
item->setPos(-200, -200);
view.show();
return app.exec();
}
I just used an empty MainWindow that is never displayed to have a slot to connect to the signal, there's no point in having a MainWindow: it was already there and I modified it instead of creating a new class. The example works, fires the signal and displays the rectangle.
I made class inherited from QLabel. This class also have public slot, that should change label caption. I "call" this SLOT with clicked() SIGNAL of button.
So nothing happened when I press the button.
#include <QApplication>
#include <QLabel>
#include <QPushButton>
class Label : public QLabel
{
public:
Label(QString a) : QLabel(a){}
public slots:
void change()
{
this->setNum(2);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton* button = new QPushButton("Button");
Label* lbl = new Label("Label");
button->show();
lbl->show();
QObject::connect(button, SIGNAL(clicked(bool)), lbl, SLOT(change()));
return a.exec();
}
What should I do to change caption from slot?
In order for the signals and slots to be recognized, the classes must use the Q_OBJECT macro in the private part.
Another thing to do is to include "main.moc", for more information on this point read this.
#include <QApplication>
#include <QLabel>
#include <QPushButton>
class Label : public QLabel
{
Q_OBJECT
public:
Label(const QString &text, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
QLabel(text, parent, f){}
public slots:
void change()
{
setNum(2);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton* button = new QPushButton("Button");
Label* lbl = new Label("Label");
button->show();
lbl->show();
QObject::connect(button, SIGNAL(clicked()), lbl, SLOT(change()));
return a.exec();
}
#include "main.moc"
At the end of making these changes you must execute the following:
Press clean all in the Build menu.
then run qmake in the same menu.
And you just compose your project.
Add Q_OBJECT after
class Label : public QLabel
{
and then you should
either place your Label class declaration to a .h file or write #include "main.moc" after main function declaration.
try to get the return value from your connect call an check it for true or false.
Add Q_OBJECT Macro to the beginning of your derived class.
Add some debug output to your slot like
qDebug()<<"This is my slot.";
Maybe this would help to get a little further.
Best regards
I've created a UI using Qt5-Designer which I load at runtime by calling
QUiLoader().load(qfile_object, this);
Works like charm but now I've promoted some QLabel elements to a widget class MyQLabel with is derived from QLabel.
When I now try to load the UI I get a warning for each promoted widget:
"QFormBuilder was unable to create a custom widget of the class 'MyQLabel'; defaulting to base class 'QLabel'."
The class looks like this:
class MyQLabel : public QLabel {
Q_OBJECT
public:
MyQLabel(QWidget *parent = nullptr) : QLabel(parent) {}
};
It's been auto-moc'ed and linked against my executable.
I have the feeling that somehow I have to tell QUiLoader about my class before trying to use it but I don't know how..
Do I have to create a plugin for this? Is there a way to reproduce, what to QUiLoader does to examine it?
You need to create your own derived version of QUiLoader, and provide an implementation of the factory method QUiLoader::createWidget that can create your widgets.
You could factor this out into a plugin that gets loaded by the QUiLoader. It would have to implement a QDesignerCustomWidgetInterface instance. See the Custom Widget Plugin Example for a complete example of a plugin.
// https://github.com/KubaO/stackoverflown/tree/master/questions/uiloader-custom-37775472
#include <QtUiTools>
#include <QtWidgets>
const char uiData[] =
"<ui version=\"4.0\"><class>Widget</class><widget class=\"MyWidget\" name=\"Widget\">\n"
"<property name=\"windowTitle\" ><string>Widget</string></property>\n"
"</widget><pixmapfunction></pixmapfunction><resources/><connections/>\n"
"</ui>";
class MyWidget : public QLabel
{
Q_OBJECT
bool m_closed = false;
public:
MyWidget(QWidget* parent = 0) : QLabel("This is MyWidget", parent) {}
bool isClosed() const { return m_closed; }
void closeEvent(QCloseEvent *) Q_DECL_OVERRIDE { m_closed = true; }
};
class MyUiLoader : public QUiLoader {
public:
MyUiLoader(QObject * parent = 0) : QUiLoader(parent) {}
QWidget * createWidget(const QString & className, QWidget * parent = 0,
const QString & name = QString()) {
if (className == "MyWidget") {
MyWidget * w = new MyWidget(parent);
w->setObjectName(name);
return w;
}
return QUiLoader::createWidget(className, parent, name);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QBuffer buf;
buf.setData(uiData, sizeof(uiData));
MyUiLoader uiLoader;
auto uiMain = qobject_cast<MyWidget*>(uiLoader.load(&buf));
uiMain->show();
return app.exec();
}
#include "main.moc"