Skip to content

Commit 010eba5

Browse files
committed
Batch mode finally working. Also, fixed such that pipelines are now properly running in background threads, with and without projects, which means that the GUI is no longer freezing. Also, did some refactoring of the pipeline solution, and finalized the wrapper, which makes the final a lot cleaner and easier to read. Also, tested signal/slots for catching when inference is done, which works well. Also fixed a bug related to initial size of the RunForProject text boxes; they are now the same size.
1 parent 2ee7f3d commit 010eba5

File tree

2 files changed

+95
-98
lines changed

2 files changed

+95
-98
lines changed

source/MainWindow.cpp

Lines changed: 88 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,10 @@ void MainWindow::loadPipelines() {
452452
foreach(QString currentFpl, pipelines) {
453453
//runPipelineMenu->addAction(QString::fromStdString(splitCustom(currentFpl.toStdString(), ".")[0]), this, &MainWindow::lowresSegmenter);
454454
auto currentAction = runPipelineMenu->addAction(currentFpl); //QString::fromStdString(splitCustom(splitCustom(currentFpl.toStdString(), "/")[-1], ".")[0]));
455-
QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline, this, cwd + "data/Pipelines/" + currentFpl.toStdString()));
455+
QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline_wrapper, this, cwd + "data/Pipelines/" + currentFpl.toStdString()));
456456

457457
//auto currentAction = runPipelineMenu->addAction(QString::fromStdString(splitCustom(someFile, ".")[0]));
458-
//QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline, this, someFile));
458+
//QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline_wrapper, this, someFile));
459459
}
460460
}
461461

@@ -2394,7 +2394,7 @@ void MainWindow::runForProject() {
23942394
for (const auto& item : m_runForProjectWsis) {
23952395
selectedFilesWidget->addItem(QString::fromStdString(item));
23962396
}
2397-
selectedFilesWidget->setMinimumWidth(selectedFilesWidget->sizeHintForColumn(0));
2397+
selectedFilesWidget->setMinimumWidth(allFilesWidget->sizeHintForColumn(0)); // set to use allFilesWidget size such that width always match
23982398

23992399
wsiDialogLayout->addWidget(allFilesWidget);
24002400
wsiDialogLayout->addWidget(buttonsWidget);
@@ -2502,13 +2502,13 @@ void MainWindow::addPipelines() {
25022502
}
25032503
}
25042504
// should update runPipelineMenu as new pipelines are being added
2505-
//runPipelineMenu->addAction(QString::fromStdString(splitCustom(someFile, ".")[0]), this, &MainWindow::runPipeline);
2505+
//runPipelineMenu->addAction(QString::fromStdString(splitCustom(someFile, ".")[0]), this, &MainWindow::runPipeline_wrapper);
25062506
//auto currentAction = new QAction(QString::fromStdString(splitCustom(someFile, ".")[0]));
25072507
auto currentAction = runPipelineMenu->addAction(QString::fromStdString(splitCustom(someFile, ".")[0]));
2508-
QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline, this, someFile));
2508+
QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline_wrapper, this, someFile));
25092509

25102510
//auto currentAction = runPipelineMenu->addAction(currentFpl); //QString::fromStdString(splitCustom(splitCustom(currentFpl.toStdString(), "/")[-1], ".")[0]));
2511-
//QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline, this, cwd + "data/Pipelines/" + currentFpl.toStdString()));
2511+
//QObject::connect(currentAction, &QAction::triggered, std::bind(&MainWindow::runPipeline_wrapper, this, cwd + "data/Pipelines/" + currentFpl.toStdString()));
25122512
}
25132513
}
25142514

@@ -3097,127 +3097,121 @@ void MainWindow::runPipeline_wrapper(std::string path) {
30973097
currentWSIs = m_runForProjectWsis;
30983098
}
30993099
else {
3100-
currentWSIs.push_back(wsiList[curr_pos]);
3100+
currentWSIs.push_back(filename);
31013101
}
31023102

