Commit 6bad5f98 authored by Dominik Charousset's avatar Dominik Charousset

Qt example highlighting actor_widget_mixin

this example optionally builds when Qt4 is found during CMake
invokation and illustrates how to use actor_widget_mixin to send
and receive messages from a QWidget-based class
parent 59134a6e
...@@ -281,3 +281,8 @@ src/recursive_queue_node.cpp ...@@ -281,3 +281,8 @@ src/recursive_queue_node.cpp
cppa/intrusive_fwd_ptr.hpp cppa/intrusive_fwd_ptr.hpp
cppa/memory_managed.hpp cppa/memory_managed.hpp
src/memory_managed.cpp src/memory_managed.cpp
examples/qtsupport/group_chat.cpp
examples/qtsupport/chatwindow.ui
examples/qtsupport/chatwidget.hpp
examples/qtsupport/chatwidget.cpp
cppa/util/scope_guard.hpp
...@@ -3,3 +3,4 @@ ...@@ -3,3 +3,4 @@
./third_party/boost_context/include/ ./third_party/boost_context/include/
/opt/local/include/gcc46/c++ /opt/local/include/gcc46/c++
/opt/local/include/ /opt/local/include/
/opt/local/Library/Frameworks/QtGui.framework/Versions/4/Headers/
...@@ -21,3 +21,22 @@ add(math_actor message_passing) ...@@ -21,3 +21,22 @@ add(math_actor message_passing)
add(distributed_math_actor remote_actors) add(distributed_math_actor remote_actors)
add(group_server remote_actors) add(group_server remote_actors)
add(group_chat remote_actors) add(group_chat remote_actors)
find_package(Qt4)
if (QT4_FOUND)
include(${QT_USE_FILE})
QT4_WRAP_UI(GROUP_CHAT_UI_HDR qtsupport/chatwindow.ui)
QT4_WRAP_CPP(GROUP_CHAT_MOC_SRC qtsupport/chatwidget.hpp)
include_directories(. qtsupport ${CMAKE_BINARY_DIR}/examples ${CPPA_INCLUDE})
add_executable(qt_group_chat
qtsupport/group_chat.cpp
qtsupport/chatwidget.cpp
${GROUP_CHAT_MOC_SRC}
${GROUP_CHAT_UI_HDR})
target_link_libraries(qt_group_chat
${CMAKE_DL_LIBS}
${CPPA_LIBRARY}
${QT_LIBRARIES})
add_dependencies(qt_group_chat all_examples)
endif (QT4_FOUND)
#include <string>
#include <utility>
#include <QMessageBox>
#include <QInputDialog>
#include "chatwidget.hpp"
#include "cppa/cppa.hpp"
#include "cppa/util/scope_guard.hpp"
using namespace std;
using namespace cppa;
ChatWidget::ChatWidget(QWidget* parent, Qt::WindowFlags f)
: super(parent, f), m_input(nullptr), m_output(nullptr) {
set_message_handler (
on(atom("join"), arg_match) >> [=](const group_ptr& what) {
for (auto g : self->joined_groups()) {
send(g, m_name + " has left the chatroom");
self->leave(g);
}
self->join(what);
print(("*** joined " + to_string(what)).c_str());
m_chatroom = what;
send(what, m_name + " has entered the chatroom");
},
on(atom("setName"), arg_match) >> [=](string& name) {
send(m_chatroom, m_name + " is now known as " + name);
m_name = std::move(name);
print("*** changed name to "
+ QString::fromUtf8(m_name.c_str()));
},
on(atom("quit")) >> [=] {
close(); // close widget
},
on<string>() >> [=](const string& txt) {
// don't print own messages
if (self != self->last_sender()) {
print(QString::fromUtf8(txt.c_str()));
}
}
);
}
void ChatWidget::sendChatMessage() {
auto cleanup = util::make_scope_guard([=] {
input()->setText(QString());
});
QString line = input()->text();
if (line.startsWith('/')) {
match_split(line.midRef(1).toUtf8().constData(), ' ') (
on("join", arg_match) >> [=](const string& mod, const string& g) {
group_ptr gptr;
try { gptr = group::get(mod, g); }
catch (exception& e) {
print("*** exception: " + QString::fromUtf8((e.what())));
}
if (gptr != nullptr) {
send_as(as_actor(), as_actor(), atom("join"), gptr);
}
},
on("setName", arg_match) >> [=](const string& str) {
send_as(as_actor(), as_actor(), atom("setName"), str);
},
others() >> [=] {
print("*** list of commands:\n"
"/join <module> <group id>\n"
"/setName <new name>\n");
}
);
return;
}
if (m_name.empty()) {
print("*** please set a name before sending messages");
return;
}
if (m_chatroom == nullptr) {
print("*** no one is listening... please join a group");
return;
}
string msg = m_name;
msg += ": ";
msg += line.toUtf8().constData();
print("<you>: " + input()->text());
// NOTE: we have to use send_as(as_actor(), ...) outside of our
// message handler, because `self` is *not* set properly
// in this context
send_as(as_actor(), m_chatroom, std::move(msg));
}
void ChatWidget::joinGroup() {
if (m_name.empty()) {
QMessageBox::information(this,
"No Name, No Chat",
"Please set a name first.");
return;
}
auto gname = QInputDialog::getText(this,
"Join Group",
"Please enter a group as <module>:<id>",
QLineEdit::Normal,
"remote:chatroom@localhost:4242");
int pos = gname.indexOf(':');
if (pos < 0) {
QMessageBox::warning(this, "Not a Group", "Invalid format");
return;
}
string mod = gname.left(pos).toUtf8().constData();
string gid = gname.midRef(pos+1).toUtf8().constData();
group_ptr gptr;
try {
auto gptr = group::get(mod, gid);
send_as(as_actor(), as_actor(), atom("join"), gptr);
}
catch (exception& e) {
QMessageBox::critical(this, "Exception", e.what());
}
}
void ChatWidget::changeName() {
auto name = QInputDialog::getText(this, "Change Name", "Please enter a new name");
if (!name.isEmpty()) {
send_as(as_actor(), as_actor(), atom("setName"), name.toUtf8().constData());
}
}
#include <exception>
#include <QWidget>
#include <QLineEdit>
#include <QTextEdit>
#include "cppa/group.hpp"
#include "cppa/qtsupport/actor_widget_mixin.hpp"
class ChatWidget : public cppa::actor_widget_mixin<QWidget> {
Q_OBJECT
typedef cppa::actor_widget_mixin<QWidget> super;
public:
ChatWidget(QWidget* parent = nullptr, Qt::WindowFlags f = 0);
public slots:
void sendChatMessage();
void joinGroup();
void changeName();
private:
template<typename T>
T* get(T*& member, const char* name) {
if (member == nullptr) {
member = findChild<T*>(name);
if (member == nullptr)
throw std::runtime_error("unable to find child: "
+ std::string(name));
}
return member;
}
inline QLineEdit* input() {
return get(m_input, "input");
}
inline QTextEdit* output() {
return get(m_output, "output");
}
inline void print(const QString& what) {
output()->append(what);
}
QLineEdit* m_input;
QTextEdit* m_output;
std::string m_name;
cppa::group_ptr m_chatroom;
};
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChatWindow</class>
<widget class="QMainWindow" name="ChatWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="ChatWidget" name="chatwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="output">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="input">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionJoin_Group"/>
<addaction name="actionSet_Name"/>
</widget>
<addaction name="menuFile"/>
</widget>
<action name="actionJoin_Group">
<property name="text">
<string>Join Group</string>
</property>
</action>
<action name="actionSet_Name">
<property name="text">
<string>Set Name</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>ChatWidget</class>
<extends>QWidget</extends>
<header>chatwidget.hpp</header>
<container>1</container>
<slots>
<slot>sendChatMessage()</slot>
<slot>changeName()</slot>
<slot>joinGroup()</slot>
</slots>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>actionJoin_Group</sender>
<signal>activated()</signal>
<receiver>chatwidget</receiver>
<slot>joinGroup()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>300</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionSet_Name</sender>
<signal>activated()</signal>
<receiver>chatwidget</receiver>
<slot>changeName()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>300</y>
</hint>
</hints>
</connection>
<connection>
<sender>input</sender>
<signal>returnPressed()</signal>
<receiver>chatwidget</receiver>
<slot>sendChatMessage()</slot>
<hints>
<hint type="sourcelabel">
<x>362</x>
<y>547</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>310</y>
</hint>
</hints>
</connection>
</connections>
</ui>
/******************************************************************************\
* This example program represents a minimal terminal chat program *
* based on group communication. *
* *
* Setup for a minimal chat between "alice" and "bob": *
* - ./build/bin/group_server -p 4242 *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\******************************************************************************/
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <sstream>
#include <time.h>
#include <cstdlib>
#include <QMainWindow>
#include <QApplication>
#include "cppa/opt.hpp"
#include "cppa/cppa.hpp"
#include "ui_chatwindow.h" // auto generated from chatwindow.ui
using namespace std;
using namespace cppa;
using namespace cppa::placeholders;
struct line { string str; };
istream& operator>>(istream& is, line& l) {
getline(is, l.str);
return is;
}
any_tuple s_last_line;
any_tuple split_line(const line& l) {
vector<string> result;
stringstream strs(l.str);
string tmp;
while (getline(strs, tmp, ' ')) {
if (!tmp.empty()) result.push_back(std::move(tmp));
}
s_last_line = any_tuple::view(std::move(result));
return s_last_line;
}
int main(int argc, char** argv) {
string name;
string group_id;
options_description desc;
bool args_valid = match_stream<string>(argv + 1, argv + argc) (
on_opt1('n', "name", &desc, "set name") >> rd_arg(name),
on_opt1('g', "group", &desc, "join group <arg1>") >> rd_arg(group_id),
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc)
);
group_ptr gptr;
// evaluate group parameter
if (!group_id.empty()) {
auto p = group_id.find(':');
if (p == std::string::npos) {
cerr << "*** error parsing argument " << group_id
<< ", expected format: <module_name>:<group_id>";
}
else {
try {
gptr = group::get(group_id.substr(0, p),
group_id.substr(p + 1));
}
catch (exception& e) {
ostringstream err;
cerr << "*** exception: group::get(\"" << group_id.substr(0, p)
<< "\", \"" << group_id.substr(p + 1) << "\") failed; "
<< to_verbose_string(e) << endl;
}
}
}
if (!args_valid) print_desc_and_exit(&desc)();
QApplication app(argc, argv);
app.setQuitOnLastWindowClosed(true);
QMainWindow mw;
Ui::ChatWindow helper;
helper.setupUi(&mw);
auto client = helper.chatwidget->as_actor();
if (!name.empty()) {
send_as(client, client, atom("setName"), move(name));
}
if (gptr != nullptr) {
send_as(client, client, atom("join"), gptr);
}
mw.show();
return app.exec();
shutdown();
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment