From edfafb11e84046385609b7836fe1fad497a19069 Mon Sep 17 00:00:00 2001 From: Stephen Houser Date: Fri, 8 May 2020 13:23:47 -0400 Subject: [PATCH] update to v0.49 --- Change_Log.txt | 26 ++++++++++++ build_macOS.sh | 4 +- egv.py | 15 +++++-- emblem.icns | Bin 6792 -> 25295 bytes k40_whisperer.py | 103 ++++++++++++++++++++++++++++++++++++++--------- macOS.patch | 26 ++++++------ nano_library.py | 14 ++++++- py2app_setup.py | 2 +- requirements.txt | 1 + svg_reader.py | 20 +++++++-- 10 files changed, 166 insertions(+), 45 deletions(-) diff --git a/Change_Log.txt b/Change_Log.txt index 954a011..3465d95 100644 --- a/Change_Log.txt +++ b/Change_Log.txt @@ -232,3 +232,29 @@ Version 0.42: - Fixed trace boundary when X and Y scaling is used - Limited minimum speed entries to prevent unexpected laser movements. - Fixed trace boundary space so that it scales with units change. + +Version 0.43: +- Fixed problem that prevented properly sending EGV file to the laser. + +Version 0.44: +- Removed unnecessary commands from data sent to laser. Reduced volume of data sent to laser. +- Improved pause function. Laser not stops immediately rather than waiting for buffer to empty. +- Added code to get around "Backend not found" error in some cases. + +Version 0.45: +- Reverted back to egv.py file from Version 0.43 fixing problem with movements introduced in Version 0.44 + +Version 0.46: +- Fixed error in positioning when using g-code input and the input coordinate system +- Improved display of g-code errors and warnings +- Changed file selection default to g-code if the last file opened was g-code + +Version 0.47: +- Updated to work with Inkscape 1.0 +- Fixed mirroring when using input axes + +Version 0.48: +- Fixed auto convert text to path when using older versions of Inkscape + +Version 0.49: +- Added new default Inkscape executable path to the search list. So users don't need to specify the Inkscape location in the settings. diff --git a/build_macOS.sh b/build_macOS.sh index c3b209d..d3f12b5 100755 --- a/build_macOS.sh +++ b/build_macOS.sh @@ -156,9 +156,9 @@ then if [ "$PYINSTALLER" = true ] then - VOLNAME=K40-Whisperer-${VERSION}-pyinstaller - else VOLNAME=K40-Whisperer-${VERSION} + else + VOLNAME=K40-Whisperer-${VERSION}-pysetup fi rm ${VOLNAME}.dmg diff --git a/egv.py b/egv.py index a858de3..58e06c7 100644 --- a/egv.py +++ b/egv.py @@ -2,7 +2,7 @@ ''' This script reads/writes egv format -Copyright (C) 2017-2019 Scorch www.scorchworks.com +Copyright (C) 2017-2020 Scorch www.scorchworks.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -326,10 +326,19 @@ def make_egv_data(self, ecoords_in, lastx,lasty,last_loop = self.ecoord_adj(ecoords_in[0],scale,FlipXoffset) if not Rapid_Feed_Rate: self.make_dir_dist(lastx-startX,lasty-startY) + self.flush(laser_on=False) self.write(ord("N")) - self.write(ord("R")) - self.write(ord("B")) + if lasty-startY <= 0: + self.write(self.DOWN) + else: + self.write(self.UP) + + if lastx-startX >= 0: + self.write(self.RIGHT) + else: + self.write(self.LEFT) + # Insert "S1E" self.write(ord("S")) self.write(ord("1")) diff --git a/emblem.icns b/emblem.icns index 94e312d4d085dc3e2204e3460ea2ac4e62db76e8..6e4cd1c03511cc6c8e1344e20577c5e4ec800d37 100644 GIT binary patch literal 25295 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 literal 6792 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 + Copyright (C) <2017-2020> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -17,7 +17,7 @@ along with this program. If not, see . """ -version = '0.42' +version = '0.49' title_text = "K40 Whisperer V"+version import sys @@ -100,7 +100,12 @@ def next(item): except: print("Unable to load Pyclipper library (Offset trace outline will not work without it)") PYCLIPPER = False - + +try: + os.chdir(os.path.dirname(__file__)) +except: + pass + QUIET = False # macOS Patch - Stephen Houser (stephenhouser@gmail.com) @@ -1761,11 +1766,21 @@ def menu_File_Open_Design(self,event=None): if ( not os.path.isdir(init_dir) ): init_dir = self.HOME_DIR - fileselect = askopenfilename(filetypes=[("Design Files", ("*.svg","*.dxf")), - ("G-Code Files", ("*.ngc","*.gcode","*.g","*.tap")),\ - ("DXF Files","*.dxf"),\ - ("SVG Files","*.svg"),\ - ("All Files","*"),\ + design_types = ("Design Files", ("*.svg","*.dxf")) + gcode_types = ("G-Code Files", ("*.ngc","*.gcode","*.g","*.tap")) + + Name, fileExtension = os.path.splitext(self.DESIGN_FILE) + TYPE=fileExtension.upper() + if TYPE != '.DXF' and TYPE!='.SVG' and TYPE!='.EGV': + default_types = gcode_types + else: + default_types = design_types + + fileselect = askopenfilename(filetypes=[default_types, + ("G-Code Files ", ("*.ngc","*.gcode","*.g","*.tap")),\ + ("DXF Files ","*.dxf"),\ + ("SVG Files ","*.svg"),\ + ("All Files ","*"),\ ("Design Files ", ("*.svg","*.dxf"))],\ initialdir=init_dir) @@ -1848,7 +1863,6 @@ def menu_File_save_EGV(self,operation_type=None,default_name="out.EGV"): def menu_File_Open_EGV(self): - self.stop[0]=False init_dir = os.path.dirname(self.DESIGN_FILE) if ( not os.path.isdir(init_dir) ): init_dir = self.HOME_DIR @@ -1859,11 +1873,9 @@ def menu_File_Open_EGV(self): self.resetPath() self.DESIGN_FILE = fileselect self.EGV_Send_Window(fileselect) - self.stop[0]=True - #self.Finish_Job() def Open_EGV(self,filemname,n_passes=1): - pass + self.stop[0]=False EGV_data=[] value1 = "" value2 = "" @@ -1945,6 +1957,7 @@ def Open_EGV(self,filemname,n_passes=1): dxmils = -(x_end_mils - x_start_mils) dymils = y_end_mils - y_start_mils self.Send_Rapid_Move(dxmils,dxmils) + self.stop[0]=True def Open_SVG(self,filemname): @@ -2279,6 +2292,46 @@ def convert_halftoning(self,image): ####################################################################### + def gcode_error_message(self,message): + error_report = Toplevel(width=525,height=60) + error_report.title("G-Code Reading Errors/Warnings") + error_report.iconname("G-Code Errors") + error_report.grab_set() + return_value = StringVar() + return_value.set("none") + + try: + error_report.iconbitmap(bitmap="@emblem64") + except: + debug_message(traceback.format_exc()) + pass + + def Close_Click(event): + return_value.set("close") + error_report.destroy() + + #Text Box + Error_Frame = Frame(error_report) + scrollbar = Scrollbar(Error_Frame, orient=VERTICAL) + Error_Text = Text(Error_Frame, width="80", height="20",yscrollcommand=scrollbar.set,bg='white') + for line in message: + Error_Text.insert(END,line+"\n") + scrollbar.config(command=Error_Text.yview) + scrollbar.pack(side=RIGHT,fill=Y) + #End Text Box + + Button_Frame = Frame(error_report) + close_button = Button(Button_Frame,text=" Close ") + close_button.bind("", Close_Click) + close_button.pack(side=RIGHT,fill=X) + + Error_Text.pack(side=LEFT,fill=BOTH,expand=1) + Button_Frame.pack(side=BOTTOM) + Error_Frame.pack(side=LEFT,fill=BOTH,expand=1) + + root.wait_window(error_report) + return return_value.get() + def Open_G_Code(self,filename): self.resetPath() @@ -2287,9 +2340,8 @@ def Open_G_Code(self,filename): MSG = g_rip.Read_G_Code(filename, XYarc2line = True, arc_angle=2, units="in", Accuracy="") Error_Text = "" if MSG!=[]: - for line in MSG: - Error_Text = Error_Text + line + "\n" - message_box("G-Code Messages", Error_Text) + self.gcode_error_message(MSG) + #except StandardError as e: except Exception as e: msg1 = "G-Code Load Failed: " @@ -3319,7 +3371,12 @@ def mirror_rotate_vector_coords(self,coords): for i in range(len(coords)): coords_rotate_mirror.append(coords[i][:]) if self.mirror.get(): - coords_rotate_mirror[i][0]=xmin+xmax-coords_rotate_mirror[i][0] + if self.inputCSYS.get() and self.RengData.image == None: + coords_rotate_mirror[i][0]=-coords_rotate_mirror[i][0] + else: + coords_rotate_mirror[i][0]=xmin+xmax-coords_rotate_mirror[i][0] + + if self.rotate.get(): x = coords_rotate_mirror[i][0] y = coords_rotate_mirror[i][1] @@ -3691,8 +3748,14 @@ def Stop(self,event=None): line1 = "Sending data to the laser from K40 Whisperer is currently Paused." line2 = "Press \"OK\" to abort any jobs currently running." line3 = "Press \"Cancel\" to resume." + if self.k40 != None: + self.k40.pause_un_pause() + if message_ask_ok_cancel("Stop Laser Job.", "%s\n\n%s\n%s" %(line1,line2,line3)): self.stop[0]=True + else: + if self.k40 != None: + self.k40.pause_un_pause() def Hide_Advanced(self,event=None): self.advanced.set(0) @@ -5634,13 +5697,13 @@ def Set_Value(other=None,width=None,height=None): Title_Text0 = Label(master, text=t0+t1+t2, anchor=W) Title_Text1 = Label(master, text=t3, anchor=W) - Radio_SVG_pxpi_96 = Radiobutton(master,text=" 96 px/in", value="96") + Radio_SVG_pxpi_96 = Radiobutton(master,text=" 96 units/in", value="96") Label_SVG_pxpi_96 = Label(master,text="(File saved with Inkscape v0.92 or newer)", anchor=W) - Radio_SVG_pxpi_90 = Radiobutton(master,text=" 90 px/in", value="90") + Radio_SVG_pxpi_90 = Radiobutton(master,text=" 90 units/in", value="90") Label_SVG_pxpi_90 = Label(master,text="(File saved with Inkscape v0.91 or older)", anchor=W) - Radio_SVG_pxpi_72 = Radiobutton(master,text=" 72 px/in", value="72") + Radio_SVG_pxpi_72 = Radiobutton(master,text=" 72 units/in", value="72") Label_SVG_pxpi_72 = Label(master,text="(File saved with Adobe Illustrator)", anchor=W) Radio_Res_Custom = Radiobutton(master,text=" Custom:", value="custom") @@ -5649,7 +5712,7 @@ def Set_Value(other=None,width=None,height=None): Entry_Custom_pxpi = Entry(master,width="10") Entry_Custom_pxpi.configure(textvariable=self.other) - Label_pxpi_units = Label(master,text="px/in", anchor=W) + Label_pxpi_units = Label(master,text="units/in", anchor=W) self.trace_id_pxpi = self.other.trace_variable("w", Entry_custom_Callback) Label_Width = Label(master,text="Width", anchor=W) diff --git a/macOS.patch b/macOS.patch index 43f0808..d1ff520 100644 --- a/macOS.patch +++ b/macOS.patch @@ -1,8 +1,8 @@ ---- K40_Whisperer-0.41_src/k40_whisperer.py 2019-12-06 19:54:12.000000000 -0500 -+++ k40_whisperer.py 2019-12-09 18:54:44.000000000 -0500 -@@ -102,7 +102,35 @@ - PYCLIPPER = False - +--- K40_Whisperer-0.49_src/k40_whisperer.py 2020-05-07 21:36:44.000000000 -0400 ++++ k40_whisperer.py 2020-05-08 13:18:34.000000000 -0400 +@@ -107,7 +107,35 @@ + pass + QUIET = False - + @@ -37,7 +37,7 @@ ################################################################################ class Application(Frame): def __init__(self, master): -@@ -553,6 +581,14 @@ +@@ -561,6 +589,14 @@ self.Label_GoToX = Label(self.master,text="X", anchor=CENTER ) self.Label_GoToY = Label(self.master,text="Y", anchor=CENTER ) @@ -52,7 +52,7 @@ ########################################################################### # End Left Column # -@@ -3742,7 +3778,10 @@ +@@ -3849,7 +3885,10 @@ U_display)) self.statusbar.configure( bg = 'white' ) @@ -64,7 +64,7 @@ def menu_Inside_First_Callback(self, varName, index, mode): if self.GcodeData.ecoords != []: if self.VcutData.sorted == True: -@@ -4646,7 +4685,8 @@ +@@ -4754,7 +4793,8 @@ xd_label_L = 12 w_label=150 @@ -74,7 +74,7 @@ w_units=45 xd_entry_L=xd_label_L+w_label+10 xd_units_L=xd_entry_L+w_entry+5 -@@ -4876,6 +4916,9 @@ +@@ -4984,6 +5024,9 @@ xd_entry_L=xd_label_L+w_label+10 xd_units_L=xd_entry_L+w_entry+5 @@ -84,7 +84,7 @@ D_Yloc=D_Yloc+D_dY -@@ -5200,7 +5243,8 @@ +@@ -5308,7 +5351,8 @@ xd_label_L = 12 w_label=150 @@ -94,7 +94,7 @@ w_units=35 xd_entry_L=xd_label_L+w_label+10 xd_units_L=xd_entry_L+w_entry+5 -@@ -5246,6 +5290,9 @@ +@@ -5354,6 +5398,9 @@ self.EGV_Send = Button(egv_send,text="Send EGV Data",command=Close_and_Send_Click) self.EGV_Send.place(x=Xbut, y=Ybut, width=130, height=30, anchor="w") @@ -104,7 +104,7 @@ ################################################################################ -@@ -5343,6 +5390,8 @@ +@@ -5451,6 +5498,8 @@ def apply(self): self.result = self.uom.get() @@ -113,7 +113,7 @@ return -@@ -5670,4 +5719,7 @@ +@@ -5778,4 +5827,7 @@ app.master.geometry("480x320") diff --git a/nano_library.py b/nano_library.py index c7db159..4dbd493 100644 --- a/nano_library.py +++ b/nano_library.py @@ -2,7 +2,7 @@ ''' This script comunicated with the K40 Laser Cutter. -Copyright (C) 2017-2019 Scorch www.scorchworks.com +Copyright (C) 2017-2020 Scorch www.scorchworks.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ try: import usb.core import usb.util + import usb.backend.libusb0 except: print("Unable to load USB library (Sending data to Laser will not work.)") import sys @@ -123,6 +124,9 @@ def reset_usb(self): def release_usb(self): usb.util.dispose_resources(self.dev) self.dev = None + + def pause_un_pause(self): + self.send_data([ord('P'),ord('N')]) ####################################################################### # The one wire CRC algorithm is derived from the OneWire.cpp Library @@ -311,10 +315,16 @@ def initialize_device(self,verbose=False): self.release_usb() except: pass + + backend = usb.backend.libusb0.get_backend() + if backend==None and os.name == 'nt': + exedir = os.path.dirname(sys.executable) + os.environ['PATH'] = exedir + os.pathsep + os.environ['PATH'] + # find the device self.dev = usb.core.find(idVendor=0x1a86, idProduct=0x5512) if self.dev is None: - raise Exception("Laser USB Device not found.") + raise Exception("Laser USB Device not found. (libUSB driver may not be installed)") #return "Laser USB Device not found." if verbose: diff --git a/py2app_setup.py b/py2app_setup.py index b64c006..0f1b63f 100644 --- a/py2app_setup.py +++ b/py2app_setup.py @@ -10,7 +10,7 @@ from setuptools import setup app_name = 'K40 Whisperer' -app_version = "0.42" +app_version = "0.49" app_copyright = u'Copyright © 2017-2019, Scorch Works, GNU General Public License' main_script = 'k40_whisperer.py' url = 'https://github.com/stephenhouser/k40-whisperer' diff --git a/requirements.txt b/requirements.txt index 2a9bb95..b06217f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ lxml pyusb pillow +pyclipper diff --git a/svg_reader.py b/svg_reader.py index 18a2cc0..8bad785 100644 --- a/svg_reader.py +++ b/svg_reader.py @@ -1,6 +1,6 @@ #!/usr/bin/env python ''' -Copyright (C) 2017-2019 Scorch www.scorchworks.com +Copyright (C) 2017-2020 Scorch www.scorchworks.com Derived from dxf_outlines.py by Aaron Spike and Alvin Penner This program is free software; you can redistribute it and/or modify @@ -130,6 +130,8 @@ def __init__(self): self.flatness = 0.01 self.image_dpi = 1000 self.inkscape_exe_list = [] + self.inkscape_exe_list.append("C:\\Program Files\\Inkscape\\bin\\inkscape.exe") + self.inkscape_exe_list.append("C:\\Program Files (x86)\\Inkscape\\bin\\inkscape.exe") self.inkscape_exe_list.append("C:\\Program Files\\Inkscape\\inkscape.exe") self.inkscape_exe_list.append("C:\\Program Files (x86)\\Inkscape\\inkscape.exe") self.inkscape_exe_list.append("/usr/bin/inkscape") @@ -688,7 +690,7 @@ def Make_PNG(self): else: cmd = [ self.inkscape_exe, self.png_area, "--export-dpi", dpi, \ "--export-background","rgb(255, 255, 255)","--export-background-opacity", \ - "255" ,"--export-type=png", "--export-file", png_temp_file, svg_temp_file ] + "255" ,"--export-type=png", "--export-filename=%s" %(png_temp_file), svg_temp_file ] run_external(cmd, self.timout) self.raster_PIL = Image.open(png_temp_file) @@ -737,8 +739,18 @@ def convert_text2paths(self): svg_temp_file = os.path.join(tmp_dir, "k40w_temp.svg") txt2path_file = os.path.join(tmp_dir, "txt2path.svg") self.document.write(svg_temp_file) - cmd = [ self.inkscape_exe, "--export-text-to-path","--export-plain-svg",txt2path_file, svg_temp_file ] - run_external(cmd, self.timout) + + # Check Version of Inkscape + cmd = [ self.inkscape_exe, "-V"] + (stdout,stderr)=run_external(cmd, self.timout) + if stdout.find(b'Inkscape 1.')==-1: + cmd = [ self.inkscape_exe, "--export-text-to-path","--export-plain-svg", \ + txt2path_file, svg_temp_file, ] + else: + cmd = [ self.inkscape_exe, "--export-text-to-path","--export-plain-svg", \ + "--export-filename=%s" %(txt2path_file), svg_temp_file, ] + + (stdout,stderr)=run_external(cmd, self.timout) self.parse_svg(txt2path_file) except Exception as e: raise Exception("Inkscape Execution Failed (while converting text to paths).\n\n"+str(e))