#include #include #include #include using namespace std::string_literals; bool executable_exists(std::string_view dir, std::string_view executable) { if (dir.size() + executable.size() + 1 >= PATH_MAX) { std::cerr << "Error: requested path longer than PATH_MAX" << std::endl; return false; } char path_buf[PATH_MAX]; auto ptr = std::copy(dir.begin(), dir.end(), path_buf); *ptr = '/'; ptr = std::copy(executable.begin(), executable.end(), ptr + 1); *ptr = '\0'; return access(path_buf, F_OK | X_OK) == 0; } auto file_filter_for_extension(std::string_view extension) { auto filter = Gtk::FileFilter::create(); auto pattern = "*."s; pattern += extension; filter->set_name(std::string(extension)); filter->add_pattern(pattern); return filter; } /** * Wrap string into a dynamically allocated c-string. * * @param str * @return */ char* wrap_string(std::string_view str) { if (str.empty()) { return nullptr; } auto wrapper = new char[str.size() + 1]; auto ptr = std::copy(str.begin(), str.end(), wrapper); *ptr = '\0'; return wrapper; } class Launcher : public Gtk::Window { public: Launcher(); ~Launcher() override = default; private: Gtk::Grid grid; Gtk::FileChooserButton fmriChooser; Gtk::FileChooserButton modelChooser; Gtk::FileChooserButton weightsChooser; Gtk::FileChooserButton labelChooser; Gtk::FileChooserButton meansChooser; Gtk::FileChooserButton inputChooser; Gtk::Button startButton; void start(); bool hasFile(const Gtk::FileChooserButton& fileChooser, const std::string& error); std::vector getInputFiles(); Gtk::Label* getManagedLabel(const std::string& contents); void findExecutable(); }; Launcher::Launcher() : Gtk::Window(), fmriChooser("Select FMRI executable"), modelChooser("Select caffe model prototxt"), weightsChooser("Select caffe model weights"), labelChooser("Select label text file"), meansChooser("Select means file"), inputChooser("Select input directory", Gtk::FileChooserAction::FILE_CHOOSER_ACTION_SELECT_FOLDER), startButton("Start FMRI") { set_size_request(400, -1); add(grid); grid.set_row_spacing(2); grid.set_column_spacing(2); findExecutable(); fmriChooser.set_hexpand(true); grid.attach(fmriChooser, 1, 0, 1, 1); grid.attach_next_to(*getManagedLabel("FMRI executable"), fmriChooser, Gtk::PositionType::POS_LEFT, 1, 1); grid.attach(modelChooser, 1, 1, 1, 1); modelChooser.add_filter(file_filter_for_extension("prototxt")); grid.attach_next_to(*getManagedLabel("Model"), modelChooser, Gtk::PositionType::POS_LEFT, 1, 1); grid.attach(weightsChooser, 1, 2, 1, 1); weightsChooser.add_filter(file_filter_for_extension("caffemodel")); grid.attach_next_to(*getManagedLabel("Weights"), weightsChooser, Gtk::PositionType::POS_LEFT, 1, 1); grid.attach(labelChooser, 1, 3, 1, 1); labelChooser.add_filter(file_filter_for_extension("txt")); grid.attach_next_to(*getManagedLabel("Labels (optional)"), labelChooser, Gtk::PositionType::POS_LEFT, 1, 1); grid.attach(inputChooser, 1, 4, 1, 1); grid.attach_next_to(*getManagedLabel("Input directory"), inputChooser, Gtk::PositionType::POS_LEFT, 1, 1); grid.attach(meansChooser, 1, 5, 1, 1); meansChooser.add_filter(file_filter_for_extension("binaryproto")); grid.attach_next_to(*getManagedLabel("Means (optional)"), meansChooser, Gtk::PositionType::POS_LEFT, 1, 1); startButton.signal_clicked().connect(sigc::mem_fun(*this, &Launcher::start)); grid.attach_next_to(startButton, Gtk::PositionType::POS_BOTTOM, 2, 1); show_all_children(true); } void Launcher::start() { if (!hasFile(fmriChooser, "Need executable") || !hasFile(modelChooser, "Need model file") || !hasFile(weightsChooser, "Need weights file") || !hasFile(inputChooser, "Need input folder")) { return; } auto executable = fmriChooser.get_file()->get_path(); auto network = modelChooser.get_file()->get_path(); auto weights = weightsChooser.get_file()->get_path(); auto inputs = getInputFiles(); if (inputs.empty()) { Gtk::MessageDialog dialog(*this, "No inputs in folder!"); dialog.run(); return; } std::vector argv = { wrap_string(executable), wrap_string("-n"), wrap_string(network), wrap_string("-w"), wrap_string(weights), }; if (labelChooser.get_file()) { argv.push_back(wrap_string("-l")); argv.push_back(wrap_string(labelChooser.get_file()->get_path())); } if (meansChooser.get_file()) { argv.push_back(wrap_string("-m")); argv.push_back(wrap_string(meansChooser.get_file()->get_path())); } std::transform(inputs.begin(), inputs.end(), std::back_inserter(argv), [](const auto& x) { return wrap_string(x); }); argv.push_back(nullptr); execv(executable.c_str(), argv.data()); } bool Launcher::hasFile(const Gtk::FileChooserButton& fileChooser, const std::string& error) { if (!fileChooser.get_file()) { Gtk::MessageDialog dialog(*this, error); dialog.run(); return false; } return true; } std::vector Launcher::getInputFiles() { using namespace boost::filesystem; std::vector result; auto folder = path(inputChooser.get_file()->get_path()); directory_iterator end_itr; for (directory_iterator itr(folder); itr!=end_itr; ++itr) { if (!is_regular(itr->status())) { continue; } result.push_back(itr->path().string()); } std::sort(result.begin(), result.end()); return result; } Gtk::Label* Launcher::getManagedLabel(const std::string& contents) { auto label = new Gtk::Label(contents); Gtk::manage(label); return label; } void Launcher::findExecutable() { if (executable_exists(".", "fmri")) { fmriChooser.set_filename("fmri"); return; } std::string_view env_path = std::getenv("PATH"); std::string_view::size_type offset = 0; while (offset != std::string_view::npos) { auto limit = env_path.find(":"); auto component = env_path.substr(offset, limit); if (limit != std::string_view::npos) { offset = limit + 1; } else { offset = limit; } if (!component.empty() && executable_exists(component, "fmri")) { fmriChooser.set_filename(std::string(component) + "/fmri"); return; } } } int main(int argc, char** argv) { auto app = Gtk::Application::create(argc, argv); Launcher launcher; return app->run(launcher); }