31033103
auto progDialog = QProgressDialog(mWidget);
31043104
progDialog.setRange(0, (int)currentWSIs.size() - 1);
3105-
//progDialog.setContentsMargins(0, 0, 0, 0);
31063105
progDialog.setVisible(true);
31073106
progDialog.setModal(false);
31083107
progDialog.setLabelText("Running pipeline across WSIs in project...");
3109-
//QRect screenrect = mWidget->screen()[0].geometry();
31103108
progDialog.move(mWidget->width() - progDialog.width() * 1.1, progDialog.height() * 0.1);
31113109
progDialog.show();
31123110

31133111
QCoreApplication::processEvents(QEventLoop::AllEvents, 0);
31143112

3115-
auto counter = 0;
3116-
for (const auto& currWSI : currentWSIs) {
3117-
3118-
// if run for project is enabled, run the inference-export pipeline in a background thread, else don't
3119-
if (m_runForProject) {
3120-
std::atomic_bool stopped(false);
3121-
std::thread inferenceThread([&, path]() {
3122-
runPipeline(path);
3123-
});
3124-
inferenceThread.detach();
3113+
// always run pipeline in background thread
3114+
std::atomic_bool stopped(false);
3115+
std::thread inferenceThread([&, path, currentWSIs]() {
3116+
auto counter = 0;
3117+
for (const auto& currWSI : currentWSIs) {
3118+
runPipeline(path, currWSI, counter);
31253119
}
3126-
else {
3127-
runPipeline(path);
3120+
});
3121+
inferenceThread.detach();
31283122

3129-
// now make it possible to edit prediction in the View Widget
3130-
// createDynamicViewWidget(modelMetadata["name"], someModelName);
3131-
}
3132-
}
3123+
// now make it possible to edit prediction in the View Widget
3124+
// createDynamicViewWidget(modelMetadata["name"], someModelName);
31333125
}
31343126

