Part skip from Orca for Bambu printers (#10875)

Ported part skip from BambuStudio. Thanks bambulab!

<img width="1350" height="902" alt="image"
src="https://github.com/user-attachments/assets/d7f89950-3420-4ea5-bbb6-e53c875e7422"
/>

Haven't fully tested yet!


### Minimum Firmware Requirements (from BambuStudio release page):

- **X1C**: v01.09.02.12 or later
- **H2D**: v01.02.00.00 or later
- **Other models**: Currently not supported. Feature support coming in
future firmware updates.

> ⚠️ Make sure your printer firmware is up to date to use this feature.
This commit is contained in:
SoftFever
2025-10-23 23:46:39 +08:00
committed by GitHub
39 changed files with 4447 additions and 168 deletions

View File

@@ -4,6 +4,8 @@ project(libslic3r_gui)
include(PrecompiledHeader)
set(SLIC3R_GUI_SOURCES
GUI/Widgets/AnimaController.hpp
GUI/Widgets/AnimaController.cpp
Config/Snapshot.cpp
Config/Snapshot.hpp
Config/Version.cpp
@@ -371,6 +373,11 @@ set(SLIC3R_GUI_SOURCES
GUI/SavePresetDialog.hpp
GUI/SceneRaycaster.cpp
GUI/SceneRaycaster.hpp
GUI/PartSkipCommon.hpp
GUI/PartSkipDialog.cpp
GUI/PartSkipDialog.hpp
GUI/SkipPartCanvas.cpp
GUI/SkipPartCanvas.hpp
GUI/Search.cpp
GUI/Search.hpp
GUI/Selection.cpp

View File

@@ -535,6 +535,13 @@ bool MachineObject::is_lan_mode_printer() const
return result;
}
std::string MachineObject::convertToIp(long long ip)
{
std::stringstream ss;
ss << ((ip >> 0) & 0xFF) << "." << ((ip >> 8) & 0xFF) << "." << ((ip >> 16) & 0xFF) << "." << ((ip >> 24) & 0xFF);
return ss.str();
}
PrinterSeries MachineObject::get_printer_series() const
{
std::string series = DeviceManager::get_printer_series(printer_type);
@@ -1853,6 +1860,17 @@ int MachineObject::command_control_fan_val(FanType fan_type, int val)
}
int MachineObject::command_task_partskip(std::vector<int> part_ids)
{
BOOST_LOG_TRIVIAL(trace) << "command_task_partskip: ";
json j;
j["print"]["command"] = "skip_objects";
j["print"]["obj_list"] = part_ids;
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump(), 1);
}
int MachineObject::command_task_abort()
{
BOOST_LOG_TRIVIAL(trace) << "command_task_abort: ";
@@ -2723,7 +2741,7 @@ void MachineObject::reset()
vt_tray.reset();
subtask_ = nullptr;
m_partskip_ids.clear();
}
void MachineObject::set_print_state(std::string status)
@@ -2959,6 +2977,29 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
print_json.load_compatible_settings(printer_type, "");
print_json.diff2all_base_reset(j_pre);
}
if (j_pre["print"].contains("s_obj")){
if(j_pre["print"]["s_obj"].is_array()){
m_partskip_ids.clear();
for(auto it=j_pre["print"]["s_obj"].begin(); it!=j_pre["print"]["s_obj"].end(); it++){
m_partskip_ids.push_back(it.value().get<int>());
}
}
}
}
}
if (j_pre["print"].contains("plate_idx")){ // && m_plate_index == -1
if (j_pre["print"]["plate_idx"].is_number())
{
m_plate_index = j_pre["print"]["plate_idx"].get<int>();
}
else if (j_pre["print"]["plate_idx"].is_string())
{
try
{
m_plate_index = std::stoi(j_pre["print"]["plate_idx"].get<std::string>());
}
catch (...) { BOOST_LOG_TRIVIAL(error) << "parse_json: failed to convert plate_idx to int"; }
}
}
}
@@ -3423,8 +3464,24 @@ int MachineObject::parse_json(std::string payload, bool key_field_only)
if (jj["net"].contains("conf")) {
network_wired = (jj["net"]["conf"].get<int>() & (0x1)) != 0;
}
if (jj["net"].contains("info")) {
for (auto info_item = jj["net"]["info"].begin(); info_item != jj["net"]["info"].end(); info_item++) {
if (info_item->contains("ip")) {
auto tmp_dev_ip = (*info_item)["ip"].get<int64_t>();
if (tmp_dev_ip == 0)
continue ;
else {
set_dev_ip(convertToIp(tmp_dev_ip));
}
} else {
break;
}
}
}
}
}
#pragma endregion
#pragma region online
@@ -5170,6 +5227,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil
if (plate_idx >= 0) {
plate_index = plate_idx;
this->m_plate_index = plate_idx;
}
else {
std::string subtask_json;
@@ -5232,8 +5290,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil
BOOST_LOG_TRIVIAL(error) << "task_info: get subtask id failed!";
}
}
this->m_plate_index = plate_index;
// this->m_plate_index = plate_index;
});
}
}
@@ -5562,6 +5619,8 @@ void MachineObject::parse_new_info(json print)
is_support_nozzle_blob_detection = get_flag_bits(fun, 13);
is_support_upgrade_kit = get_flag_bits(cfg, 14);
is_support_command_homing = get_flag_bits(fun, 32);
is_support_brtc = get_flag_bits(fun, 31);
is_support_partskip = get_flag_bits(fun, 49);
}
/*aux*/

View File

@@ -509,6 +509,7 @@ public:
int subscribe_counter{3};
std::string dev_connection_type; /* lan | cloud */
std::string connection_type() { return dev_connection_type; }
std::string dev_connection_name; /* lan | eth */
void set_dev_ip(std::string ip) {dev_ip = ip;}
std::string get_ftp_folder();
@@ -520,6 +521,7 @@ public:
void erase_user_access_code();
std::string get_user_access_code() const;
bool is_lan_mode_printer() const;
std::string convertToIp(long long ip);
//PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
std::string printer_type; /* model_id */
@@ -831,6 +833,9 @@ public:
int xcam_filament_tangle_detect_count = 0;
int ams_print_option_count = 0;
// part skip
std::vector<int> m_partskip_ids;
//supported features
bool is_support_chamber_edit{false};
bool is_support_extrusion_cali{false};
@@ -870,6 +875,8 @@ public:
bool is_support_agora{false};
bool is_support_upgrade_kit{false};
bool is_support_command_homing { false };// fun[32]
bool is_support_brtc{false}; // fun[31], support tcp and upload protocol
bool is_support_partskip{false};
bool installed_upgrade_kit{false};
int nozzle_max_temperature = -1;
@@ -952,6 +959,7 @@ public:
int command_control_fan_val(FanType fan_type, int val);
int command_task_abort();
/* cancelled the job_id */
int command_task_partskip(std::vector<int> part_ids);
int command_task_cancel(std::string job_id);
int command_task_pause();
int command_task_resume();

View File

@@ -297,7 +297,12 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj)
case PrinterFileSystem::Initializing: icon = m_bmp_loading; msg = _L("Initializing..."); break;
case PrinterFileSystem::Connecting: icon = m_bmp_loading; msg = _L("Connecting..."); break;
case PrinterFileSystem::Failed: icon = m_bmp_failed; if (extra != 1) msg = _L("Please check the network and try again. You can restart or update the printer if the issue persists."); break;
case PrinterFileSystem::ListSyncing: icon = m_bmp_loading; msg = _L("Loading file list..."); break;
case PrinterFileSystem::ListSyncing: {
icon = m_bmp_loading;
msg = _L("Loading file list...");
fs->ListAllFiles();
break;
}
case PrinterFileSystem::ListReady: icon = extra == 0 ? m_bmp_empty : m_bmp_failed; msg = extra == 0 ? _L("No files") : _L("Load failed"); break;
}
int err = fs->GetLastError();

View File

@@ -0,0 +1,18 @@
#ifndef PARTSKIPCOMMON_H
#define PARTSKIPCOMMON_H
namespace Slic3r { namespace GUI {
enum PartState {
psUnCheck,
psChecked,
psSkipped
};
typedef std::vector<std::pair<int, PartState>> PartsInfo;
}}
#endif // PARTSKIPCOMMON_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
#include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/sizer.h>
#include <wx/gbsizer.h>
#include <wx/webrequest.h>
#include <wx/control.h>
#include <wx/dcclient.h>
#include <wx/display.h>
#include <wx/mstream.h>
#include <wx/sstream.h>
#include <wx/zstream.h>
#include <wx/window.h>
#include <wx/dcgraph.h>
#include <wx/simplebook.h>
#include "Widgets/Label.hpp"
#include "Widgets/CheckBox.hpp"
#include "Widgets/Button.hpp"
#include "Widgets/AnimaController.hpp"
#include "DeviceManager.hpp"
#include "PartSkipCommon.hpp"
#include "Printer/PrinterFileSystem.h"
#include "I18N.hpp"
#include "GUI_Utils.hpp"
namespace Slic3r { namespace GUI {
class SkipPartCanvas;
enum URL_STATE {
URL_TCP,
URL_TUTK,
};
class PartSkipConfirmDialog : public DPIDialog
{
private:
protected:
Label *m_msg_label;
Label *m_tip_label;
Button *m_apply_button;
public:
PartSkipConfirmDialog(wxWindow *parent);
~PartSkipConfirmDialog();
void on_dpi_changed(const wxRect &suggested_rect);
Button *GetConfirmButton();
void SetMsgLabel(wxString msg);
void SetTipLabel(wxString msg);
bool Show(bool show);
};
class PartSkipDialog : public DPIDialog
{
public:
PartSkipDialog(wxWindow *parent);
~PartSkipDialog();
void on_dpi_changed(const wxRect &suggested_rect);
bool Show(bool show);
void UpdatePartsStateFromPrinter(MachineObject *obj_);
void SetSimplebookPage(int page);
void InitSchedule(MachineObject *obj_);
void InitDialogUI();
int GetAllSkippedPartsNum();
MachineObject *m_obj{nullptr};
wxSimplebook *m_simplebook;
wxPanel *m_book_third_panel;
wxPanel *m_book_second_panel;
wxPanel *m_book_first_panel;
SkipPartCanvas *m_canvas;
Button *m_zoom_in_btn;
Button *m_zoom_out_btn;
Button *m_switch_drag_btn;
CheckBox *m_all_checkbox;
Button *m_percent_label;
Label *m_all_label;
wxPanel *m_line;
wxPanel *m_line_top;
wxScrolledWindow *m_list_view;
wxPanel *m_dlg_placeholder;
Label *m_cnt_label;
Label *m_tot_label;
Button *m_apply_btn;
Label *m_loading_label;
Label *m_retry_label;
ScalableBitmap *m_retry_icon;
wxStaticBitmap *m_retry_bitmap;
wxBoxSizer *m_sizer;
wxBoxSizer *m_dlg_sizer;
wxBoxSizer *m_dlg_content_sizer;
wxBoxSizer *m_dlg_btn_sizer;
wxBoxSizer *m_canvas_sizer;
wxBoxSizer *m_canvas_btn_sizer;
wxBoxSizer *m_list_sizer;
wxBoxSizer *m_scroll_sizer;
wxBoxSizer *m_book_first_sizer;
wxBoxSizer *m_book_second_sizer;
wxBoxSizer *m_book_second_btn_sizer;
Button *m_second_retry_btn;
AnimaIcon *m_loading_icon;
private:
int m_plate_idx{-1};
int m_zoom_percent{100};
bool m_is_drag{false};
bool m_print_lock{true};
bool m_enable_apply_btn{false};
bool is_model_support_partskip{false};
std::map<uint32_t, PartState> m_parts_state;
std::map<uint32_t, std::string> m_parts_name;
std::vector<int> m_partskip_ids;
enum URL_STATE m_url_state = URL_STATE::URL_TCP;
PartsInfo GetPartsInfo();
bool is_drag_mode();
boost::shared_ptr<PrinterFileSystem> m_file_sys;
bool m_file_sys_result{false};
std::string m_timestamp;
std::string m_tmp_path;
std::vector<string> m_local_paths;
std::vector<string> m_target_paths;
std::string create_tmp_path();
bool is_local_file_existed(const std::vector<string> &local_paths);
void DownloadPartsFile();
void OnFileSystemEvent(wxCommandEvent &event);
void OnFileSystemResult(wxCommandEvent &event);
void fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs);
void OnZoomIn(wxCommandEvent &event);
void OnZoomOut(wxCommandEvent &event);
void OnSwitchDrag(wxCommandEvent &event);
void OnZoomPercent(wxCommandEvent &event);
void UpdatePartsStateFromCanvas(wxCommandEvent &event);
void UpdateZoomPercent();
void UpdateCountLabel();
void UpdateDialogUI();
void UpdateApplyButtonStatus();
bool IsAllChecked();
bool IsAllCancled();
void OnRetryButton(wxCommandEvent &event);
void OnAllCheckbox(wxCommandEvent &event);
void OnApplyDialog(wxCommandEvent &event);
};
}} // namespace Slic3r::GUI

View File

