diff --git a/Applications/shapeworks/Commands.cpp b/Applications/shapeworks/Commands.cpp index 0dd129f7e6..90a8e320cd 100644 --- a/Applications/shapeworks/Commands.cpp +++ b/Applications/shapeworks/Commands.cpp @@ -41,6 +41,32 @@ bool Example::execute(const optparse::Values &options, SharedCommandData &shared } #endif +static void setup_callbacks(bool show_progress, bool xml_status) { + if (show_progress) { + auto progress_callback = [](double progress, std::string message) { + // show status message and percentage complete + std::cout << fmt::format("{} ({:.1f}%) \r", message, progress); + std::cout.flush(); + }; + Logging::Instance().set_progress_callback(progress_callback); + } + + if (xml_status) { + auto progress_callback = [](double progress, std::string message) { + // print status message and percentage complete + std::cout << fmt::format("{}{:.1f}\n", message, progress); + std::cout.flush(); + }; + Logging::Instance().set_progress_callback(progress_callback); + + auto error_callback = [](std::string message) { + std::cout << fmt::format("{}\n", message); + std::cout.flush(); + }; + Logging::Instance().set_error_callback(error_callback); + } +} + /////////////////////////////////////////////////////////////////////////////// // Seed /////////////////////////////////////////////////////////////////////////////// @@ -75,12 +101,16 @@ void OptimizeCommand::buildParser() { parser.prog(prog).description(desc); parser.add_option("--name").action("store").type("string").set_default("").help("Path to project file."); + parser.add_option("--progress").action("store_true").set_default(false).help("Show progress [default: false]."); + parser.add_option("--xmlconsole").action("store_true").set_default(false).help("XML console output [default: false]."); Command::buildParser(); } bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData& sharedData) { const std::string& projectFile(static_cast(options.get("name"))); + bool show_progress = static_cast(options.get("progress")); + bool xml_status = static_cast(options.get("xmlconsole")); if (projectFile.length() == 0) { std::cerr << "Must specify project name with --name \n"; @@ -90,6 +120,8 @@ bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData bool isProject = StringUtils::hasSuffix(projectFile, "xlsx") || StringUtils::hasSuffix(projectFile, "swproj"); Optimize app; + setup_callbacks(show_progress, xml_status); + if (isProject) { try { // load spreadsheet project @@ -117,7 +149,7 @@ bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData return success; } catch (std::exception& e) { - std::cerr << "Error: " << e.what() << "\n"; + SW_ERROR(e.what()); return false; } } else { @@ -136,18 +168,24 @@ void GroomCommand::buildParser() { parser.prog(prog).description(desc); parser.add_option("--name").action("store").type("string").set_default("").help("Path to project file."); + parser.add_option("--progress").action("store_true").set_default(false).help("Show progress [default: false]."); + parser.add_option("--xmlconsole").action("store_true").set_default(false).help("XML console output [default: false]."); Command::buildParser(); } bool GroomCommand::execute(const optparse::Values& options, SharedCommandData& sharedData) { const std::string& projectFile(static_cast(options.get("name"))); + bool show_progress = static_cast(options.get("progress")); + bool xml_status = static_cast(options.get("xmlconsole")); if (projectFile.length() == 0) { std::cerr << "Must specify project name with --name \n"; return false; } + setup_callbacks(show_progress, xml_status); + try { ProjectHandle project = std::make_shared(); project->load(projectFile); @@ -168,7 +206,7 @@ bool GroomCommand::execute(const optparse::Values& options, SharedCommandData& s } return success; } catch (std::exception& e) { - std::cerr << "Error: " << e.what() << "\n"; + SW_ERROR(e.what()); return false; } } diff --git a/Libs/Analyze/MeshGenerator.cpp b/Libs/Analyze/MeshGenerator.cpp index 1c78007b79..fcc2cd5fd8 100644 --- a/Libs/Analyze/MeshGenerator.cpp +++ b/Libs/Analyze/MeshGenerator.cpp @@ -84,6 +84,8 @@ MeshHandle MeshGenerator::build_mesh_from_points(const Eigen::VectorXd& shape, i if (!poly_data) { std::string message = "Unable to warp mesh"; SW_ERROR(message); + poly_data = vtkSmartPointer::New(); + mesh->set_poly_data(poly_data); mesh->set_error_message(message); return mesh; } @@ -122,9 +124,9 @@ MeshHandle MeshGenerator::build_mesh_from_image(ImageType::Pointer image, float mesh->set_poly_data(Mesh(marching->GetOutput()).clean().computeNormals().getVTKMesh()); } catch (itk::ExceptionObject& excep) { - std::cerr << "Exception caught!" << std::endl; - std::cerr << excep << std::endl; - mesh->set_error_message(std::string("Exception: ") + excep.what()); + auto message = std::string("Exception: ") + excep.what(); + SW_ERROR(message); + mesh->set_error_message(message); } return mesh; } diff --git a/Libs/Analyze/MeshManager.h b/Libs/Analyze/MeshManager.h index 6353deac08..12345bcc6a 100644 --- a/Libs/Analyze/MeshManager.h +++ b/Libs/Analyze/MeshManager.h @@ -76,7 +76,7 @@ class MeshManager : public QObject { void error_encountered(QString message); void progress(int); - void status(QString); + void status(std::string); private: std::shared_ptr reconstructors_ = std::make_shared(); diff --git a/Libs/Common/Logging.cpp b/Libs/Common/Logging.cpp index 878f62ef9f..082c76c4f5 100644 --- a/Libs/Common/Logging.cpp +++ b/Libs/Common/Logging.cpp @@ -10,9 +10,9 @@ namespace spd = spdlog; namespace shapeworks { //----------------------------------------------------------------------------- -static std::string create_header(const int line, const char *filename) { - const char *name = (strrchr(filename, '/') ? strrchr(filename, '/') + 1 : filename); - const char *name2 = (strrchr(name, '\\') ? strrchr(name, '\\') + 1 : name); +static std::string create_header(const int line, const char* filename) { + const char* name = (strrchr(filename, '/') ? strrchr(filename, '/') + 1 : filename); + const char* name2 = (strrchr(name, '\\') ? strrchr(name, '\\') + 1 : name); std::string header = "[" + std::string(name2) + "|" + std::to_string(line) + "]"; return header; } @@ -53,7 +53,7 @@ bool Logging::check_log_open() const { return log_open_; } std::string Logging::get_log_filename() const { return log_filename_; } //----------------------------------------------------------------------------- -void Logging::log_message(const std::string& message, const int line, const char *file) const { +void Logging::log_message(const std::string& message, const int line, const char* file) const { spd::info(message); if (log_open_) { spd::get("file")->info(message); @@ -72,7 +72,7 @@ void Logging::log_stack(const std::string& message) const { } //----------------------------------------------------------------------------- -void Logging::log_error(const std::string& message, const int line, const char *file) const { +void Logging::log_error(const std::string& message, const int line, const char* file) const { spd::error(message); if (log_open_) { spd::get("file")->error(message); @@ -83,7 +83,7 @@ void Logging::log_error(const std::string& message, const int line, const char * } //----------------------------------------------------------------------------- -void Logging::show_message(const std::string& message, const int line, const char *file) const { +void Logging::show_message(const std::string& message, const int line, const char* file) const { log_message(message, line, file); if (message_callback_) { message_callback_(message); @@ -91,15 +91,14 @@ void Logging::show_message(const std::string& message, const int line, const cha } //----------------------------------------------------------------------------- -void Logging::show_status(const std::string& message, const int line, const char *file) const { - log_message(message, line, file); - if (message_callback_) { - message_callback_(message); +void Logging::show_status(const std::string& message, const int line, const char* file) const { + if (status_callback_) { + status_callback_(message); } } //----------------------------------------------------------------------------- -void Logging::log_debug(const std::string& message, const int line, const char *file) const { +void Logging::log_debug(const std::string& message, const int line, const char* file) const { std::string str = create_header(line, file) + " " + message; spd::debug(str); if (log_open_) { @@ -113,7 +112,7 @@ void Logging::log_debug(const std::string& message, const int line, const char * } //----------------------------------------------------------------------------- -void Logging::log_warning(const std::string& message, const int line, const char *file) const { +void Logging::log_warning(const std::string& message, const int line, const char* file) const { spd::warn(message); if (log_open_) { spd::get("file")->warn(message); @@ -123,6 +122,13 @@ void Logging::log_warning(const std::string& message, const int line, const char } } +//----------------------------------------------------------------------------- +void Logging::show_progress(double value, const std::string& message) { + if (progress_callback_) { + progress_callback_(value, message); + } +} + //----------------------------------------------------------------------------- void Logging::close_log() { if (!log_open_) { @@ -145,4 +151,10 @@ void Logging::set_warning_callback(const std::function& callb //----------------------------------------------------------------------------- void Logging::set_debug_callback(const std::function& callback) { debug_callback_ = callback; } +//----------------------------------------------------------------------------- +void Logging::set_status_callback(const std::function& callback) { status_callback_ = callback; } + +//----------------------------------------------------------------------------- +void Logging::set_progress_callback(const std::function& callback) { progress_callback_ = callback; } + } // namespace shapeworks diff --git a/Libs/Common/Logging.h b/Libs/Common/Logging.h index 6de6cace24..3b69f80f07 100644 --- a/Libs/Common/Logging.h +++ b/Libs/Common/Logging.h @@ -2,9 +2,8 @@ #include -#include - #include +#include template <> struct fmt::formatter { @@ -102,6 +101,9 @@ class Logging { //! Log a message, use SW_STATUS macro void show_status(const std::string& message, const int line, const char* file) const; + //! Display progress (0-100) + void show_progress(double value, const std::string& message); + //! Log a debug message, use SW_DEBUG macro void log_debug(const std::string& message, const int line, const char* file) const; @@ -120,9 +122,15 @@ class Logging { //! Set a warning callback function to be called whenever a warning is posted void set_warning_callback(const std::function& callback); - //! Set a debug messagecallback function to be called whenever a debug message is posted + //! Set a debug message callback function to be called whenever a debug message is posted void set_debug_callback(const std::function& callback); + //! Set a status callback function to be called whenever a status message is posted + void set_status_callback(const std::function& callback); + + //! Set a progress callback function to be called whenever a progress update is posted + void set_progress_callback(const std::function& callback); + private: //! Constructor Logging(); @@ -137,6 +145,10 @@ class Logging { std::function warning_callback_; std::function debug_callback_; + + std::function status_callback_; + + std::function progress_callback_; }; //! Log stack macro @@ -169,7 +181,19 @@ class Logging { #define SW_STATUS(message, ...) \ shapeworks::Logging::Instance().show_status(fmt::format(message, ##__VA_ARGS__), __LINE__, __FILE__) +#define SW_PROGRESS(value, message, ...) \ + shapeworks::Logging::Instance().show_progress(value, fmt::format(message, ##__VA_ARGS__)); + //! Close session macro #define SW_CLOSE_LOG() shapeworks::Logging::Instance().close_log(); +//! Log once macro, will only log the message once +#define SW_LOG_ONCE(message, ...) \ +{ \ + static bool logged = false; \ + if (!logged) { \ + SW_LOG(message, ##__VA_ARGS__); \ + logged = true; \ + } \ +} } // namespace shapeworks diff --git a/Libs/Groom/Groom.cpp b/Libs/Groom/Groom.cpp index 165124c8b7..42f95cf6f1 100644 --- a/Libs/Groom/Groom.cpp +++ b/Libs/Groom/Groom.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -470,7 +471,7 @@ void Groom::increment_progress(int amount) { std::scoped_lock lock(mutex); this->progress_counter_ += amount; this->progress_ = static_cast(this->progress_counter_) / static_cast(this->total_ops_) * 100.0; - this->update_progress(); + SW_PROGRESS(progress_, fmt::format("Grooming ({}/{} ops)", progress_counter_, total_ops_)); } //--------------------------------------------------------------------------- diff --git a/Libs/Groom/Groom.h b/Libs/Groom/Groom.h index 082dfddc2f..5451fbd24f 100644 --- a/Libs/Groom/Groom.h +++ b/Libs/Groom/Groom.h @@ -33,8 +33,6 @@ class Groom { vtkSmartPointer target); protected: - //! call to be overridden by subclasses - virtual void update_progress(){}; std::atomic progress_ = 0; std::atomic total_ops_ = 0; diff --git a/Libs/Mesh/MeshWarper.cpp b/Libs/Mesh/MeshWarper.cpp index 1f39647d26..2abab322ea 100644 --- a/Libs/Mesh/MeshWarper.cpp +++ b/Libs/Mesh/MeshWarper.cpp @@ -45,7 +45,7 @@ vtkSmartPointer MeshWarper::build_mesh(const Eigen::MatrixXd& parti double* p = poly_data->GetPoint(i); if (std::isnan(p[0]) || std::isnan(p[1]) || std::isnan(p[2])) { this->warp_available_ = false; // failed - std::cerr << "Reconstruction Failed\n"; + SW_ERROR("Reconstruction failed. NaN detected in mesh."); return nullptr; } } @@ -438,7 +438,7 @@ bool MeshWarper::generate_warp() { Eigen::MatrixXd vertices = referenceMesh.points(); this->faces_ = referenceMesh.faces(); - // perform warp + // generate the warp if (!MeshWarper::generate_warp_matrix(vertices, this->faces_, this->vertices_, this->warp_)) { this->update_progress(1.0); this->warp_available_ = false; @@ -473,7 +473,7 @@ bool MeshWarper::generate_warp_matrix(Eigen::MatrixXd TV, Eigen::MatrixXi TF, co // faster and looks OK const int k = 2; if (!igl::biharmonic_coordinates(TV, TF, S, k, W)) { - std::cerr << "igl:biharmonic_coordinates failed\n"; + SW_ERROR("Mesh Warp Error: igl:biharmonic_coordinates failed"); return false; } // Throw away interior tet-vertices, keep weights and indices of boundary diff --git a/Libs/Optimize/Constraints.cpp b/Libs/Optimize/Constraints.cpp index d7348d910a..a1143523f0 100644 --- a/Libs/Optimize/Constraints.cpp +++ b/Libs/Optimize/Constraints.cpp @@ -777,7 +777,6 @@ bool Constraints::hasConstraints() { return true; } - SW_LOG("no constraints"); return false; } diff --git a/Libs/Optimize/Optimize.cpp b/Libs/Optimize/Optimize.cpp index 9fb3a14c1f..892099a7f5 100644 --- a/Libs/Optimize/Optimize.cpp +++ b/Libs/Optimize/Optimize.cpp @@ -28,14 +28,13 @@ #include "Optimize.h" #include "OptimizeParameterFile.h" #include "OptimizeParameters.h" +#include "ParticleGoodBadAssessment.h" #include "ParticleImageDomain.h" #include "ParticleImplicitSurfaceDomain.h" #include "VtkMeshWrapper.h" #include "object_reader.h" #include "object_writer.h" -#include "ParticleGoodBadAssessment.h" - // pybind #include #include @@ -67,6 +66,9 @@ Optimize::Optimize() { this->m_sampler = std::make_shared(); } //--------------------------------------------------------------------------- bool Optimize::Run() { + m_start_time = std::chrono::system_clock::now(); + m_last_update_time = m_start_time; + // control number of threads int num_threads = tbb::info::default_concurrency(); const char* num_threads_env = getenv("TBB_NUM_THREADS"); @@ -193,11 +195,6 @@ bool Optimize::Run() { //--------------------------------------------------------------------------- int Optimize::SetParameters() { - if (this->m_verbosity_level == 0) { - std::cout << "Verbosity 0: This will be the only output on your screen, " - "unless there are any errors. Increase the verbosity if needed.\n"; - } - // sanity check if (m_domains_per_shape != m_number_of_particles.size()) { SW_ERROR("Inconsistency in parameters... m_domains_per_shape != m_number_of_particles.size()"); @@ -967,6 +964,7 @@ void Optimize::IterateCallback(itk::Object*, const itk::EventObject&) { } } } + UpdateProgress(); } //--------------------------------------------------------------------------- @@ -2164,6 +2162,7 @@ void Optimize::SetSharedBoundaryEnabled(bool enabled) { m_sampler->SetSharedBoun //--------------------------------------------------------------------------- void Optimize::SetSharedBoundaryWeight(double weight) { m_sampler->SetSharedBoundaryWeight(weight); } +//--------------------------------------------------------------------------- void Optimize::ComputeTotalIterations() { total_particle_iterations_ = 0; @@ -2201,4 +2200,47 @@ void Optimize::ComputeTotalIterations() { } } +//--------------------------------------------------------------------------- +void Optimize::UpdateProgress() { + auto now = std::chrono::system_clock::now(); + + if ((now - m_last_update_time) > std::chrono::milliseconds(100)) { + m_last_update_time = now; + std::chrono::duration elapsed_seconds = + std::chrono::duration_cast(now - m_start_time); + double seconds_per_iteration = elapsed_seconds.count() / current_particle_iterations_; + double seconds_remaining = seconds_per_iteration * (total_particle_iterations_ - current_particle_iterations_); + int hours = static_cast(seconds_remaining / 3600); + int minutes = static_cast((seconds_remaining - (hours * 3600)) / 60); + int seconds = static_cast(seconds_remaining - (hours * 3600) - (minutes * 60)); + + std::string message; + if (m_optimizing) { + message = "Optimizing"; + } else { + message = "Initializing"; + } + + int stage_num_iterations = m_sampler->GetOptimizer()->GetNumberOfIterations(); + int stage_total_iterations = m_sampler->GetOptimizer()->GetMaximumNumberOfIterations(); + int num_particles = m_sampler->GetParticleSystem()->GetNumberOfParticles(0); + + message = fmt::format("{} : Particles: {}, Iteration: {} / {}", message, num_particles, stage_num_iterations, + stage_total_iterations); + + if ((now - m_last_remaining_update_time) > std::chrono::seconds(1)) { + m_remaining_time_message = fmt::format("({:02d}:{:02d}:{:02d} remaining)", hours, minutes, seconds); + m_last_remaining_update_time = now; + } + + // only show the time remaining if it's been more than 3 seconds + if (elapsed_seconds > std::chrono::seconds(3)) { + message = fmt::format("{} {}", message, m_remaining_time_message); + } + + double progress = + static_cast(current_particle_iterations_) * 100 / static_cast(total_particle_iterations_); + SW_PROGRESS(progress, message); + } +} } // namespace shapeworks diff --git a/Libs/Optimize/Optimize.h b/Libs/Optimize/Optimize.h index 3551eab98f..47976a97ba 100644 --- a/Libs/Optimize/Optimize.h +++ b/Libs/Optimize/Optimize.h @@ -302,6 +302,8 @@ class Optimize { //! transform a point if necessary vnl_vector_fixed TransformPoint(int domain, vnl_vector_fixed input); + void UpdateProgress(); + protected: //! Set the iteration callback. Derived classes should override to set their own callback virtual void SetIterationCallback(); @@ -460,6 +462,12 @@ class Optimize { shapeworks::OptimizationVisualizer visualizer_; std::shared_ptr project_; + + std::chrono::system_clock::time_point m_start_time; + std::chrono::system_clock::time_point m_last_update_time; + std::chrono::system_clock::time_point m_last_remaining_update_time; + std::string m_remaining_time_message; + }; } // namespace shapeworks diff --git a/Libs/Project/ProjectUtils.cpp b/Libs/Project/ProjectUtils.cpp index 4a672d3295..a6beef55b9 100644 --- a/Libs/Project/ProjectUtils.cpp +++ b/Libs/Project/ProjectUtils.cpp @@ -140,7 +140,7 @@ StringList ProjectUtils::get_values(StringList prefixes, StringList domain_names for (auto& [key, value] : key_map) { for (const auto& prefix : prefixes) { if (key == prefix + domain) { - values.push_back(value); + values.push_back(StringUtils::replace_string(value, "\\", "/")); } } } diff --git a/Studio/Analysis/AnalysisTool.cpp b/Studio/Analysis/AnalysisTool.cpp index 73b004a686..2cf773093a 100644 --- a/Studio/Analysis/AnalysisTool.cpp +++ b/Studio/Analysis/AnalysisTool.cpp @@ -920,17 +920,19 @@ void AnalysisTool::enable_actions(bool newly_enabled) { update_domain_alignment_box(); } - auto domain_types = session_->get_groomed_domain_types(); - bool image_domain = domain_types.size() > 0 && domain_types[0] == DomainType::Image; - ui_->distance_transfom_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present() && - image_domain); + if (session_->particles_present()) { + auto domain_types = session_->get_groomed_domain_types(); + bool image_domain = domain_types.size() > 0 && domain_types[0] == DomainType::Image; + ui_->distance_transfom_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present() && + image_domain); - ui_->mesh_warping_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present()); + ui_->mesh_warping_radio_button->setEnabled(session_->particles_present() && session_->get_groomed_present()); - if (!ui_->mesh_warping_radio_button->isEnabled()) { - ui_->legacy_radio_button->setChecked(true); + if (!ui_->mesh_warping_radio_button->isEnabled()) { + ui_->legacy_radio_button->setChecked(true); + } + reconstruction_method_changed(); } - reconstruction_method_changed(); update_group_boxes(); ui_->sampleSpinBox->setMaximum(session_->get_num_shapes() - 1); diff --git a/Studio/Analysis/AnalysisTool.ui b/Studio/Analysis/AnalysisTool.ui index 6976368958..7eaad8b4bd 100644 --- a/Studio/Analysis/AnalysisTool.ui +++ b/Studio/Analysis/AnalysisTool.ui @@ -535,7 +535,7 @@ QWidget#particles_panel { - 2 + 0 @@ -603,7 +603,7 @@ QWidget#particles_panel { - + Group slider @@ -1059,16 +1059,10 @@ QWidget#particles_panel { - + false - - - 100 - 0 - - Standard deviation slider @@ -2190,6 +2184,11 @@ Reference Domain
jkqtplotter/jkqtplotter.h
1 + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
diff --git a/Studio/CMakeLists.txt b/Studio/CMakeLists.txt index f586adb731..56b3c99924 100644 --- a/Studio/CMakeLists.txt +++ b/Studio/CMakeLists.txt @@ -115,11 +115,9 @@ SET(STUDIO_JOB_MOC_HDRS SET(STUDIO_GROOM_SRCS Groom/GroomTool.cpp - Groom/QGroom.cpp ) SET(STUDIO_GROOM_MOC_HDRS Groom/GroomTool.h - Groom/QGroom.h ) SET(STUDIO_PYTHON_SRCS @@ -171,6 +169,7 @@ SET(STUDIO_UTILS_MOC_HDRS SET(STUDIO_INTERFACE_SRCS Interface/CompareWidget.cpp + Interface/CustomSlider.cpp Interface/ExportImageDialog.cpp Interface/KeyboardShortcuts.cpp Interface/LogWindow.cpp @@ -184,6 +183,7 @@ SET(STUDIO_INTERFACE_SRCS ) SET(STUDIO_INTERFACE_MOC_HDRS Interface/CompareWidget.h + Interface/CustomSlider.h Interface/ExportImageDialog.h Interface/KeyboardShortcuts.h Interface/LogWindow.h diff --git a/Studio/Data/DataTool.cpp b/Studio/Data/DataTool.cpp index 020e971c58..ed89cc1550 100644 --- a/Studio/Data/DataTool.cpp +++ b/Studio/Data/DataTool.cpp @@ -109,7 +109,7 @@ void DataTool::set_session(QSharedPointer session) { connect(session.data(), &Session::ffc_changed, this, &DataTool::update_ffc_table); landmark_table_model_->set_session(session); - connect(ui_->ffc_brush_size_, &QSlider::valueChanged, session.data(), &Session::set_ffc_paint_size); + connect(ui_->ffc_brush_size_, &CustomSlider::valueChanged, session.data(), &Session::set_ffc_paint_size); connect(ui_->ffc_included_mode_, &QRadioButton::toggled, session.data(), &Session::set_ffc_paint_mode_inclusive); connect(ui_->ffc_active_, &QCheckBox::toggled, session.data(), &Session::set_ffc_paint_active); diff --git a/Studio/Data/DataTool.ui b/Studio/Data/DataTool.ui index 3b20dd691a..eaed9088c7 100644 --- a/Studio/Data/DataTool.ui +++ b/Studio/Data/DataTool.ui @@ -381,8 +381,8 @@ QWidget#specificity_panel { 0 0 - 470 - 340 + 464 + 330 @@ -848,7 +848,7 @@ QWidget#specificity_panel {
- + 5 @@ -1053,7 +1053,14 @@ QWidget#specificity_panel { - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -1078,6 +1085,13 @@ QWidget#specificity_panel { + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
diff --git a/Studio/Data/PreferencesWindow.cpp b/Studio/Data/PreferencesWindow.cpp index 3d49ea4c33..48be5ad709 100644 --- a/Studio/Data/PreferencesWindow.cpp +++ b/Studio/Data/PreferencesWindow.cpp @@ -46,7 +46,7 @@ PreferencesWindow::PreferencesWindow(QWidget* parent, Preferences& prefs) : pref &PreferencesWindow::save_to_preferences); connect(ui_->orientation_marker_corner, qOverload(&QComboBox::currentIndexChanged), this, &PreferencesWindow::save_to_preferences); - connect(ui_->geodesic_cache_multiplier, &QSlider::valueChanged, this, &PreferencesWindow::save_to_preferences); + connect(ui_->geodesic_cache_multiplier, &CustomSlider::valueChanged, this, &PreferencesWindow::save_to_preferences); connect(ui_->color_map, qOverload(&QComboBox::currentIndexChanged), this, &PreferencesWindow::save_to_preferences); connect(ui_->particle_colors, qOverload(&QComboBox::currentIndexChanged), this, diff --git a/Studio/Data/PreferencesWindow.ui b/Studio/Data/PreferencesWindow.ui index 99781038d7..16259c0e9c 100644 --- a/Studio/Data/PreferencesWindow.ui +++ b/Studio/Data/PreferencesWindow.ui @@ -107,7 +107,7 @@ - + 0 @@ -200,7 +200,7 @@ - + 100 @@ -240,7 +240,7 @@ - + 1 @@ -587,6 +587,13 @@ + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
pca_range pca_steps diff --git a/Studio/Data/ShapeWorksWorker.cpp b/Studio/Data/ShapeWorksWorker.cpp index 991d9118b6..8e02e6e415 100644 --- a/Studio/Data/ShapeWorksWorker.cpp +++ b/Studio/Data/ShapeWorksWorker.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -10,7 +10,7 @@ namespace shapeworks { //--------------------------------------------------------------------------- -ShapeworksWorker::ShapeworksWorker(ThreadType type, QSharedPointer groom, QSharedPointer optimize, +ShapeworksWorker::ShapeworksWorker(ThreadType type, QSharedPointer groom, QSharedPointer optimize, QSharedPointer optimize_parameters, QSharedPointer session, double maxAngle, float decimationPercent, int numClusters) diff --git a/Studio/Data/ShapeWorksWorker.h b/Studio/Data/ShapeWorksWorker.h index adf9caa19a..dbf170a583 100644 --- a/Studio/Data/ShapeWorksWorker.h +++ b/Studio/Data/ShapeWorksWorker.h @@ -6,7 +6,7 @@ namespace shapeworks { class Optimize; -class QGroom; +class Groom; class OptimizeParameters; class ShapeworksWorker : public QObject { @@ -18,7 +18,7 @@ Q_OBJECT }; ShapeworksWorker(ThreadType type, - QSharedPointer groom, + QSharedPointer groom, QSharedPointer optimize, QSharedPointer optimize_parameters, QSharedPointer session, @@ -37,7 +37,7 @@ public Q_SLOTS: private: - QSharedPointer groom_; + QSharedPointer groom_; QSharedPointer optimize_; QSharedPointer optimize_parameters_; QSharedPointer session_; diff --git a/Studio/Data/Telemetry.cpp b/Studio/Data/Telemetry.cpp index 8639d4d4d3..34138a4879 100644 --- a/Studio/Data/Telemetry.cpp +++ b/Studio/Data/Telemetry.cpp @@ -40,13 +40,13 @@ void Telemetry::record_event(const QString& name, const QVariantMap& params) { QString api_secret{GA_API_SECRET}; if (measurement_id.isEmpty() || api_secret.isEmpty()) { - SW_LOG("Telemetry disabled, no measurement id or api secret"); + SW_LOG_ONCE("Telemetry disabled, no measurement id or api secret"); enabled_ = false; return; } if (!prefs_.get_telemetry_enabled()) { - SW_LOG("Telemetry disabled by preferences"); + SW_LOG_ONCE("Telemetry disabled by preferences"); enabled_ = false; return; } diff --git a/Studio/Groom/GroomTool.cpp b/Studio/Groom/GroomTool.cpp index 95d445dc2f..a4b51d8a6c 100644 --- a/Studio/Groom/GroomTool.cpp +++ b/Studio/Groom/GroomTool.cpp @@ -73,13 +73,13 @@ GroomTool::GroomTool(Preferences& prefs, Telemetry& telemetry) : preferences_(pr "Set the adaptivity of remeshing, higher will allocate more triangles around areas of high curvature."); // connect percent controls - connect(ui_->remesh_percent_slider, &QSlider::valueChanged, this, + connect(ui_->remesh_percent_slider, &CustomSlider::valueChanged, this, [this](int value) { ui_->remesh_percent_spinbox->setValue(value); }); connect(ui_->remesh_percent_spinbox, qOverload(&QDoubleSpinBox::valueChanged), this, [=](double value) { ui_->remesh_percent_slider->setValue(static_cast(value)); }); // connect gradation controls - connect(ui_->remesh_gradation_slider, &QSlider::valueChanged, this, + connect(ui_->remesh_gradation_slider, &CustomSlider::valueChanged, this, [=](int value) { ui_->remesh_gradation_spinbox->setValue(value / 50.0); }); connect(ui_->remesh_gradation_spinbox, qOverload(&QDoubleSpinBox::valueChanged), this, [=](double value) { ui_->remesh_gradation_slider->setValue(static_cast(value * 50.0)); }); @@ -490,7 +490,7 @@ void GroomTool::on_run_groom_button_clicked() { SW_LOG("Please wait: running groom step..."); Q_EMIT progress(0); - groom_ = QSharedPointer(new QGroom(session_->get_project())); + groom_ = QSharedPointer(new Groom(session_->get_project())); enable_actions(); @@ -500,7 +500,6 @@ void GroomTool::on_run_groom_button_clicked() { worker->moveToThread(thread); connect(thread, SIGNAL(started()), worker, SLOT(process())); connect(worker, &ShapeworksWorker::finished, this, &GroomTool::handle_thread_complete); - connect(groom_.data(), &QGroom::progress, this, &GroomTool::handle_progress); connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); thread->start(); @@ -561,7 +560,7 @@ void GroomTool::skip_grooming_toggled() { store_params(); if (ui_->skip_grooming->isChecked()) { - groom_ = QSharedPointer(new QGroom(session_->get_project())); + groom_ = QSharedPointer(new Groom(session_->get_project())); groom_->run(); SW_MESSAGE("Skipped Grooming"); Q_EMIT groom_complete(); diff --git a/Studio/Groom/GroomTool.h b/Studio/Groom/GroomTool.h index ee325da021..6dfc1512ac 100644 --- a/Studio/Groom/GroomTool.h +++ b/Studio/Groom/GroomTool.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -97,7 +97,7 @@ class GroomTool : public QWidget { Ui_GroomTool* ui_; QSharedPointer session_; - QSharedPointer groom_; + QSharedPointer groom_; QElapsedTimer timer_; diff --git a/Studio/Groom/GroomTool.ui b/Studio/Groom/GroomTool.ui index 747718b6f6..30880f04eb 100644 --- a/Studio/Groom/GroomTool.ui +++ b/Studio/Groom/GroomTool.ui @@ -1607,7 +1607,7 @@ QWidget#domain_panel { - + 100 @@ -1620,7 +1620,7 @@ QWidget#domain_panel { - + 100 @@ -2013,6 +2013,13 @@ QWidget#domain_panel { + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
mesh_fill_holes mesh_smooth diff --git a/Studio/Groom/QGroom.cpp b/Studio/Groom/QGroom.cpp deleted file mode 100644 index c7ad229335..0000000000 --- a/Studio/Groom/QGroom.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "QGroom.h" - -namespace shapeworks { - -//--------------------------------------------------------------------------- -void QGroom::update_progress() -{ - Q_EMIT progress(static_cast(this->progress_)); -} - -//--------------------------------------------------------------------------- -QGroom::QGroom(ProjectHandle project) : Groom(project) -{ - -} -} \ No newline at end of file diff --git a/Studio/Groom/QGroom.h b/Studio/Groom/QGroom.h deleted file mode 100644 index 2105ad11de..0000000000 --- a/Studio/Groom/QGroom.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -#ifndef Q_MOC_RUN -#include -#endif - -namespace shapeworks { - -//! Qt Wrapper for Groom -/*! - * The QGroom class wraps the Groom class to provide a QObject with a progress signal - * - */ -class QGroom : public QObject, public Groom { - -Q_OBJECT; - -public: - - QGroom(ProjectHandle project); - -protected: - // override update_progress to emit q_signal - void update_progress(); - -Q_SIGNALS: - void progress(int); - -}; -} diff --git a/Studio/Interface/CompareWidget.cpp b/Studio/Interface/CompareWidget.cpp index a771dde412..f900d03f4c 100644 --- a/Studio/Interface/CompareWidget.cpp +++ b/Studio/Interface/CompareWidget.cpp @@ -12,7 +12,7 @@ CompareWidget::CompareWidget(QWidget *parent) : QWidget(parent), ui_(new Ui::Com connect(ui_->original, &QCheckBox::toggled, this, &CompareWidget::settings_changed); connect(ui_->groomed, &QCheckBox::toggled, this, &CompareWidget::settings_changed); connect(ui_->reconstructed, &QCheckBox::toggled, this, &CompareWidget::settings_changed); - connect(ui_->opacity, &QSlider::valueChanged, this, &CompareWidget::settings_changed); + connect(ui_->opacity, &CustomSlider::valueChanged, this, &CompareWidget::settings_changed); connect(ui_->mean_shape, &QCheckBox::toggled, this, &CompareWidget::settings_changed); } diff --git a/Studio/Interface/CompareWidget.ui b/Studio/Interface/CompareWidget.ui index ad60f1aa2d..e14b272c06 100644 --- a/Studio/Interface/CompareWidget.ui +++ b/Studio/Interface/CompareWidget.ui @@ -7,7 +7,7 @@ 0 0 470 - 159 + 172 @@ -15,7 +15,7 @@ - + 100 @@ -74,6 +74,13 @@ + + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
+
diff --git a/Studio/Interface/CustomSlider.cpp b/Studio/Interface/CustomSlider.cpp new file mode 100644 index 0000000000..49a00c0955 --- /dev/null +++ b/Studio/Interface/CustomSlider.cpp @@ -0,0 +1,71 @@ + + +#include "CustomSlider.h" + +#include +#include +#include +#include +#include + +#include "math.h" + +CustomSlider::CustomSlider(QWidget* parent) : QSlider(parent) { + this->setStyleSheet( + "\ + QSlider {\ + min-height: 24px\ + }\ + QSlider::groove:horizontal {\ + border: 1px solid #262626;\ + height: 3px;\ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4);\ + margin: 0 5px;\ + }\ + \ + QSlider::handle:horizontal {\ + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);\ + border: 1px solid #5c5c5c;\ + width: 12px;\ + margin: -8px -6px;\ + border-radius: 3px;\ + }\ + "); +}; + +void CustomSlider::paintEvent(QPaintEvent* ev) { + QStylePainter p(this); + QStyleOptionSlider opt; + initStyleOption(&opt); + + QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + + // draw tick marks + // do this manually because they are very badly behaved with style sheets + int interval = tickInterval(); + if (interval == 0) { + interval = pageStep(); + } + + if (tickPosition() != NoTicks) { + for (int i = minimum(); i <= maximum(); i += interval) { + int x = + std::round((double)((double)((double)(i - this->minimum()) / (double)(this->maximum() - this->minimum())) * + (double)(this->width() - handle.width()) + + (double)(handle.width() / 2.0))) - + 1; + int h = 4; + p.setPen(QColor("#a5a294")); + if (tickPosition() == TicksBothSides || tickPosition() == TicksAbove) { + int y = this->rect().top(); + p.drawLine(x, y, x, y + h); + } + if (tickPosition() == TicksBothSides || tickPosition() == TicksBelow) { + int y = this->rect().bottom(); + p.drawLine(x, y, x, y - h); + } + } + } + + QSlider::paintEvent(ev); +} diff --git a/Studio/Interface/CustomSlider.h b/Studio/Interface/CustomSlider.h new file mode 100644 index 0000000000..2c76041cbe --- /dev/null +++ b/Studio/Interface/CustomSlider.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +// adapted from: +// https://stackoverflow.com/questions/69890284/qslider-in-qt-misbehaves-in-new-macos-monterey-v12-0-1-any-workaround/69890285#69890285 +class CustomSlider : public QSlider { + public: + explicit CustomSlider(Qt::Orientation orientation, QWidget* parent = nullptr) : QSlider(orientation, parent){}; + explicit CustomSlider(QWidget* parent = nullptr); + + protected: + virtual void paintEvent(QPaintEvent* ev); +}; \ No newline at end of file diff --git a/Studio/Interface/ShapeWorksStudioApp.cpp b/Studio/Interface/ShapeWorksStudioApp.cpp index 9acec6b0b0..7336b5fb23 100644 --- a/Studio/Interface/ShapeWorksStudioApp.cpp +++ b/Studio/Interface/ShapeWorksStudioApp.cpp @@ -76,6 +76,8 @@ ShapeWorksStudioApp::ShapeWorksStudioApp() { connect(&logger_, &StudioLogger::error, this, &ShapeWorksStudioApp::handle_error); connect(&logger_, &StudioLogger::warning, this, &ShapeWorksStudioApp::handle_warning); connect(&logger_, &StudioLogger::debug, this, &ShapeWorksStudioApp::handle_debug); + connect(&logger_, &StudioLogger::status, this, &ShapeWorksStudioApp::handle_status); + connect(&logger_, &StudioLogger::progress, this, &ShapeWorksStudioApp::handle_progress_with_message); // default hide ui_->feature_widget->hide(); @@ -655,9 +657,10 @@ void ShapeWorksStudioApp::handle_message(std::string str) { } //--------------------------------------------------------------------------- -void ShapeWorksStudioApp::handle_status(QString str) { - status_bar_->set_message(MessageType::normal, str); - current_message_ = str; +void ShapeWorksStudioApp::handle_status(std::string str) { + auto qstr = QString::fromStdString(str); + status_bar_->set_message(MessageType::normal, qstr); + current_message_ = qstr; } //--------------------------------------------------------------------------- @@ -690,10 +693,15 @@ void ShapeWorksStudioApp::message_callback(std::string str) { // QMetaObject::invokeMethod(this,) } +//--------------------------------------------------------------------------- +void ShapeWorksStudioApp::handle_progress_with_message(int value, std::string str) { + handle_progress(value); + handle_status(str); +} + //--------------------------------------------------------------------------- void ShapeWorksStudioApp::handle_progress(int value) { status_bar_->set_progress(value); - handle_message(current_message_.toStdString()); } //--------------------------------------------------------------------------- @@ -721,7 +729,7 @@ void ShapeWorksStudioApp::create_glyph_submenu() { layout->addWidget(glyph_size_label_, 0, 1, 1, 1); layout->addWidget(glyph_quality_label_, 1, 1, 1, 1); - glyph_size_slider_ = new QSlider(widget); + glyph_size_slider_ = new CustomSlider(widget); glyph_size_slider_->setOrientation(Qt::Horizontal); glyph_size_slider_->setMinimum(1); glyph_size_slider_->setMaximum(100); @@ -734,7 +742,7 @@ void ShapeWorksStudioApp::create_glyph_submenu() { glyph_arrow_scale_ = new QCheckBox("Scale arrows"); - glyph_quality_slider_ = new QSlider(widget); + glyph_quality_slider_ = new CustomSlider(widget); glyph_quality_slider_->setMinimum(1); glyph_quality_slider_->setMaximum(20); glyph_quality_slider_->setPageStep(3); @@ -758,8 +766,8 @@ void ShapeWorksStudioApp::create_glyph_submenu() { glyph_quality_label_->setText(QString::number(preferences_.get_glyph_quality())); glyph_size_label_->setText(QString::number(preferences_.get_glyph_size())); - connect(glyph_size_slider_, &QSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); - connect(glyph_quality_slider_, &QSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); + connect(glyph_size_slider_, &CustomSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); + connect(glyph_quality_slider_, &CustomSlider::valueChanged, this, &ShapeWorksStudioApp::handle_glyph_changed); connect(glyph_auto_size_, &QCheckBox::clicked, this, &ShapeWorksStudioApp::handle_glyph_changed); connect(glyph_arrow_scale_, &QCheckBox::clicked, this, &ShapeWorksStudioApp::handle_glyph_changed); @@ -821,7 +829,7 @@ void ShapeWorksStudioApp::create_iso_submenu() { QLabel* size_label = new QLabel(text); layout->addWidget(size_label, row, 0, 1, 1); - QSlider* slider = new QSlider(widget); + CustomSlider* slider = new CustomSlider(widget); slider->setOrientation(Qt::Horizontal); slider->setMinimum(1); slider->setMaximum(100); @@ -830,7 +838,7 @@ void ShapeWorksStudioApp::create_iso_submenu() { slider->setTickInterval(10); slider->setValue(100); slider->setMinimumWidth(200); - connect(slider, &QSlider::valueChanged, this, &ShapeWorksStudioApp::handle_opacity_changed); + connect(slider, &CustomSlider::valueChanged, this, &ShapeWorksStudioApp::handle_opacity_changed); layout->addWidget(slider, row, 1, 1, 1); widget->setLayout(layout); diff --git a/Studio/Interface/ShapeWorksStudioApp.h b/Studio/Interface/ShapeWorksStudioApp.h index 649174cc4e..07663bde63 100644 --- a/Studio/Interface/ShapeWorksStudioApp.h +++ b/Studio/Interface/ShapeWorksStudioApp.h @@ -20,9 +20,9 @@ #include #include #include -#include #include #include +#include // Forward Qt class declarations class Ui_ShapeWorksStudioApp; @@ -129,11 +129,11 @@ class ShapeWorksStudioApp : public QMainWindow { void handle_error(std::string str); void handle_warning(std::string str); void handle_debug(std::string str); + void handle_status(std::string str); + void handle_progress_with_message(int amt, std::string str); + void handle_progress(int amt); void message_callback(std::string str); - - void handle_status(QString str); - void handle_progress(int amt); void handle_new_mesh(); void handle_clear_cache(); void handle_compare_settings_changed(); @@ -239,8 +239,8 @@ class ShapeWorksStudioApp : public QMainWindow { QSharedPointer wheel_event_forwarder_; // programmatic UI elements - QSlider* glyph_size_slider_; - QSlider* glyph_quality_slider_; + CustomSlider* glyph_size_slider_; + CustomSlider* glyph_quality_slider_; QLabel* glyph_size_label_; QLabel* glyph_quality_label_; QCheckBox* glyph_auto_size_; @@ -250,7 +250,7 @@ class ShapeWorksStudioApp : public QMainWindow { QPointer status_bar_; QSharedPointer splash_screen_; QErrorMessage error_message_dialog_; - std::vector iso_opacity_sliders_; + std::vector iso_opacity_sliders_; std::vector domain_particle_checkboxes_; QString current_message_; diff --git a/Studio/Interface/ShapeWorksStudioApp.ui b/Studio/Interface/ShapeWorksStudioApp.ui index bb411e0475..4d824ebc34 100644 --- a/Studio/Interface/ShapeWorksStudioApp.ui +++ b/Studio/Interface/ShapeWorksStudioApp.ui @@ -368,7 +368,7 @@ QToolBar QToolButton::checked { - + 100 @@ -1215,6 +1215,11 @@ QToolBar QToolButton::checked { + + CustomSlider + QSlider +
Interface/CustomSlider.h
+
QVTKOpenGLNativeWidget QWidget diff --git a/Studio/Interface/StudioLogger.cpp b/Studio/Interface/StudioLogger.cpp index e22e4ec143..5e2af2c108 100644 --- a/Studio/Interface/StudioLogger.cpp +++ b/Studio/Interface/StudioLogger.cpp @@ -14,16 +14,22 @@ void StudioLogger::register_callbacks() { Logging::Instance().set_warning_callback(warning_callback); auto debug_callback = std::bind(&StudioLogger::handle_debug, this, std::placeholders::_1); Logging::Instance().set_debug_callback(debug_callback); + auto status_callback = std::bind(&StudioLogger::handle_status, this, std::placeholders::_1); + Logging::Instance().set_status_callback(status_callback); + auto progress_callback = + std::bind(&StudioLogger::handle_progress, this, std::placeholders::_1, std::placeholders::_2); + Logging::Instance().set_progress_callback(progress_callback); } //--------------------------------------------------------------------------- void StudioLogger::handle_message(std::string str) { Q_EMIT message(str); } - //--------------------------------------------------------------------------- void StudioLogger::handle_error(std::string str) { Q_EMIT error(str); } - //--------------------------------------------------------------------------- void StudioLogger::handle_warning(std::string str) { Q_EMIT warning(str); } - //--------------------------------------------------------------------------- void StudioLogger::handle_debug(std::string str) { Q_EMIT debug(str); } +//--------------------------------------------------------------------------- +void StudioLogger::handle_status(std::string str) { Q_EMIT status(str); } +//--------------------------------------------------------------------------- +void StudioLogger::handle_progress(double value, std::string str) { Q_EMIT progress(static_cast(value), str); } diff --git a/Studio/Interface/StudioLogger.h b/Studio/Interface/StudioLogger.h index ba2e0d3df6..bc477204b4 100644 --- a/Studio/Interface/StudioLogger.h +++ b/Studio/Interface/StudioLogger.h @@ -17,10 +17,14 @@ class StudioLogger : public QObject { void handle_error(std::string str); void handle_warning(std::string str); void handle_debug(std::string str); + void handle_status(std::string str); + void handle_progress(double value, std::string str); Q_SIGNALS: void message(std::string str); void error(std::string str); void warning(std::string str); void debug(std::string str); + void status(std::string str); + void progress(int value, std::string str); }; diff --git a/Studio/Interface/UpdateChecker.cpp b/Studio/Interface/UpdateChecker.cpp index 84fc426038..41d4e9008d 100644 --- a/Studio/Interface/UpdateChecker.cpp +++ b/Studio/Interface/UpdateChecker.cpp @@ -58,48 +58,53 @@ void UpdateChecker::handleNetworkReply(QNetworkReply* reply) { std::string response = QString(reply->readAll()).toStdString(); // get the json response - auto j = json::parse(response); - - std::string platform = StudioUtils::get_platform_string().toStdString(); - - int major = j[platform]["major"].get(); - int minor = j[platform]["minor"].get(); - int patch = j[platform]["patch"].get(); - QString message = QString::fromStdString(j[platform]["message"].get()); - - bool update_available = false; - if (major > SHAPEWORKS_MAJOR_VERSION) { - update_available = true; - } else if (major == SHAPEWORKS_MAJOR_VERSION && minor > SHAPEWORKS_MINOR_VERSION) { - update_available = true; - } else if (major == SHAPEWORKS_MAJOR_VERSION && minor == SHAPEWORKS_MINOR_VERSION && - patch > SHAPEWORKS_PATCH_VERSION) { - update_available = true; - } + try { + auto j = json::parse(response); + std::string platform = StudioUtils::get_platform_string().toStdString(); + + int major = j[platform]["major"].get(); + int minor = j[platform]["minor"].get(); + int patch = j[platform]["patch"].get(); + QString message = QString::fromStdString(j[platform]["message"].get()); + + bool update_available = false; + if (major > SHAPEWORKS_MAJOR_VERSION) { + update_available = true; + } else if (major == SHAPEWORKS_MAJOR_VERSION && minor > SHAPEWORKS_MINOR_VERSION) { + update_available = true; + } else if (major == SHAPEWORKS_MAJOR_VERSION && minor == SHAPEWORKS_MINOR_VERSION && + patch > SHAPEWORKS_PATCH_VERSION) { + update_available = true; + } - if (update_available) { - auto url = QString("https://github.com/SCIInstitute/ShapeWorks/releases/latest"); - - auto title = QString("New version available"); - setWindowTitle(title); - ui_->label->setTextFormat(Qt::RichText); - ui_->label->setOpenExternalLinks(true); - ui_->label->setTextInteractionFlags(Qt::TextBrowserInteraction); - ui_->label->setText(QString("A new version of ShapeWorks is available.

" - "To download the latest version, please visit:

%1

" + - message) - .arg(url)); - - show(); - raise(); - - } else { - if (manual_trigger_) { - // show a messagebox saying there is no update - auto title = QString("No update available"); - auto info = QString("You are running the latest version of ShapeWorks."); - QMessageBox::information(nullptr, title, info, QMessageBox::Ok); + if (update_available) { + auto url = QString("https://github.com/SCIInstitute/ShapeWorks/releases/latest"); + + auto title = QString("New version available"); + setWindowTitle(title); + ui_->label->setTextFormat(Qt::RichText); + ui_->label->setOpenExternalLinks(true); + ui_->label->setTextInteractionFlags(Qt::TextBrowserInteraction); + ui_->label->setText(QString("A new version of ShapeWorks is available.

" + "To download the latest version, please visit:

%1

" + + message) + .arg(url)); + + show(); + raise(); + + } else { + if (manual_trigger_) { + // show a messagebox saying there is no update + auto title = QString("No update available"); + auto info = QString("You are running the latest version of ShapeWorks."); + QMessageBox::information(nullptr, title, info, QMessageBox::Ok); + } } + + } catch (json::exception& e) { + SW_LOG("Unable to check for updates: " + std::string(e.what())); + return; } } } // namespace shapeworks \ No newline at end of file diff --git a/Studio/Interface/UpdateChecker.h b/Studio/Interface/UpdateChecker.h index 2fd6042411..0393efe353 100644 --- a/Studio/Interface/UpdateChecker.h +++ b/Studio/Interface/UpdateChecker.h @@ -1,10 +1,10 @@ #pragma once +#include + #include #include -#include - namespace Ui { class UpdateChecker; } @@ -25,7 +25,6 @@ class UpdateChecker : public QDialog { void run_auto_update_check(); void run_manual_update_check(); - public Q_SLOTS: void handleNetworkReply(QNetworkReply* reply); @@ -38,7 +37,6 @@ class UpdateChecker : public QDialog { Ui::UpdateChecker* ui_; Preferences& prefs_; - }; } // namespace shapeworks \ No newline at end of file diff --git a/Studio/Optimize/OptimizeTool.cpp b/Studio/Optimize/OptimizeTool.cpp index 8a5e3fb164..7cac360c53 100644 --- a/Studio/Optimize/OptimizeTool.cpp +++ b/Studio/Optimize/OptimizeTool.cpp @@ -113,8 +113,8 @@ void OptimizeTool::handle_progress(int val, QString progress_message) { return; } - Q_EMIT progress(val); - Q_EMIT status(progress_message); + //Q_EMIT progress(val); + //Q_EMIT status(progress_message.toStdString()); auto particles = optimize_->GetParticles(); session_->update_particles(particles); diff --git a/Studio/Optimize/OptimizeTool.h b/Studio/Optimize/OptimizeTool.h index bd2d3c67bd..e62b58a896 100644 --- a/Studio/Optimize/OptimizeTool.h +++ b/Studio/Optimize/OptimizeTool.h @@ -68,7 +68,7 @@ public Q_SLOTS: void optimize_complete(); void progress(int); - void status(QString); + void status(std::string); private: diff --git a/Studio/Optimize/QOptimize.cpp b/Studio/Optimize/QOptimize.cpp index da88c712af..4c8c49069f 100644 --- a/Studio/Optimize/QOptimize.cpp +++ b/Studio/Optimize/QOptimize.cpp @@ -1,137 +1,109 @@ #include "QOptimize.h" -#include #include +#include + namespace shapeworks { -QOptimize::QOptimize(QObject* parent) : - QObject(parent), - Optimize() -{} +QOptimize::QOptimize(QObject* parent) : QObject(parent), Optimize() {} //--------------------------------------------------------------------------- -QOptimize::~QOptimize() -{} +QOptimize::~QOptimize() {} //--------------------------------------------------------------------------- -std::vector>> QOptimize::GetLocalPoints() -{ +std::vector>> QOptimize::GetLocalPoints() { QMutexLocker locker(&qmutex_); - return this->m_local_points; + return m_local_points; } //--------------------------------------------------------------------------- -std::vector>> QOptimize::GetGlobalPoints() -{ +std::vector>> QOptimize::GetGlobalPoints() { QMutexLocker locker(&qmutex_); - return this->m_global_points; + return m_global_points; } //--------------------------------------------------------------------------- -void QOptimize::UpdateExportablePoints() -{ +void QOptimize::UpdateExportablePoints() { QMutexLocker locker(&qmutex_); Optimize::UpdateExportablePoints(); } //--------------------------------------------------------------------------- -void QOptimize::SetIterationCallback() -{ - this->iterate_command_ = itk::MemberCommand::New(); - this->iterate_command_->SetCallbackFunction(this, &QOptimize::IterateCallback); - m_sampler->GetOptimizer()->AddObserver(itk::IterationEvent(), this->iterate_command_); +void QOptimize::SetIterationCallback() { + iterate_command_ = itk::MemberCommand::New(); + iterate_command_->SetCallbackFunction(this, &QOptimize::IterateCallback); + m_sampler->GetOptimizer()->AddObserver(itk::IterationEvent(), iterate_command_); } //--------------------------------------------------------------------------- -void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) -{ - - if (this->m_aborted) { +void QOptimize::IterateCallback(itk::Object* caller, const itk::EventObject& e) { + if (m_aborted) { return; } // run superclass iterateCallback Optimize::IterateCallback(caller, e); - auto transform = this->m_sampler->GetParticleSystem()->GetTransform(); + auto transform = m_sampler->GetParticleSystem()->GetTransform(); if (transform(0, 0) != transform(0, 0)) { - //throw on NaN + // throw on NaN throw std::runtime_error("Optimize failed! Please try changing parameters."); } bool update = false; - if (!this->time_since_last_update_.isValid()) { + if (!time_since_last_update_.isValid()) { update = true; - } - else { - auto time_since = this->time_since_last_update_.elapsed(); + } else { + auto time_since = time_since_last_update_.elapsed(); if (time_since > 100) { update = true; } } - int stage_num_iterations = m_sampler->GetOptimizer()->GetNumberOfIterations(); - int stage_total_iterations = m_sampler->GetOptimizer()->GetMaximumNumberOfIterations(); - int num_particles = m_sampler->GetParticleSystem()->GetNumberOfParticles(0); - QString message; - if (this->m_optimizing) { - message = "Optimizing: "; - } - else { - message = "Initializing: "; - } - - message = message + "Particles: " + QString::number(num_particles) + ", Iteration: " + - QString::number(stage_num_iterations) + " / " + QString::number(stage_total_iterations); - if (update) { - this->time_since_last_update_.start(); + time_since_last_update_.start(); { QMutexLocker locker(&qmutex_); - this->m_local_points.clear(); - this->m_global_points.clear(); + m_local_points.clear(); + m_global_points.clear(); // copy particles - for (size_t d = 0; d < this->m_sampler-> - GetParticleSystem()->GetNumberOfDomains(); d++) { - + for (size_t d = 0; d < m_sampler->GetParticleSystem()->GetNumberOfDomains(); d++) { // blank set of points - this->m_local_points.push_back(std::vector>()); - this->m_global_points.push_back(std::vector>()); + m_local_points.push_back(std::vector>()); + m_global_points.push_back(std::vector>()); // for each particle - for (size_t j = 0; j < this->m_sampler-> - GetParticleSystem()->GetNumberOfParticles(d); j++) { - auto pos = this->m_sampler->GetParticleSystem()->GetPosition(j, d); - auto pos2 = this->m_sampler->GetParticleSystem()->GetTransformedPosition(j, d); - this->m_local_points[d].push_back(pos); - this->m_global_points[d].push_back(pos2); + for (size_t j = 0; j < m_sampler->GetParticleSystem()->GetNumberOfParticles(d); j++) { + auto pos = m_sampler->GetParticleSystem()->GetPosition(j, d); + auto pos2 = m_sampler->GetParticleSystem()->GetTransformedPosition(j, d); + m_local_points[d].push_back(pos); + m_global_points[d].push_back(pos2); } } } - Q_EMIT progress(current_particle_iterations_ * 100 / total_particle_iterations_, message); + Q_EMIT progress(0, ""); } } //--------------------------------------------------------------------------- -std::vector QOptimize::GetParticles() -{ +std::vector QOptimize::GetParticles() { QMutexLocker locker(&qmutex_); std::vector particles; - int num_domains_per_subject = this->GetDomainsPerShape(); - int num_subjects = this->GetNumShapes() / num_domains_per_subject; + int num_domains_per_subject = GetDomainsPerShape(); + int num_subjects = GetNumShapes() / num_domains_per_subject; particles.resize(num_subjects); int subject = 0; int domain = 0; - for (int i = 0; i < this->m_local_points.size(); i++) { - particles[subject].set_local_particles(domain, this->m_local_points[i]); - particles[subject].set_world_particles(domain, this->m_global_points[i]); + for (int i = 0; i < m_local_points.size(); i++) { + particles[subject].set_local_particles(domain, m_local_points[i]); + particles[subject].set_world_particles(domain, m_global_points[i]); domain++; if (domain == num_domains_per_subject) { subject++; @@ -143,11 +115,10 @@ std::vector QOptimize::GetParticles() } //--------------------------------------------------------------------------- -std::vector>> QOptimize::GetProcrustesTransforms() -{ +std::vector>> QOptimize::GetProcrustesTransforms() { QMutexLocker locker(&qmutex_); auto transforms = Optimize::GetProcrustesTransforms(); return transforms; } -} +} // namespace shapeworks diff --git a/Studio/Optimize/QOptimize.h b/Studio/Optimize/QOptimize.h index 8d913be74d..2488ca1137 100644 --- a/Studio/Optimize/QOptimize.h +++ b/Studio/Optimize/QOptimize.h @@ -4,17 +4,17 @@ #include #include #endif -#include -#include #include +#include +#include namespace shapeworks { //! Wraps Optimize as a QObject class QOptimize : public QObject, public Optimize { -Q_OBJECT; + Q_OBJECT; -public: + public: QOptimize(QObject* parent = nullptr); virtual ~QOptimize(); @@ -27,22 +27,20 @@ Q_OBJECT; void UpdateExportablePoints() override; -protected: + protected: virtual void SetIterationCallback() override; virtual void IterateCallback(itk::Object* caller, const itk::EventObject&) override; -Q_SIGNALS: + Q_SIGNALS: void progress(int, QString); -private: - + private: itk::MemberCommand::Pointer iterate_command_; // for concurrent access QMutex qmutex_; QElapsedTimer time_since_last_update_; - }; -} +} // namespace shapeworks diff --git a/install_shapeworks.sh b/install_shapeworks.sh index ac6011f315..d25697bfaa 100644 --- a/install_shapeworks.sh +++ b/install_shapeworks.sh @@ -105,7 +105,6 @@ function install_conda() { CONDA_PACKAGES=(python=3.9.13 \ openblas=0.3.20 \ 'vtk=9.1.0=qt*' \ - scikit-learn=1.1.1 \ pip=22.1.2 ) @@ -185,6 +184,8 @@ function install_conda() { if ! pip install SimpleITK==2.1.1.2; then return 1; fi if ! pip install bokeh==2.4.3; then return 1; fi if ! pip install seaborn==0.11.2; then return 1; fi + if ! pip install scikit-learn==1.1.1; then return 1; fi + if ! pip install Python/DatasetUtilsPackage; then return 1; fi # install the local GirderConnector code as a package if ! pip install Python/DocumentationUtilsPackage; then return 1; fi # install shapeworks auto-documentation as a package if ! pip install Python/DataAugmentationUtilsPackage; then return 1; fi # install data augmentation code as a package