3135-
void MainWindow::runPipeline(std::string path) {
3127+
void MainWindow::runPipeline(std::string path, std::string currWSI, int counter) {
31363128

3137-
std::vector<std::string> currentWSIs;
3138-
if (m_runForProject) {
3139-
currentWSIs = m_runForProjectWsis;
3140-
}
3141-
else {
3142-
currentWSIs.push_back(filename);
3143-
}
3129+
// setup paths for saving results
3130+
QString wsiResultPath = (projectFolderName.toStdString() + "/results/" + splitCustom(splitCustom(currWSI, "/").back(), ".")[0]).c_str();
3131+
wsiResultPath = wsiResultPath.replace("//", "/");
31443132

3145-
auto progDialog = QProgressDialog(mWidget);
3146-
progDialog.setRange(0, (int)currentWSIs.size() - 1);
3147-
progDialog.setVisible(true);
3148-
progDialog.setModal(false);
3149-
progDialog.setLabelText("Running pipeline across WSIs in project...");
3150-
progDialog.move(mWidget->width() - progDialog.width() * 1.1, progDialog.height() * 0.1);
3151-
progDialog.show();
3133+
// if folder does not exist, create one
3134+
if (!QDir(wsiResultPath).exists()) {
3135+
QDir().mkdir(wsiResultPath);
3136+
}
31523137

3153-
QCoreApplication::processEvents(QEventLoop::AllEvents, 0);
3138+
// TODO: This now always saves result as TIFF, which is not correct. Need generic way of knowing which results that are exported and how to save these
3139+
auto currPath = wsiResultPath.toStdString() + "/" + splitCustom(wsiResultPath.toStdString(), "/").back() + ".tiff";
31543140

3155-
auto counter = 0;
3156-
for (const auto& currWSI : currentWSIs) {
3141+
// TODO: Perhaps use corresponding .txt-file to feed arguments in the pipeline
3142+
// pipeline requires some user-defined inputs, e.g. which WSI to use (and which model?)
3143+
std::map<std::string, std::string> arguments;
3144+
arguments["filename"] = currWSI;
3145+
arguments["exportPath"] = currPath;
3146+
//arguments["modelPath"] = path;
31573147

3158-
// setup paths for saving results
3159-
std::cout << "Current save location: " << projectFolderName.toStdString() << std::endl;
3160-
QString wsiResultPath = (projectFolderName.toStdString() + "/results/" + splitCustom(splitCustom(currWSI, "/").back(), ".")[0]).c_str();
3161-
wsiResultPath = wsiResultPath.replace("//", "/");
3148+
// parse fpl-file, and run pipeline with corresponding input arguments
3149+
auto pipeline = Pipeline(path, arguments);
3150+
if (m_runForProject) {
3151+
pipeline.parse({}, false);
3152+
}
3153+
else {
3154+
pipeline.parse();
3155+
}
31623156

3163-
// if folder does not exist, create one
3164-
if (!QDir(wsiResultPath).exists()) {
3165-
QDir().mkdir(wsiResultPath);
3157+
// get and start running POs
3158+
for (auto&& po : pipeline.getProcessObjects()) {
3159+
if (po.second->getNrOfOutputPorts() == 0 && std::dynamic_pointer_cast<Renderer>(po.second) == nullptr) {
3160+
// Process object has no output ports, must add to window to make sure it is updated.
3161+
reportInfo() << "Process object " << po.first << " had no output ports defined in pipeline, therefore adding to window so it is updated." << reportEnd();
3162+
addProcessObject(po.second);
31663163
}
3164+
}
31673165

3168-
// TODO: This now always saves result as TIFF, which is not correct. Need generic way of knowing which results that are exported and how to save these
3169-
auto currPath = wsiResultPath.toStdString() + "/" + splitCustom(wsiResultPath.toStdString(), "/").back() + ".tiff";
3166+
if (!m_runForProject) {
3167+
// load renderers, if any
3168+
for (const auto& renderer : pipeline.getRenderers()) {
3169+
auto currId = createRandomNumbers_(8);
3170+
insertRenderer("result_" + currId, renderer);
3171+
createDynamicViewWidget("result_" + currId, "result_" + currId);
3172+
}
3173+
}
31703174

3171-
// TODO: Perhaps use corresponding .txt-file to feed arguments in the pipeline
3172-
// pipeline requires some user-defined inputs, e.g. which WSI to use (and which model?)
3173-
std::map<std::string, std::string> arguments;
3174-
arguments["filename"] = filename;
3175-
arguments["exportPath"] = currPath;
3176-
//arguments["modelPath"] = path;
3175+
if (m_runForProject) {
3176+
// connect signal to update flag
3177+
connect(this, &MainWindow::currentPipelineFinished, this, &MainWindow::_nextPipeline);
3178+
3179+
auto data = pipeline.getAllPipelineOutputData([&, this](float progress) {
3180+
// this never runs? Why?
3181+
std::cout << "Progress: " << 100 * progress << "%" << std::endl;
3182+
if (int(progress) == 1) { // run until finished
3183+
std::cout << "PIPELINE IS DONE --- STOP SIGNAL WAS EMITTED!" << std::endl;
3184+
emit currentPipelineFinished();
3185+
}
3186+
});
3187+
std::cout << "Done" << std::endl;
31773188

3178-
// parse fpl-file, and run pipeline with corresponding input arguments
3179-
auto pipeline = Pipeline(path, arguments);
3180-
if (m_runForProject) {
3181-
pipeline.parse({}, false);
3182-
}
3183-
else {
3184-
pipeline.parse();
3185-
}
3189+
// test if signal/slot stuff works -> WORKS!
3190+
/*
3191+
std::cout << "Current stop flag value: " << m_pipelineStopped << std::endl;
3192+
emit currentPipelineFinished();
3193+
std::cout << "After emit: " << m_pipelineStopped << std::endl;
3194+
*/
31863195

3187-
if (m_runForProject) {
3188-
auto data = pipeline.getAllPipelineOutputData([](float progress) {
3189-
std::cout << "Progress: " << 100 * progress << "%" << std::endl;
3190-
});
3191-
std::cout << "Done" << std::endl;
3192-
}
3196+
//while (!m_pipelineStopped) { QCoreApplication::processEvents(QEventLoop::AllEvents, 0); }; // perhaps need to force update if I do this? Why doesn't it update with this? Does it interact with the mainthread somehow?
31933197

3194-
// get and start running POs
3195-
for (auto&& po : pipeline.getProcessObjects()) {
3196-
if (po.second->getNrOfOutputPorts() == 0 && std::dynamic_pointer_cast<Renderer>(po.second) == nullptr) {
3197-
// Process object has no output ports, must add to window to make sure it is updated.
3198-
reportInfo() << "Process object " << po.first << " had no output ports defined in pipeline, therefore adding to window so it is updated." << reportEnd();
3199-
addProcessObject(po.second);
3200-
}
3201-
}
3198+
m_pipelineStopped = false; // reset when done
3199+
}
32023200

3203-
if (!m_runForProject) {
3204-
// load renderers, if any
3205-
for (const auto& renderer : pipeline.getRenderers()) {
3206-
auto currId = createRandomNumbers_(8);
3207-
insertRenderer("result_" + currId, renderer);
3208-
createDynamicViewWidget("result_" + currId, "result_" + currId);
3209-
}
3210-
}
3201+
// update progress bar
3202+
//progDialog.setValue(counter); // cannot update this in another thread -> should add some signal/slot stuff for this
3203+
counter++;
32113204

3212-
// update progress bar
3213-
progDialog.setValue(counter);
3214-
counter++;
3205+
// to render straight away (avoid waiting on all WSIs to be handled before rendering)
3206+
QCoreApplication::processEvents(QEventLoop::AllEvents, 0);
3207+
}
32153208

3216-
// to render straight away (avoid waiting on all WSIs to be handled before rendering)
3217-
QCoreApplication::processEvents(QEventLoop::AllEvents, 0);
3218-
}
3209+
void MainWindow::_nextPipeline() {
3210+
std::cout << "---FINALLY Done" << std::endl;
3211+
m_pipelineStopped = true;
32193212
}
32203213

3214+
32213215
// Setting parameters for different methods
32223216
std::map<std::string, std::string> MainWindow::setParameterDialog(std::map<std::string, std::string> modelMetadata, int *successFlag) {
32233217
QDialog paramDialog;

source/MainWindow.hpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class MainWindow : public Window {
5555
bool advancedMode = false;
5656
bool m_wsiSharpening = false;
5757
bool m_doneFirstWSI = false;
58+
bool m_pipelineStopped = false;
5859

5960
int curr_pos = 0;
6061
float magn_lvl;
@@ -342,9 +343,12 @@ class MainWindow : public Window {
342343
bool segmentTissue();
343344
/**
344345
* Deploys a selected pipeline, through parsing the text pipeline script and creating FAST POs before execution.
346+
* Inputs the path of the FPL file, the path of the current WSIs, and the current pipeline in a list
345347
* @param path
348+
* @param currWSI
349+
* @param counter
346350
*/
347-
void runPipeline(std::string path);
351+
void runPipeline(std::string path, std::string currWSI, int counter);
348352
/**
349353
* Simple wrapper of the alternative inference method, runPipeline. If ran with Projects enabled, it will be ran in a
350354
* non-blocking background thread and will not render any results. Otherwise the rendered results will be
@@ -532,11 +536,10 @@ class MainWindow : public Window {
532536
std::shared_ptr<Image> m_tumorMap;
533537

534538
signals:
535-
void inferenceFinished(std::string name);
539+
void currentPipelineFinished();
536540

537541
private slots:
538542
void updateChannelValue(int index);
539-
543+
void _nextPipeline();
540544
};
541-
542545
}

0 commit comments

Comments
 (0)