@@ -39,18 +39,31 @@ wxDEFINE_EVENT(EVT_FILE_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_THUMBNAIL, wxCommandEvent);
wxDEFINE_EVENT(EVT_DOWNLOAD, wxCommandEvent);
wxDEFINE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent);
wxDEFINE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_UPLOADING, wxCommandEvent);
wxDEFINE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_FILE_CALLBACK, wxCommandEvent);
static wxBitmap default_thumbnail;
static std::map<int, std::string> error_messages = {
{PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")},
{PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")},
{PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")},
{PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")},
{PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")},
{PrinterFileSystem::STORAGE_UNAVAILABLE, L("Storage unavailable, insert SD card.")}
{PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")},
{PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")},
{PrinterFileSystem::ERROR_TIME_OUT, L("Timeout, please try again.")},
{PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")},
{PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")},
{PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")},
{PrinterFileSystem::STORAGE_UNAVAILABLE, L("Please check if the SD card is inserted into the printer.\nIf it still cannot be read, you can try formatting the SD card.")},
{PrinterFileSystem::API_VERSION_UNSUPPORT, L("The firmware version of the printer is too low. Please update the firmware and try again.")},
{PrinterFileSystem::FILE_EXIST, L("The file already exists, do you want to replace it?")},
{PrinterFileSystem::STORAGE_SPACE_NOT_ENOUGH, L("Insufficient storage space, please clear the space and try again.")},
{PrinterFileSystem::FILE_CREATE_ERR, L("File creation failed, please try again.")},
{PrinterFileSystem::FILE_WRITE_ERR, L("File write failed, please try again.")},
{PrinterFileSystem::MD5_COMPARE_ERR, L("MD5 verification failed, please try again.")},
{PrinterFileSystem::FILE_RENAME_ERR, L("File renaming failed, please try again.")},
{PrinterFileSystem::SEND_ERR, L("File upload failed, please try again.")}
};
struct StaticBambuLib : BambuLib {
@@ -117,7 +130,6 @@ void PrinterFileSystem::SetFileType(FileType type, std::string const &storage)
return;
m_status = Status::ListSyncing;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
ListAllFiles();
}
void PrinterFileSystem::SetGroupMode(GroupMode mode)
@@ -248,6 +260,156 @@ struct PrinterFileSystem::Download : Progress
boost::uuids::detail::md5 boost_md5;
};
struct PrinterFileSystem::Upload : Progress
{
std::string error;
boost::uint32_t frag_id{0};
MD5_CTX ctx;
boost::filesystem::ifstream ifs;
};
void PrinterFileSystem::GetPickImages(const std::vector<std::string> &local_paths, const std::vector<std::string> &targetpaths)
{
m_download_states.clear();
GetPickImage(1, local_paths[0], targetpaths[0]);
GetPickImage(2, local_paths[1], targetpaths[1]);
GetPickImage(3, local_paths[2], targetpaths[2]);
}
void PrinterFileSystem::GetPickImage(int id, const std::string &local_path, const std::string &targetpath)
{
json j;
j["sequence_id"] = id;
j["version"] = 1;
j["peer_host"] = "studio";
j["command"] = "get_project_file";
j["file_rel_path"] = targetpath;
std::string param = j.dump();
DownloadRamFile(16, local_path, param);
}
void PrinterFileSystem::DownloadRamFile(int index, const std::string &local_path, const std::string & param)
{
std::shared_ptr<Download> download(new Download);
download->local_path = local_path;
json req;
req["path"] = "mem:/" + std::to_string(index);
req["offset"] = 0;
req["mem_dl_param_size"] = param.size();
m_download_seq = SendRequest<Progress>(
FILE_DOWNLOAD, req,
[download](json const &resp, Progress &prog, unsigned char const *data) -> int {
size_t size = resp.value("size", 0);
prog.size = resp["offset"];
prog.total = resp["total"];
if (resp.contains("mem_dl_param_size")) {
size_t s = resp["mem_dl_param_size"].get<size_t>();
std::string json_str(reinterpret_cast<const char *>(data), s);
// OutputDebugStringA(json_str.c_str());
// OutputDebugStringA("\n");
json mem_dl_json = json::parse(json_str);
// download->mem_dl_param_size = size;
if (!mem_dl_json.contains("result") || mem_dl_json["result"] == 1 ) {
wxLogWarning("Download failed: result = 1");
return ERROR_JSON;
}
if(mem_dl_json.contains("size") && mem_dl_json["size"] == 0 )
return FILE_SIZE_ERR;
return CONTINUE;
}
if (prog.size == 0 ) {
download->ofs.open(download->local_path, std::ios::binary);
if (!download->ofs) {
download->error = last_system_error();
wxLogWarning("DownloadImageFromRam open error: %s\n", wxString::FromUTF8(download->error));
return FILE_OPEN_ERR;
}
}
download->ofs.write(reinterpret_cast<const char *>(data), size);
if (!download->ofs) {
download->error = last_system_error();
wxLogWarning("DownloadImageFromRam write error: %s\n", wxString::FromUTF8(download->error));
return FILE_READ_WRITE_ERR;
}
download->boost_md5.process_bytes(data, size);
prog.size += size;
download->total = prog.total;
download->size = prog.size;
if (prog.size < prog.total) {
return 0;
}
download->ofs.close();
std::string md5 = resp["file_md5"];
boost::uuids::detail::md5::digest_type digest;
download->boost_md5.get_digest(digest);
for (int i = 0; i < 4; ++i) digest[i] = boost::endian::endian_reverse(digest[i]);
std::string str_md5;
const auto char_digest = reinterpret_cast<const char *>(&digest[0]);
boost::algorithm::hex(char_digest, char_digest + sizeof(digest), std::back_inserter(str_md5));
if (!boost::iequals(str_md5, md5)) {
wxLogWarning("DownloadImageFromRam checksum error: %s != %s\n", str_md5, md5);
boost::system::error_code ec;
boost::filesystem::rename(download->local_path, download->local_path + ".tmp", ec);
return FILE_CHECK_ERR;
}
return SUCCESS;
},
[this, download](int result, Progress const &data) {
//OutputDebugStringA(std::to_string(result).c_str());
//OutputDebugStringA("\n");
if (result == CONTINUE) { return; }
std::string msg;
if (result == SUCCESS) {
if (std::filesystem::exists(download->local_path)) {
m_download_states.emplace_back(true);
BOOST_LOG_TRIVIAL(info) <<"DownloadImageFromRam finished: " << download->local_path << "result = " << result;
}else{
m_download_states.emplace_back(false);
BOOST_LOG_TRIVIAL(warning) <<"DownloadImageFromRam finished, but file not exist: " << download->local_path << "result = " << result;
}
} else if (result != CONTINUE) {
m_download_states.emplace_back(false);
BOOST_LOG_TRIVIAL(warning) << "DownloadImageFromRam failed: " << download->error << "result = " << result;
}
if(m_download_states.size() == 3){
if(m_download_states[0] && m_download_states[1] && m_download_states[2]){
SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS);
}else{
// FILE_NO_EXIST is not really error_code
SendChangedEvent(EVT_RAMDOWNLOAD, FILE_NO_EXIST);
}
}else{
BOOST_LOG_TRIVIAL(warning) << "m_download_states current size is : " << m_download_states.size();
}
},param);
}
void PrinterFileSystem::SendExistedFile(){
SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS);
}
void PrinterFileSystem::SendConnectFail(){
SendChangedEvent(EVT_RAMDOWNLOAD, ERROR_PIPE);
}
void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path)
{
if (index == (size_t) -1) {
@@ -281,6 +443,10 @@ void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path)
DownloadNextFile();
}
void PrinterFileSystem::DownloadCheckFiles(std::string const &path)
{
for (size_t i = 0; i < m_file_list.size(); ++i) {
@@ -524,6 +690,16 @@ void PrinterFileSystem::Stop(bool quit)
m_cond.notify_all();
}
void PrinterFileSystem::SetUploadFile(const std::string &path, const std::string &name, const std::string &select_storage)
{
if (!m_upload_file) {
m_upload_file = std::make_unique<UploadFile>();
}
m_upload_file->path = path;
m_upload_file->name = name;
m_upload_file->select_storage = select_storage;
}
void PrinterFileSystem::BuildGroups()
{
m_group_year.clear();
@@ -1030,21 +1206,216 @@ void PrinterFileSystem::DumpLog(void * thiz, int, tchar const *msg)
static_cast<PrinterFileSystem*>(thiz)->Bambu_FreeLogMsg(msg);
}
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback)
boost::uint32_t PrinterFileSystem::RequestMediaAbility(int api_version)
{
json req;
req["peer"] = "studio";
req["api_version"] = api_version;
return SendRequest<MediaAbilityList>(
REQUEST_MEDIA_ABILITY, req, [this](const json &resp, MediaAbilityList &list, auto) -> int {
json abliity_list = resp["storage"];
list = abliity_list.get<MediaAbilityList>();
return 0;
},
[this](int result, MediaAbilityList list){
if (result != 0) {
m_last_error = result;
m_media_ability_list.clear();
SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_FAILED, "", m_last_error);
return result;
}
m_media_ability_list.swap(list);
SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_SUCCESS);
return 0;
});
}
void PrinterFileSystem::RequestUploadFile()
{
json req;
req["type"] = "model";
req["storage"] = m_upload_file->select_storage;
req["path"] = m_upload_file->name;
m_upload_file->upload = std::make_unique<Upload>();
boost::filesystem::path path = boost::filesystem::path(m_upload_file->path);
boost::system::error_code ec;
boost::uint32_t file_size = boost::filesystem::file_size(path, ec);
req["total"] = file_size;
m_upload_file->size = file_size;
m_upload_file->upload->total = file_size;
m_upload_seq = SendRequest(
FILE_UPLOAD, req,
[this](int result, const json& resp, auto) -> int{
if (result != SUCCESS && result != CONTINUE && result != FILE_EXIST) {
std::string error_msg = "";
if (result == ERROR_CANCEL) {
error_msg = L("User cancels task.");
} else if (result == FILE_READ_WRITE_ERR || result == FILE_OPEN_ERR) {
error_msg = L("Failed to read file, please try again.");
}
wxLogWarning("PrinterFileSystem::UploadFile error: %d\n", result);
SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADCANCEL, error_msg, result);
} else if (result == SUCCESS) {
SendChangedEvent(EVT_UPLOADING, 100);
SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADDONE);
} else if (result == CONTINUE || result == FILE_EXIST) {
if (m_upload_file) {
m_upload_file->chunk_size = resp["chunk_size"];
m_upload_file->upload->size = resp["offset"];
m_upload_file->flags |= FF_UPLOADING;
}
{
boost::unique_lock l(m_mutex);
auto cb = [this, upload_file = m_upload_file, seq = m_upload_seq](std::string &msg) -> int {
return UploadFileTask(upload_file, seq, msg);
};
m_produce_message_cb_map[m_upload_seq] = cb;
}
return CONTINUE;
}
// reset m_upload_file
if (m_upload_file) {
m_upload_file.reset();
}
return result;
});
}
int PrinterFileSystem::UploadFileTask(std::shared_ptr<UploadFile> upload_file, boost::uint64_t seq, std::string &msg)
{
if (!upload_file)
return FILE_OPEN_ERR;
if (!(upload_file->flags & FF_UPLOADING))
return FILE_OPEN_ERR;
auto &upload = upload_file->upload;
if (!upload->ifs.is_open()) {
upload->ifs.open(upload_file->path, std::ios::binary);
if (!upload_file->upload->ifs) {
wxLogWarning("PrinterFileSystem::UploadFile open error: %s\n", wxString::FromUTF8(upload_file->path));
return FILE_OPEN_ERR;
}
MD5_Init(&upload->ctx);
}
const boost::uint32_t buffer_size = upload_file->chunk_size * 1024;
char *buffer = new char[buffer_size];
upload->ifs.seekg(upload->size, std::ios::beg);
upload->ifs.read(buffer, buffer_size);
boost::int32_t read_size = upload->ifs.gcount();
if (read_size <= 0) {
wxLogWarning("PrinterFileSystem::Upload read error.\n");
upload->ifs.close();
if (buffer) {
delete[] buffer;
buffer = nullptr;
}
return FILE_READ_WRITE_ERR;
}
json req;
req["frag_id"] = upload->frag_id;
req["offset"] = upload->size;
req["size"] = read_size;
MD5_Update(&upload->ctx, buffer, read_size);
upload->size += read_size;
if (upload->size == upload->total) {
unsigned char digest[16];
MD5_Final(digest, &upload->ctx);
char md5_str[33];
for (int j = 0; j < 16; j++) { sprintf(&md5_str[j * 2], "%02X", (unsigned int) digest[j]); }
std::string md5_out = std::string(md5_str);
std::transform(md5_out.begin(), md5_out.end(), md5_out.begin(), ::tolower);
req["file_md5"] = md5_out;
// OutputDebugStringA(md5_out.c_str());
// OutputDebugStringA("\n");
}
if (m_upload_file && m_upload_file->flags & FF_UPLOADING) {
upload->frag_id++;
upload->progress = upload->size * 100 / upload->total;
int progress = upload->progress == 100 ? 99 : upload->progress;
SendChangedEvent(EVT_UPLOADING, progress);
}
json root;
root["cmdtype"] = FILE_UPLOAD;
root["sequence"] = seq;
root["req"] = req;
std::ostringstream oss;
oss << root;
oss << "\n\n";
oss << std::string(buffer, read_size);
msg = oss.str();
if (buffer) {
delete[] buffer;
buffer = nullptr;
}
if (upload->size == upload->total) {
upload->ifs.close();
return SUCCESS;
}
return CONTINUE;
}
PrinterFileSystem::MediaAbilityList PrinterFileSystem::GetMediaAbilityList() const
{
return m_media_ability_list;
}
void PrinterFileSystem::CancelUploadTask(bool send_cancel_req)
{
if (!m_upload_file)
return;
if (send_cancel_req) {
CancelRequest(m_upload_seq);
} else {
CancelRequests2({m_upload_seq});
}
}
boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback,const std::string& param)
{
if (m_session.tunnel == nullptr) {
Retry();
callback(ERROR_PIPE, json(), nullptr);
return 0;
}
boost::uint32_t seq = m_sequence + m_callbacks.size();
boost::uint32_t seq = m_sequence + m_callbacks.size();
json root;
root["cmdtype"] = type;
root["sequence"] = seq;
root["req"] = req;
std::ostringstream oss;
oss << root;
auto msg = oss.str();
if (!param.empty()) {
oss << "\n\n";
oss << param;
}
// OutputDebugStringA(oss.str().c_str());
// OutputDebugStringA("\n");
auto msg = oss.str();
boost::unique_lock l(m_mutex);
m_messages.push_back(msg);
m_callbacks.push_back(callback);
@@ -1085,13 +1456,15 @@ void PrinterFileSystem::CancelRequests2(std::vector<boost::uint32_t> const &seqs
for (auto &f : seqs) {
boost::uint32_t seq = f;
seq -= m_sequence;
if (size_t(seq) >= m_callbacks.size())
continue;
if (size_t(seq) >= m_callbacks.size()) continue;
auto &c = m_callbacks[seq];
if (c == nullptr)
continue;
if (c == nullptr) continue;
callbacks.emplace_back(f, c);
c = nullptr;
// erase m_produce_message_cb
if (m_produce_message_cb_map.find(seq) != m_produce_message_cb_map.end())
m_produce_message_cb_map.erase(seq);
}
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
m_callbacks.pop_front();
@@ -1118,6 +1491,45 @@ void PrinterFileSystem::RecvMessageThread()
break;
}
}
if (m_messages.empty() && !m_produce_message_cb_map.empty()) {
auto it = m_produce_message_cb_map.begin();
while(it != m_produce_message_cb_map.end()) {
std::string msg;
auto prodeuce_message_cb = it->second;
l.unlock();
int res = prodeuce_message_cb(msg);
l.lock();
if (res == CONTINUE || res == SUCCESS) {
m_messages.emplace_back(msg);
if (res == SUCCESS) {
it = m_produce_message_cb_map.erase(it);
continue;
}
it++;
} else {
int seq2 = it->first - m_sequence;
// erase it
it = m_produce_message_cb_map.erase(it);
if (size_t(seq2) >= m_callbacks.size())
continue;
auto c = m_callbacks[seq2];
if (c == nullptr)
continue;;
m_callbacks[seq2] = nullptr;
if (seq2 == 0) {
// if produce message return error, erase callback and sequence should plus
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
m_callbacks.pop_front();
++m_sequence;
}
}
l.unlock();
c(res, json(), nullptr);
l.lock();
}
}
}
if (!m_messages.empty()) {
auto & msg = m_messages.front();
// OutputDebugStringA(msg.c_str());
@@ -1201,7 +1613,7 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock<boost::mutex> &l, Bamb
int result2 = c(result, resp, json_end);
l.lock();
if (result2 != CONTINUE) {
int seq2 = seq - m_sequence;
int seq2 = seq - m_sequence;
m_callbacks[seq2] = callback_t2();
if (seq2 == 0) {
while (!m_callbacks.empty() && m_callbacks.front() == nullptr) {
@@ -1214,6 +1626,11 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock<boost::mutex> &l, Bamb
CancelRequest(seq);
l.lock();
}
// error should erase m_produce_message_cb
if (m_produce_message_cb_map.find(seq2) != m_produce_message_cb_map.end()) {
m_produce_message_cb_map.erase(seq2);
}
}
}
}
@@ -1232,7 +1649,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
if (m_session.owner == nullptr)
return;
json r;
while (!m_callbacks.empty()) {
while(!m_callbacks.empty()) {
auto c = m_callbacks.front();
m_callbacks.pop_front();
++m_sequence;
@@ -1245,7 +1662,9 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
while (m_stopped) {
if (m_session.owner == nullptr)
return;
m_cond.wait(l);
m_status = Status::Reconnecting;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
m_cond.wait(l);
}
wxLogMessage("PrinterFileSystem::Reconnect Initializing");
m_status = Status::Initializing;
@@ -1271,6 +1690,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
Bambu_Tunnel tunnel = nullptr;
int ret = Bambu_Create(&tunnel, url.c_str());
if (ret == 0) {
Bambu_SetLogger(tunnel, DumpLog, this);
ret = Bambu_Open(tunnel);
}
@@ -1297,15 +1717,19 @@ void PrinterFileSystem::Reconnect(boost::unique_lock<boost::mutex> &l, int resul
}
wxLogMessage("PrinterFileSystem::Reconnect Failed");
m_status = Status::Failed;
SendChangedEvent(EVT_STATUS_CHANGED, m_status, "", url.size() < 2 ? 1 : m_last_error);
m_cond.timed_wait(l, boost::posix_time::seconds(10));
}
m_status = Status::ListSyncing;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
#ifdef PRINTER_FILE_SYSTEM_TEST
PostCallback([this] { SendChangedEvent(EVT_FILE_CHANGED); });
#else
PostCallback([this] { m_task_flags = 0; ListAllFiles(); });
PostCallback([this] {
m_task_flags = 0;
m_status = Status::ListSyncing;
SendChangedEvent(EVT_STATUS_CHANGED, m_status);
});
#endif
}

