Broadcasting model for multiple Wt::WTableViews

Added by Jakub Pavelka 2 months ago

Hello,

I've tried to make a table from model that is updated by another thread than WApplication (sessions too) runs on. It doesn't work.
So I've looked for some examples how to make such ting and found broadcast.wt and it seemed to be the way.
I've changed "int Server::counter_;" to "std::shared_ptr<Wt::WStandardItemModel> Server::model_;".
But I still get stuck on the same error when the model is updated: "Access violation".
OK so I've looked through debuger and found out that (and I'm little bit guessing there) model sends signal from itself to view to make render changes and then from some reasons I can't let this changes happen.

So my question is, how to stop (?or use?) these signals to correctly update the view by the push from Server class?
And also if there is simple way?

Code:


#include <Wt/WApplication.h>
#include <Wt/WText.h>
#include <Wt/WString.h>
#include <Wt/WServer.h>
#include <Wt/WStandardItem.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WTableView.h>

#include <thread>
#include <chrono>
#include <mutex>
#include <memory>
#include <vector>

class Client {
};

class Server {
public:
    Server()
        :    model_(std::make_unique<Wt::WStandardItemModel>()),
            stop_(false)
    {
        thread_ = std::thread(std::bind(&Server::run, this));
    }

    ~Server()
    {
        stop_ = true;
        thread_.join();
    }

    void connect(Client *client, const std::function<void()>& function)
    {
        std::unique_lock<std::mutex> lock(mutex_);

        connections_.push_back
            (Connection(Wt::WApplication::instance()->sessionId(), client, function));
    }

    void disconnect(Client *client)
    {
        std::unique_lock<std::mutex> lock(mutex_);

        for (unsigned i = 0; i < connections_.size(); ++i) {
            if (connections_[i].client == client) {
    connections_.erase(connections_.begin() + i);
    return;
            }
        }

        assert(false);
    }

    std::shared_ptr<Wt::WStandardItemModel> getModel() const {
        std::unique_lock<std::mutex> lock(mutex_);

        return model_;
    }

private:
    struct Connection {
        Connection(const std::string& id, Client *c,
                             const std::function<void()>& f)
            : sessionId(id),
    client(c),
    function(f)
        { }

        std::string sessionId;
        Client *client;
        std::function<void()> function;
    };

    mutable std::mutex mutex_;
    std::thread thread_;
    std::shared_ptr<Wt::WStandardItemModel> model_;
    bool stop_;

    std::vector<Connection> connections_;

    void run();
};

Server server;

class ClientTable : public Wt::WTableView, public Client
{
public:
    ClientTable()
        : Wt::WTableView()
    {
        Wt::WApplication *app = Wt::WApplication::instance();

        server.connect(this, std::bind(&ClientTable::updateData, this));

        app->enableUpdates(true);

        setModel(server.getModel());
        updateData();

    }

    virtual ~ClientTable() {
        server.disconnect(this);

        Wt::WApplication::instance()->enableUpdates(false);
    }

private:
    void updateData() {
        Wt::WApplication::instance()->triggerUpdate();
    }
};

void Server::run()
{

    for (;;) {
        std::this_thread::sleep_for(std::chrono::seconds(1));

        if (stop_)
            return;

        {
            std::vector<std::unique_ptr<Wt::WStandardItem>> _row_;
            for (auto &&s : {"0", "1", "2", "3"})
                _row_.emplace_back(std::move(std::make_unique<Wt::WStandardItem>(s)));

            std::unique_lock<std::mutex> lock(mutex_);
            model_->insertRow(0, std::move(_row_));

            for (unsigned i = 0; i < connections_.size(); ++i) {
                Connection& c = connections_[i];
                Wt::WServer::instance()->post(c.sessionId, c.function);
            }
        }
    }
}

std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment& env)
{
    std::unique_ptr<Wt::WApplication> app = std::make_unique<Wt::WApplication>(env);
    app->setCssTheme("");
    app->root()->addWidget(std::make_unique<ClientTable>());
    return app;
}

int main(int argc, char **argv)
{
    return Wt::WRun(argc, argv, &createApplication);
}

Thanks,
Jakub Pavelka


Replies (3)

RE: Broadcasting model for multiple Wt::WTableViews - Added by Wim Dumon 2 months ago

Hello Jakub,

You cannot share a Wt model class between multiple sessions.

RE: Broadcasting model for multiple Wt::WTableViews - Added by Jakub Pavelka 2 months ago

But the only problem i have is just that the signal about adding/removing row/item to/from model(in my case read only model and only broadcast class can add/remove) is send to a View.
If i'd be able to set the model in a way that it's not sending this signal then there won't be a problem.
I forked a wt on github and made several changes to signals (real fast so it's propablly not the good way) to get a function that block them and it appears that it's working good.
But it would be nice to have that in classic wt.

(1-3/3)