add an example to embed a server in a Qt GUI app
This commit is contained in:
@@ -23,6 +23,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${BUILD_DIR}/bin/release)
|
|||||||
option(WEBCC_ENABLE_AUTOTEST "Build automation test?" OFF)
|
option(WEBCC_ENABLE_AUTOTEST "Build automation test?" OFF)
|
||||||
option(WEBCC_ENABLE_UNITTEST "Build unit test?" OFF)
|
option(WEBCC_ENABLE_UNITTEST "Build unit test?" OFF)
|
||||||
option(WEBCC_ENABLE_EXAMPLES "Build examples?" ON)
|
option(WEBCC_ENABLE_EXAMPLES "Build examples?" ON)
|
||||||
|
option(WEBCC_ENABLE_QT_EXAMPLES "Build Qt application examples?" OFF)
|
||||||
|
|
||||||
set(WEBCC_ENABLE_LOG 1 CACHE STRING "Enable logging? (1:Yes, 0:No)")
|
set(WEBCC_ENABLE_LOG 1 CACHE STRING "Enable logging? (1:Yes, 0:No)")
|
||||||
set(WEBCC_ENABLE_SSL 0 CACHE STRING "Enable SSL/HTTPS (need OpenSSL)? (1:Yes, 0:No)")
|
set(WEBCC_ENABLE_SSL 0 CACHE STRING "Enable SSL/HTTPS (need OpenSSL)? (1:Yes, 0:No)")
|
||||||
|
|||||||
@@ -40,10 +40,14 @@ target_link_libraries(form_urlencoded_client ${EXAMPLE_LIBS})
|
|||||||
add_executable(form_server form_server.cc)
|
add_executable(form_server form_server.cc)
|
||||||
target_link_libraries(form_server ${EXAMPLE_LIBS})
|
target_link_libraries(form_server ${EXAMPLE_LIBS})
|
||||||
|
|
||||||
add_subdirectory(book_server)
|
|
||||||
add_subdirectory(book_client)
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_executable(url_unicode url_unicode.cc encoding.cc encoding.h)
|
add_executable(url_unicode url_unicode.cc encoding.cc encoding.h)
|
||||||
target_link_libraries(url_unicode ${EXAMPLE_LIBS})
|
target_link_libraries(url_unicode ${EXAMPLE_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(book_server)
|
||||||
|
add_subdirectory(book_client)
|
||||||
|
|
||||||
|
if(WEBCC_ENABLE_QT_EXAMPLES)
|
||||||
|
add_subdirectory(qt_app_server)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
project(qt_app_server)
|
||||||
|
|
||||||
|
find_package(Qt5 CONFIG REQUIRED Core Gui Widgets)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
|
file(GLOB SRCS *.cpp *.h)
|
||||||
|
|
||||||
|
add_executable(qt_app_server ${SRCS})
|
||||||
|
|
||||||
|
target_link_libraries(qt_app_server Qt5::Core Qt5::Gui Qt5::Widgets ${EXAMPLE_LIBS})
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
#include "examples/qt_app_server/main_window.h"
|
||||||
|
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
#include "webcc/response_builder.h"
|
||||||
|
#include "webcc/server.h"
|
||||||
|
#include "webcc/view.h"
|
||||||
|
|
||||||
|
class ApiView : public webcc::View {
|
||||||
|
public:
|
||||||
|
explicit ApiView(Context* context) : context_(context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
webcc::ResponsePtr Handle(webcc::RequestPtr request) override {
|
||||||
|
if (request->method() == "GET") {
|
||||||
|
emit context_->SomeApiSignal("A GET request has been handled");
|
||||||
|
return webcc::ResponseBuilder{}.OK().Body("Hello, World!")();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Context* context_;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
QApplication app{ argc, argv };
|
||||||
|
|
||||||
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||||
|
|
||||||
|
Context context;
|
||||||
|
|
||||||
|
// Create a server and bind view.
|
||||||
|
webcc::Server server{ boost::asio::ip::tcp::v4(), 8000 };
|
||||||
|
server.Route("/", std::make_shared<ApiView>(&context));
|
||||||
|
|
||||||
|
// Run the server in a thread to avoid blocking the GUI.
|
||||||
|
std::thread server_thread{ [&server]() { server.Run(); } };
|
||||||
|
|
||||||
|
// Now show our Qt main window and execute the application loop.
|
||||||
|
MainWindow main_window{ &context };
|
||||||
|
main_window.resize(400, 300);
|
||||||
|
main_window.showNormal();
|
||||||
|
|
||||||
|
int code = app.exec();
|
||||||
|
|
||||||
|
// Stop the server and wait for it to finish.
|
||||||
|
server.Stop();
|
||||||
|
server_thread.join();
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "examples/qt_app_server/main_window.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(Context* context, QWidget* parent)
|
||||||
|
: QMainWindow(parent) {
|
||||||
|
setWindowTitle("Qt App Server");
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
connect(context, &Context::SomeApiSignal, this, &MainWindow::OnSomeApiSignal);
|
||||||
|
#else
|
||||||
|
// NOTE: Seems not necessary to use QueuedConnection (because we are using
|
||||||
|
// std::thread instead of QThread?).
|
||||||
|
connect(context, &Context::SomeApiSignal, this, &MainWindow::OnSomeApiSignal,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnSomeApiSignal(const QString& text) {
|
||||||
|
qDebug() << text;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef MAIN_WINDOW_H_
|
||||||
|
#define MAIN_WINDOW_H_
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
// Some global QObject with public signals for ApiView to inform the handling
|
||||||
|
// of client requests.
|
||||||
|
// "Context" is a bad name, you should change it.
|
||||||
|
class Context : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Context(QObject* parent = nullptr) : QObject(parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public signals emitted from ApiView:
|
||||||
|
signals:
|
||||||
|
void SomeApiSignal(const QString& text);
|
||||||
|
// Add other signals here...
|
||||||
|
};
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(Context* context, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// You should encapsulate the required information as the signal parameters.
|
||||||
|
// I use QString just for demostrating how to transfer data from ApiView.
|
||||||
|
void OnSomeApiSignal(const QString& text);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAIN_WINDOW_H_
|
||||||
@@ -118,6 +118,7 @@ bool RequestParser::ParseMultipartContent(const char* data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next boundary found.
|
// Next boundary found.
|
||||||
|
LOG_INFO("Next boundary found, off=%u", off);
|
||||||
|
|
||||||
// This part has ended.
|
// This part has ended.
|
||||||
if (off >= 2) {
|
if (off >= 2) {
|
||||||
@@ -128,7 +129,7 @@ bool RequestParser::ParseMultipartContent(const char* data,
|
|||||||
// +2 for including the CRLF after the boundary.
|
// +2 for including the CRLF after the boundary.
|
||||||
pending_data_.erase(0, off + count + 2);
|
pending_data_.erase(0, off + count + 2);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERRO("Invalid part data.");
|
LOG_ERRO("Invalid part data. off=%u", off);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user