Skip to content

Commit be43489

Browse files
committed
fix signals/threads/connection problems
1 parent c651b82 commit be43489

File tree

7 files changed

+124
-167
lines changed

7 files changed

+124
-167
lines changed

database/tombll_manage_data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ def database_make_connection():
352352
(sqlite3.Connection): An open SQLite database connection.
353353
354354
"""
355-
return sqlite3.connect(os.path.dirname(os.path.abspath(__file__)) + '/tombll.db')
355+
return sqlite3.connect(os.path.dirname(os.path.abspath(__file__)) + '/tombll.db', timeout=10)
356356

357357

358358
def database_begin_write(con):

src/Controller.cpp

Lines changed: 52 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,112 +12,92 @@
1212
*/
1313

1414
#include "../src/Controller.hpp"
15-
#include <QDebug>
15+
#include <QMetaObject>
1616

17-
Controller::Controller() : controllerThread(new QThread()) {
18-
initializeThread();
19-
}
17+
Controller::Controller() {
18+
threadA.reset(new QThread());
19+
threadB.reset(new QThread());
20+
workerA.reset(new QObject());
21+
workerB.reset(new QObject());
22+
23+
workerA->moveToThread(threadA.data());
24+
workerB->moveToThread(threadB.data());
25+
26+
threadA->start();
27+
threadB->start();
2028

21-
Controller::~Controller() {
22-
controllerThread->quit();
23-
controllerThread->wait();
24-
}
25-
26-
void Controller::initializeThread() {
27-
this->moveToThread(controllerThread.data());
28-
connect(controllerThread.data(), &QThread::finished,
29-
controllerThread.data(), &QThread::deleteLater);
30-
controllerThread->start();
31-
32-
// Using the controller thread to start model work
33-
connect(this, &Controller::getCoverListThreadSignal,
34-
this, [this](QVector<ListItemData*>* items) {
35-
model.getCoverList(items);
36-
});
37-
38-
connect(this, &Controller::setupThreadSignal,
39-
this, [this](const QString& level, const QString& game) {
40-
model.setup(level, game);
41-
});
42-
43-
connect(this, &Controller::setupLevelThreadSignal,
44-
this, [this](int id) {
45-
model.getLevel(id);
46-
});
47-
48-
connect(this, &Controller::setupGameThreadSignal,
49-
this, [this](int id) {
50-
model.setupGame(id);
51-
});
52-
53-
connect(this, &Controller::updateLevelThreadSignal,
54-
this, [this](int id) {
55-
model.updateLevel(id);
56-
});
57-
58-
connect(this, &Controller::syncLevelsThreadSignal,
59-
this, [this]() {
60-
model.syncLevels();
61-
});
62-
63-
// Comming back from model or other model objects
64-
auto tickSignal = [this](){ emit controllerTickSignal(); };
6529
connect(&model, &Model::modelTickSignal,
66-
this, tickSignal, Qt::QueuedConnection);
30+
this, &Controller::controllerTickSignal,
31+
Qt::QueuedConnection);
6732

6833
connect(&fileManager, &FileManager::fileWorkTickSignal,
69-
this, tickSignal, Qt::QueuedConnection);
34+
this, &Controller::controllerTickSignal,
35+
Qt::QueuedConnection);
7036

7137
connect(&downloader, &Downloader::networkWorkTickSignal,
72-
this, tickSignal, Qt::QueuedConnection);
38+
this, &Controller::controllerTickSignal,
39+
Qt::QueuedConnection);
7340

7441
connect(&downloader, &Downloader::networkWorkErrorSignal,
75-
this, [this](int status) {
76-
emit controllerDownloadError(status);
77-
}, Qt::QueuedConnection);
42+
this, &Controller::controllerDownloadError,
43+
Qt::QueuedConnection);
7844

7945
connect(&model, &Model::generateListSignal,
80-
this, [this](const QList<int>& availableGames) {
81-
emit controllerGenerateList(availableGames);
82-
}, Qt::QueuedConnection);
46+
this, &Controller::controllerGenerateList,
47+
Qt::QueuedConnection);
8348

8449
connect(&model, &Model::modelReloadLevelListSignal,
85-
this, [this]() {
86-
emit controllerReloadLevelList();
87-
}, Qt::QueuedConnection);
50+
this, &Controller::controllerReloadLevelList,
51+
Qt::QueuedConnection);
8852

8953
connect(&model, &Model::modelLoadingDoneSignal,
90-
this, [this]() {
91-
emit controllerLoadingDone();
92-
}, Qt::QueuedConnection);
54+
this, &Controller::controllerLoadingDone,
55+
Qt::QueuedConnection);
9356
}
9457

95-
void Controller::setup(const QString& level, const QString& game) {
96-
emit setupThreadSignal(level, game);
58+
Controller::~Controller() {
59+
threadA->quit();
60+
threadB->quit();
61+
threadA->wait();
62+
threadB->wait();
9763
}
9864

99-
void Controller::getCoverList(QVector<ListItemData*>* items) {
100-
emit getCoverListThreadSignal(items);
65+
void Controller::runOnThreadA(std::function<void()> func) {
66+
QMetaObject::invokeMethod(
67+
workerA.data(), [func]() { func(); }, Qt::QueuedConnection);
68+
}
69+
70+
void Controller::runOnThreadB(std::function<void()> func) {
71+
QMetaObject::invokeMethod(
72+
workerB.data(), [func]() { func(); }, Qt::QueuedConnection);
73+
}
74+
75+
// Threaded work
76+
void Controller::setup(const QString& level, const QString& game) {
77+
runOnThreadA([=]() { model.setup(level, game); });
10178
}
10279

10380
void Controller::setupGame(int id) {
104-
emit setupGameThreadSignal(id);
81+
runOnThreadA([=]() { model.setupGame(id); });
10582
}
10683

10784
void Controller::setupLevel(int id) {
108-
emit setupLevelThreadSignal(id);
85+
runOnThreadA([=]() { model.getLevel(id); });
10986
}
11087

11188
void Controller::updateLevel(int id) {
112-
emit updateLevelThreadSignal(id);
89+
runOnThreadA([=]() { model.updateLevel(id); });
11390
}
11491

11592
void Controller::syncLevels() {
116-
emit syncLevelsThreadSignal();
93+
runOnThreadA([=]() { model.syncLevels(); });
11794
}
11895

96+
void Controller::getCoverList(QVector<ListItemData*>* items) {
97+
runOnThreadB([=]() { model.getCoverList(items); });
98+
}
11999

120-
// Using the GUI Threads
100+
// UI/main thread work
121101
int Controller::checkGameDirectory(int id) {
122102
return model.checkGameDirectory(id);
123103
}
@@ -141,6 +121,3 @@ bool Controller::link(int id) {
141121
int Controller::getItemState(int id) {
142122
return model.getItemState(id);
143123
}
144-
145-
146-

src/Controller.hpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,15 @@ class Controller : public QObject {
3131
return instance;
3232
}
3333

34-
int checkGameDirectory(int id);
35-
// void checkCommonFiles();
3634
void setup(const QString& level, const QString& game);
3735
void setupGame(int id);
3836
void setupLevel(int id);
3937
void updateLevel(int id);
4038
void syncLevels();
39+
void getCoverList(QVector<ListItemData*>* items);
4140

41+
int checkGameDirectory(int id);
4242
void getList(QVector<ListItemData>* list);
43-
void getCoverList(QVector<ListItemData*>* items);
4443
const InfoData getInfo(int id);
4544
const QString getWalkthrough(int id);
4645
bool link(int id);
@@ -53,23 +52,23 @@ class Controller : public QObject {
5352
void controllerReloadLevelList();
5453
void controllerLoadingDone();
5554

56-
void updateLevelThreadSignal(int id);
57-
void syncLevelsThreadSignal();
58-
void setupThreadSignal(const QString& level, const QString& game);
59-
void setupGameThreadSignal(int id);
60-
void setupLevelThreadSignal(int id);
61-
void getCoverListThreadSignal(QVector<ListItemData*>* items);
62-
6355
private:
6456
Controller();
65-
void initializeThread();
6657
~Controller();
58+
void initThreads();
59+
60+
void runOnThreadA(std::function<void()> func);
61+
void runOnThreadB(std::function<void()> func);
62+
63+
QScopedPointer<QThread> threadA;
64+
QScopedPointer<QThread> threadB;
65+
QScopedPointer<QObject> workerA;
66+
QScopedPointer<QObject> workerB;
6767

6868
Data& data = Data::getInstance();
6969
FileManager& fileManager = FileManager::getInstance();
7070
Model& model = Model::getInstance();
7171
Downloader& downloader = Downloader::getInstance();
72-
QScopedPointer<QThread> controllerThread;
7372

7473
Q_DISABLE_COPY(Controller)
7574
};

src/Model.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,12 @@ void Model::setupGame(int id) {
207207
}
208208

209209
void Model::updateLevel(const int id) {
210-
// qint64 status = m_pyRunner.updateLevel(id);
211-
(void)m_pyRunner.updateLevel(id);
210+
qint64 status = m_pyRunner.updateLevel(id);
212211
emit this->modelLoadingDoneSignal();
213212
}
214213

215214
void Model::syncLevels() {
216-
// qint64 status = m_pyRunner.syncCards();
217-
(void)m_pyRunner.syncCards();
215+
qint64 status = m_pyRunner.syncCards();
218216
emit this->modelLoadingDoneSignal();
219217
}
220218

src/PyRunner.cpp

Lines changed: 42 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -13,103 +13,76 @@
1313

1414
#include "../src/PyRunner.hpp"
1515
#include <QDebug>
16-
#include <QString>
17-
#include <QVector>
1816
#include <QFile>
19-
#include <cstdio>
20-
21-
#undef slots
22-
#undef signals
23-
#include <Python.h>
24-
PyThreadState *g_mainState = nullptr;
25-
26-
PyRunner::PyRunner() : m_status(0) {
27-
Py_Initialize();
28-
g_mainState = PyEval_SaveThread();
29-
}
30-
31-
PyRunner::PyRunner(const QString& cwd) : m_cwd(cwd), m_status(0) {
32-
Py_Initialize();
33-
g_mainState = PyEval_SaveThread();
34-
}
35-
36-
PyRunner::~PyRunner() {
37-
PyEval_RestoreThread(g_mainState);
38-
Py_FinalizeEx();
39-
}
17+
#include <QProcess>
4018

4119
bool PyRunner::setUpCamp(const QString& level) {
42-
bool status = true;
43-
if (!QFile::exists(level)) {
44-
status = false;
45-
}
20+
if (!QFile::exists(level)) return false;
4621
m_cwd = level;
47-
return status;
22+
m_isRunning = false;
23+
return true;
4824
}
4925

50-
void PyRunner::run(const QString& script,
51-
const QVector<QString>& args) {
52-
if (!Py_IsInitialized()) {
53-
qDebug() << "Python not initialized!";
54-
m_status = 1;
26+
void PyRunner::run(const QString& script, const QVector<QString>& args) {
27+
if (m_isRunning) {
28+
qWarning() << "[PyRunner] Already running!";
5529
return;
5630
}
31+
m_isRunning = true;
32+
QProcess process;
33+
QString program = "python3";
5734

58-
PyGILState_STATE gil = PyGILState_Ensure();
59-
qDebug() << "[PyRunner] Executing script file: " << script << Qt::endl;
35+
// Build full path to script if cwd is set
36+
QString fullScript =
37+
m_cwd.isEmpty() ? script : (m_cwd + "/" + script + ".py");
6038

61-
// Add working dir to sys.path
62-
if (!m_cwd.isEmpty()) {
63-
PyObject *sysPath = PySys_GetObject("path");
64-
PyObject *cwdPath = PyUnicode_FromString(m_cwd.toStdString().c_str());
65-
PyList_Insert(sysPath, 0, cwdPath);
66-
Py_DECREF(cwdPath);
39+
QStringList arguments;
40+
arguments << fullScript;
41+
for (const QString& arg : args) {
42+
arguments << arg;
6743
}
6844

69-
// Set sys.argv
70-
PyObject *argvList = PyList_New(args.size());
71-
for (size_t i = 0; i < args.size(); ++i) {
72-
PyObject *arg = PyUnicode_FromString(args[i].toStdString().c_str());
73-
PyList_SET_ITEM(argvList, i, arg); // steals ref
74-
}
75-
PySys_SetObject("argv", argvList);
76-
Py_DECREF(argvList);
45+
if (!m_cwd.isEmpty())
46+
process.setWorkingDirectory(m_cwd);
7747

78-
QString fullpath =
79-
m_cwd.isEmpty() ? script : (m_cwd + "/" + script + ".py");
80-
FILE* fp = fopen(fullpath.toStdString().c_str(), "r");
81-
if (!fp) {
82-
qDebug() << "[PyRunner] Failed to open script file: "
83-
<< fullpath << Qt::endl;
48+
process.setProcessChannelMode(QProcess::MergedChannels);
49+
50+
QObject::connect(&process, &QProcess::readyReadStandardOutput, [&]() {
51+
QByteArray output = process.readAllStandardOutput();
52+
qDebug().noquote() << QString::fromUtf8(output);
53+
});
54+
55+
qDebug() << "[PyRunner] Starting Python script:" << fullScript;
56+
process.start(program, arguments);
57+
58+
if (!process.waitForStarted()) {
59+
qWarning() << "[PyRunner] Failed to start Python process.";
8460
m_status = 1;
8561
return;
8662
}
8763

88-
int result = PyRun_SimpleFile(fp, fullpath.toStdString().c_str());
89-
fclose(fp);
90-
PyGILState_Release(gil);
91-
92-
if (result != 0) {
93-
qDebug() << "[PyRunner] Python script returned error.\n";
94-
m_status = 1;
95-
} else {
96-
qDebug() << "[PyRunner] Script executed successfully.\n";
97-
m_status = 0;
64+
while (process.state() != QProcess::NotRunning) {
65+
process.waitForReadyRead(50);
9866
}
67+
68+
int exitCode = process.exitCode();
69+
m_status = (exitCode == 0) ? 0 : 1;
70+
71+
qDebug() << "[PyRunner] Script finished with code:" << exitCode;
72+
73+
m_isRunning = false;
9974
}
10075

10176
qint64 PyRunner::updateLevel(qint64 lid) {
102-
run("tombll_manage_data",
103-
{"tombll_manage_data.py", "-u", QString("%1").arg(lid)});
77+
run("tombll_manage_data", {"-u", QString::number(lid)});
10478
return m_status;
10579
}
10680

10781
qint64 PyRunner::syncCards() {
108-
run("tombll_manage_data", {"tombll_manage_data.py", "-sc"});
82+
run("tombll_manage_data", {"-sc"});
10983
return m_status;
11084
}
11185

11286
qint64 PyRunner::getStatus() const {
11387
return m_status;
11488
}
115-

0 commit comments

Comments
 (0)