PDA

View Full Version : GUI Thread is not safe



bds
24th October 2016, 22:24
Hello all,

My program executes and works with sockets and all, although I get an error; QPixmap: It is not safe to use pixmaps outside the GUI thread. But the thing is that I'm not even using QPixmap? Also, can this be the problem that my update wont trigger the paintEvent?
Here is the code:


#include "mainwindow.h"
#include <QApplication>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstring>
#include <stdlib.h>
#include <vector>
#include <pthread.h>
#include <string>
#include <iostream>
#include <X11/Xlib.h>
#include "msg.h"
#define NUM_THREADS 1
using namespace std;

class ConnectionVar{
public:
int socket;
int seq;
int playerID;
int x = 0;
int y = 0;
};
class players{
public:
int playerID;
int x = 0;
int y = 0;
};

ConnectionVar var;

void sendInitMessage(){
int results;
var.seq = 0;
JoinMsg join;
join.head.id = 0;
join.head.seq_no = var.seq;
join.head.type = Join;
join.head.length = sizeof(join);

var.seq = var.seq + 1;
char sendBuffer[sizeof(join)];

memcpy((void*)sendBuffer, (void*)&join, sizeof(join));
results = send(var.socket, sendBuffer, sizeof(sendBuffer), 0);

}

void *recvieveMsg(void *arg){
int results;
MainWindow mw;
vector<players> pl;
char recBuffer[1024];
while(1){
results = recv(var.socket, recBuffer, sizeof(recBuffer), 0);
printf("Something recieved");
if(results > 0){
MsgHead* msghead;
msghead = (MsgHead*)recBuffer;
if(msghead->type == Join){
var.playerID = msghead->id;
printf("%d", var.playerID);
}else if(msghead->type == Leave){
printf("LEAVE!!\n");
}else if(msghead->type == Change){
ChangeMsg* changemsg;
changemsg = (ChangeMsg*)recBuffer;
if(changemsg->type == NewPlayer){
NewPlayerMsg* newPlayer;
newPlayer = (NewPlayerMsg*)recBuffer;
if(msghead->id != var.playerID){
players *player = new players();
player->playerID = msghead->id;
pl.push_back(*player);
}
}else if(changemsg->type == PlayerLeave){
printf("Player Leave");
}else if(changemsg->type == NewPlayerPosition){
NewPlayerPositionMsg* newPos;
newPos = (NewPlayerPositionMsg*)recBuffer;
if(msghead->id == var.playerID){
var.x = newPos->pos.x;
var.y = newPos->pos.y;
//mw.pos(1, 1);
}else{
for(int i=0;i<pl.size();i++){
if(pl.at(i).playerID == msghead->id){
pl.at(i).x = newPos->pos.x;
pl.at(i).y = newPos->pos.y;
}
}
}
cout << "Pos" << newPos->pos.x << " "<<newPos->pos.y <<endl;
}else{
printf("Change msg went wrong");
}
}else if(msghead->type == Event){
printf("Event!!\n");
}else if(msghead->type == TextMessage){
printf("TextMessage!!\n");
}else{
printf("What was that?");
}
}else if(results == 0){
printf("Connection closed!!!");
break;
}else{
printf("Something went wrong");
break;
}
fflush(stdout);
}
}

void sendMessage(char* s, MsgType type){
std::cout << s << endl;
if(type == Event){
MoveEvent ev;
ev.event.type = Move;
ev.event.head.id = var.playerID;
ev.event.head.seq_no = var.seq;
ev.event.head.type = Event;
if(strcmp(s, "Left") == 0){
ev.pos.x = var.x -1;
ev.pos.y = var.y;
}else if(strcmp(s, "Right") == 0){
ev.pos.x = var.x + 1;
ev.pos.y = var.y;
}else if(strcmp(s, "Up") == 0){
ev.pos.x = var.x;
ev.pos.y = var.y -1;
}else if(strcmp(s, "Down") == 0){
ev.pos.x = var.x;
ev.pos.y = var.y + 1;
}else{
std::cout << "Something wrong" << endl;
}
ev.event.head.length = sizeof(ev);

char sendBuffer[sizeof(ev)];
memcpy((void*)sendBuffer, (void*)&ev, sizeof(ev));

send(var.socket, sendBuffer, sizeof(sendBuffer), 0);
}

}



