class Slicer_Wizard(FloatLayout): def __init__(self, robosm, back_destination): super(Slicer_Wizard, self).__init__() #make default meta data self.meta = { 'layer height': '--', 'layers': '--', 'infill': '--', 'time': { 'hours': str(0), 'minutes': str(0), 'seconds': str(0) } } #make sure the tmp directory exists self.search_for_temp_dir() self.sm = robosm self.oprint = roboprinter.printer_instance #show the confirmation screen self.show_confirmation_screen() def show_confirmation_screen(self): next_action = self.open_file_select_screen screen_name = "slicing wizard" title = roboprinter.lang.pack['Slicer_Wizard']['Confirmation']['Title'] back_destination = self.sm.current layout = STL_Confirmation_Screen(next_action) self.sm._generate_backbutton_screen(name=screen_name, title=title, back_destination=back_destination, content=layout) def open_file_select_screen(self): layout = File_Explorer('model', self.create_button, enable_editing=False) #continue to the stl select screen back_destination = self.sm.current title = roboprinter.lang.pack['Slicer_Wizard']['Select_File']['Title'] name = "choose_file" self.sm._generate_backbutton_screen(name=name, title=title, back_destination=back_destination, content=layout) #a helper function so we can use the file explorer def create_button(self, filename, date, path, **kwargs): return stl_Button(filename, path, date, self.choose_overrides) def search_for_temp_dir(self): if not os.path.exists(TEMP_DIR): os.makedirs(TEMP_DIR) Logger.info("Made temp directory for slicing") def choose_overrides(self, name, path): screen_name = 'slicer_overrides' title = roboprinter.lang.pack['Slicer_Wizard']['Overrides']['Title'] back_destination = self.sm.current real_path = roboprinter.printer_instance._file_manager.path_on_disk( 'local', path) Logger.info(real_path) layout = Override_Page(name, real_path, self.slice_stl) def slice_stl(self, name, path, overrides): #get the profile from octoprint self.progress_pop = USB_Progress_Popup( roboprinter.lang.pack['Slicer_Wizard']['Progress']['Sub_Title'] + name, 1) self.progress_pop.show() self.stl_name = name.replace(".stl", "") self.stl_name = self.stl_name.replace(".STL", "") self.stl_name = self.stl_name + ".gcode" self.stl_path = path self.overrides = overrides Clock.schedule_once(self.start_slice, 0.1) def start_slice(self, dt): profiles = roboprinter.printer_instance._slicing_manager.all_profiles( 'cura', require_configured=False) if 'robo' in profiles: #start slice self.temp_path = TEMP_DIR + "/" + self.stl_name Logger.info("Starting Slice") Logger.info(self.overrides) roboprinter.printer_instance._slicing_manager.slice( 'cura', self.stl_path, self.temp_path, 'robo', self.sliced, overrides=self.overrides, on_progress=self.slice_progress) else: #put our profile in the profile list profile_path = os.path.dirname(os.path.realpath(__file__)) profile_path += '/slicer_profile/robo.profile' if os.path.isfile(profile_path): #copy a backup of the profile to the default profile directory shutil.copyfile(profile_path, CURA_DIR + '/robo.profile') #if the backup exists and we have tried restoring it 5 times give up and error out if dt < 5: Logger.info('Restarting the slice, Rec Depth = ' + str(dt + 1)) self.start_slice(dt + 1) else: ep = Error_Popup(roboprinter.lang.pack['Slicer_Wizard'] ['Error']['Profile']['Sub_Title'], roboprinter.lang.pack['Slicer_Wizard'] ['Error']['Profile']['Body'], callback=partial( roboprinter.robosm.go_back_to_main, tab='printer_status_tab')) ep.show() #if the backup does not exist then error out else: Logger.info('Slicer Error: Path Does not exist') ep = Error_Popup(roboprinter.lang.pack['Slicer_Wizard'] ['Error']['Profile']['Sub_Title'], roboprinter.lang.pack['Slicer_Wizard'] ['Error']['Profile']['Body'], callback=partial( roboprinter.robosm.go_back_to_main, tab='printer_status_tab')) ep.show() def sliced(self, **kwargs): Logger.info(kwargs) if '_error' in kwargs: #doing this will get rid of graphical errors. Kivy does not like being managed from an outside thread. Logger.info(str(kwargs['_error'])) Clock.schedule_once(self.error_pop, 0.01) elif '_analysis' in kwargs: #initialize meta data ept = 0 lh = str(self.overrides['layer_height']) infill = str(self.overrides['fill_density']) if 'estimatedPrintTime' in kwargs['_analysis']: ept = kwargs['_analysis']['estimatedPrintTime'] #save meta data self.meta = {'layer height': lh, 'infill': infill, 'time': ept} Logger.info("finished Slice") self.progress_pop.hide() #after slicing ask the user where they want the file to be saved at Clock.schedule_once(self.save_file, 0.01) else: Logger.info("finished Slice") self.progress_pop.hide() #after slicing ask the user where they want the file to be saved at Clock.schedule_once(self.save_file, 0.01) def error_pop(self, dt, *args, **kwargs): self.progress_pop.hide() os.remove(self.temp_path) ep = Error_Popup( roboprinter.lang.pack['Slicer_Wizard']['Error']['Slice'] ['Sub_Title'], roboprinter.lang.pack['Slicer_Wizard']['Error']['Slice']['Body'], callback=partial(roboprinter.robosm.go_back_to_main, tab='printer_status_tab')) ep.show() # This takes a number in seconds and returns a dictionary of the hours/minutes/seconds def parse_time(self, time): m, s = divmod(time, 60) h, m = divmod(m, 60) time_dict = {'hours': str(h), 'minutes': str(m), 'seconds': str(s)} return time_dict # this function exists because calling the Save_File class directly from the sliced function resulted in Graphical issues # Setting a clock to call this function fixed the graphical issues. I believe it is because the sliced function gets called # by the slicing manager thread, and graphical issues do present themselves when calling kivy objects outside the thread # they are created in. def save_file(self, dt): Logger.info('Saving data ' + self.temp_path + ' along with the meta data: ' + str(self.meta)) Save_File(self.temp_path, meta_data=self.meta) def slice_progress(self, *args, **kwargs): if '_progress' in kwargs: #Logger.info(str(kwargs['_progress'])) self.current_progress = kwargs['_progress'] #Just trying to avoid graphical issues Clock.schedule_once(self.get_progress, 0) def get_progress(self, dt, *args, **kwargs): self.progress_pop.update_progress(self.current_progress)
class PrintUSB(PrintFile): """ This class encapsulates the dynamic properties that get rendered on the PrintUSB and the methods that allow the user to start a print from usb or save the file to local. """ def __init__(self, **kwargs): super(PrintUSB, self).__init__(**kwargs) self.progress_pop = USB_Progress_Popup("Saving File", 1) pass def save_file_to_local(self, *args): self.progress_pop.show() Clock.schedule_once(self.attempt_to_save, 0.01) def attempt_to_save(self, dt): try: copy_path = FILES_DIR + '/' + self.file_name real_path = roboprinter.printer_instance._file_manager.path_on_disk( 'local', self.file_path) #shutil.copy2(real_path, copy_path) Logger.info("Started the Copy src: " + real_path + " cp to dst: " + copy_path) copied = self.copy_file(real_path, copy_path, progress_callback=self.progress_update) if not copied: self.progress_pop.hide() ep = Error_Popup( roboprinter.lang.pack['Files']['File_Error']['Title'], roboprinter.lang.pack['Files']['File_Error']['Body'], callback=partial(roboprinter.robosm.go_back_to_main, tab='printer_status_tab')) ep.show() Logger.info("attempt to save Error") except Exception as e: #raise error self.progress_pop.hide() ep = Error_Popup( roboprinter.lang.pack['Files']['File_Error']['Title'], roboprinter.lang.pack['Files']['File_Error']['Body'], callback=partial(roboprinter.robosm.go_back_to_main, tab='printer_status_tab')) ep.show() Logger.info("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " + str(e)) traceback.print_exc() def copy_file(self, fsrc, fdst, progress_callback=None, complete_callback=None, length=16 * 1024, **kwargs): self.copied = 0 self.file_size = 0 self.length = length self.p_callback = progress_callback self.c_callback = complete_callback if not os.path.isfile(fsrc): Logger.info("Will not copy") return False else: self.file_size = float(os.path.getsize(fsrc)) #make the new file self.src_obj = open(fsrc, 'rb') self.dst_obj = open(fdst, 'wb') #Do the copy as fast as possible without blocking the UI thread Clock.schedule_interval(self.copy_object, 0) return True #doing it this way with a clock object does not block the UI def copy_object(self, dt): #grab part of the file buf = self.src_obj.read(self.length) #if there isn't anything to read then close the files and return if not buf: self.src_obj.close() self.dst_obj.close() if self.c_callback != None: self.c_callback() return False #Write the buffer to the new file self.dst_obj.write(buf) #update how much of the file has been copied self.copied += len(buf) #report progress if self.p_callback != None: progress = float(self.copied / self.file_size) self.p_callback(progress) def progress_update(self, progress): self.progress_pop.update_progress(progress) #Logger.info(str(progress)) if progress == 1.0: self.progress_pop.hide() ep = Error_Popup( roboprinter.lang.pack['Files']['File_Saved']['Title'], roboprinter.lang.pack['Files']['File_Saved']['Body'], callback=partial(roboprinter.robosm.go_back_to_main, tab='printer_status_tab')) ep.show() if 'file_callback' in session_saver.saved: session_saver.saved['file_callback']()