I have custom QWidget that contains custom QWindow. QWindow with OpenGL is used as a "connector" between render framework and Qt application.
Mouse and keyboard events are handled with overriding QWindow methods.
Pseudo-code:
class MyWindow : public QWindow
{
public:
MyWindow : QWindow() { /* GL stuff init*/ }
protected:
// mouse/keyboard event handling
// expose event handling
// resize event handling
// ...
};
class MyWidget : public QWidget
{
public:
MyWidget : QWidget()
{
auto window = new MyWindow();
auto container = createWindowContainer(window);
layout()->addWidget( container );
setAcceptDrops( true );
}
protected:
// overriding drop event, but is doesn't work
};
Question: how to handle drop events (it doesn't matter where)?
Problems:
QWindow doesn't provide virtual methods for drag-n-drop support.
QWidget::dragEnterEvent, QWidget::dropEvent (and similar) are not called.
QWindow still accept mouse events, even setMouseGrabEnabled( false ); is set.
Note: I found that call of setMouseGrabEnabled( false ); doesn't blocks mouse events handling in QWindow.
I found a solution:
It is necessary to install event filter on QWindow and process events there (eventFilter).
It is possible to install event filter on QWidget (container) but it doesn't work on OS X. Probably it is a bug in Qt, because under Win everything is fine.
Related
everyone!
I am having a problem using QObject::connect with some custom classes I've created. First of all, I have created 2 classes that inherit from QObject, they are called: Valve and PushButton. They are instantiated inside controllers called PanelController and SynopticController, which are also QObjects. And these controllers are instantiated inside another class called MasterController, also a QObject. I find this information useful since I think it is a problem of referencing the classes or the way I might be instantiating my classes inside these controllers. I strongly think this, because in my main method, when I do the following snippet of code, the connection works:
...
avionics::synoptic::Valve valveTest(nullptr, avionics::synoptic::ValveName::ABV);
avionics::panel::PushButton pushButtonTest(nullptr, avionics::panel::PanelNames::RECIRC);
QObject::connect(&pushButtonTest, &avionics::panel::PushButton::onStateColorChanged, &valveTest, &avionics::synoptic::Valve::updateState);
...
Basically, the controller classes are:
// MasterController
class MasterController : public QObject {
...
private:
panel::PanelController* panelController{nullptr};
synoptic::SynopticController* synopticController{nullptr};
}
// Panel Controller
class PanelController : public QObject {
...
explicit PanelController(QObject *parent = nulptr){
this->pushButtons.append(new avionics::panel::PushButton(_panelController, avionics::panel::PanelNames::RECIRC));
}
private:
QList<avionics::panel::PushButton*> pushButtons{};
}
// SynopticController
class SynopticController : public QObject {
private:
QList<avionics::synoptic::Valve*> iceValves{};
explicit SynopticController(QObject *parent = nullptr) {
antiIcePneumaticLines.append(new avionics::synoptic::PneumaticLine(_synopticController, avionics::synoptic::PneumaticLineName::APU_2_ABV));
}
}
My problem is that when I do the same call for the QObject::connect either from my MasterController constructor or my main method, the signal doesn't call the slot function. I want to connect pushButtons to valves, and to do this I am using getters from my controllers. The call to QObject::connect that doesn't work is:
QObject::connect(panelController->getpushButtons().at(1), &avionics::panel::PushButton::onStateColorChanged, synopticController->getValves().at(1), &avionics::synoptic::Valve::updateState);
// Example of getter
QList<avionics::panel::PushButton*> PanelController::getPushButtons(){
return pushButtons;
}
I've put some prints inside the method that emits the signal and tried debugging it, but the signal is emitted and the slot isn't called. The classes return from the getters are not undefined or null, I've checked it. Let me know if something wasn't clear. Thanks in advance!
I have a 2 widgets inherited from QDialog. One of these widgets is called by another widget.
I need to pass data from the parent widget to the child. For example, I want passing QStringList.
I can make signals and slots in both classes. Slot of parent widget class - transferList(QStringList) - filling my QStringList.
How should I make the signal and slot connection? The child widget, of course, knows nothing about the parent.
// .h-file of parent widget.
class ElectricIndicationDialog : public QDialog {
#include "PollIndication.h" // class of child widget
QSharedPointer <PollIndication> pollInd;
public slots:
void transferList(QStringList);
signals:
void listTfansfer(QStringList);
private:
QStringList sendList;
};
// .cpp-file of parent widget
pollInd = QSharedPointer <PollIndication>(new PollIndication());
pollInd->show();
void ConfIndication::transferList(QStringList lst) {
lst.append("str1");
lst.append("str2");
}
// .h-file of child widget
class PollIndication : public QDialog {
public slots:
void getList(QStringList);
signals:
void listGet(QStringList);
private:
QStringList recList; // We transfer data to it
}
You don't need a signal/slot for that: your parent knows the type of its child and has a pointer on it. So, you can call a method of PollIndication when you need to send data to your dialog.
void ConfIndication::transferList(QStringList lst) {
lst.append("str1");
lst.append("str2");
pollInd->changeTransferList(lst);
}
If your dialog is modal, you can also create your dialog only when needed and give your list as parameter of the constructor.
void ConfIndication::transferList(QStringList lst) {
lst.append("str1");
lst.append("str2");
PollIndication* pollInd = new PollIndication(lst, this);
pollInd->exec();
}
It is normally a bad idea to make a parent class to know what are their children....
you can in the parent class define an abstract method (think about some pure virtual) so every childclass is forced to implement it... after that, the parent class can invoke the method and the child will implement the login depending on how it must react to it...
I need to make a GUI button that tells it's parent (or parent's parent or even parent's parent's parent...) that different widget in the QStackedLayout should be shown. I created a custom QEvent:
class SwitchScreenEventWidget : public QEvent {
public:
SwitchScreenEventWidget(QWidget* w) : SwitchScreenEvent(), widget(w) {
if(widget==nullptr)
throw "SwitchScreenEventWidget received null widget.";
}
virtual QWidget* getWidget() const {;return widget;}
private:
QWidget* const widget;
};
I invoke it like this:
// Through debugger I checked that this is getting called properly
void GraphButton::buttonClicked()
{
if(qApp!=nullptr && parent()!=nullptr)
qApp->notify(parent(), new SwitchScreenEventWidget(getGraph()));
}
And handle it like this:
bool ViewStack::eventFilter(QEvent* e)
{
if(e->type()>=QEvent::User) {
if(SwitchScreenEvent* event = dynamic_cast<SwitchScreenEvent*>(e)) {
// Show the given widget
}
return true;
}
return false;
}
I use eventFilter which is then registered to the main app widget. But the event is not getting captured. Somewhere I read some QEvents simply don't bubble up through the hierarchy.
So do all events bubble or not? If not, which do, which don't and why? And how do I make my event bubble properly?
I think the best way for you is subclassing QApplication, override notify method and do your "bubbles events" by yourself. I'm pretty sure that mouse and keyboard events "bubbles up" by this method and other events don't.
bool QCoreApplication::notify(QObject * receiver, QEvent * event)
Sends event to receiver: receiver->event(event). Returns the value
that is returned from the receiver's event handler. Note that this
function is called for all events sent to any object in any thread.
For certain types of events (e.g. mouse and key events), the event
will be propagated to the receiver's parent and so on up to the
top-level object if the receiver is not interested in the event (i.e.,
it returns false).
So you can change this behavior by your realisation.
Also about you code example. I think this check
if( qApp!=nullptr )
is useless beacause of you always will have instance of qApp.
I have a screen class as
class Screen : public QLayout
{
public:
Screen();
~Screen();
void paintEvent(QPaintEvent *e);
};
When I am creating the object I got an error that can not create an object for pure abstract class. Since QLayoput is pure abstract , How can I create an object for a class which is inherits the QLayout ?
definitions:
Screen::Screen( )
{
}
Screen::~Screen()
{
delete this ;
//Screen(new QSize (100,100));
}
void Screen::paintEvent(QPaintEvent *e)
{
}
QLayout is pure abstract, meaning it has virtual members without a definition. To subclass it, you need to provide definitions for all such methods in your class. Specifically, Qt Docs state that
To make your own layout manager, implement the functions addItem(),
sizeHint(), setGeometry(), itemAt() and takeAt().
For more, see there (there are additional optional advices for further functions which should be implemented): http://qt-project.org/doc/qt-4.8/qlayout.html
I'm primarily a .Net developer and have been investigating Qt for a while now. I'm now at the stage of trying to implement the model / view framework in Qt. I think I have a grasp of the basic principles but am unclear of how to hang things together in a more complex UI where widgets need to communicate with each other. Given the following:
// 'domain' model classes
class NestedDomainModel1
{
public:
NestedDomainModel1();
QString name() const;
void setName(const QString& newName);
// other properties
private:
QString m_name;
};
class NestedDomainModel2
{
public:
NestedDomainModel2();
QString name() const;
void setName(const QString& newName);
// other properties
};
class MyDomainModel
{
public:
MyDomainModel();
void addNestedModel1(const NestedDomainModel1& modelToAdd);
NestedDomainModel& nestedObjectModel1At(int index);
int nestedObjectModel1Count() const;
// repeat for model 2
private:
QList<NestedDomainModel1> m_nestedModels1;
QList<NestedDomainModel2> m_nestedModels2;
};
// 'GUI' classes
class MainWindow : public QMainWindow
{
private:
MyDomainModel* m_model;
MyTreeViewWidget* m_treeWidget; // -> this sits in a left dock window
MyInfoDisplayWidget* m_infoWidget; // -> this sits in a right dock window and display details about the item selected in the tree
};
class MyDomainModelTreeModel : public QAbstractItemModel
{
public:
explicit MyDomainModelTreeModel(MyDomainModel* model);
// required overrides for QAbstractItemModel
private:
MyDomainModel* m_model;
};
class MyTreeViewWidget : public QWidget
{
public:
// Take a pointer to the domain model and create a model for the 'view'.
// Will create a tree like:
// Nested Objects 1
// |- object 001
// |- object 002
// |- you get the idea
// Nested Objects 2
// |- other object 001
// |- more of the same
explicit MyTreeViewWidget(MyDomainModel* model);
public slots:
// Used to notify widget when an item is added to the underlying model.
void nestedModel1Added();
void nestedModel2Added();
signals:
void nestedModel1Selected(NestedDomainModel1& selectedModel);
void nestedModel2Selected(NestedDomainModel2& selectedModel);
private slots:
// connect to tree view event when an item is selected and if all ok, emit one of the selected events
void onTreeItemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
private:
QTreeView* m_treeView;
MyDomainModelTreeModel* m_treeModel;
};
class MyNestedClass1ViewModel : QAbstractItemModel
{
public:
explicit MyNestedClass1ViewModel(NestedDomainModel1* model);
setModel(NestedDomainModel1* model);
// required overrides for QAbstractItemModel
private:
NestedDomainModel1* m_model
};
class MyInfoDisplayWidget : public QWidget
{
public:
explicit MyInfoDisplayWidget(QWidget* parent = 0);
public slots:
// this is connected to the 'tree' widget signal in MainWindow
void setModel(NestedDomainModel1& selectedModel);
};
The basic premise of the UI is something similar in feel to Visual Studio. The tree is similar to the Solution Explorer and the 'info display' is similar to the properties window.
Is this how you use the model / view framework? For those familar with WPF / Silverlight development, is the model / view framework similar to MVVM (at a high level) in that it is the 'model of the view' and wraps / contains the domain model?
Is this how you connect the widgets using the model / view framework (ie. one widget passes a pointer or reference of the model to another)? Or should I be using the SelectionModel? Does that work since the tree model contains different types of objects?
How do you identify the root nodes? For instance, when a MyNestedObject1 is created and needs to be added to tree do I rely on the knowledge that root node is at a model index QModelIndex(0, 0) (ie. row 0 with an invalid parent index)?
I'm finding the terminology you're using a bit awkward, for example MyNestedClass1ViewModel is just a model. I'm not sure what a ViewModel would be.
What you're missing in this example is an actual view. MyTreeViewWidget is just a dumb widget that isn't actually a view in Qt terms at all, it's essdentialy just a dumb 'canvas' that you want to display data in. So the way to do this is:
You have underlying data in ordinary objects such as NestedDomainModel2. These are not Models in the Qt sense though and I wouldn't name them as such. They're just ordinary objects and don't implement any of the MVC interfaces.
Your MyNestedClass1ViewModel, which is a Qt model class. It accesses the underlying data objects above (1) in the implementation of it's data() and setData() methods.
A view class subclassed from QAbstractItemView. This is what you're missing. It has all the magic hooks to plug into the API of the model class from (2) above. It gets signals from the model telling it when there have been changed, which invoke methods such as dataChanged(), rowsInserted(). You implement these methods to make appropriate changes in your display widget below in point (4).
Your display widget. It doesn't implement any of the model/view API itself and is updated by your view. If it's interactive and can be used to change model data, you do that by calling setData(), insertRows(), removeRows(), etc on the model. The display changes will automatically propagate back to the widget via the view. Be careful not to generate infinite loops of changes propagating from widget->model->view->widget->model->view etc.
I have done a similar thing to use a QGraphicsScene/QGraphicsView to display items in a model. Despite it's name QGraphicsView isn't part of the model/view framework, so I implemented a custom view class which drew the model data on the QGraphicsScene.
Here's my code, in Python. It draws worlds on a map for an SF wargame:
class WorldItemView(QtGui.QAbstractItemView):
""" Hidden view which interfaces between the model and the scene.
"""
def __init__(self, model, parent=None):
QtGui.QAbstractItemView.__init__(self, parent)
self.hide()
self.setModel(model)
self.my_model = model
self.scene = MapScene(self.my_model)
self.resetWorlds()
def dataChanged(self, topLeft, bottomRight):
top_row = topLeft.row()
bottom_row = bottomRight.row()
#debug_log("Top row " + str(top_row) + " Bottom row " + str(bottom_row))
for row in range(top_row, (bottom_row + 1)):
self.scene.worldChanged(row)
def rowsInserted(self, parent, start, end):
for row in range(start, (end + 1) ):
pmi = self.my_model.getPMI(row)
self.scene.insertWorld(pmi)
def rowsAboutToBeRemoved(self, parent, start, end):
for row in range(start, (end + 1)):
self.scene.removeWorld(row)
def resetWorlds(self):
self.scene.clearWorlds()
# Add worlds to scene
last_row = self.my_model.rowCount() - 1
self.rowsInserted(None, 0, last_row)
I hope that helped.