View File

@@ -22,39 +22,53 @@ wxDECLARE_EVENT(EVT_FILE_CHANGED, wxCommandEvent);
wxDECLARE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent);
wxDECLARE_EVENT(EVT_THUMBNAIL, wxCommandEvent);
wxDECLARE_EVENT(EVT_DOWNLOAD, wxCommandEvent);
wxDECLARE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent);
wxDECLARE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent);
wxDECLARE_EVENT(EVT_UPLOADING, wxCommandEvent);
wxDECLARE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent);
class PrinterFileSystem : public wxEvtHandler, public boost::enable_shared_from_this<PrinterFileSystem>, BambuLib
{
static const int CTRL_TYPE = 0x3001;
enum {
LIST_INFO = 0x0001,
SUB_FILE = 0x0002,
FILE_DEL = 0x0003,
FILE_DOWNLOAD = 0X0004,
NOTIFY_FIRST = 0x0100,
LIST_CHANGE_NOTIFY = 0x0100,
LIST_RESYNC_NOTIFY = 0x0101,
TASK_CANCEL = 0x1000
LIST_INFO = 0x0001,
SUB_FILE = 0x0002,
FILE_DEL = 0x0003,
FILE_DOWNLOAD = 0x0004,
FILE_UPLOAD = 0x0005,
REQUEST_MEDIA_ABILITY = 0x0007,
NOTIFY_FIRST = 0x0100,
LIST_CHANGE_NOTIFY = 0x0100,
LIST_RESYNC_NOTIFY = 0x0101,
TASK_CANCEL = 0x1000
};
public:
enum {
SUCCESS = 0,
CONTINUE = 1,
ERROR_JSON = 2,
ERROR_PIPE = 3,
ERROR_CANCEL = 4,
ERROR_RES_BUSY = 5,
FILE_NO_EXIST = 10,
FILE_NAME_INVALID = 11,
FILE_SIZE_ERR = 12,
FILE_OPEN_ERR = 13,
FILE_READ_WRITE_ERR = 14,
FILE_CHECK_ERR = 15,
FILE_TYPE_ERR = 16,
STORAGE_UNAVAILABLE = 17,
SUCCESS = 0,
CONTINUE = 1,
ERROR_JSON = 2,
ERROR_PIPE = 3,
ERROR_CANCEL = 4,
ERROR_RES_BUSY = 5,
ERROR_TIME_OUT = 6,
FILE_NO_EXIST = 10,
FILE_NAME_INVALID = 11,
FILE_SIZE_ERR = 12,
FILE_OPEN_ERR = 13,
FILE_READ_WRITE_ERR = 14,
FILE_CHECK_ERR = 15,
FILE_TYPE_ERR = 16,
STORAGE_UNAVAILABLE = 17,
API_VERSION_UNSUPPORT = 18,
FILE_EXIST = 19,
STORAGE_SPACE_NOT_ENOUGH = 20,
FILE_CREATE_ERR = 21,
FILE_WRITE_ERR = 22,
MD5_COMPARE_ERR = 23,
FILE_RENAME_ERR = 24,
SEND_ERR = 25,
};
@@ -90,12 +104,28 @@ public:
template<typename T> using Callback = std::function<void(int, T)>;
enum Flags {
FF_SELECT = 1,
FF_THUMNAIL = 2, // Thumbnail ready
FF_DOWNLOAD = 4, // Request download
FF_DELETED = 8, // Request delete
FF_FETCH_MODEL = 16,// Request model
FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry
FF_SELECT = 1,
FF_THUMNAIL = 2, // Thumbnail ready
FF_DOWNLOAD = 4, // Request download
FF_DELETED = 8, // Request delete
FF_FETCH_MODEL = 16, // Request model
FF_UPLOADING = 1 << 5, // File uploading
FF_UPLOADDONE = 1 << 6, // File upload done
FF_UPLOADCANCEL = 1 << 7, // File upload cancel
FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry
};
enum UploadStatus
{
Uploading = 1 << 0,
UploadDone = 1 << 1,
UploadCancel = 1 << 2,
};
enum RequestMediaAbilityStatus
{
S_SUCCESS,
S_FAILED
};
struct Progress
@@ -106,6 +136,7 @@ public:
};
struct Download;
struct Upload;
struct File
{
@@ -128,9 +159,23 @@ public:
friend bool operator<(File const & l, File const & r) { return l.time > r.time; }
};
struct UploadFile
{
std::string name;
std::string path;
std::string select_storage;
int flags{0};
boost::uint32_t size{0};
boost::uint32_t chunk_size{0}; // KB
std::unique_ptr<Upload> upload;
bool IsUploading() const { return flags & FF_UPLOADING; }
};
struct Void {};
typedef std::vector<File> FileList;
typedef std::vector<std::string> MediaAbilityList;
void ListAllFiles();
@@ -138,6 +183,17 @@ public:
void DownloadFiles(size_t index, std::string const &path);
void GetPickImage(int id, const std::string &local_path, const std::string &path);
void GetPickImages(const std::vector<std::string> &local_paths, const std::vector<std::string> &targetpaths);
void DownloadRamFile(int index, const std::string &local_path, const std::string &param);
void SendExistedFile();
void SendConnectFail();
void DownloadCheckFiles(std::string const &path);
bool DownloadCheckFile(size_t index);
@@ -170,6 +226,7 @@ public:
ListSyncing,
ListReady,
Failed,
Reconnecting,
};
Status GetStatus() const { return m_status; }
@@ -185,6 +242,16 @@ public:
void Stop(bool quit = false);
boost::uint32_t RequestMediaAbility(int api_version);
void RequestUploadFile();
MediaAbilityList GetMediaAbilityList() const;
void SetUploadFile(const std::string& path, const std::string& name, const std::string& select_storage);
void CancelUploadTask(bool send_cancel_req = true);
private:
void BuildGroups();
@@ -217,13 +284,14 @@ private:
typedef std::function<int(int, json const &resp, unsigned char const *data)> callback_t2;
template <typename T>
boost::uint32_t SendRequest(int type, json const& req, Translator<T> const& translator, Callback<T> const& callback)
typedef std::function<int(std::string &msg)> callback_t3;
template<typename T> boost::uint32_t SendRequest(int type, json const &req, Translator<T> const &translator, Callback<T> const &callback, const std::string &param = "")
{
auto c = [translator, callback, this](int result, json const &resp, unsigned char const *data) -> int
{
T t;
if (result == 0 || result == CONTINUE) {
if (result == 0 || result == CONTINUE || result == FILE_EXIST) {
try {
int n = (translator != nullptr) ? translator(resp, t, data) : 0;
result = n == 0 ? result : n;
@@ -235,7 +303,7 @@ private:
PostCallback<T>(callback, result, t);
return result;
};
return SendRequest(type, req, c);
return SendRequest(type, req, c, param);
}
template<typename T> using Applier = std::function<void(T const &)>;
@@ -265,7 +333,7 @@ private:
InstallNotify(type, c);
}
boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback);
boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback, const std::string &param = "");
void InstallNotify(int type, callback_t2 const &callback);
@@ -289,6 +357,8 @@ private:
void PostCallback(std::function<void(void)> const & callback);
int UploadFileTask(std::shared_ptr<UploadFile> upload_file, boost::uint64_t seq, std::string &msg);
protected:
FileType m_file_type = F_INVALID_TYPE;
std::string m_file_storage;
@@ -298,6 +368,7 @@ protected:
std::vector<size_t> m_group_year;
std::vector<size_t> m_group_month;
std::vector<int> m_group_flags;
std::shared_ptr<UploadFile> m_upload_file;
private:
size_t m_select_count = 0;
@@ -305,6 +376,8 @@ private:
size_t m_lock_end = 0;
int m_task_flags = 0;
std::vector<bool> m_download_states;
private:
struct Session
{
@@ -315,6 +388,7 @@ private:
boost::uint32_t m_sequence = 0;
boost::uint32_t m_download_seq = 0;
boost::uint32_t m_fetch_model_seq = 0;
boost::uint32_t m_upload_seq = 0;
std::deque<std::string> m_messages;
std::deque<callback_t2> m_callbacks;
std::deque<callback_t2> m_notifies;
@@ -324,6 +398,9 @@ private:
boost::thread m_recv_thread;
Status m_status;
int m_last_error = 0;
MediaAbilityList m_media_ability_list;
std::map<boost::uint32_t, callback_t3> m_produce_message_cb_map;
};
#endif // !slic3r_GUI_PrinterFileSystem_h_

View File

@@ -88,7 +88,9 @@ enum PrintDialogStatus {
PrintStatusNotSupportedPrintAll,
PrintStatusBlankPlate,
PrintStatusUnsupportedPrinter,
PrintStatusTimelapseWarning
PrintStatusTimelapseWarning,
PrintStatusPublicInitFailed,
PrintStatusPublicUploadFiled
};
class Material

View File

