I have one abstract class, TableWidget, which inherits from QWidget. It implements paintEvent.
class TableWidget : public QWidget
{
Q_OBJECT
private:
int width;
int height;
public:
explicit TableWidget(QWidget *parent = 0);
virtual int getWidthInCells() = 0;
virtual int getHeightInCells() = 0;
virtual QString getCellText(byte x, byte y) = 0;
virtual QString getColumnLabel(byte a) = 0;
virtual QString getRowLabel(byte a) = 0;
byte getCellHighlight(byte x, byte y);
void setCellHighlight(byte x, byte y, bool highlighted);
void setHeight(int h);
void setWidth(int w);
protected:
void paintEvent(QPaintEvent *e);
signals:
public slots:
};
I tried testing TableWidget out with a dummy class with next to no functionality.
class DummyTable : public TableWidget {
public:
explicit DummyTable(QWidget *parent = 0) {}
int getWidthInCells() {return 2;}
int getHeightInCells() {return 2;}
QString getCellText(byte x, byte y) {return "0";}
QString getColumnLabel(byte a) {return QString(a);}
QString getRowLabel(byte a) {return QString(a);}
};
The problem is that paintEvent (implemented in TableWidget.cpp), is never called. The first mistake I noticed was the absence of the Q_OBJECT macro in DummyTable. I added it in and tested it, but this resulted in a failure to terminate correctly. I had to completely exit out of QT to kill the process. I looked into QPushButton and it seemed to do things the same as me, except for the inclusion of Q_OBJECT.
So, how can I get paintEvent to be called properly?
How have you added your DummyTable instance to the parent window?
You could try calling on the parent window the debug method dumpObjectTree ( http://qt-project.org/doc/qt-4.8/qobject.html#dumpObjectTree ) to check that your instance is correctly parented.
Also you could try implementing showEvent(...) and instrumenting that to check the widget is actually being displayed.
Have you looked at QTableWidget? Does it not do what you need? http://qt-project.org/doc/qt-4.8/qtablewidget.html
Regarding the QObject macro you only need that in classes where you're creating signal and slot connections, or using some of the other meta-programming features of Qt (such as properties). Its not likely to be the root cause of your issues.
You may need to give your widget a proper width and height. If it has no size, paintEvent will not be called.
It turned out to be a pretty stupid mistake. I wasn't calling the parent class's constructor.
Related
I have the following code:
void AppMPhase::closeEvent(QCloseEvent *closeEvent) {
QMessageBox* dialog = new QMessageBox(this);
dialog->setText("Warning: Initial configuration changed\nDo you want to restore it ?");
dialog->setIcon(QMessageBox::Warning);
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
connect(dialog, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(restoreInitialConfig(QAbstractButton*)));
dialog->exec();
}
void AppMPhase::restoreInitialConfig(QAbstractButton *button) {
if(!button->text().compare(QString("Yes"))) {
// restore Config
}
else {
// don't restore
}
MainWindow::closeEvent(closeEvent);
}
with AppMPhase the derived class of MainWindow
I would like to call the method closeEvent of the base class MainWindow in the "restoreInitialConfig" method, to close the window after we restore the config.
Is it possible ? it's different from the other questions I have seen because the method closeEvent is overriden in the AppMPhase class.
the AppMPhase class:
class AppMPhase : public QtUi::MainWindow
{
Q_OBJECT
public:
AppMPhase(QWidget *const parent = Q_NULLPTR, const Qt::WindowFlags flags = 0);
~AppMPhase();
int readConfigFromExternFile(QString path);
int readCalibrationDate(QString path);
QSize sizeHint() const Q_DECL_OVERRIDE;
QtWrapperTestManager * testManager;
public Q_SLOTS:
void show();
public slots:
void currentTestFinished();
void createTest(unsigned int,QString);
void restoreInitialConfig(QAbstractButton* button);
signals:
void notifyPageTestCurrentTestFinished();
private:
enum AppPage
{
PAGE_START,
PAGE_ABOUT
};
bool isTestMangaerCreated;
std::map<QString, std::map<std::string, std::string> > conversion_Table_Cable_Ref_sensorParamMap;
Pages::GenericAppPage * appPage(const int index) const;
QToolButton * appPageButton(const int index) const;
virtual void closeEvent(QCloseEvent *closeEvent) Q_DECL_OVERRIDE;
The MainWindow class:
class SHARED_EXPORT_LIB_QT_UI MainWindow : public QMainWindow
{
Q_OBJECT
signals:
void aboutToClose();
public slots:
virtual void show();
private slots:
void changeLanguage(const QString &language);
public:
MainWindow(QWidget *const parent = Q_NULLPTR, const Qt::WindowFlags flags = 0);
virtual ~MainWindow();
protected:
void showCopyright(const QString ©RightHeader);
void showLanguageSelector(const QStringList &languages);
void setupUi(const MainPageList &pageList);
void setupUi(QWidget *centerWidget = Q_NULLPTR);
virtual void closeEvent(QCloseEvent *closeEvent) Q_DECL_OVERRIDE;
const Ui::MainWindow *const _ui;
MainPageItemList _mainPageItemList;
};
Thanks in advance.
Flo
There is a far easier way to achieve what you're wanting to do, without having to use signals and slots, and call base class functions from your slot.
It is possible to do the restoration directly from within your closeEvent handler.
This is made possible by the fact that QMessageBox::exec returns an integer code which matches one of the values in the StandardButton enum, depending on what button was pressed.
That then allows you to call restoreInitialConfig directly from within your closeEvent handler, as you know which button was pressed.
void AppMPhase::closeEvent(QCloseEvent* ev)
{
int res = QMessageBox(
QMessageBox::Icon::Warning,
"Restore configuration?",
"Warning: Initial configuration changed\nDo you want to restore it?",
QMessageBox::Yes | QMessageBox::No,
this).exec();
if (res == QMessageBox::Yes)
restoreInitialConfig();
MainWindow::closeEvent(ev);
}
Note that this also simplifies your restoreInitialConfig function, as there is no need to check button text, you know the answer was yes.
Note also I made use of this QMessageBox constructor which makes it very easy to create simple message boxes.
I'm using QGraphicsScene and I would like to have some items which emit signals when they are moved around.
Unfortunately, QGraphicsPixmapItem doesn't have any signals, so I subclassed it:
class InteractiveGraphicsPixmapItem : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
public:
InteractiveGraphicsPixmapItem();
InteractiveGraphicsPixmapItem(QPixmap pm) : QGraphicsPixmapItem(pm)
{
setFlag(QGraphicsItem::ItemIsMovable, true);
setAcceptedMouseButtons(Qt::LeftButton|Qt::RightButton);
}
private:
void mouseMoveEvent(QGraphicsSceneMouseEvent *);
signals:
void moved_by_mouse(QPointF newpos);
};
InteractiveGraphicsPixmapItem::InteractiveGraphicsPixmapItem()
{
}
void InteractiveGraphicsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *)
{
emit moved_by_mouse(this->pos());
}
However, it is not movable.
If I change it back into a QGraphicsPixmapItem in the main program, and call item->setFlags(QGraphicsItem::ItemIsMovable); it becomes movable. For my custom class, it doesn't.
item = new InteractiveGraphicsPixmapItem(QPixmap(":/img/icon.png"));
scene->addItem(item);
item->setFlags(QGraphicsItem::ItemIsMovable);
A similar question was asked about selectability, and it was suggested that the setAcceptedMouseButtons(Qt::LeftButton|Qt::RightButton); must be added to the constructor. It didn't help in my case.
If you override the mouseMoveEvent, and don't call the base class's function, it won't move and you'll have to handle it yourself.
However, simply calling the base class function is all you need here
void InteractiveGraphicsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent* evt)
{
QGraphicsPixmapItem::mouseMoveEvent(evt);
emit moved_by_mouse(this->pos());
}
Also note that if you override one of the mouse events (press / release / move) you should also handle the others too.
I have the following class that extends QListWidget, but I can't seem to connect the doubleClicked signal to the slot I desire. Here's the code implemented - in VS2012. The idea is to be able to double click a item and edit it. I connect the signal to the slot in the constructor, but the slot is never called when I run it through the debugger.
# .h file
class DisplayFeed :
public QListWidget
{
Q_OBJECT
public:
DisplayFeed(QWidget *parent, Logic *logic, int xpos, int ypos, int width, int height, std::string color);
~DisplayFeed(void);
void setColor(std::string color);
void refresh(std::vector<Event*> *thingsToInclude);
private:
Logic* logic;
private slots:
void editItem(QEventStore *item);
};
Below is the .cpp file. QEventStore extends QListWidgetItem. I placed the MessageBox to test the system as well, in case it was my other code that didn't work.
# .cpp file, only relevant methods included
DisplayFeed::DisplayFeed(QWidget *parent, Logic *logic, int xpos, int ypos, int width, int height, std::string color)
: QListWidget(parent)
{
this->logic = logic;
setGeometry(xpos, ypos, width, height);
setColor(color);
QObject::connect(this, SIGNAL(itemClicked(QEventStore*)), this, SLOT(editItem(QEventStore*)));
show();
}
void DisplayFeed::editItem(QEventStore *item){
QMessageBox::information(this,"Hello!","You clicked \""+item->text()+"\"");
QEventEditor *editor = new QEventEditor(item->getEvent());
}
You forgot the Q_OBJECT macro in your DisplayFeed class. It should be like :
# .h file
class DisplayFeed :
public QListWidget
{
Q_OBJECT
public:
DisplayFeed(QWidget *parent, Logic *logic, int xpos, int ypos, int width, int height, std::string color);
~DisplayFeed(void);
void setColor(std::string color);
void refresh(std::vector<Event*> *thingsToInclude);
private:
Logic* logic;
private slots:
void editItem(QEventStore *item);
};
That's the first thing I noticed and may solve your problem. If not I'll look deeper.
EDIT: Read the first answer here
There are several changes to do:
Add Q_OBJECT in the .h of displayFeed class
class DisplayFeed : public QListWidget
{
Q_OBJECT
...
};
Change your slot with a public slot and a QListWidgetItem* parameter
public slots:
void editItem(QListWidgetItem *item);
Connect with the good SIGNAL which have the same parameter that your SLOT
connect(this,SIGNAL(itemDoubleClicked(QListWidgetItem*)), this,SLOT(editItem(QListWidgetItem*)));
This works fine for me, hope it helps you.
I have found the answer. The problem is that the default signal for itemDoubleClicked emits the QListWidgetItem* and emitting a subclass of that doesn't work. So what I had to do was to go to editItem and get it to dynamic_cast the QListWidgetItem* to a QEventStore*
Following case:
Let's say there is a binary library which defines the class "Base", and many subclasses ("Derivative1", "Derivative2" etc) of it.
I want to extend these subclasses in my own code, but because my extensions are the same for all subclasses as they only deal with parts of Base, it would be tedious to subclass every Derivative class and add the same code over and over again.
My first idea was to simply write a class template which would do the work for me, but because the library I'm dealing with is Qt, QObject has foiled me.
My second idea was to use macros to generate each class structure, but this was also thwarted by moc.
The "reparent" in the title is because I thought of deriving from Base and creating BaseExtended, and then somehow tell the compiler to reparent every Derivative to this extended class. Isn't there a way to for example declare the "Base" in "BaseExtended" virtual, and then just write
class Derivative1Extended : public Derivative1, public BaseExtended {}
and have the virtual Base in BaseExtended point to the Base in Derivative1, thus basically "squeezing ing" my extensions between Base and Derivative1?
(By the way, I tried to keep the above as generic as possible, but what I'm actually doing is trying add signals for "focusIn" and "focusOut" to every QWidget derivative without writing the same code over and over again for every QWidget subclass I use)
EDIT:
For reference, here's my current implementation:
// qwidgetfs.h
class QLineEditFS : public QLineEdit
{
Q_OBJECT
private:
void focusInEvent(QFocusEvent *);
void focusOutEvent(QFocusEvent *);
public:
QLineEditFS(QWidget *parent = 0);
signals:
void focusReceived(QWidgetFS::InputType);
void focusLost();
};
// qwidgetfs.cpp
QLineEditFS::QLineEditFS(QWidget *parent /*= 0*/)
: QLineEdit(parent)
{}
void QLineEditFS::focusInEvent(QFocusEvent *e)
{
QLineEdit::focusInEvent(e);
emit focusReceived(QWidgetFS::InputText);
}
void QLineEditFS::focusOutEvent(QFocusEvent *e)
{
QLineEdit::focusOutEvent(e);
emit focusLost();
}
And this repeated for QSpinBoxFS, QComboBoxFS, QCheckBoxFS and so on...
Instead I would like to just define this logic in a common class QWidgetFS, and then "inject" it into other classes derived from QWidget
I'm not sure you'll really be able to do what you are suggesting without modifying Qt and recompiling it.
Perhaps to do what you want, you could use event filters installed on the objects that you want to handle focus events from?
little test app:
header:
class FocusEventFilter : public QObject
{
Q_OBJECT
public:
FocusEventFilter(QObject* parent)
: QObject(parent)
{}
Q_SIGNALS:
void focusIn(QWidget* obj, QFocusEvent* e);
void focusOut(QWidget* obj, QFocusEvent* e);
protected:
bool eventFilter(QObject *obj, QEvent *e);
};
class testfocus : public QMainWindow
{
Q_OBJECT
public:
testfocus(QWidget *parent = 0, Qt::WFlags flags = 0);
~testfocus();
public Q_SLOTS:
void onFocusIn(QWidget*, QFocusEvent*);
void onFocusOut(QWidget*, QFocusEvent*);
private:
Ui::testfocusClass ui;
};
Implementation
#include <QFocusEvent>
#include "testfocus.h"
bool FocusEventFilter::eventFilter(QObject *obj, QEvent *e)
{
if (e->type() == QEvent::FocusIn) {
bool r = QObject::eventFilter(obj, e);
QFocusEvent *focus = static_cast<QFocusEvent*>(e);
QWidget* w = qobject_cast<QWidget*>(obj);
if (w) {
emit focusIn(w, focus);
}
return r;
}
else if (e->type() == QEvent::FocusOut) {
bool r = QObject::eventFilter(obj, e);
QFocusEvent *focus = static_cast<QFocusEvent*>(e);
QWidget* w = qobject_cast<QWidget*>(obj);
if (w) {
emit focusOut(w, focus);
}
return r;
}
else {
// standard event processing
return QObject::eventFilter(obj, e);
}
}
testfocus::testfocus(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
FocusEventFilter* filter = new FocusEventFilter(this);
ui.lineEdit->installEventFilter(filter);
ui.lineEdit_2->installEventFilter(filter);
connect(filter, SIGNAL(focusIn(QWidget*, QFocusEvent*)), this, SLOT(onFocusIn(QWidget*, QFocusEvent*)));
connect(filter, SIGNAL(focusOut(QWidget*, QFocusEvent*)), this, SLOT(onFocusOut(QWidget*, QFocusEvent*)));
}
testfocus::~testfocus()
{
}
void testfocus::onFocusIn(QWidget* obj, QFocusEvent*)
{
obj->setStyleSheet("background-color:#aaaaff;");
}
void testfocus::onFocusOut(QWidget* obj, QFocusEvent*)
{
obj->setStyleSheet("background-color:#ffaaaa;");
}
Of course, YMMV. You could always have a separate filter per object. This method means you can avoid deriving from everything. Not as efficient but it should work.
You may be able to do what you want in the event filter itself rather than using signals/slots.
I have done stuff like this in the past with templates. The problem is that you can't use signals.
I'm typing this up without a compiler so please be kind :):
template<typename T>
class FSWidget: public T
{
public:
FSWidget()
{
_delegate = NULL;
}
setDelegate(FSDelegate *delegate)
{
_delegate = delegate;
}
protected:
virtual void focusInEvent(QFocusEvent *e)
{
T::focusInEvent(e);
if (_delegate) {
_delegate->focusInEvent(this);
}
}
virtual void focusOutEvent(QFocusEvent *e)
{
T::focusOutEvent(e);
if (_delegate) {
_delegate->focusOutEvent(this);
}
}
private:
FSDelegate *_delegate;
};
So, the advantage is when you need to use this you can basically create a class like this:
FSWidget<QLineEdit *> lineEdit = new FSWidget<QLineEdit *>;
lineEdit->setDelegate(delegate);
You can put in whatever you want instead of QLineEdit and it will work.
And then teh FSDelegate could be just an interface that you mix into whatever class needs to act on the info. It could be one of these:
class FSDelegate
{
public:
virtual void focusInEvent(QWidget *w) = 0;
virtual void focusOutEvent(QWidget *w) = 0;
};
If you're always doing the same thing in on focusInEvent and focusOutEvents, you can implement these functions and make a real Mixin class.
Hopefully this can avoid some code duplication for you.
this is a simple OOP QT question.
my app consists of main window (QMainWindow) and a table (QTableWidget).
in the main window i have arguments and variables which i would like to pass to the table class, and to access methods in main widnow class from the table class, how should i do it ?
mainwindow.h
class MainWindow : public QMainWindow {
Q_OBJECT
private:
int a;
int b;
Spreadsheet *spreadsheet;
public:
void set_a(int);
void set_b(int);
spreadsheet.h
class Spreadsheet : public QTableWidget {
Q_OBJECT
public:
Spreadsheet(QWidget *parent = 0);
atm i define Spreadsheet like this:
spreadsheet = new Spreadsheet(this);
and i'd like to access set_a() from spreadsheet.cpp...
You should consider a different design, you are tightly coupling your code.
Maybe something like the following...
class Spreadsheet : public QTableWidget
{
Q_OBJECT
signals:
void aValueChanged(int value);
void bValueChanged(int value);
public:
void doSomething()
{
emit aValueChanged(100);
}
};
class MainWindow : public QMainWindow
{
public:
MainWindow() :
a(0),
b(0)
{
connect(&spreadsheet, SIGNAL(aValueChanged(int)), this, SLOT(setA(int)));
connect(&spreadsheet, SIGNAL(bValueChanged(int)), this, SLOT(setB(int)));
spreadsheet.doSomething();
}
slots:
void setA(int value) { a = value; }
void setB(int value) { b = value; }
private:
Spreadsheet spreadsheet;
int a;
int b;
};
This is completely untested but gives you an idea.
You can use the parent() method in the Spreadsheet object to get a pointer to your MainWindow.
For example,
// spreadsheet.cpp
MainWindow* mainWindow = (MainWindow*) this->parent();
mainWindow->set_a(123);
Of course, the parent object passed to Spreadsheet's constructor should be your MainWindow instance for this to work.
However, you should seriously consider oscode's suggestion, since it also points you towards creating a more Qt-like API.