void connection(){
int socket, results;



struct addrinfo *serverInfo, hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

results = getaddrinfo("localhost", "49152", &hints, &serverInfo);
if(results != 0){
printf("getaddrinfo went wrong");
exit(1);
}

socket = ::socket(serverInfo->ai_family, serverInfo->ai_socktype, serverInfo->ai_protocol);
var.socket = socket;
results = connect(socket, serverInfo->ai_addr, serverInfo->ai_addrlen);
if(results == -1){
printf("Could not connect");
exit(1);
}
freeaddrinfo(serverInfo);

pthread_t threads[NUM_THREADS];
sendInitMessage();
results = pthread_create(&threads[0], NULL, recvieveMsg, NULL);

}

void startGame(){

connection();
}



int main(int argc, char *argv[])
{
XInitThreads();
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}




#include "mainwindow.h"
#include "msg.h"
#include "ui_mainwindow.h"
#include <QKeyEvent>
#include <QPainter>
#include <QWidget>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
gameStatus = false;
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::on_connect_clicked()
{
ui->connect->setEnabled(false);
ui->disconnect->setEnabled(true);
gameStatus = true;
startGame();
}

void MainWindow::keyPressEvent(QKeyEvent *event){
if(gameStatus){
switch(event->key()){
case Qt::Key_Left:
sendMessage("Left", Event);
break;
case Qt::Key_Right:
sendMessage("Right", Event);
break;
case Qt::Key_Up:
sendMessage("Up", Event);
break;
case Qt::Key_Down:
sendMessage("Down", Event);
break;
default:
event->ignore();
break;
}

}
}

void MainWindow::paintEvent(QPaintEvent *e){
printf("SDA");
fflush(stdout);
if(gameStatus){
QPainter painter(this);
painter.drawLine(115, 115, 5, 5);
}


}

void MainWindow::updateEvent(){
printf("SAJDIDAS");
fflush(stdout);
this->update();
}

void MainWindow::pos(int x, int y){
this->updateEvent();
}

/*
void MainWindow::on_disconnect_clicked()
{

}
*/


(the printf is just for me seeing that the function actually calling which it does when I'm moving the mouse over my button but not when updateEvent is called.)

Thanks for all help!

Lesiok
25th October 2016, 06:46
Problem is line 54 in function recvieveMsg(void *arg).

bds
25th October 2016, 08:50
Uhm okay, why is that a problem and how would you fix it?

anda_skoa
25th October 2016, 09:08
Uhm okay, why is that a problem and how would you fix it?

You are either accidentally calling recvieveMsg from a secondary thread or you are accidentally using UI objects like MainWindow in there.

Any specific reason you need to use native thread API and native socket API other than making your life more complicated?

Cheers,
_

bds
25th October 2016, 09:13
Well, our teacher thought it was funny to give us an assignment in C++ although the course is in Java. So we had a week on us learning C++ and I developed first in g++ terminal compiler before using native thread/socket API.

anda_skoa
25th October 2016, 10:52
Ah, then using Qt from the beginning would have been a lot easier.

I moved from Java to C++ because Qt gave me such a similar experience.

So you likely don't need any threads at all, Qt's sockets, unlike Java's, works nicely with the main event loop.

If you prefer keeping the thread, make sure you don't access any UI objects from secondary threads.

Cheers,
_

bds
25th October 2016, 11:32
Ah okay. But the thing is, how can I update the GUI in my thread without calling the GUI thread? Can I use some Observers, like in Java, or do you have any good way to go?

Lesiok
25th October 2016, 11:46
Read about signals and slots (http://doc.qt.io/qt-5.7/signalsandslots.html).

bds
25th October 2016, 12:08
I'm not really following signals and slots. So in my function *recvieveMessage(), and I want to connected it to MainWindow::pos, can I use connection this way; connection(recieveMessage(), x, MainWindow::pos, x);? Because I read that it has to be an object, and my recvieveMessage is not an object.

anda_skoa
25th October 2016, 16:35
QWidget::pos() is a "getter" so you unlikely want to call this, you probably meant "move".

QWidget::move() is not a slot, but of yours you could create one with matching arguments in your calls.

You can't use a function as a sender object, the type of the first argument of QObject::connect() is "QObject*", i.e. a pointer to a QObject.

You can, however, call a slot by name using QMetaObject::invokeMethod()


QMetaObject::invokeMethod(receiver, "slotname", Qt::QueuedConnection, ....);

Where "...." is any arguments you want to pass.

Cheers,
_