@@ -29,12 +29,13 @@ namespace GUI {
#define LIST_REFRESH_INTERVAL 200
#define MACHINE_LIST_REFRESH_INTERVAL 2000
constexpr int timeout_period = 15000; // ms
wxDEFINE_EVENT(EVT_UPDATE_USER_MACHINE_LIST, wxCommandEvent);
wxDEFINE_EVENT(EVT_PRINT_JOB_CANCEL, wxCommandEvent);
wxDEFINE_EVENT(EVT_SEND_JOB_SUCCESS, wxCommandEvent);
wxDEFINE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent);
void SendToPrinterDialog::stripWhiteSpace(std::string& str)
{
if (str == "") { return; }
@@ -260,6 +261,14 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
m_button_refresh->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_refresh, this);
m_sizer_printer->Add(m_button_refresh, 0, wxALL | wxLEFT, FromDIP(5));
/*select storage*/
m_storage_panel = new wxPanel(this);
m_storage_panel->SetBackgroundColour(*wxWHITE);
m_storage_sizer = new wxBoxSizer(wxHORIZONTAL);
m_storage_panel->SetSizer(m_storage_sizer);
m_storage_panel->Layout();
m_statictext_printer_msg = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
m_statictext_printer_msg->SetFont(::Label::Body_13);
m_statictext_printer_msg->SetForegroundColour(*wxBLACK);
@@ -517,6 +526,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
m_sizer_main->Add(m_line_materia, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(12));
m_sizer_main->Add(m_sizer_printer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30));
m_sizer_main->Add(m_storage_panel, 0, wxALIGN_CENTER|wxTOP, FromDIP(8));
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(11));
m_sizer_main->Add(m_statictext_printer_msg, 0, wxALIGN_CENTER_HORIZONTAL, 0);
m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(22));
@@ -538,6 +548,49 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
wxGetApp().UpdateDlgDarkUI(this);
}
std::string SendToPrinterDialog::get_storage_selected()
{
for (const auto& radio : m_storage_radioBox) {
if (radio->GetValue()) {
return radio->GetLabel().ToStdString();
}
}
return "";
}
void SendToPrinterDialog::update_storage_list(std::vector<std::string> storages)
{
m_storage_radioBox.clear();
m_storage_panel->DestroyChildren();
for (int i=0; i < storages.size(); i++) {
RadioBox* radiobox = new RadioBox(m_storage_panel);
Label* storage_text = new Label(m_storage_panel, storages[i]);
radiobox->SetLabel(storages[i]);
radiobox->Bind(wxEVT_LEFT_DOWN, [this, radiobox](auto& e) {
for (const auto& radio : m_storage_radioBox) {
radio->SetValue(false);
}
radiobox->SetValue(true);
});
if (m_storage_radioBox.size() > 0) {
m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6));
auto radio = m_storage_radioBox.front();
radio->SetValue(true);
}
m_storage_sizer->Add(radiobox, 0, wxALIGN_CENTER, 0);
m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6));
m_storage_sizer->Add(storage_text, 0, wxALIGN_CENTER,0);
m_storage_radioBox.push_back(radiobox);
}
m_storage_panel->Layout();
m_storage_panel->Fit();
Layout();
Fit();
}
void SendToPrinterDialog::update_print_error_info(int code, std::string msg, std::string extra)
{
m_print_error_code = code;
@@ -669,6 +722,15 @@ void SendToPrinterDialog::init_timer()
void SendToPrinterDialog::on_cancel(wxCloseEvent &event)
{
m_worker->cancel_all();
if (m_file_sys) {
m_file_sys->CancelUploadTask();
if (m_task_timer && m_task_timer->IsRunning()) {
m_task_timer->Stop();
m_task_timer.reset();
}
}
this->EndModal(wxID_CANCEL);
}
@@ -706,6 +768,9 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
m_status_bar->set_cancel_callback_fina([this]() {
BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled";
m_worker->cancel_all();
if (m_file_sys) {
m_file_sys->CancelUploadTask();
}
m_is_canceled = true;
wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL);
wxQueueEvent(this, event);
@@ -760,42 +825,70 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event)
fs::path default_output_file_path = boost::filesystem::path(default_output_file.c_str());
file_name = default_output_file_path.filename().string();
}*/
if (obj_->is_lan_mode_printer()) {
update_print_status_msg(wxEmptyString, false, false);
if (m_file_sys) {
PrintPrepareData print_data;
m_plater->get_print_job_data(&print_data);
std::string project_name = m_current_project_name.utf8_string() + ".3mf";
std::string _3mf_path = print_data._3mf_path.string();
m_file_sys->SetUploadFile(_3mf_path, project_name, get_storage_selected());
m_file_sys->RequestUploadFile();
// time out
if (m_task_timer && m_task_timer->IsRunning())
m_task_timer->Stop();
m_task_timer.reset(new wxTimer());
m_task_timer->SetOwner(this);
this->Bind(
wxEVT_TIMER,
[this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
fs->CancelUploadTask(false);
update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true);
},
m_task_timer->GetId());
m_task_timer->StartOnce(timeout_period);
}
} else {
auto m_send_job = std::make_unique<SendJob>(m_printer_last_select);
m_send_job->m_dev_ip = obj_->dev_ip;
m_send_job->m_access_code = obj_->get_access_code();
m_send_job->m_dev_ip = obj_->dev_ip;
m_send_job->m_access_code = obj_->get_access_code();
#if !BBL_RELEASE_TO_PUBLIC
m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false;
m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false;
m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false;
m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false;
#else
m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp;
m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt;
m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp;
m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt;
#endif
m_send_job->connection_type = obj_->connection_type();
m_send_job->cloud_print_only = true;
m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL;
m_send_job->set_project_name(m_current_project_name.utf8_string());
m_send_job->connection_type = obj_->connection_type();
m_send_job->cloud_print_only = true;
m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL;
m_send_job->set_project_name(m_current_project_name.utf8_string());
enable_prepare_mode = false;
enable_prepare_mode = false;
m_send_job->on_check_ip_address_fail([this](int result) {
wxCommandEvent* evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS);
wxQueueEvent(this, evt);
wxGetApp().show_ip_address_enter_dialog();
});
m_send_job->on_check_ip_address_fail([this](int result) {
wxCommandEvent *evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS);
wxQueueEvent(this, evt);
wxGetApp().show_ip_address_enter_dialog();
});
if (obj_->is_lan_mode_printer()) {
m_send_job->set_check_mode();
m_send_job->check_and_continue();
if (obj_->is_lan_mode_printer()) {
m_send_job->set_check_mode();
m_send_job->check_and_continue();
}
replace_job(*m_worker, std::move(m_send_job));
}
replace_job(*m_worker, std::move(m_send_job));
BOOST_LOG_TRIVIAL(info) << "send_job: send print job";
}
@@ -837,9 +930,10 @@ void SendToPrinterDialog::update_user_machine_list()
void SendToPrinterDialog::on_refresh(wxCommandEvent &event)
{
BOOST_LOG_TRIVIAL(info) << "m_printer_last_select: on_refresh";
show_status(PrintDialogStatus::PrintStatusRefreshingMachineList);
update_user_machine_list();
/* show_status(PrintDialogStatus::PrintStatusRefreshingMachineList);
update_user_machine_list();*/
/*todo refresh*/
if (m_file_sys) { m_file_sys->Retry(); }
}
void SendToPrinterDialog::on_print_job_cancel(wxCommandEvent &evt)
@@ -991,8 +1085,11 @@ void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event)
obj->command_request_push_all();
if (!dev->get_selected_machine()) {
dev->set_selected_machine(m_printer_last_select, true);
if (m_file_sys) m_file_sys.reset();
}else if (dev->get_selected_machine()->dev_id != m_printer_last_select) {
update_storage_list(std::vector<std::string>());
dev->set_selected_machine(m_printer_last_select, true);
if (m_file_sys) m_file_sys.reset();
}
}
else {
@@ -1076,8 +1173,137 @@ void SendToPrinterDialog::update_show_status()
}
if (!m_is_in_sending_mode) {
show_status(PrintDialogStatus::PrintStatusReadingFinished);
return;
if (obj_->connection_type() == "lan") {
show_status(PrintDialogStatus::PrintStatusReadingFinished);
return;
} else if (obj_->connection_type() == "cloud") {
Enable(obj_ && obj_->is_connected() && obj_->m_push_count > 0);
std::string dev_id = obj_->dev_ip;
if (m_file_sys) {
if (dev_id == m_device_select) {
if ((m_waiting_enable && IsEnabled()) || (m_waiting_support && obj_->file_remote))
m_file_sys->Retry();
return;
}
}
m_device_select.swap(dev_id);
m_file_sys = boost::make_shared<PrinterFileSystem>();
m_file_sys->Attached();
m_file_sys->Bind(EVT_STATUS_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
e.Skip();
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
wxString msg;
int status = e.GetInt();
int extra = e.GetExtraLong();
switch (status) {
case PrinterFileSystem::Initializing:
case PrinterFileSystem::Connecting: show_status(PrintDialogStatus::PrintStatusReading); break;
case PrinterFileSystem::ListSyncing: {
show_status(PrintDialogStatus::PrintStatusReading);
boost::uint32_t seq = fs->RequestMediaAbility(3);
if (m_task_timer && m_task_timer->IsRunning())
m_task_timer->Stop();
m_task_timer.reset(new wxTimer());
m_task_timer->SetOwner(this);
this->Bind(wxEVT_TIMER, [this, wfs_1 = boost::weak_ptr(fs), seq](auto e) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
boost::shared_ptr fs_1(wfs_1.lock());
if (!fs_1) return;
fs_1->CancelUploadTask(false);
update_print_status_msg(_L("Media capability acquisition timeout, please check if the firmware version supports it."), false, true);
}, m_task_timer->GetId());
m_task_timer->StartOnce(timeout_period);
break;
}
case PrinterFileSystem::Failed: msg = _L("Please check the network and try again, You can restart or update the printer if the issue persists."); break;
}
if (!msg.empty()) {
show_status(PrintDialogStatus::PrintStatusPublicInitFailed);
update_print_status_msg(msg, false, true);
}
if (e.GetInt() == PrinterFileSystem::Initializing) {
CallAfter([=] {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
fetchUrl(boost::weak_ptr(fs));
});
}
});
m_file_sys->Bind(EVT_MEDIA_ABILITY_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (m_task_timer && m_task_timer->IsRunning()) {
m_task_timer->Stop();
m_task_timer.reset();
}
m_ability_list = fs->GetMediaAbilityList();
if (e.GetInt() == PrinterFileSystem::RequestMediaAbilityStatus::S_SUCCESS) {
update_storage_list(m_ability_list);
show_status(PrintDialogStatus::PrintStatusReadingFinished);
} else {
show_status(PrintDialogStatus::PrintStatusPublicInitFailed);
update_print_status_msg(e.GetString(), false, true);
}
});
m_file_sys->Bind(EVT_UPLOADING, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
int progress = e.GetInt();
m_status_bar->set_progress(10 + std::floor(progress * 0.9));
if (m_task_timer && m_task_timer->IsRunning()) m_task_timer->Stop();
if (progress == 99) {
m_task_timer.reset(new wxTimer());
m_task_timer->SetOwner(this);
this->Bind(
wxEVT_TIMER,
[this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
fs->CancelUploadTask(false);
update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true);
},
m_task_timer->GetId());
m_task_timer->StartOnce(timeout_period);
}
});
m_file_sys->Bind(EVT_UPLOAD_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (e.GetInt() == PrinterFileSystem::FF_UPLOADDONE) {
show_status(PrintDialogStatus::PrintStatusReadingFinished);
wxCommandEvent *evt = new wxCommandEvent(m_plater->get_send_finished_event());
evt->SetString(from_u8(m_current_project_name.utf8_string()));
wxQueueEvent(m_plater, evt);
} else if (PrinterFileSystem::FF_UPLOADCANCEL) {
show_status(PrintDialogStatus::PrintStatusPublicUploadFiled);
wxString err_msg = e.GetString();
if (err_msg.IsEmpty())
err_msg = _u8L("Sending failed, please try again!");
update_print_status_msg(err_msg, false, true);
}
});
m_file_sys->Start();
}
}
}
@@ -1139,7 +1365,7 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector<wxSt
if (status == PrintDialogStatus::PrintStatusInit) {
update_print_status_msg(wxEmptyString, false, false);
Enable_Send_Button(false);
Enable_Refresh_Button(true);
Enable_Refresh_Button(false);
}
else if (status == PrintDialogStatus::PrintStatusNoUserLogin) {
wxString msg_text = _L("No login account, only printers in LAN mode are displayed");
@@ -1217,6 +1443,13 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector<wxSt
update_print_status_msg(msg_text, true, true);
Enable_Send_Button(false);
Enable_Refresh_Button(true);
} else if (status == PrintDialogStatus::PrintStatusPublicInitFailed) {
Enable_Send_Button(false);
Enable_Refresh_Button(true);
} else if (status == PrintDialogStatus::PrintStatusPublicUploadFiled) {
prepare_mode();
Enable_Send_Button(true);
Enable_Refresh_Button(true);
}
else {
Enable_Send_Button(true);
@@ -1369,6 +1602,7 @@ bool SendToPrinterDialog::Show(bool show)
// set default value when show this dialog
if (show) {
update_storage_list(std::vector<std::string>());
wxGetApp().reset_to_active();
set_default();
update_user_machine_list();
@@ -1383,12 +1617,84 @@ bool SendToPrinterDialog::Show(bool show)
Layout();
Fit();
if (show) { CenterOnParent(); }
if (m_file_sys) {
show ? m_file_sys->Start() : m_file_sys->Stop();
}
return DPIDialog::Show(show);
}
extern wxString hide_passwd(wxString url, std::vector<wxString> const &passwords);
extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url));
void SendToPrinterDialog::fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs)
{
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (!IsEnabled()) {
m_waiting_enable = true;
fs->SetUrl("0");
return;
}
m_waiting_enable = false;
DeviceManager *dm = GUI::wxGetApp().getDeviceManager();
MachineObject *obj = dm->get_selected_machine();
std::string dev_ver = obj->get_ota_version();
std::string dev_id = obj->dev_id;
int remote_proto = obj->file_remote;
if (!remote_proto) {
m_waiting_support = true;
fs->SetUrl("0");
return;
}
if (obj->is_camera_busy_off()) {
fs->SetUrl("0");
return;
}
m_waiting_support = false;
NetworkAgent *agent = wxGetApp().getAgent();
std::string agent_version = agent ? agent->get_version() : "";
if (agent) {
std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""};
agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[remote_proto],
[this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) {
if (boost::algorithm::starts_with(url, "bambu:///")) {
url += "&device=" + m;
url += "&net_ver=" + v;
url += "&dev_ver=" + dv;
url += "&refresh_url=" + boost::lexical_cast<std::string>(&refresh_agora_url);
url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid");
url += "&cli_ver=" + std::string(SLIC3R_VERSION);
}
BOOST_LOG_TRIVIAL(info) << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="});
std::cout << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="});
CallAfter([=] {
boost::shared_ptr fs(wfs.lock());
if (!fs) return;
if (boost::algorithm::starts_with(url, "bambu:///")) {
fs->SetUrl(url);
} else {
fs->SetUrl("3");
}
});
});
}
return;
}
SendToPrinterDialog::~SendToPrinterDialog()
{
delete m_refresh_timer;
if (m_task_timer && m_task_timer->IsRunning())
m_task_timer->Stop();
}
}

View File

@@ -38,6 +38,7 @@
#include "Widgets/ScrolledWindow.hpp"
#include <wx/simplebook.h>
#include <wx/hashmap.h>
#include "Printer/PrinterFileSystem.h"
namespace Slic3r {
namespace GUI {
@@ -62,6 +63,7 @@ private:
std::string m_print_error_extra;
std::string m_print_info;
std::string m_printer_last_select;
std::string m_device_select;
wxString m_current_project_name;
TextInput* m_rename_input{ nullptr };
@@ -83,6 +85,8 @@ private:
wxPanel* m_panel_image{ nullptr };
wxPanel* m_rename_normal_panel{ nullptr };
wxPanel* m_line_materia{ nullptr };
wxBoxSizer* m_storage_sizer{ nullptr };
wxPanel* m_storage_panel{ nullptr };
wxSimplebook* m_simplebook{ nullptr };
wxStaticText* m_statictext_finish{ nullptr };
wxStaticText* m_stext_sending{ nullptr };
@@ -111,10 +115,17 @@ private:
wxColour m_colour_def_color{ wxColour(255, 255, 255) };
wxColour m_colour_bold_color{ wxColour(38, 46, 48) };
wxTimer* m_refresh_timer{ nullptr };
std::unique_ptr<wxTimer> m_task_timer{ nullptr };
std::shared_ptr<BBLStatusBarSend> m_status_bar;
std::unique_ptr<Worker> m_worker;
wxScrolledWindow* m_sw_print_failed_info{nullptr};
std::shared_ptr<int> m_token = std::make_shared<int>(0);
std::vector<RadioBox*> m_storage_radioBox;
bool m_waiting_support{ false };
bool m_waiting_enable{ false };
boost::shared_ptr<PrinterFileSystem> m_file_sys;
std::vector<std::string> m_ability_list;
public:
SendToPrinterDialog(Plater* plater = nullptr);
@@ -153,8 +164,13 @@ public:
void show_print_failed_info(bool show, int code = 0, wxString description = wxEmptyString, wxString extra = wxEmptyString);
void update_print_error_info(int code, std::string msg, std::string extra);
void on_change_color_mode() { wxGetApp().UpdateDlgDarkUI(this); }
void update_storage_list(std::vector<std::string> storages);
std::string get_storage_selected();
wxString format_text(wxString& m_msg);
std::vector<std::string> sort_string(std::vector<std::string> strArray);
void fetchUrl(boost::weak_ptr<PrinterFileSystem> wfs);
};
wxDECLARE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent);

View File

