Reputation: 6655
What techniques do you use to display messages in a GUI without popup dialogs?
Popup dialogs are generally quite horrible for a user - they get in the way and often you are not interested that you just caused an error. An alternative is just to ignore errors and do nothing
However, there may be the occasional user who wants to know when they caused an error...
So you want to display informative messages but without requiring that the user has to click away annoying popup boxes.
One option could be to use a statusbar of the mainwindow, but in order for any widget to use it, you need to pass around references to this damn statusbar (I'm thinking of python/qt here)...it quickly gets confusing and removes 'resuability' of your widgets (imagine you create another app, without a statusbar and you want to reuse a widget in it...)...
Any ideas?
Upvotes: 0
Views: 213
Reputation: 98435
First of all, the message and the display widget for it are two separate things. It's a serious design error to mangle them together.
A typical solution would have some sort of a logger/message sink that is a semantically a singleton, but not necessarily implemented using the singleton pattern. The sink could be a QObject
so that you can easily connect message sources to the sink. The sink can then be attached to one or more display widgets.
Passing the sink around is very easy thanks to QObject
and the fact that qApp
is a global instance pointer, and QCoreApplication
is a QObject
. Thus you can:
pass the pointer via the dynamic property system, or
make the sink a sole child of the global application object.
See example below. Note that the Widget
only needs to know the declaration of the MessageSink
class. It doesn't need to be passed any instances explicitly. Neither is the usual singleton pattern used as-is.
class MessageSink : public QObject {
Q_OBJECT
public:
MessageSink(QObject * parent = 0) : QObject(parent) {}
Q_SIGNAL void message(const QString &);
static MessageSink * instance() { return qApp->findChild<MessageSink*>(); }
}
class Widget : public QWidget {
QVBoxLayout m_layout;
QLabel m_l1, m_l2;
public:
Widget(QWidget * parent = 0) : QWidget(parent), m_layout(this) {
m_layout.addWidget(&m_l1);
m_layout.addWidget(&m_l2);
m_l1.connect(MessageSink::instance(), SIGNAL(message(QString)), SLOT(setText(QString)));
m_l2.connect(MessageSink::instance(), SIGNAL(message(QString)), SLOT(setText(QString)));
}
}
int main(int argc, char ** argv) {
QApplication app(argc, argv);
MessageSink sink(&app);
Widget w;
w.show();
emit sink.message("Hello!");
return app.exec();
}
Note: It is not a bug to have a local QObject
that has a parent. You just have to make sure it gets destructed before the parent. C++ guarantees that here.
Upvotes: 0
Reputation: 4021
What I've done in my QT/C++ application, is a QDockedWidget in the main window called "Message Board", containing a list of warning/error/info messages. It could be anyway removed by the user. To do not pass a reference to all the widgets of this QDockedWidget, I use (for many other purpose too...) a SharedData class, with global visibility, built as singleton for the application. So every widget as a global reference to it and can set an error or warning or something else:
Gshared->setError("oops!", ErrorType::Critical);
In setError function here I emit a signal, that is catched by a slot in the QDockedWidget (for displaying the error), by a logger manager (that writes in a log file more details about the error), etc...
An another option would be the "do not show again" checkbox in a custom messageBox.
Upvotes: 1
Reputation: 27611
One option could be to use a statusbar of the mainwindow, but in order for any widget to use it, you need to pass around references to this damn statusbar
Designed correctly, this is not the case. Many of my classes have a Log signal like this:-
void Log(const QString& message, enum LogPriority priority);
The priority is an enum, used to define the level of information, whether it's a debug message, warning, error, critical error etc.
In addition, I have a Logging class with a matching Log slot. You could make this a singleton, or simply have a static method.
Classes connect their signals either directly to the logging class, or to a parent's signal. This ensures that the class doesn't care what happens when it sends a log message. You can also disable a class from logging by removing its connect.
As for the logging itself, the Log class can choose to either set the text on the message bar, write to a file, display a notification (OSX) or any other method you want.
While my method uses C++, I expect you can do the same or similar in Python.
Upvotes: 1