Example of Communication Between Qt C++ and Python Using ZMQ

Below is a complete example demonstrating how to use ZeroMQ for communication between a Qt C++ application and a Python script. This example uses the REQ-REP pattern, where the Qt C++ client sends requests to the Python server and receives responses.

Preparation

First, install the necessary dependencies:

# Install ZeroMQ C++ library
sudo apt-get install libzmq3-dev  # Ubuntu/Debian
# Or use vcpkg (Windows)
vcpkg install zmq
# Install Python ZeroMQ bindings
pip install pyzmq

Python Server Code (server.py)

import zmq
import time
def run_server():    context = zmq.Context()    socket = context.socket(zmq.REP)    socket.bind("tcp://*:5555")
    print("Python server started, waiting for Qt client connection...")
    while True:        # Wait for requests from the Qt client        message = socket.recv_string()        print(f"Received message from Qt client: {message}")
        # Process request        if message == "Hello":            response = "World"        elif message == "GetTime":            response = time.strftime("%Y-%m-%d %H:%M:%S")        elif message == "Exit":            response = "Goodbye"            socket.send_string(response)            break        else:            response = f"Echo: {message}"
        # Send reply        socket.send_string(response)        print(f"Sent reply: {response}")
    print("Server shutting down")    socket.close()    context.term()
if __name__ == "__main__":    run_server()

Qt C++ Client Code

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
// Forward declaration
namespace zmq {    class context_t;    class socket_t;}
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow{    Q_OBJECT
public:    MainWindow(QWidget *parent = nullptr);    ~MainWindow();
private slots:    void on_btnSend_clicked();    void on_btnConnect_clicked();    void on_btnExit_clicked();    void checkForMessages();
private:    Ui::MainWindow *ui;    zmq::context_t* m_context;    zmq::socket_t* m_socket;    QTimer* m_timer;    bool m_connected;};#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <zmq.hpp>
#include <QMessageBox>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)    : QMainWindow(parent)    , ui(new Ui::MainWindow)    , m_context(nullptr)    , m_socket(nullptr)    , m_connected(false){    ui->setupUi(this);
    // Set up timer to check for messages    m_timer = new QTimer(this);    connect(m_timer, &QTimer::timeout, this, &MainWindow::checkForMessages);
    ui->statusbar->showMessage("Not connected");}
MainWindow::~MainWindow(){    if (m_socket) {        delete m_socket;    }    if (m_context) {        delete m_context;    }    delete ui;}
void MainWindow::on_btnConnect_clicked(){    try {        if (!m_context) {            m_context = new zmq::context_t(1);        }
        if (!m_socket) {            m_socket = new zmq::socket_t(*m_context, ZMQ_REQ);            m_socket->connect("tcp://localhost:5555");            m_socket->set(zmq::sockopt::rcvtimeo, 1000); // Set receive timeout to 1 second        }
        // Test connection        m_socket->send(zmq::buffer("Hello"), zmq::send_flags::none);
        zmq::message_t reply;        auto result = m_socket->recv(reply, zmq::recv_flags::none);
        if (result) {            m_connected = true;            ui->statusbar->showMessage("Connected to Python server");            m_timer->start(100); // Check for messages every 100 milliseconds        } else {            ui->statusbar->showMessage("Connection failed");        }    } catch (const zmq::error_t& e) {        QMessageBox::critical(this, "Error", QString("ZMQ Error: %1").arg(e.what()));        ui->statusbar->showMessage("Connection error");    }}
void MainWindow::on_btnSend_clicked(){    if (!m_connected || !m_socket) {        QMessageBox::warning(this, "Warning", "Please connect to the server first");        return;    }
    QString message = ui->txtMessage->text();    if (message.isEmpty()) {        return;    }
    try {        // Send message        m_socket->send(zmq::buffer(message.toStdString()), zmq::send_flags::none);        ui->listMessages->addItem("Sent: " + message);        ui->txtMessage->clear();    } catch (const zmq::error_t& e) {        QMessageBox::critical(this, "Error", QString("Send error: %1").arg(e.what()));    }}
void MainWindow::on_btnExit_clicked(){    if (m_connected && m_socket) {        try {            m_socket->send(zmq::buffer("Exit"), zmq::send_flags::none);            zmq::message_t reply;            m_socket->recv(reply, zmq::recv_flags::none);        } catch (...) {            // Ignore errors on exit        }    }
    qApp->quit();}
void MainWindow::checkForMessages(){    if (!m_connected || !m_socket) {        return;    }
    try {        zmq::message_t reply;        auto result = m_socket->recv(reply, zmq::recv_flags::dontwait);
        if (result) {            std::string response = std::string(static_cast<char*>(reply.data()), reply.size());            ui->listMessages->addItem("Received: " + QString::fromStdString(response));        }    } catch (const zmq::error_t& e) {        // Ignore timeout errors        if (e.num() != EAGAIN) {            qDebug() << "Receive error:" << e.what();        }    }}

main.cpp

#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[]){    QApplication a(argc, argv);    MainWindow w;    w.show();    return a.exec();}

CMakeLists.txt (for Qt project)

cmake_minimum_required(VERSION 3.16)
project(QtZMQExample)
set(CMAKE_CXX_STANDARD 17)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
# Find ZeroMQ library
find_package(ZeroMQ REQUIRED)
set(SOURCES    main.cpp    mainwindow.cpp)
set(HEADERS    mainwindow.h)
set(FORMS    mainwindow.ui)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)    qt6_wrap_cpp(MOC_SOURCES ${HEADERS})    qt6_wrap_ui(UI_SOURCES ${FORMS})    add_executable(${PROJECT_NAME} ${SOURCES} ${MOC_SOURCES} ${UI_SOURCES})    target_link_libraries(${PROJECT_NAME} Qt${QT_VERSION_MAJOR}::Widgets ZeroMQ::ZeroMQ)else()    qt5_wrap_cpp(MOC_SOURCES ${HEADERS})    qt5_wrap_ui(UI_SOURCES ${FORMS})    add_executable(${PROJECT_NAME} ${SOURCES} ${MOC_SOURCES} ${UI_SOURCES})    target_link_libraries(${PROJECT_NAME} Qt5::Widgets ${ZEROMQ_LIBRARIES})endif()
target_include_directories(${PROJECT_NAME} PRIVATE ${ZEROMQ_INCLUDE_DIRS})

Usage Instructions

  1. First, run the Python server:

    python server.py
  1. Then, start the Qt client application.

  2. Click the “Connect” button to establish a connection with the Python server.

  3. Enter a message in the text box and click the “Send” button; the message will be sent to the Python server.

  4. The server will respond to the message, and the client will display the response.

  5. Special commands can be sent:

  • “Hello” – the server replies “World”

  • “GetTime” – the server replies with the current time

  • “Exit” – the server closes the connection

Notes

  • Ensure the Qt application is linked to the correct ZeroMQ library.

  • If using a different network or port, adjust the connection string accordingly.

  • This example uses the REQ-REP pattern, which means each request must have a response. For asynchronous communication, consider using other ZMQ patterns like PUB-SUB.

This example demonstrates the basic communication flow, and you can extend functionality as needed, such as adding more message types, error handling, or data serialization (e.g., using Protocol Buffers or JSON).

Leave a Comment