@@ -0,0 +1,697 @@
#include <GL/glew.h>
#include "SkipPartCanvas.hpp"
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <boost/log/trivial.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/nowide/fstream.hpp>
#include <expat.h>
#include <earcut/earcut.hpp>
#include <libslic3r/Color.hpp>
#include <filesystem>
#include <map>
wxDEFINE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent);
wxDEFINE_EVENT(EVT_CANVAS_PART, wxCommandEvent);
namespace Slic3r {
namespace GUI {
SkipPartCanvas::SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs)
: wxGLCanvas(parent, dispAttrs) {
context_ = new wxGLContext(this);
this->Bind(wxEVT_PAINT, &SkipPartCanvas::OnPaint, this);
this->Bind(wxEVT_MOUSEWHEEL, &SkipPartCanvas::OnMouseWheel, this);
this->Bind(wxEVT_LEFT_DOWN, &SkipPartCanvas::OnMouseLeftDown, this);
this->Bind(wxEVT_LEFT_DCLICK, &SkipPartCanvas::OnMouseLeftDown, this);
this->Bind(wxEVT_LEFT_UP, &SkipPartCanvas::OnMouseLeftUp, this);
this->Bind(wxEVT_RIGHT_DOWN, &SkipPartCanvas::OnMouseRightDown, this);
this->Bind(wxEVT_RIGHT_UP, &SkipPartCanvas::OnMouseRightUp, this);
this->Bind(wxEVT_SIZE, &SkipPartCanvas::OnSize, this);
this->Bind(wxEVT_MOTION, &SkipPartCanvas::OnMouseMotion, this);
}
void SkipPartCanvas::LoadPickImage(const std::string & path)
{
if(!std::filesystem::exists(path)) return;
auto ParseShapeId = [](cv::Mat image, const std::vector<std::vector<cv::Point>> &contours, const std::vector<cv::Vec4i> &hierarchy, int root_idx) -> uint32_t {
cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1);
cv::drawContours(mask, contours, root_idx, 255, cv::FILLED);
int child = hierarchy[root_idx][2];
while (child != -1) {
cv::drawContours(mask, contours, child, 0, cv::FILLED);
child = hierarchy[child][0];
}
std::vector<cv::Vec3b> pixels;
for (int y = 0; y < image.rows; ++y) {
for (int x = 0; x < image.cols; ++x) {
if (mask.at<uchar>(y, x)) { pixels.push_back(image.at<cv::Vec3b>(y, x)); }
}
}
std::map<cv::Vec3b, int, std::function<bool(const cv::Vec3b &, const cv::Vec3b &)>> colorCount(
[](const cv::Vec3b &a, const cv::Vec3b &b) { return std::lexicographical_compare(a.val, a.val + 3, b.val, b.val + 3); });
for (auto &c : pixels) colorCount[c]++;
cv::Vec3b main_color;
int max_count = 0;
int total_count = 0;
for (const auto &kv : colorCount) {
if (kv.second > max_count) {
max_count = kv.second;
main_color = kv.first;
}
total_count += kv.second;
}
SkipIdHelper helper{main_color[2], main_color[1], main_color[0]};
helper.reverse();
return (max_count * 2 > total_count) ? helper.value : 0;
};
parts_state_.clear();
parts_triangles_.clear();
pick_parts_.clear();
int preffered_w{FromDIP(400)}, preffered_h{FromDIP(400)};
cv::Mat src_image = cv::imread(path, cv::IMREAD_UNCHANGED);
cv::cvtColor(src_image, src_image, cv::COLOR_BGRA2BGR); // remove alpha
float zoom_x{static_cast<float>(preffered_w) / src_image.cols};
float zoom_y{static_cast<float>(preffered_h) / src_image.rows};
float image_scale{0};
if (abs(zoom_x - 1) > abs(zoom_y - 1))
image_scale = zoom_x;
else
image_scale = zoom_y;
image_view_scale_ = 1 / image_scale;
pick_image_ = src_image;
std::vector<cv::Mat> channels;
cv::Mat gray; // convert to gray
cv::cvtColor(pick_image_, gray, cv::COLOR_BGR2GRAY);
cv::Mat mask; // convery to binary
cv::threshold(gray, mask, 0, 255, cv::THRESH_BINARY);
std::vector<std::vector<cv::Point>> pick_counters;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(mask, pick_counters, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_TC89_KCOS);
auto compute_depth = [&](int idx) {
int depth = 0;
while (hierarchy[idx][3] != -1) {
depth++;
idx = hierarchy[idx][3];
}
return depth;
};
for (int i = 0; i < pick_counters.size(); ++i) {
int depth = compute_depth(i);
int parent = hierarchy[i][3];
if (parent != -1) continue;
auto id = ParseShapeId(pick_image_, pick_counters, hierarchy, i);
if (id > 0) {
std::vector<FloatPoint> flat_points;
std::vector<std::vector<FloatPoint>> polygon;
// part body
{
polygon.emplace_back();
for (const auto &pt : pick_counters[i]) {
FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f};
polygon.back().push_back(fp);
flat_points.push_back(fp);
}
int child = hierarchy[i][2];
while (child != -1) {
polygon.emplace_back();
for (const auto &pt : pick_counters[child]) {
FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f};
polygon.back().push_back(fp);
flat_points.push_back(fp);
}
child = hierarchy[child][0];
}
std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
std::vector<FloatPoint> final_counter;
for (size_t j = 0; j < indices.size(); j += 3) {
final_counter.push_back(flat_points[indices[j]]);
final_counter.push_back(flat_points[indices[j + 1]]);
final_counter.push_back(flat_points[indices[j + 2]]);
}
parts_triangles_[id].emplace_back(final_counter);
}
// part outlines
{
pick_parts_[id].emplace_back(pick_counters[i]);
int child = hierarchy[i][2];
while (child != -1) {
pick_parts_[id].emplace_back(pick_counters[child]);
child = hierarchy[child][0];
}
}
if (parts_state_.find(id) == parts_state_.end()) parts_state_.emplace(id, psUnCheck);
}
}
}
void SkipPartCanvas::ZoomIn(const int zoom_percent)
{
SetZoomPercent(zoom_percent_ + zoom_percent);
Refresh();
}
void SkipPartCanvas::ZoomOut(const int zoom_percent)
{
SetZoomPercent(zoom_percent_ - zoom_percent);
Refresh();
}
void SkipPartCanvas::SwitchDrag(const bool drag_on)
{
fixed_draging_ = drag_on;
AutoSetCursor();
}
void SkipPartCanvas::UpdatePartsInfo(const PartsInfo& parts)
{
for (auto const& part : parts) {
if (auto res = parts_state_.find(part.first); res != parts_state_.end())
res->second = part.second;
}
Refresh();
}
void DrawRoundedRect(float x, float y, float width, float height, float radius, const ColorRGB& color, int segments = 16)
{
glColor3f(color.r(), color.g(), color.b());
// 1. Draw center rectangle
glBegin(GL_QUADS);
glVertex2f(x + radius, y + radius);
glVertex2f(x + width - radius, y + radius);
glVertex2f(x + width - radius, y + height - radius);
glVertex2f(x + radius, y + height - radius);
glEnd();
// 2. Draw side rectangles (excluding corners)
glBegin(GL_QUADS);
// Left
glVertex2f(x, y + radius);
glVertex2f(x + radius, y + radius);
glVertex2f(x + radius, y + height - radius);
glVertex2f(x, y + height - radius);
// Right
glVertex2f(x + width - radius, y + radius);
glVertex2f(x + width, y + radius);
glVertex2f(x + width, y + height - radius);
glVertex2f(x + width - radius, y + height - radius);
// Top
glVertex2f(x + radius, y + height - radius);
glVertex2f(x + width - radius, y + height - radius);
glVertex2f(x + width - radius, y + height);
glVertex2f(x + radius, y + height);
// Bottom
glVertex2f(x + radius, y);
glVertex2f(x + width - radius, y);
glVertex2f(x + width - radius, y + radius);
glVertex2f(x + radius, y + radius);
glEnd();
// 3. Draw corners
auto drawCorner = [&](float cx, float cy, float startAngle) {
glBegin(GL_TRIANGLE_FAN);
glVertex2f(cx, cy);
for (int i = 0; i <= segments; ++i) {
float angle = startAngle + (M_PI * 0.5f) * (float)i / segments;
glVertex2f(cx + cosf(angle) * radius, cy + sinf(angle) * radius);
}
glEnd();
};
drawCorner(x + radius, y + radius, M_PI); // bottom-left
drawCorner(x + width - radius, y + radius, 1.5f * M_PI); // bottom-right
drawCorner(x + width - radius, y + height - radius, 0.0f); // top-right
drawCorner(x + radius, y + height - radius, 0.5f * M_PI); // top-left
}
void SkipPartCanvas::Render()
{
constexpr float border_w = 3.f;
constexpr int uncheckd_stencil =1;
constexpr int checkd_stencil = 2;
constexpr int skipped_stencil = 3;
SetCurrent(*context_);
glPushAttrib(GL_ALL_ATTRIB_BITS);
int w, h;
GetClientSize(&w, &h);
#if defined(__APPLE__)
double scale = GetDPIScaleFactor();
glViewport(0, 0, w * scale, h * scale);
#else
glViewport(0, 0, w, h);
#endif
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
auto view_rect = ViewPtToImagePt(wxPoint(w, h));
glOrtho(offset_.x, view_rect.x, view_rect.y, offset_.y, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(parent_color_.r(), parent_color_.g(), parent_color_.b(), 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
float rx = offset_.x;
float ry = offset_.y;
float rw = view_rect.x - offset_.x;
float rh = view_rect.y - offset_.y;
float radius = std::min(rw, rh) * 0.05f;
DrawRoundedRect(rx, ry, rw, rh, radius, ColorRGB{0.9f, 0.9f, 0.9f});
glEnable(GL_BLEND);
glEnable(GL_MULTISAMPLE);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glEnable(GL_STENCIL_TEST);
auto draw_shape = [this, border_w](const int stencil, const PartState part_type, const ColorRGB& rgb) {
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_ALWAYS, stencil, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
for (const auto& contour : parts_triangles_) {
auto part_info = parts_state_.find(contour.first);
if (part_info == parts_state_.end() || part_info->second != part_type)
continue;
glColor3f(1, 1, 1);
for (const auto &contour_item : contour.second) {
glBegin(GL_TRIANGLES);
for (size_t i = 0; i < contour_item.size(); i += 3) {
glVertex2f(contour_item[i][0], contour_item[i][1]);
glVertex2f(contour_item[i + 1][0], contour_item[i + 1][1]);
glVertex2f(contour_item[i + 2][0], contour_item[i + 2][1]);
}
glEnd();
}
}
for (const auto& contour : pick_parts_) {
if (contour.first != this->hover_id_) continue;
auto part_info = parts_state_.find(contour.first);
if (part_info == parts_state_.end() || part_info->second != part_type)
continue;
glColor3f(rgb.r(), rgb.g(), rgb.b());
glLineWidth(border_w);
for (const auto &contour_item : contour.second) {
glBegin(GL_LINE_LOOP);
for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); }
glEnd();
}
}
};
// draw unchecked shapes
// stencil1 => unchecked
draw_shape(uncheckd_stencil, psUnCheck, ColorRGB{0, 174 / 255.f, 66 / 255.f});
// draw checked shapes
// stencil2 => checked
draw_shape(checkd_stencil, psChecked, ColorRGB{208 / 255.f, 27 / 255.f, 66 / 255.f});
// draw skipped shapes
// stencil3 => skipped
draw_shape(skipped_stencil, psSkipped, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f});
auto draw_mask = [this, view_rect, border_w, w, h](const int stencil, const PartState part_type,
const ColorRGB& background, const ColorRGB& line, const ColorRGB& bound) {
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_EQUAL, stencil, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Don't change stencil
glColor3f(background.r(), background.g(), background.b());
glBegin(GL_POLYGON);
glVertex2f(offset_.x, offset_.y);
glVertex2f(offset_.x, view_rect.y);
glVertex2f(view_rect.x, view_rect.y);
glVertex2f(view_rect.x, offset_.y);
glEnd();
// draw main color
glColor3f(line.r(), line.g(), line.b());
// re-draw shape bound
for (const auto& contour : pick_parts_) {
auto part_info = parts_state_.find(contour.first);
if (part_info == parts_state_.end() || part_info->second != part_type)
continue;
glColor3f(bound.r(), bound.g(), bound.b());
glLineWidth(border_w);
for (const auto &contour_item : contour.second) {
glBegin(GL_LINE_LOOP);
for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); }
glEnd();
}
}
};
draw_mask(checkd_stencil, psChecked, ColorRGB{239 / 255.f, 175 / 255.f, 175 / 255.f},
ColorRGB{225 / 255.f, 71 / 255.f, 71 / 255.f}, ColorRGB{208 / 255.f, 27 / 255.f, 27 / 255.f});
draw_mask(skipped_stencil, psSkipped, ColorRGB{159 / 255.f, 159 / 255.f, 159 / 255.f},
ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f}, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f});
glDisable(GL_STENCIL_TEST);
glPopAttrib();
glFlush();
}
void SkipPartCanvas::DebugLogLine(std::string str)
{
//if (!log_ctrl)
// return;
//log_ctrl->AppendText(str + "\n");
}
void SkipPartCanvas::SendSelectEvent(int id, PartState state) {
wxCommandEvent evt(EVT_CANVAS_PART);
evt.SetExtraLong(id);
evt.SetInt(static_cast<int>(state));
wxPostEvent(this, evt);
}
void SkipPartCanvas::SendZoomEvent(int zoom_percent) {
wxCommandEvent evt(EVT_ZOOM_PERCENT);
evt.SetInt(zoom_percent_);
wxPostEvent(this, evt);
}
inline double SkipPartCanvas::Zoom() const
{
return zoom_percent_ / 100.0f;
}
inline wxPoint SkipPartCanvas::ViewPtToImagePt(const wxPoint& view_pt) const
{
return wxPoint(view_pt.x * image_view_scale_ / Zoom(), view_pt.y * image_view_scale_ / Zoom()) + offset_;
}
uint32_t SkipPartCanvas::GetIdAtImagePt(const wxPoint& image_pt) const
{
if (image_pt.x >= 0 && image_pt.x < pick_image_.cols
&& image_pt.y >= 0 && image_pt.y < pick_image_.rows) {
// at(row, col)=>at(y, x)
cv::Vec3b bgr = pick_image_.at<cv::Vec3b>(image_pt.y, image_pt.x);
SkipIdHelper helper{bgr[2], bgr[1], bgr[0]};
helper.reverse();
return helper.value;
} else {
return 0;
}
}
inline uint32_t SkipPartCanvas::GetIdAtViewPt(const wxPoint& view_pt) const
{
wxPoint pt_at_image = ViewPtToImagePt(view_pt);
return GetIdAtImagePt(pt_at_image);
}
void SkipPartCanvas::SetZoomPercent(const int value)
{
zoom_percent_ = std::clamp(value, 100, 1000);
std::ostringstream oss;
oss << "zoom to " << zoom_percent_;
DebugLogLine(oss.str());
SendZoomEvent(zoom_percent_);
}
void SkipPartCanvas::SetOffset(const wxPoint& value)
{
int w, h;
GetClientSize(&w, &h);
int max_w = static_cast<int>(w * (1 - 1 / Zoom())) >= 0 ? static_cast<int>(w * (1 - 1 / Zoom())) : 0;
int max_h = static_cast<int>(w * (1 - 1 / Zoom())) >= 0 ? static_cast<int>(h * (1 - 1 / Zoom())) : 0;
offset_.x = std::clamp(value.x, 0, max_w);
offset_.y = std::clamp(value.y, 0, max_h);
}
void SkipPartCanvas::AutoSetCursor()
{
if(is_draging_ || fixed_draging_)
SetCursor(wxCursor(wxCURSOR_HAND));
else
SetCursor(wxCursor(wxCURSOR_NONE));
}
void SkipPartCanvas::StartDrag(const wxPoint& mouse_pt)
{
drag_start_pt_ = mouse_pt;
drag_start_offset_ = offset_;
is_draging_ = true;
AutoSetCursor();
}
void SkipPartCanvas::ProcessDrag(const wxPoint& mouse_pt)
{
wxPoint drag_offset = (mouse_pt - drag_start_pt_) * image_view_scale_;
SetOffset(- wxPoint(drag_offset.x / Zoom(), drag_offset.y / Zoom()) + drag_start_offset_);
Refresh();
}
void SkipPartCanvas::ProcessHover(const wxPoint& mouse_pt)
{
auto id_at_mouse = GetIdAtViewPt(mouse_pt);
int new_hover_id { -1 };
auto part_state = parts_state_.find(id_at_mouse);
if (part_state != parts_state_.end() && part_state->second == psUnCheck) {
new_hover_id = id_at_mouse;
};
if (new_hover_id != this->hover_id_) {
this->hover_id_ = new_hover_id;
Refresh();
}
}
void SkipPartCanvas::EndDrag()
{
is_draging_ = false;
AutoSetCursor();
}
void SkipPartCanvas::OnPaint(wxPaintEvent &event)
{
wxPaintDC dc(this);
if (!IsShown()) return;
SetCurrent(*context_);
Render();
SwapBuffers();
}
void SkipPartCanvas::OnSize(wxSizeEvent& event)
{
event.Skip();
}
void SkipPartCanvas::OnMouseLeftDown(wxMouseEvent& event)
{
DebugLogLine("OnMouseLeftDown");
if (!event.LeftIsDown()) {
event.Skip();
DebugLogLine("skip----OnMouseLeftDown");
return;
}
if (fixed_draging_)
StartDrag(wxPoint(event.GetX(), event.GetY()));
left_down_ = true;
}
void SkipPartCanvas::OnMouseLeftUp(wxMouseEvent& event)
{
DebugLogLine("OnMouseLeftUp");
if (event.LeftIsDown() || !left_down_) {
event.Skip();
DebugLogLine("skip----OnMouseLeftUp");
return;
}
auto id_at_mouse = GetIdAtViewPt(wxPoint(event.GetX(), event.GetY()));
auto part_state = parts_state_.find(id_at_mouse);
if (part_state != parts_state_.end() && part_state->second != psSkipped) {
if (part_state->second == psUnCheck)
part_state = parts_state_.insert_or_assign(part_state->first, psChecked).first;
else
part_state = parts_state_.insert_or_assign(part_state->first, psUnCheck).first;
// if (select_callback_)
// select_callback_(part_state->first, part_state->second);
SendSelectEvent(part_state->first, part_state->second);
}
left_down_ = false;
if (fixed_draging_)
EndDrag();
else {
Refresh();
}
}
void SkipPartCanvas::OnMouseRightDown(wxMouseEvent& event)
{
DebugLogLine("OnMouseRightDown");
if (!event.RightIsDown()) {
event.Skip();
DebugLogLine("skip----OnMouseRightDown");
return;
}
StartDrag(wxPoint(event.GetX(), event.GetY()));
}
void SkipPartCanvas::OnMouseRightUp(wxMouseEvent& event)
{
DebugLogLine("OnMouseRightUp");
if (event.RightIsDown() || !is_draging_) {
event.Skip();
DebugLogLine("skip----OnMouseRightUp");
return;
}
EndDrag();
}
void SkipPartCanvas::OnMouseMotion(wxMouseEvent& event)
{
ProcessHover(wxPoint(event.GetX(), event.GetY()));
if (!event.RightIsDown() && !(event.LeftIsDown() && fixed_draging_)) {
event.Skip();
return;
}
ProcessDrag(wxPoint(event.GetX(), event.GetY()));
}
void SkipPartCanvas::OnMouseWheel(wxMouseEvent& event)
{
wxPoint view_mouse = wxPoint(event.GetX(), event.GetY());
auto pre_image_pos = ViewPtToImagePt(view_mouse);
SetZoomPercent(zoom_percent_ + 10 * (event.GetWheelRotation() / 120.0));
auto now_image_pos = ViewPtToImagePt(view_mouse);
SetOffset(offset_ - (now_image_pos - pre_image_pos));
Refresh();
}
// Base class with error messages management
void _BBS_3MF_Base::add_error(const std::string &error) const
{
boost::unique_lock l(mutex);
m_errors.push_back(error);
}
void _BBS_3MF_Base::clear_errors() { m_errors.clear(); }
void _BBS_3MF_Base::log_errors()
{
for (const std::string &error : m_errors) BOOST_LOG_TRIVIAL(error) << error;
}
ModelSettingHelper::ModelSettingHelper(const std::string &path) : path_(path) {}
bool ModelSettingHelper::Parse()
{
boost::nowide::fstream fs(path_);
if (!fs) {
add_error("Failed to open file\n");
return false;
}
XML_Parser parser = XML_ParserCreate(nullptr);
if (!parser) {
add_error("Unable to create parser");
return false;
}
XML_SetUserData(parser, this);
XML_SetElementHandler(parser, ModelSettingHelper::StartElementHandler, ModelSettingHelper::EndElementHandler);
try {
char buffer[4000];
while (fs.read(buffer, sizeof(buffer)) || fs.gcount() > 0) {
auto ret = XML_Parse(parser, buffer, static_cast<int>(fs.gcount()), fs.eof());
if (ret != XML_STATUS_OK) {
add_error("return value of XML_Parse doesn't match XM_STATUS_OK");
XML_ParserFree(parser);
return false;
}
}
}
catch (std::exception& e) {
add_error(std::string("exception:") + e.what());
XML_ParserFree(parser);
return false;
}
XML_ParserFree(parser);
return true;
}
void XMLCALL ModelSettingHelper::StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts)
{
ModelSettingHelper *self = static_cast<ModelSettingHelper *>(userData);
if (strcmp(name, "plate") == 0) {
self->context_.current_plate = PlateInfo(); // start a new plate
self->context_.in_plate = true;
} else if (strcmp(name, "metadata") == 0 && self->context_.in_plate) {
std::string key, value;
for (int i = 0; atts[i]; i += 2) {
if (strcmp(atts[i], "key") == 0) key = atts[i + 1];
if (strcmp(atts[i], "value") == 0) value = atts[i + 1];
}
if (key == "index") { self->context_.current_plate.index = std::stoi(value); }
if (key == "label_object_enabled") { self->context_.current_plate.label_object_enabled = value == "true"; }
} else if (strcmp(name, "object") == 0 && self->context_.in_plate) {
ObjectInfo obj;
for (int i = 0; atts[i]; i += 2) {
if (strcmp(atts[i], "identify_id") == 0) obj.identify_id = atoi(atts[i + 1]);
if (strcmp(atts[i], "name") == 0) obj.name = atts[i + 1];
}
self->context_.current_plate.objects.push_back(obj);
}
}
void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Char *name)
{
ModelSettingHelper *self = static_cast<ModelSettingHelper *>(userData);
if (strcmp(name, "plate") == 0 && self->context_.in_plate) {
self->context_.plates.push_back(self->context_.current_plate);
self->context_.current_plate = PlateInfo(); // reset
self->context_.in_plate = false;
}
}
std::vector<ObjectInfo> ModelSettingHelper::GetPlateObjects(int plate_idx) {
for (const auto &plate : context_.plates) {
if (plate.index == plate_idx) {
return plate.objects;
}
}
return std::vector<ObjectInfo>();
}
bool ModelSettingHelper::GetLabelObjectEnabled(int plate_idx)
{
for (const auto &plate : context_.plates) {
if (plate.index == plate_idx) { return plate.label_object_enabled; }
}
return false;
}
void ModelSettingHelper::DataHandler(const XML_Char *s, int len)
{
// do nothing
}
}
}

View File

@@ -0,0 +1,169 @@
#ifndef SKIPPARTCANVAS_H
#define SKIPPARTCANVAS_H
#include <wx/wx.h>
#include <wx/glcanvas.h>
#include <opencv2/opencv.hpp>
#include <wx/textctrl.h>
#include <vector>
#include <expat.h>
#include <libslic3r/Color.hpp>
#include <boost/thread/mutex.hpp>
#include "PartSkipCommon.hpp"
wxDECLARE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent);
wxDECLARE_EVENT(EVT_CANVAS_PART, wxCommandEvent);
namespace Slic3r {
namespace GUI {
using Coord = float;
using FloatPoint = std::array<Coord, 2>;
struct ObjectInfo {
std::string name{""};
int identify_id{-1};
PartState state{psUnCheck};
};
class SkipPartCanvas : public wxGLCanvas
{
union SkipIdHelper
{
uint32_t value = 0;
struct
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t _padding;
};
uint8_t bytes[4];
SkipIdHelper() = default;
SkipIdHelper(uint8_t red, uint8_t green, uint8_t blue)
: r(red), g(green), b(blue), _padding(0) {}
SkipIdHelper(uint32_t val): value(val){}
void reverse() {
uint8_t tmp{r};
r = b;
b = tmp;
}
};
public:
SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs);
~SkipPartCanvas() = default;
void SetParentBackground(const ColorRGB& color) {
parent_color_ = color;
}
void LoadPickImage(const std::string& path);
void ZoomIn(const int zoom_percent);
void ZoomOut(const int zoom_percent);
void SwitchDrag(const bool drag_on);
void UpdatePartsInfo(const PartsInfo& parts);
void SetZoomPercent(const int value);
void SetOffset(const wxPoint &value);
wxTextCtrl* log_ctrl;
protected:
void OnPaint(wxPaintEvent& event);
void OnSize(wxSizeEvent& event);
void OnMouseLeftDown(wxMouseEvent& event);
void OnMouseLeftUp(wxMouseEvent& event);
void OnMouseRightDown(wxMouseEvent& event);
void OnMouseRightUp(wxMouseEvent& event);
void OnMouseMotion(wxMouseEvent& event);
void OnMouseWheel(wxMouseEvent& event);
private:
wxGLContext* context_;
cv::Mat pick_image_;
std::unordered_map < uint32_t, std::vector<std::vector<FloatPoint>>> parts_triangles_;
std::unordered_map < uint32_t, std::vector<std::vector<cv::Point>>> pick_parts_;
std::unordered_map<uint32_t, PartState> parts_state_;
bool gl_inited_{false};
int zoom_percent_{100};
wxPoint offset_{0,0};
wxPoint drag_start_offset_{0,0};
wxPoint drag_start_pt_{0,0};
bool is_draging_{false};
bool fixed_draging_{false};
bool left_down_{false};
ColorRGB parent_color_ = ColorRGB();
int hover_id_{-1};
double image_view_scale_{1};
void SendSelectEvent(int id, PartState state);
void SendZoomEvent(int zoom_percent);
inline double Zoom() const;
inline wxPoint ViewPtToImagePt(const wxPoint& view_pt) const;
uint32_t GetIdAtImagePt(const wxPoint& image_pt) const;
inline uint32_t GetIdAtViewPt(const wxPoint& view_pt) const;
void ProcessHover(const wxPoint& mouse_pt);
void AutoSetCursor();
void StartDrag(const wxPoint& mouse_pt);
void ProcessDrag(const wxPoint& mouse_pt);
void EndDrag();
void Render();
void DebugLogLine(std::string str);
};
class _BBS_3MF_Base
{
mutable boost::mutex mutex;
mutable std::vector<std::string> m_errors;
protected:
void add_error(const std::string& error) const;
void clear_errors();
public:
void log_errors();
};
struct PlateInfo
{
int index{-1};
std::vector<ObjectInfo> objects;
bool label_object_enabled = false;
};
class ModelSettingHelper : public _BBS_3MF_Base
{
struct ParseContext
{
std::vector<PlateInfo> plates;
PlateInfo current_plate;
ObjectInfo temp_object;
bool in_plate = false;
};
public:
ModelSettingHelper(const std::string &path);
bool Parse();
std::vector<ObjectInfo> GetPlateObjects(int plate_idx);
bool GetLabelObjectEnabled(int plate_idx);
private:
std::string path_;
ParseContext context_;
static void XMLCALL StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts);
static void XMLCALL EndElementHandler(void *userData, const XML_Char *name);
void DataHandler(const XML_Char *s, int len);
};
}
}
#endif //SKIPPARTCANVAS_H

View File

@@ -233,12 +233,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
bSizer_task_name->Add(task_name_panel, 0, wxEXPAND, FromDIP(5));
/* wxFlexGridSizer *fgSizer_task = new wxFlexGridSizer(2, 2, 0, 0);
fgSizer_task->AddGrowableCol(0);
fgSizer_task->SetFlexibleDirection(wxVERTICAL);
fgSizer_task->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);*/
m_printing_stage_value = new wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END);
m_printing_stage_value->Wrap(-1);
m_printing_stage_value->SetMaxSize(wxSize(FromDIP(800),-1));
@@ -261,24 +255,34 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_staticText_profile_value->SetForegroundColour(0x6B6B6B);
auto progress_lr_panel = new wxPanel(parent, wxID_ANY);
progress_lr_panel->SetBackgroundColour(*wxWHITE);
auto m_panel_progress = new wxPanel(parent, wxID_ANY);
m_panel_progress->SetBackgroundColour(*wxWHITE);
auto m_sizer_progressbar = new wxBoxSizer(wxHORIZONTAL);
m_gauge_progress = new ProgressBar(m_panel_progress, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize);
m_gauge_progress = new ProgressBar(progress_lr_panel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize);
m_gauge_progress->SetValue(0);
m_gauge_progress->SetHeight(PROGRESSBAR_HEIGHT);
m_gauge_progress->SetMaxSize(wxSize(FromDIP(600), -1));
m_panel_progress->SetSizer(m_sizer_progressbar);
m_panel_progress->Layout();
m_panel_progress->SetSize(wxSize(-1, FromDIP(24)));
m_panel_progress->SetMaxSize(wxSize(-1, FromDIP(24)));
wxBoxSizer *bSizer_task_btn = new wxBoxSizer(wxHORIZONTAL);
bSizer_task_btn->Add(FromDIP(10), 0, 0);
m_button_pause_resume = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true);
StateColor white_bg(std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Disabled), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Hovered), std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Enabled),
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal));
m_button_partskip = new Button(progress_lr_panel, wxEmptyString, "print_control_partskip_disable", 0, 20, wxID_ANY);
m_button_partskip->Enable(false);
m_button_partskip->Hide();
m_button_partskip->SetBackgroundColor(white_bg);
m_button_partskip->SetIcon("print_control_partskip_disable");
m_button_partskip->SetBorderColor(*wxWHITE);
m_button_partskip->SetFont(Label::Body_12);
m_button_partskip->SetCornerRadius(0);
m_button_partskip->SetToolTip(_L("Parts Skip"));
m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip_hover"); });
m_button_partskip->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); });
m_button_pause_resume = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true);
m_button_pause_resume->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
if (m_button_pause_resume->GetToolTipText() == _L("Pause")) {
@@ -301,7 +305,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
}
});
m_button_abort = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
m_button_abort = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
m_button_abort->SetToolTip(_L("Stop"));
m_button_abort->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
@@ -312,19 +316,14 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_button_abort->SetBitmap_("print_control_stop"); }
);
m_sizer_progressbar->Add(m_gauge_progress, 1, wxALIGN_CENTER_VERTICAL, 0);
m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18));
m_sizer_progressbar->Add(m_button_pause_resume, 0, wxALL, FromDIP(5));
m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18));
m_sizer_progressbar->Add(m_button_abort, 0, wxALL, FromDIP(5));
wxBoxSizer *bSizer_buttons = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *bSizer_text = new wxBoxSizer(wxHORIZONTAL);
wxPanel* penel_bottons = new wxPanel(parent);
wxPanel* penel_text = new wxPanel(penel_bottons);
wxBoxSizer *bSizer_finish_time = new wxBoxSizer(wxHORIZONTAL);
wxPanel* penel_text = new wxPanel(progress_lr_panel);
wxPanel* penel_finish_time = new wxPanel(progress_lr_panel);
penel_text->SetBackgroundColour(*wxWHITE);
penel_bottons->SetBackgroundColour(*wxWHITE);
penel_finish_time->SetBackgroundColour(*wxWHITE);
wxBoxSizer *sizer_percent = new wxBoxSizer(wxVERTICAL);
sizer_percent->Add(0, 0, 1, wxEXPAND, 0);
@@ -357,55 +356,65 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_staticText_progress_left->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_progress_left->SetForegroundColour(wxColour(146, 146, 146));
// Orca: display the end time of the print
m_staticText_progress_end = new wxStaticText(penel_text, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0);
m_staticText_progress_end->Wrap(-1);
m_staticText_progress_end->SetFont(
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146));
//fgSizer_task->Add(bSizer_buttons, 0, wxEXPAND, 0);
//fgSizer_task->Add(0, 0, 0, wxEXPAND, FromDIP(5));
wxPanel* panel_button_block = new wxPanel(penel_bottons, wxID_ANY);
panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1));
panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1));
panel_button_block->SetSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 2, -1));
panel_button_block->SetBackgroundColour(*wxWHITE);
m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layer: N/A"));
m_staticText_layers->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_layers->SetForegroundColour(wxColour(146, 146, 146));
m_staticText_layers->Hide();
//bSizer_text->Add(m_staticText_progress_percent, 0, wxALL, 0);
bSizer_text->Add(sizer_percent, 0, wxEXPAND, 0);
bSizer_text->Add(sizer_percent_icon, 0, wxEXPAND, 0);
bSizer_text->Add(0, 0, 1, wxEXPAND, 0);
bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER | wxALL, 0);
bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0);
bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(20));
bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER | wxALL, 0);
// Orca: display the end time of the print
bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(8));
bSizer_text->Add(m_staticText_progress_end, 0, wxALIGN_CENTER | wxALL, 0);
bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0);
penel_text->SetMaxSize(wxSize(FromDIP(600), -1));
// penel_text->SetMaxSize(wxSize(FromDIP(600), -1));
penel_text->SetSizer(bSizer_text);
penel_text->Layout();
bSizer_buttons->Add(penel_text, 1, wxEXPAND | wxALL, 0);
bSizer_buttons->Add(panel_button_block, 0, wxALIGN_CENTER | wxALL, 0);
// Orca: display the end time of the print
m_staticText_progress_end = new wxStaticText(penel_finish_time, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0);
m_staticText_progress_end->Wrap(-1);
m_staticText_progress_end->SetFont(
wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC")));
m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146));
bSizer_finish_time->Add(0, 0, 1, wxEXPAND, 0);
bSizer_finish_time->Add(m_staticText_progress_end, 0, wxLEFT | wxEXPAND, 0);
// penel_finish_time->SetMaxSize(wxSize(FromDIP(600), -1));
penel_finish_time->SetSizer(bSizer_finish_time);
penel_finish_time->Layout();
penel_bottons->SetSizer(bSizer_buttons);
penel_bottons->Layout();
auto progress_lr_sizer = new wxBoxSizer(wxHORIZONTAL);
auto progress_left_sizer = new wxBoxSizer(wxVERTICAL);
auto progress_right_sizer = new wxBoxSizer(wxHORIZONTAL);
progress_left_sizer->Add(penel_text, 0, wxEXPAND | wxALL, 0);
progress_left_sizer->Add(m_gauge_progress, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10));
progress_left_sizer->Add(penel_finish_time, 0, wxEXPAND |wxALL, 0);
// progress_left_sizer->SetMaxSize(wxSize(FromDIP(600), -1));
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_right_sizer->Add(m_button_partskip, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));//5
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_right_sizer->Add(m_button_pause_resume, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_right_sizer->Add(m_button_abort, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));
progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18));
progress_lr_sizer->Add(progress_left_sizer, 1, wxEXPAND | wxALL, 0);
progress_lr_sizer->Add(progress_right_sizer, 0, wxEXPAND | wxALL , 0);
progress_lr_panel->SetSizer(progress_lr_sizer);
progress_lr_panel->SetMaxSize(wxSize(FromDIP(720), -1));
progress_lr_panel->Layout();
progress_lr_panel->Fit();
bSizer_subtask_info->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14));
bSizer_subtask_info->Add(bSizer_task_name, 0, wxEXPAND|wxRIGHT, FromDIP(18));
bSizer_subtask_info->Add(m_staticText_profile_value, 0, wxEXPAND | wxTOP, FromDIP(5));
bSizer_subtask_info->Add(m_printing_stage_value, 0, wxEXPAND | wxTOP, FromDIP(5));
bSizer_subtask_info->Add(penel_bottons, 0, wxEXPAND | wxTOP, FromDIP(10));
bSizer_subtask_info->Add(m_panel_progress, 0, wxEXPAND|wxRIGHT, FromDIP(25));
bSizer_subtask_info->Add(progress_lr_panel, 0, wxEXPAND | wxTOP, FromDIP(5));
m_printing_sizer = new wxBoxSizer(wxHORIZONTAL);
m_printing_sizer->SetMinSize(wxSize(PAGE_MIN_WIDTH, -1));
@@ -413,7 +422,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent)
m_printing_sizer->Add(FromDIP(8), 0, 0, wxEXPAND, 0);
m_printing_sizer->Add(bSizer_subtask_info, 1, wxALL | wxEXPAND, 0);
m_staticline = new wxPanel( parent, wxID_ANY);
m_staticline->SetBackgroundColour(wxColour(238,238,238));
m_staticline->Layout();
@@ -638,6 +646,24 @@ void PrintingTaskPanel::reset_printing_value()
this->set_plate_index(-1);
}
void PrintingTaskPanel::enable_partskip_button(MachineObject* obj, bool enable)
{
int stage = 0;
bool in_calibration_mode = false;
if( obj && (obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(obj->subtask_name, stage) != CalibMode::Calib_None)){
in_calibration_mode = true;
}
if (!enable || in_calibration_mode) {
m_button_partskip->Enable(false);
m_button_partskip->SetLabel("");
m_button_partskip->SetIcon("print_control_partskip_disable");
}else if(obj && obj->is_support_brtc){
m_button_partskip->Enable(true);
m_button_partskip->SetIcon("print_control_partskip");
}
}
void PrintingTaskPanel::enable_pause_resume_button(bool enable, std::string type)
{
if (!enable) {
@@ -1731,6 +1757,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
m_switch_cham_fan->SetValue(false);
/* set default enable state */
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_pause_resume_button(false, "resume_disable");
m_project_task_panel->enable_abort_button(false);
@@ -1751,6 +1778,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
// Connect Events
m_project_task_panel->get_bitmap_thumbnail()->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
m_project_task_panel->get_partskip_button()->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
m_project_task_panel->get_pause_resume_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
m_project_task_panel->get_abort_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
m_project_task_panel->get_market_scoring_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
@@ -1810,6 +1838,7 @@ StatusPanel::~StatusPanel()
{
// Disconnect Events
m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
@@ -1940,6 +1969,35 @@ void StatusPanel::on_market_retry(wxCommandEvent &event)
}
}
void StatusPanel::update_partskip_button(MachineObject *obj) {
if (!obj) return;
auto partskip_button = m_project_task_panel->get_partskip_button();
if( obj->is_support_partskip ){
partskip_button->Show();
}else{
partskip_button->Hide();
}
BOOST_LOG_TRIVIAL(info) << "part skip: is_support_partskip: "<< obj->is_support_partskip;
}
void StatusPanel::on_subtask_partskip(wxCommandEvent &event)
{
if (m_partskip_dlg == nullptr) {
m_partskip_dlg = new PartSkipDialog(this->GetParent());
}
auto dm = GUI::wxGetApp().getDeviceManager();
m_partskip_dlg->InitSchedule(dm->get_selected_machine());
BOOST_LOG_TRIVIAL(info) << "part skip: initial part skip dialog.";
if(m_partskip_dlg->ShowModal() == wxID_OK){
int cnt = m_partskip_dlg->GetAllSkippedPartsNum();
m_project_task_panel->set_part_skipped_count(cnt);
m_project_task_panel->set_part_skipped_dirty(5);
BOOST_LOG_TRIVIAL(info) << "part skip: prepare to filter printer dirty data.";
}
}
void StatusPanel::on_subtask_pause_resume(wxCommandEvent &event)
{
if (obj) {
@@ -3084,14 +3142,18 @@ void StatusPanel::update_subtask(MachineObject *obj)
}
update_model_info();
update_partskip_button(obj);
if (obj->is_system_printing()
|| obj->is_in_calibration()) {
reset_printing_values();
} else if (obj->is_in_printing() || obj->print_status == "FINISH") {
update_partskip_subtask(obj);
if (obj->is_in_prepare() || obj->print_status == "SLICING") {
m_project_task_panel->market_scoring_hide();
m_project_task_panel->get_request_failed_panel()->Hide();
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_abort_button(false);
m_project_task_panel->enable_pause_resume_button(false, "pause_disable");
wxString prepare_text;
@@ -3133,7 +3195,7 @@ void StatusPanel::update_subtask(MachineObject *obj)
} else {
m_project_task_panel->enable_pause_resume_button(true, "pause");
}
m_project_task_panel->enable_partskip_button(obj, true);
// update printing stage
m_project_task_panel->update_left_time(obj->mc_left_time);
if (obj->subtask_) {
@@ -3150,6 +3212,7 @@ void StatusPanel::update_subtask(MachineObject *obj)
if (obj->is_printing_finished()) {
obj->update_model_task();
m_project_task_panel->enable_abort_button(false);
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_pause_resume_button(false, "resume_disable");
// is makeworld subtask
if (wxGetApp().has_model_mall() && obj->is_makeworld_subtask()) {
@@ -3217,6 +3280,32 @@ void StatusPanel::update_subtask(MachineObject *obj)
Layout();
}
void StatusPanel::update_partskip_subtask(MachineObject *obj){
if (!obj) return;
if (!obj->subtask_) return;
auto partskip_button = m_project_task_panel->get_partskip_button();
if (partskip_button) {
int part_cnt = 0;
if(m_project_task_panel->get_part_skipped_dirty() > 0){
m_project_task_panel->set_part_skipped_dirty(m_project_task_panel->get_part_skipped_dirty() - 1);
part_cnt = m_project_task_panel->get_part_skipped_count();
BOOST_LOG_TRIVIAL(info) << "part skip: stop recv printer dirty data.";
}else{
part_cnt = obj->m_partskip_ids.size();
BOOST_LOG_TRIVIAL(info) << "part skip: recv printer normal data.";
}
if (part_cnt > 0)
partskip_button->SetLabel(wxString::Format(_L("(%d)"), part_cnt));
else
partskip_button->SetLabel("");
}
if(m_partskip_dlg && m_partskip_dlg->IsShown()) {
m_partskip_dlg->UpdatePartsStateFromPrinter(obj);
}
}
void StatusPanel::update_cloud_subtask(MachineObject *obj)
{
if (!obj) return;
@@ -3281,6 +3370,7 @@ void StatusPanel::update_sdcard_subtask(MachineObject *obj)
void StatusPanel::reset_printing_values()
{
m_project_task_panel->enable_partskip_button(nullptr, false);
m_project_task_panel->enable_pause_resume_button(false, "pause_disable");
m_project_task_panel->enable_abort_button(false);
m_project_task_panel->reset_printing_value();

View File

@@ -32,6 +32,7 @@
#include "Widgets/AMSControl.hpp"
#include "Widgets/FanControl.hpp"
#include "HMS.hpp"
#include "PartSkipDialog.hpp"
class StepIndicator;
@@ -160,7 +161,7 @@ public:
private:
MachineObject* m_obj;
MachineObject* m_obj{nullptr};
ScalableBitmap m_thumbnail_placeholder;
wxBitmap m_thumbnail_bmp_display;
ScalableBitmap m_bitmap_use_time;
@@ -192,6 +193,7 @@ private:
wxStaticBitmap* m_bitmap_static_use_weight;
ScalableButton* m_button_pause_resume;
ScalableButton* m_button_abort;
Button* m_button_partskip;
Button* m_button_market_scoring;
Button* m_button_clean;
Button * m_button_market_retry;
@@ -203,6 +205,10 @@ private:
std::vector<ScalableButton *> m_score_star;
bool m_star_count_dirty = false;
// partskip button
int m_part_skipped_count{ 0 };
int m_part_skipped_dirty{ 0 };
ProgressBar* m_gauge_progress;
Label* m_error_text;
PrintingTaskType m_type;
@@ -217,6 +223,7 @@ public:
void msw_rescale();
public:
void enable_partskip_button(MachineObject* obj, bool enable);
void enable_pause_resume_button(bool enable, std::string type);
void enable_abort_button(bool enable);
void update_subtask_name(wxString name);
@@ -236,6 +243,7 @@ public:
public:
ScalableButton* get_abort_button() {return m_button_abort;};
ScalableButton* get_pause_resume_button() {return m_button_pause_resume;};
Button* get_partskip_button() { return m_button_partskip; };
Button* get_market_scoring_button() {return m_button_market_scoring;};
Button * get_market_retry_buttom() { return m_button_market_retry; };
Button* get_clean_button() {return m_button_clean;};
@@ -246,6 +254,10 @@ public:
std::vector<ScalableButton *> &get_score_star() { return m_score_star; }
bool get_star_count_dirty() { return m_star_count_dirty; }
void set_star_count_dirty(bool dirty) { m_star_count_dirty = dirty; }
int get_part_skipped_count() { return m_part_skipped_count; }
void set_part_skipped_count(int count) { m_part_skipped_count = count; }
int get_part_skipped_dirty() { return m_part_skipped_dirty; }
void set_part_skipped_dirty(int dirty) { m_part_skipped_dirty = dirty; }
void set_has_reted_text(bool has_rated);
void paint(wxPaintEvent&);
};
@@ -328,6 +340,7 @@ protected:
wxStaticText * m_staticText_progress_left;
wxStaticText * m_staticText_layers;
Button * m_button_report;
Button * m_button_partskip;
ScalableButton *m_button_pause_resume;
ScalableButton *m_button_abort;
Button * m_button_clean;
@@ -402,6 +415,7 @@ protected:
PrintingTaskPanel * m_project_task_panel;
// Virtual event handlers, override them in your derived class
virtual void on_subtask_partskip(wxCommandEvent &event) { event.Skip(); }
virtual void on_subtask_pause_resume(wxCommandEvent &event) { event.Skip(); }
virtual void on_subtask_abort(wxCommandEvent &event) { event.Skip(); }
virtual void on_lamp_switch(wxCommandEvent &event) { event.Skip(); }
@@ -484,6 +498,7 @@ protected:
FanControlPopup* m_fan_control_popup{nullptr};
ExtrusionCalibration *m_extrusion_cali_dlg{nullptr};
PartSkipDialog *m_partskip_dlg{nullptr};
wxString m_request_url;
bool m_start_loading_thumbnail = false;
@@ -525,6 +540,7 @@ protected:
void on_market_scoring(wxCommandEvent &event);
void on_market_retry(wxCommandEvent &event);
void on_subtask_partskip(wxCommandEvent &event);
void on_subtask_pause_resume(wxCommandEvent &event);
void on_subtask_abort(wxCommandEvent &event);
void on_print_error_clean(wxCommandEvent &event);
@@ -602,6 +618,7 @@ protected:
void update_basic_print_data(bool def = false);
void update_model_info();
void update_subtask(MachineObject* obj);
void update_partskip_subtask(MachineObject *obj);
void update_cloud_subtask(MachineObject *obj);
void update_sdcard_subtask(MachineObject *obj);
void update_temp_ctrl(MachineObject *obj);
@@ -621,6 +638,9 @@ protected:
void update_camera_state(MachineObject* obj);
bool show_vcamera = false;
// partskip button
void update_partskip_button(MachineObject* obj);
public:
void update_error_message();

View File

@@ -0,0 +1,77 @@
#include "AnimaController.hpp"
#include <wx/dcclient.h>
#include <wx/dcgraph.h>
#ifdef __APPLE__
#include "libslic3r/MacUtils.hpp"
#endif
AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector<std::string> img_list, std::string img_enable, int ivt)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize), m_ivt(ivt)
{
auto sizer = new wxBoxSizer(wxHORIZONTAL);
SetBackgroundColour((wxColour(255, 255, 255)));
m_size = 25;
//add ScalableBitmap
for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, m_size));
m_image_enable = create_scaled_bitmap(img_enable, this, m_size-8);
// show first wxStaticBitmap
if (!m_images.empty()) m_bitmap = new wxStaticBitmap(this, wxID_ANY, m_images[0], wxDefaultPosition, wxSize(FromDIP(m_size), FromDIP(m_size)));
m_timer = new wxTimer();
m_timer->SetOwner(this);
Bind(wxEVT_TIMER, [this](wxTimerEvent &) {
if (m_timer->IsRunning() && !m_images.empty()) {
m_current_frame = (m_current_frame + 1) % 4;
m_bitmap->SetBitmap(m_images[m_current_frame]);
}
});
m_bitmap->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
wxMouseEvent evt(wxEVT_LEFT_DOWN);
evt.SetEventObject(this);
wxPostEvent(this, evt);
});
m_bitmap->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) {
if (!m_timer->IsRunning())
SetCursor(wxCursor(wxCURSOR_HAND));
else
SetCursor(wxCursor(wxCURSOR_ARROW));
e.Skip();
});
m_bitmap->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) {
SetCursor(wxCursor(wxCURSOR_ARROW));
e.Skip();
});
sizer->Add(m_bitmap, 0, wxALIGN_CENTER, 0);
SetSizer(sizer);
SetSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
SetMaxSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
SetMinSize(wxSize(FromDIP(m_size), FromDIP(m_size)));
Layout();
Fit();
Play();
}
void AnimaIcon::Play()
{
if (true)
m_timer->Start(m_ivt);
}
void AnimaIcon::Stop()
{
m_timer->Stop();
}
void AnimaIcon::Enable()
{
if (m_bitmap) { m_bitmap->SetBitmap(m_image_enable); }
}

View File

@@ -0,0 +1,28 @@
#ifndef slic3r_GUI_AnimaController_hpp_
#define slic3r_GUI_AnimaController_hpp_
#include "../wxExtensions.hpp"
#include "Label.hpp"
class AnimaIcon : public wxPanel
{
public:
AnimaIcon(wxWindow *parent, wxWindowID id, std::vector<std::string> img_list, std::string img_enable, int ivt = 1000);
void Play();
void Stop();
void Enable();
bool IsRunning() const;
private:
wxBitmap m_image_enable;
wxStaticBitmap * m_bitmap{nullptr};
std::vector<wxBitmap> m_images;
wxTimer * m_timer;
int m_current_frame = 0;
int m_ivt;
int m_size;
};
#endif // !slic3r_GUI_AnimaController_hpp_

View File

@@ -15,7 +15,7 @@ ProgressBar::ProgressBar(wxWindow *parent, wxWindowID id, int max, const wxPoint
{
m_shownumber = shown;
SetBackgroundColour(wxColour(255,255,255));
if (size.y >= miniHeight) {
m_miniHeight = size.y;
} else {
@@ -64,20 +64,20 @@ void ProgressBar::create(wxWindow *parent, wxWindowID id, const wxPoint &pos, w
}
void ProgressBar::SetRadius(double radius) {
void ProgressBar::SetRadius(double radius) {
m_radius = radius;
Refresh();
}
void ProgressBar::SetProgressForedColour(wxColour colour)
void ProgressBar::SetProgressForedColour(wxColour colour)
{
m_progress_background_colour = colour;
Refresh();
}
void ProgressBar::SetProgressBackgroundColour(wxColour colour)
{
m_progress_colour = colour;
void ProgressBar::SetProgressBackgroundColour(wxColour colour)
{
m_progress_colour = colour;
Refresh();
}
@@ -86,29 +86,29 @@ void ProgressBar::Rescale()
;
}
void ProgressBar::ShowNumber(bool shown)
void ProgressBar::ShowNumber(bool shown)
{
m_shownumber = shown;
Refresh();
}
void ProgressBar::Disable(wxString text)
{
void ProgressBar::Disable(wxString text)
{
if (m_disable) return;
m_disable_text = text;
m_disable = true;
Refresh();
}
void ProgressBar::SetValue(int step)
{
void ProgressBar::SetValue(int step)
{
m_disable = false;
SetProgress(step);
}
void ProgressBar::Reset()
{
m_step = 0;
void ProgressBar::Reset()
{
m_step = 0;
SetValue(0);
}
@@ -122,9 +122,9 @@ void ProgressBar::SetProgress(int step)
}
void ProgressBar::SetMinSize(const wxSize &size)
{
if (size.y >= miniHeight) {
void ProgressBar::SetMinSize(const wxSize &size)
{
if (size.y >= miniHeight) {
m_miniHeight = size.y;
} else {
return;
@@ -177,7 +177,7 @@ void ProgressBar::doRender(wxDC &dc)
dc.DrawRoundedRectangle(0, 0, size.x, size.y, m_radius);
}
//draw progress
//draw progress
if (m_disable) {
m_proportion = float(size.x * float(this->m_step) / float(this->m_max));
if (m_proportion < m_radius * 2 && m_proportion != 0) { m_proportion = m_radius * 2; }
@@ -228,11 +228,11 @@ void ProgressBar::doRender(wxDC &dc)
dc.DrawText(text + wxString("%"), pt);
}
}
}
void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
}