1#!/usr/local/bin/python3.8
2"""
3    K40 Whisperer
4
5    Copyright (C) <2017-2021>  <Scorch>
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19"""
20version = '0.58'
21title_text = "K40 Whisperer V"+version
22
23import sys
24from math import *
25from egv import egv
26from nano_library import K40_CLASS
27from dxf import DXF_CLASS
28from svg_reader import SVG_READER
29from svg_reader import SVG_TEXT_EXCEPTION
30from svg_reader import SVG_PXPI_EXCEPTION
31from g_code_library import G_Code_Rip
32from interpolate import interpolate
33from ecoords import ECoord
34from convex_hull import hull2D
35from embedded_images import K40_Whisperer_Images
36
37import inkex
38import simplestyle
39import simpletransform
40import cubicsuperpath
41import cspsubdiv
42import traceback
43import struct
44
45DEBUG = False
46
47if DEBUG:
48    import inspect
49
50VERSION = sys.version_info[0]
51LOAD_MSG = ""
52
53if VERSION == 3:
54    from tkinter import *
55    from tkinter.filedialog import *
56    import tkinter.messagebox
57    MAXINT = sys.maxsize
58
59else:
60    from Tkinter import *
61    from tkFileDialog import *
62    import tkMessageBox
63    MAXINT = sys.maxint
64
65if VERSION < 3 and sys.version_info[1] < 6:
66    def next(item):
67        #return item.next()
68        return item.__next__()
69
70try:
71    import psyco
72    psyco.full()
73    LOAD_MSG = LOAD_MSG+"\nPsyco Loaded\n"
74except:
75    pass
76
77import math
78from time import time
79import os
80import re
81import binascii
82import getopt
83import operator
84import webbrowser
85from PIL import Image
86from PIL import ImageOps
87from PIL import ImageFilter
88
89try:
90    Image.warnings.simplefilter('ignore', Image.DecompressionBombWarning)
91except:
92    pass
93try:
94    from PIL import ImageTk
95    from PIL import _imaging
96except:
97    pass #Don't worry everything will still work
98
99PYCLIPPER=True
100try:
101    import pyclipper
102except:
103    print("Unable to load Pyclipper library (Offset trace outline will not work without it)")
104    PYCLIPPER = False
105
106try:
107    os.chdir(os.path.dirname(__file__))
108except:
109    pass
110
111QUIET = False
112
113################################################################################
114class Application(Frame):
115    def __init__(self, master):
116        self.trace_window = toplevel_dummy()
117        Frame.__init__(self, master)
118        self.w = 780
119        self.h = 490
120        frame = Frame(master, width= self.w, height=self.h)
121        self.master = master
122        self.x = -1
123        self.y = -1
124        self.createWidgets()
125        self.micro = False
126
127
128    def resetPath(self):
129        self.RengData  = ECoord()
130        self.VengData  = ECoord()
131        self.VcutData  = ECoord()
132        self.GcodeData = ECoord()
133        self.SCALE = 1
134        self.Design_bounds = (0,0,0,0)
135        self.UI_image = None
136        #if self.HomeUR.get():
137        self.move_head_window_temporary([0.0,0.0])
138        #else:
139        #    self.move_head_window_temporary([0.0,0.0])
140
141        self.pos_offset=[0.0,0.0]
142
143    def createWidgets(self):
144        self.initComplete = 0
145        self.stop=[True]
146
147        self.k40 = None
148        self.run_time = 0
149
150        self.master.bind("<Configure>", self.Master_Configure)
151        self.master.bind('<Enter>', self.bindConfigure)
152        self.master.bind('<F1>', self.KEY_F1)
153        self.master.bind('<F2>', self.KEY_F2)
154        self.master.bind('<F3>', self.KEY_F3)
155        self.master.bind('<F4>', self.KEY_F4)
156        self.master.bind('<F5>', self.KEY_F5)
157        self.master.bind('<F6>', self.KEY_F6)
158        self.master.bind('<Home>', self.Home)
159
160
161        self.master.bind('<Control-Left>'  , self.Move_Left)
162        self.master.bind('<Control-Right>' , self.Move_Right)
163        self.master.bind('<Control-Up>'    , self.Move_Up)
164        self.master.bind('<Control-Down>'  , self.Move_Down)
165
166        self.master.bind('<Control-Home>'  , self.Move_UL)
167        self.master.bind('<Control-Prior>' , self.Move_UR)
168        self.master.bind('<Control-Next>'  , self.Move_LR)
169        self.master.bind('<Control-End>'   , self.Move_LL)
170        self.master.bind('<Control-Clear>' , self.Move_CC)
171
172        self.master.bind('<Control-Key-4>' , self.Move_Left)
173        self.master.bind('<Control-6>'     , self.Move_Right)
174        self.master.bind('<Control-8>'     , self.Move_Up)
175        self.master.bind('<Control-Key-2>' , self.Move_Down)
176
177        self.master.bind('<Control-7>'     , self.Move_UL)
178        self.master.bind('<Control-9>'     , self.Move_UR)
179        self.master.bind('<Control-Key-3>' , self.Move_LR)
180        self.master.bind('<Control-Key-1>' , self.Move_LL)
181        self.master.bind('<Control-Key-5>' , self.Move_CC)
182
183        #####
184
185        self.master.bind('<Alt-Control-Left>' , self.Move_Arb_Left)
186        self.master.bind('<Alt-Control-Right>', self.Move_Arb_Right)
187        self.master.bind('<Alt-Control-Up>'   , self.Move_Arb_Up)
188        self.master.bind('<Alt-Control-Down>' , self.Move_Arb_Down)
189
190        self.master.bind('<Alt-Control-Key-4>', self.Move_Arb_Left)
191        self.master.bind('<Alt-Control-6>'    , self.Move_Arb_Right)
192        self.master.bind('<Alt-Control-8>'    , self.Move_Arb_Up)
193        self.master.bind('<Alt-Control-Key-2>', self.Move_Arb_Down)
194
195
196        self.master.bind('<Alt-Left>' , self.Move_Arb_Left)
197        self.master.bind('<Alt-Right>', self.Move_Arb_Right)
198        self.master.bind('<Alt-Up>'   , self.Move_Arb_Up)
199        self.master.bind('<Alt-Down>' , self.Move_Arb_Down)
200
201        self.master.bind('<Alt-Key-4>', self.Move_Arb_Left)
202        self.master.bind('<Alt-6>'    , self.Move_Arb_Right)
203        self.master.bind('<Alt-8>'    , self.Move_Arb_Up)
204        self.master.bind('<Alt-Key-2>', self.Move_Arb_Down)
205
206        #####
207        self.master.bind('<Control-i>' , self.Initialize_Laser)
208        self.master.bind('<Control-o>' , self.menu_File_Open_Design)
209        self.master.bind('<Control-l>' , self.menu_Reload_Design)
210        self.master.bind('<Control-h>' , self.Home)
211        self.master.bind('<Control-u>' , self.Unlock)
212        self.master.bind('<Escape>'    , self.Stop)
213        self.master.bind('<Control-t>' , self.TRACE_Settings_Window)
214
215        self.include_Reng = BooleanVar()
216        self.include_Rpth = BooleanVar()
217        self.include_Veng = BooleanVar()
218        self.include_Vcut = BooleanVar()
219        self.include_Gcde = BooleanVar()
220        self.include_Time = BooleanVar()
221
222        self.advanced = BooleanVar()
223
224        self.halftone     = BooleanVar()
225        self.mirror       = BooleanVar()
226        self.rotate       = BooleanVar()
227        self.negate       = BooleanVar()
228        self.inputCSYS    = BooleanVar()
229        self.HomeUR       = BooleanVar()
230        self.engraveUP    = BooleanVar()
231        self.init_home    = BooleanVar()
232        self.post_home    = BooleanVar()
233        self.post_beep    = BooleanVar()
234        self.post_disp    = BooleanVar()
235        self.post_exec    = BooleanVar()
236
237        self.pre_pr_crc   = BooleanVar()
238        self.inside_first = BooleanVar()
239        self.rotary       = BooleanVar()
240
241
242        self.ht_size    = StringVar()
243        self.Reng_feed  = StringVar()
244        self.Veng_feed  = StringVar()
245        self.Vcut_feed  = StringVar()
246
247        self.Reng_passes = StringVar()
248        self.Veng_passes = StringVar()
249        self.Vcut_passes = StringVar()
250        self.Gcde_passes = StringVar()
251
252
253        self.board_name = StringVar()
254        self.units      = StringVar()
255        self.jog_step   = StringVar()
256        self.rast_step  = StringVar()
257        self.funits     = StringVar()
258
259
260        self.bezier_M1     = StringVar()
261        self.bezier_M2     = StringVar()
262        self.bezier_weight = StringVar()
263
264##        self.unsharp_flag = BooleanVar()
265##        self.unsharp_r    = StringVar()
266##        self.unsharp_p    = StringVar()
267##        self.unsharp_t    = StringVar()
268##        self.unsharp_flag.set(False)
269##        self.unsharp_r.set("40")
270##        self.unsharp_p.set("350")
271##        self.unsharp_t.set("3")
272
273        self.LaserXsize = StringVar()
274        self.LaserYsize = StringVar()
275
276        self.LaserXscale = StringVar()
277        self.LaserYscale = StringVar()
278        self.LaserRscale = StringVar()
279
280        self.rapid_feed = StringVar()
281
282        self.gotoX = StringVar()
283        self.gotoY = StringVar()
284
285        self.n_egv_passes = StringVar()
286
287        self.inkscape_path = StringVar()
288        self.batch_path    = StringVar()
289        self.ink_timeout   = StringVar()
290
291        self.t_timeout  = StringVar()
292        self.n_timeouts  = StringVar()
293
294        self.Reng_time = StringVar()
295        self.Veng_time = StringVar()
296        self.Vcut_time = StringVar()
297        self.Gcde_time = StringVar()
298
299        self.comb_engrave = BooleanVar()
300        self.comb_vector  = BooleanVar()
301        self.zoom2image   = BooleanVar()
302
303        self.trace_w_laser  = BooleanVar()
304        self.trace_gap      = StringVar()
305        self.trace_speed    = StringVar()
306
307        ###########################################################################
308        #                         INITILIZE VARIABLES                             #
309        #    if you want to change a default setting this is the place to do it   #
310        ###########################################################################
311        self.include_Reng.set(1)
312        self.include_Rpth.set(0)
313        self.include_Veng.set(1)
314        self.include_Vcut.set(1)
315        self.include_Gcde.set(1)
316        self.include_Time.set(0)
317        self.advanced.set(0)
318
319        self.halftone.set(1)
320        self.mirror.set(0)
321        self.rotate.set(0)
322        self.negate.set(0)
323        self.inputCSYS.set(0)
324        self.HomeUR.set(0)
325        self.engraveUP.set(0)
326        self.init_home.set(1)
327        self.post_home.set(0)
328        self.post_beep.set(0)
329        self.post_disp.set(0)
330        self.post_exec.set(0)
331
332        self.pre_pr_crc.set(1)
333        self.inside_first.set(1)
334        self.rotary.set(0)
335
336        self.ht_size.set(500)
337
338        self.Reng_feed.set("100")
339        self.Veng_feed.set("20")
340        self.Vcut_feed.set("10")
341        self.Reng_passes.set("1")
342        self.Veng_passes.set("1")
343        self.Vcut_passes.set("1")
344        self.Gcde_passes.set("1")
345
346
347        self.jog_step.set("10.0")
348        self.rast_step.set("0.002")
349
350        self.bezier_weight.set("3.5")
351        self.bezier_M1.set("2.5")
352        self.bezier_M2.set("0.50")
353
354        self.bezier_weight_default = float(self.bezier_weight.get())
355        self.bezier_M1_default     = float(self.bezier_M1.get())
356        self.bezier_M2_default     = float(self.bezier_M2.get())
357
358
359        self.board_name.set("LASER-M2") # Options are
360                                        #    "LASER-M2",
361                                        #    "LASER-M1",
362                                        #    "LASER-M",
363                                        #    "LASER-B2",
364                                        #    "LASER-B1",
365                                        #    "LASER-B",
366                                        #    "LASER-A"
367
368
369        self.units.set("mm")            # Options are "in" and "mm"
370
371        self.ink_timeout.set("3")
372        self.t_timeout.set("200")
373        self.n_timeouts.set("30")
374
375        self.HOME_DIR    = os.path.expanduser("~")
376
377        if not os.path.isdir(self.HOME_DIR):
378            self.HOME_DIR = ""
379
380        self.DESIGN_FILE = (self.HOME_DIR+"/None")
381        self.EGV_FILE    = None
382
383        self.aspect_ratio =  0
384        self.segID   = []
385
386        self.LaserXsize.set("325")
387        self.LaserYsize.set("220")
388
389        self.LaserXscale.set("1.000")
390        self.LaserYscale.set("1.000")
391        self.LaserRscale.set("1.000")
392
393        self.rapid_feed.set("0.0")
394
395        self.gotoX.set("0.0")
396        self.gotoY.set("0.0")
397
398        self.n_egv_passes.set("1")
399
400        self.comb_engrave.set(0)
401        self.comb_vector.set(0)
402        self.zoom2image.set(0)
403
404
405        self.trace_w_laser.set(0)
406        self.trace_gap.set(0)
407        self.trace_speed.set(50)
408
409        self.laserX    = 0.0
410        self.laserY    = 0.0
411        self.PlotScale = 1.0
412        self.GUI_Disabled = False
413
414        # PAN and ZOOM STUFF
415        self.panx = 0
416        self.panx = 0
417        self.lastx = 0
418        self.lasty = 0
419        self.move_start_x = 0
420        self.move_start_y = 0
421
422
423        self.RengData  = ECoord()
424        self.VengData  = ECoord()
425        self.VcutData  = ECoord()
426        self.GcodeData = ECoord()
427        self.SCALE = 1
428        self.Design_bounds = (0,0,0,0)
429        self.UI_image = None
430        self.pos_offset=[0.0,0.0]
431        self.inkscape_warning = False
432
433        # Derived variables
434        if self.units.get() == 'in':
435            self.funits.set('in/min')
436            self.units_scale = 1.0
437        else:
438            self.units.set('mm')
439            self.funits.set('mm/s')
440            self.units_scale = 25.4
441
442        self.statusMessage = StringVar()
443        self.statusMessage.set("Welcome to K40 Whisperer")
444
445
446        self.Reng_time.set("0")
447        self.Veng_time.set("0")
448        self.Vcut_time.set("0")
449        self.Gcde_time.set("0")
450
451        self.min_vector_speed = 1.1 #in/min
452        self.min_raster_speed = 12  #in/min
453
454        ##########################################################################
455        ###                     END INITILIZING VARIABLES                      ###
456        ##########################################################################
457
458        # make a Status Bar
459        self.statusbar = Label(self.master, textvariable=self.statusMessage, \
460                                   bd=1, relief=SUNKEN , height=1)
461        self.statusbar.pack(anchor=SW, fill=X, side=BOTTOM)
462
463
464        # Canvas
465        lbframe = Frame( self.master )
466        self.PreviewCanvas_frame = lbframe
467        self.PreviewCanvas = Canvas(lbframe, width=self.w-(220+20), height=self.h-200, background="grey75")
468        self.PreviewCanvas.pack(side=LEFT, fill=BOTH, expand=1)
469        self.PreviewCanvas_frame.place(x=230, y=10)
470
471        self.PreviewCanvas.tag_bind('LaserTag',"<1>"              , self.mousePanStart)
472        self.PreviewCanvas.tag_bind('LaserTag',"<B1-Motion>"      , self.mousePan)
473        self.PreviewCanvas.tag_bind('LaserTag',"<ButtonRelease-1>", self.mousePanStop)
474
475        self.PreviewCanvas.tag_bind('LaserDot',"<3>"              , self.right_mousePanStart)
476        self.PreviewCanvas.tag_bind('LaserDot',"<B3-Motion>"      , self.right_mousePan)
477        self.PreviewCanvas.tag_bind('LaserDot',"<ButtonRelease-3>", self.right_mousePanStop)
478
479        # Left Column #
480        self.separator1 = Frame(self.master, height=2, bd=1, relief=SUNKEN)
481        self.separator2 = Frame(self.master, height=2, bd=1, relief=SUNKEN)
482        self.separator3 = Frame(self.master, height=2, bd=1, relief=SUNKEN)
483        self.separator4 = Frame(self.master, height=2, bd=1, relief=SUNKEN)
484
485        self.Label_Reng_feed_u = Label(self.master,textvariable=self.funits, anchor=W)
486        self.Entry_Reng_feed   = Entry(self.master,width="15")
487        self.Entry_Reng_feed.configure(textvariable=self.Reng_feed,justify='center',fg="black")
488        self.Reng_feed.trace_variable("w", self.Entry_Reng_feed_Callback)
489        self.NormalColor =  self.Entry_Reng_feed.cget('bg')
490
491        self.Label_Veng_feed_u = Label(self.master,textvariable=self.funits, anchor=W)
492        self.Entry_Veng_feed   = Entry(self.master,width="15")
493        self.Entry_Veng_feed.configure(textvariable=self.Veng_feed,justify='center',fg="blue")
494        self.Veng_feed.trace_variable("w", self.Entry_Veng_feed_Callback)
495        self.NormalColor =  self.Entry_Veng_feed.cget('bg')
496
497        self.Label_Vcut_feed_u = Label(self.master,textvariable=self.funits, anchor=W)
498        self.Entry_Vcut_feed   = Entry(self.master,width="15")
499        self.Entry_Vcut_feed.configure(textvariable=self.Vcut_feed,justify='center',fg="red")
500        self.Vcut_feed.trace_variable("w", self.Entry_Vcut_feed_Callback)
501        self.NormalColor =  self.Entry_Vcut_feed.cget('bg')
502
503        # Buttons
504        self.Reng_Button  = Button(self.master,text="Raster Engrave", command=self.Raster_Eng)
505        self.Veng_Button  = Button(self.master,text="Vector Engrave", command=self.Vector_Eng)
506        self.Vcut_Button  = Button(self.master,text="Vector Cut"    , command=self.Vector_Cut)
507        self.Grun_Button  = Button(self.master,text="Run G-Code"    , command=self.Gcode_Cut)
508
509
510        self.Reng_Veng_Button      = Button(self.master,text="Raster and\nVector Engrave", command=self.Raster_Vector_Eng)
511        self.Veng_Vcut_Button      = Button(self.master,text="Vector Engrave\nand Cut", command=self.Vector_Eng_Cut)
512        self.Reng_Veng_Vcut_Button = Button(self.master,text="Raster Engrave\nVector Engrave\nand\nVector Cut", command=self.Raster_Vector_Cut)
513
514        self.Label_Position_Control = Label(self.master,text="Position Controls:", anchor=W)
515
516        self.Initialize_Button = Button(self.master,text="Initialize Laser Cutter", command=self.Initialize_Laser)
517
518        self.Open_Button       = Button(self.master,text="Open\nDesign File",   command=self.menu_File_Open_Design)
519        self.Reload_Button     = Button(self.master,text="Reload\nDesign File", command=self.menu_Reload_Design)
520
521        self.Home_Button       = Button(self.master,text="Home",            command=self.Home)
522        self.UnLock_Button     = Button(self.master,text="Unlock Rail",     command=self.Unlock)
523        self.Stop_Button       = Button(self.master,text="Pause/Stop",      command=self.Stop)
524
525        try:
526            self.left_image  = PhotoImage(data=K40_Whisperer_Images.left_B64,  format='gif')
527            self.right_image = PhotoImage(data=K40_Whisperer_Images.right_B64, format='gif')
528            self.up_image    = PhotoImage(data=K40_Whisperer_Images.up_B64,    format='gif')
529            self.down_image  = PhotoImage(data=K40_Whisperer_Images.down_B64,  format='gif')
530
531            self.Right_Button   = Button(self.master,image=self.right_image, command=self.Move_Right)
532            self.Left_Button    = Button(self.master,image=self.left_image,  command=self.Move_Left)
533            self.Up_Button      = Button(self.master,image=self.up_image,    command=self.Move_Up)
534            self.Down_Button    = Button(self.master,image=self.down_image,  command=self.Move_Down)
535
536            self.UL_image  = PhotoImage(data=K40_Whisperer_Images.UL_B64, format='gif')
537            self.UR_image  = PhotoImage(data=K40_Whisperer_Images.UR_B64, format='gif')
538            self.LR_image  = PhotoImage(data=K40_Whisperer_Images.LR_B64, format='gif')
539            self.LL_image  = PhotoImage(data=K40_Whisperer_Images.LL_B64, format='gif')
540            self.CC_image  = PhotoImage(data=K40_Whisperer_Images.CC_B64, format='gif')
541
542            self.UL_Button = Button(self.master,image=self.UL_image, command=self.Move_UL)
543            self.UR_Button = Button(self.master,image=self.UR_image, command=self.Move_UR)
544            self.LR_Button = Button(self.master,image=self.LR_image, command=self.Move_LR)
545            self.LL_Button = Button(self.master,image=self.LL_image, command=self.Move_LL)
546            self.CC_Button = Button(self.master,image=self.CC_image, command=self.Move_CC)
547
548        except:
549            self.Right_Button   = Button(self.master,text=">",          command=self.Move_Right)
550            self.Left_Button    = Button(self.master,text="<",          command=self.Move_Left)
551            self.Up_Button      = Button(self.master,text="^",          command=self.Move_Up)
552            self.Down_Button    = Button(self.master,text="v",          command=self.Move_Down)
553
554            self.UL_Button = Button(self.master,text=" ", command=self.Move_UL)
555            self.UR_Button = Button(self.master,text=" ", command=self.Move_UR)
556            self.LR_Button = Button(self.master,text=" ", command=self.Move_LR)
557            self.LL_Button = Button(self.master,text=" ", command=self.Move_LL)
558            self.CC_Button = Button(self.master,text=" ", command=self.Move_CC)
559
560        self.Label_Step   = Label(self.master,text="Jog Step", anchor=CENTER )
561        self.Label_Step_u = Label(self.master,textvariable=self.units, anchor=W)
562        self.Entry_Step   = Entry(self.master,width="15")
563        self.Entry_Step.configure(textvariable=self.jog_step, justify='center')
564        self.jog_step.trace_variable("w", self.Entry_Step_Callback)
565
566        ###########################################################################
567        self.GoTo_Button    = Button(self.master,text="Move To", command=self.GoTo)
568
569        self.Entry_GoToX   = Entry(self.master,width="15",justify='center')
570        self.Entry_GoToX.configure(textvariable=self.gotoX)
571        self.gotoX.trace_variable("w", self.Entry_GoToX_Callback)
572        self.Entry_GoToY   = Entry(self.master,width="15",justify='center')
573        self.Entry_GoToY.configure(textvariable=self.gotoY)
574        self.gotoY.trace_variable("w", self.Entry_GoToY_Callback)
575
576        self.Label_GoToX   = Label(self.master,text="X", anchor=CENTER )
577        self.Label_GoToY   = Label(self.master,text="Y", anchor=CENTER )
578        ###########################################################################
579        # End Left Column #
580
581        # Advanced Column     #
582        self.separator_vert = Frame(self.master, height=2, bd=1, relief=SUNKEN)
583        self.Label_Advanced_column = Label(self.master,text="Advanced Settings",anchor=CENTER)
584        self.separator_adv = Frame(self.master, height=2, bd=1, relief=SUNKEN)
585
586        self.Label_Halftone_adv = Label(self.master,text="Halftone (Dither)")
587        self.Checkbutton_Halftone_adv = Checkbutton(self.master,text=" ", anchor=W)
588        self.Checkbutton_Halftone_adv.configure(variable=self.halftone)
589        self.halftone.trace_variable("w", self.View_Refresh_and_Reset_RasterPath) #self.menu_View_Refresh_Callback
590
591        self.Label_Negate_adv = Label(self.master,text="Invert Raster Color")
592        self.Checkbutton_Negate_adv = Checkbutton(self.master,text=" ", anchor=W)
593        self.Checkbutton_Negate_adv.configure(variable=self.negate)
594        self.negate.trace_variable("w", self.View_Refresh_and_Reset_RasterPath)
595
596        self.separator_adv2 = Frame(self.master, height=2, bd=1, relief=SUNKEN)
597
598        self.Label_Mirror_adv = Label(self.master,text="Mirror Design")
599        self.Checkbutton_Mirror_adv = Checkbutton(self.master,text=" ", anchor=W)
600        self.Checkbutton_Mirror_adv.configure(variable=self.mirror)
601        self.mirror.trace_variable("w", self.View_Refresh_and_Reset_RasterPath)
602
603        self.Label_Rotate_adv = Label(self.master,text="Rotate Design")
604        self.Checkbutton_Rotate_adv = Checkbutton(self.master,text=" ", anchor=W)
605        self.Checkbutton_Rotate_adv.configure(variable=self.rotate)
606        self.rotate.trace_variable("w", self.View_Refresh_and_Reset_RasterPath)
607
608        self.separator_adv3 = Frame(self.master, height=2, bd=1, relief=SUNKEN)
609
610        self.Label_inputCSYS_adv = Label(self.master,text="Use Input CSYS")
611        self.Checkbutton_inputCSYS_adv = Checkbutton(self.master,text=" ", anchor=W)
612        self.Checkbutton_inputCSYS_adv.configure(variable=self.inputCSYS)
613        self.inputCSYS.trace_variable("w", self.menu_View_inputCSYS_Refresh_Callback)
614
615        self.Label_Inside_First_adv = Label(self.master,text="Cut Inside First")
616        self.Checkbutton_Inside_First_adv = Checkbutton(self.master,text=" ", anchor=W)
617        self.Checkbutton_Inside_First_adv.configure(variable=self.inside_first)
618        self.inside_first.trace_variable("w", self.menu_Inside_First_Callback)
619
620        self.Label_Inside_First_adv = Label(self.master,text="Cut Inside First")
621        self.Checkbutton_Inside_First_adv = Checkbutton(self.master,text=" ", anchor=W)
622        self.Checkbutton_Inside_First_adv.configure(variable=self.inside_first)
623
624        self.Label_Rotary_Enable_adv = Label(self.master,text="Use Rotary Settings")
625        self.Checkbutton_Rotary_Enable_adv = Checkbutton(self.master,text="")
626        self.Checkbutton_Rotary_Enable_adv.configure(variable=self.rotary)
627        self.rotary.trace_variable("w", self.Reset_RasterPath_and_Update_Time)
628
629
630        #####
631        self.separator_comb = Frame(self.master, height=2, bd=1, relief=SUNKEN)
632
633        self.Label_Comb_Engrave_adv = Label(self.master,text="Group Engrave Tasks")
634        self.Checkbutton_Comb_Engrave_adv = Checkbutton(self.master,text=" ", anchor=W)
635        self.Checkbutton_Comb_Engrave_adv.configure(variable=self.comb_engrave)
636        self.comb_engrave.trace_variable("w", self.menu_View_Refresh_Callback)
637
638        self.Label_Comb_Vector_adv = Label(self.master,text="Group Vector Tasks")
639        self.Checkbutton_Comb_Vector_adv = Checkbutton(self.master,text=" ", anchor=W)
640        self.Checkbutton_Comb_Vector_adv.configure(variable=self.comb_vector)
641        self.comb_vector.trace_variable("w", self.menu_View_Refresh_Callback)
642        #####
643
644        self.Label_Reng_passes = Label(self.master,text="Raster Eng. Passes")
645        self.Entry_Reng_passes   = Entry(self.master,width="15")
646        self.Entry_Reng_passes.configure(textvariable=self.Reng_passes,justify='center',fg="black")
647        self.Reng_passes.trace_variable("w", self.Entry_Reng_passes_Callback)
648        self.NormalColor =  self.Entry_Reng_passes.cget('bg')
649
650        self.Label_Veng_passes = Label(self.master,text="Vector Eng. Passes")
651        self.Entry_Veng_passes   = Entry(self.master,width="15")
652        self.Entry_Veng_passes.configure(textvariable=self.Veng_passes,justify='center',fg="blue")
653        self.Veng_passes.trace_variable("w", self.Entry_Veng_passes_Callback)
654        self.NormalColor =  self.Entry_Veng_passes.cget('bg')
655
656        self.Label_Vcut_passes = Label(self.master,text="Vector Cut Passes")
657        self.Entry_Vcut_passes   = Entry(self.master,width="15")
658        self.Entry_Vcut_passes.configure(textvariable=self.Vcut_passes,justify='center',fg="red")
659        self.Vcut_passes.trace_variable("w", self.Entry_Vcut_passes_Callback)
660        self.NormalColor =  self.Entry_Vcut_passes.cget('bg')
661
662        self.Label_Gcde_passes = Label(self.master,text="G-Code Passes")
663        self.Entry_Gcde_passes   = Entry(self.master,width="15")
664        self.Entry_Gcde_passes.configure(textvariable=self.Gcde_passes,justify='center',fg="black")
665        self.Gcde_passes.trace_variable("w", self.Entry_Gcde_passes_Callback)
666        self.NormalColor =  self.Entry_Gcde_passes.cget('bg')
667
668
669        self.Hide_Adv_Button = Button(self.master,text="Hide Advanced", command=self.Hide_Advanced)
670
671        # End Right Column #
672        self.calc_button = Button(self.master,text="Calculate Raster Time", command=self.menu_Calc_Raster_Time)
673
674        #GEN Setting Window Entry initializations
675        self.Entry_Sspeed    = Entry()
676        self.Entry_BoxGap    = Entry()
677        self.Entry_ContAngle = Entry()
678
679        # Make Menu Bar
680        self.menuBar = Menu(self.master, relief = "raised", bd=2)
681
682
683
684
685        top_File = Menu(self.menuBar, tearoff=0)
686        top_File.add("command", label = "Save Settings File", command = self.menu_File_Save)
687        top_File.add("command", label = "Read Settings File", command = self.menu_File_Open_Settings_File)
688
689        top_File.add_separator()
690        top_File.add("command", label = "Open Design (SVG/DXF/G-Code)"  , command = self.menu_File_Open_Design)
691        top_File.add("command", label = "Reload Design"          , command = self.menu_Reload_Design)
692
693        top_File.add_separator()
694        top_File.add("command", label = "Send EGV File to Laser"             , command = self.menu_File_Open_EGV)
695
696        SaveEGVmenu = Menu(self.master, relief = "raised", bd=2, tearoff=0)
697        top_File.add_cascade(label="Save EGV File", menu=SaveEGVmenu)
698        SaveEGVmenu.add("command", label = "Raster Engrave"     , command = self.menu_File_Raster_Engrave)
699        SaveEGVmenu.add("command", label = "Vector Engrave"     , command = self.menu_File_Vector_Engrave)
700        SaveEGVmenu.add("command", label = "Vector Cut"         , command = self.menu_File_Vector_Cut)
701        SaveEGVmenu.add("command", label = "G-Code Operations"  , command = self.menu_File_G_Code)
702        SaveEGVmenu.add_separator()
703        SaveEGVmenu.add("command", label = "Raster and Vector Engrave"             , command = self.menu_File_Raster_Vector_Engrave)
704        SaveEGVmenu.add("command", label = "Vector Engrave and Cut"                , command = self.menu_File_Vector_Engrave_Cut)
705        SaveEGVmenu.add("command", label = "Raster, Vector Engrave and Vector Cut" , command = self.menu_File_Raster_Vector_Cut)
706
707
708        top_File.add_separator()
709        top_File.add("command", label = "Exit"              , command = self.menu_File_Quit)
710
711        self.menuBar.add("cascade", label="File", menu=top_File)
712
713        #top_Edit = Menu(self.menuBar, tearoff=0)
714        #self.menuBar.add("cascade", label="Edit", menu=top_Edit)
715
716        top_View = Menu(self.menuBar, tearoff=0)
717        top_View.add("command", label = "Refresh   <F5>", command = self.menu_View_Refresh)
718        top_View.add_separator()
719        top_View.add_checkbutton(label = "Show Raster Image"  ,  variable=self.include_Reng ,command= self.menu_View_Refresh)
720        if DEBUG:
721            top_View.add_checkbutton(label = "Show Raster Paths" ,variable=self.include_Rpth ,command= self.menu_View_Refresh)
722
723        top_View.add_checkbutton(label = "Show Vector Engrave",   variable=self.include_Veng ,command= self.menu_View_Refresh)
724        top_View.add_checkbutton(label = "Show Vector Cut"    ,   variable=self.include_Vcut ,command= self.menu_View_Refresh)
725        top_View.add_checkbutton(label = "Show G-Code Paths"  ,   variable=self.include_Gcde ,command= self.menu_View_Refresh)
726        top_View.add_separator()
727        top_View.add_checkbutton(label = "Show Time Estimates",   variable=self.include_Time ,command= self.menu_View_Refresh)
728        top_View.add_checkbutton(label = "Zoom to Design Size",   variable=self.zoom2image   ,command= self.menu_View_Refresh)
729
730        #top_View.add_separator()
731        #top_View.add("command", label = "computeAccurateReng",command= self.computeAccurateReng)
732        #top_View.add("command", label = "computeAccurateVeng",command= self.computeAccurateVeng)
733        #top_View.add("command", label = "computeAccurateVcut",command= self.computeAccurateVcut)
734
735        self.menuBar.add("cascade", label="View", menu=top_View)
736
737        top_Tools = Menu(self.menuBar, tearoff=0)
738        self.menuBar.add("cascade", label="Tools", menu=top_Tools)
739        USBmenu = Menu(self.master, relief = "raised", bd=2, tearoff=0)
740
741        top_Tools.add("command", label = "Calculate Raster Time", command = self.menu_Calc_Raster_Time)
742        top_Tools.add("command", label = "Trace Design Boundary <Ctrl-t>", command = self.TRACE_Settings_Window)
743        top_Tools.add_separator()
744        top_Tools.add("command", label = "Initialize Laser <Ctrl-i>", command = self.Initialize_Laser)
745        top_Tools.add_cascade(label="USB", menu=USBmenu)
746        USBmenu.add("command", label = "Reset USB", command = self.Reset)
747        USBmenu.add("command", label = "Release USB", command = self.Release_USB)
748
749
750
751        #top_USB = Menu(self.menuBar, tearoff=0)
752        #top_USB.add("command", label = "Reset USB", command = self.Reset)
753        #top_USB.add("command", label = "Release USB", command = self.Release_USB)
754        #top_USB.add("command", label = "Initialize Laser", command = self.Initialize_Laser)
755        #self.menuBar.add("cascade", label="USB", menu=top_USB)
756
757
758        top_Settings = Menu(self.menuBar, tearoff=0)
759        top_Settings.add("command", label = "General Settings <F2>", command = self.GEN_Settings_Window)
760        top_Settings.add("command", label = "Raster Settings <F3>",  command = self.RASTER_Settings_Window)
761        top_Settings.add("command", label = "Rotary Settings <F4>",  command = self.ROTARY_Settings_Window)
762        top_Settings.add_separator()
763        top_Settings.add_checkbutton(label = "Advanced Settings <F6>", variable=self.advanced ,command= self.menu_View_Refresh)
764
765        self.menuBar.add("cascade", label="Settings", menu=top_Settings)
766
767        top_Help = Menu(self.menuBar, tearoff=0)
768        top_Help.add("command", label = "About (e-mail)", command = self.menu_Help_About)
769        top_Help.add("command", label = "K40 Whisperer Web Page", command = self.menu_Help_Web)
770        top_Help.add("command", label = "Manual (Web Page)", command = self.menu_Help_Manual)
771        self.menuBar.add("cascade", label="Help", menu=top_Help)
772
773        self.master.config(menu=self.menuBar)
774
775        ##########################################################################
776        #                  Config File and command line options                  #
777        ##########################################################################
778        config_file = "k40_whisperer.txt"
779        home_config1 = self.HOME_DIR + "/" + config_file
780        if ( os.path.isfile(config_file) ):
781            self.Open_Settings_File(config_file)
782        elif ( os.path.isfile(home_config1) ):
783            self.Open_Settings_File(home_config1)
784
785
786#        opts, args = None, None
787#        try:
788#            opts, args = getopt.getopt(sys.argv[1:], "ho:",["help", "other_option"])
789#        except:
790#            debug_message('Unable interpret command line options')
791#            sys.exit()
792#        for option, value in opts:
793##            if option in ('-h','--help'):
794##                fmessage(' ')
795##                fmessage('Usage: python .py [-g file]')
796##                fmessage('-o    : unknown other option (also --other_option)')
797##                fmessage('-h    : print this help (also --help)\n')
798##                sys.exit()
799#            if option in ('-m','--micro'):
800#                self.micro = True
801
802        ##########################################################################
803
804################################################################################
805    def entry_set(self, val2, calc_flag=0, new=0):
806        if calc_flag == 0 and new==0:
807            try:
808                self.statusbar.configure( bg = 'yellow' )
809                val2.configure( bg = 'yellow' )
810                self.statusMessage.set(" Recalculation required.")
811            except:
812                pass
813        elif calc_flag == 3:
814            try:
815                val2.configure( bg = 'red' )
816                self.statusbar.configure( bg = 'red' )
817                self.statusMessage.set(" Value should be a number. ")
818            except:
819                pass
820        elif calc_flag == 2:
821            try:
822                self.statusbar.configure( bg = 'red' )
823                val2.configure( bg = 'red' )
824            except:
825                pass
826        elif (calc_flag == 0 or calc_flag == 1) and new==1 :
827            try:
828                self.statusbar.configure( bg = 'white' )
829                self.statusMessage.set(" ")
830                val2.configure( bg = 'white' )
831            except:
832                pass
833        elif (calc_flag == 1) and new==0 :
834            try:
835                self.statusbar.configure( bg = 'white' )
836                self.statusMessage.set(" ")
837                val2.configure( bg = 'white' )
838            except:
839                pass
840
841        elif (calc_flag == 0 or calc_flag == 1) and new==2:
842            return 0
843        return 1
844
845################################################################################
846    def Write_Config_File(self, event):
847
848        config_data = self.WriteConfig()
849        config_file = "k40_whisperer.txt"
850        configname_full = self.HOME_DIR + "/" + config_file
851
852        current_name = event.widget.winfo_parent()
853        win_id = event.widget.nametowidget(current_name)
854
855        if ( os.path.isfile(configname_full) ):
856            try:
857                win_id.withdraw()
858            except:
859                pass
860
861            if not message_ask_ok_cancel("Replace", "Replace Exiting Configuration File?\n"+configname_full):
862                try:
863                    win_id.deiconify()
864                except:
865                    pass
866                return
867        try:
868            fout = open(configname_full,'w')
869        except:
870            self.statusMessage.set("Unable to open file for writing: %s" %(configname_full))
871            self.statusbar.configure( bg = 'red' )
872            return
873        for line in config_data:
874            try:
875                fout.write(line+'\n')
876            except:
877                fout.write('(skipping line)\n')
878        fout.close
879        self.statusMessage.set("Configuration File Saved: %s" %(configname_full))
880        self.statusbar.configure( bg = 'white' )
881        try:
882            win_id.deiconify()
883        except:
884            pass
885
886    ################################################################################
887    def WriteConfig(self):
888        global Zero
889        header = []
890        header.append('( K40 Whisperer Settings: '+version+' )')
891        header.append('( by Scorch - 2019 )')
892        header.append("(=========================================================)")
893        # BOOL
894        header.append('(k40_whisperer_set include_Reng  %s )'  %( int(self.include_Reng.get())  ))
895        header.append('(k40_whisperer_set include_Veng  %s )'  %( int(self.include_Veng.get())  ))
896        header.append('(k40_whisperer_set include_Vcut  %s )'  %( int(self.include_Vcut.get())  ))
897        header.append('(k40_whisperer_set include_Gcde  %s )'  %( int(self.include_Gcde.get())  ))
898        header.append('(k40_whisperer_set include_Time  %s )'  %( int(self.include_Time.get())  ))
899
900        header.append('(k40_whisperer_set halftone      %s )'  %( int(self.halftone.get())      ))
901        header.append('(k40_whisperer_set HomeUR        %s )'  %( int(self.HomeUR.get())        ))
902        header.append('(k40_whisperer_set inputCSYS     %s )'  %( int(self.inputCSYS.get())     ))
903        header.append('(k40_whisperer_set advanced      %s )'  %( int(self.advanced.get())      ))
904        header.append('(k40_whisperer_set mirror        %s )'  %( int(self.mirror.get())        ))
905        header.append('(k40_whisperer_set rotate        %s )'  %( int(self.rotate.get())        ))
906        header.append('(k40_whisperer_set negate        %s )'  %( int(self.negate.get())        ))
907
908        header.append('(k40_whisperer_set engraveUP     %s )'  %( int(self.engraveUP.get())     ))
909        header.append('(k40_whisperer_set init_home     %s )'  %( int(self.init_home.get())     ))
910        header.append('(k40_whisperer_set post_home     %s )'  %( int(self.post_home.get())     ))
911        header.append('(k40_whisperer_set post_beep     %s )'  %( int(self.post_beep.get())     ))
912        header.append('(k40_whisperer_set post_disp     %s )'  %( int(self.post_disp.get())     ))
913        header.append('(k40_whisperer_set post_exec     %s )'  %( int(self.post_exec.get())     ))
914
915        header.append('(k40_whisperer_set pre_pr_crc    %s )'  %( int(self.pre_pr_crc.get())    ))
916        header.append('(k40_whisperer_set inside_first  %s )'  %( int(self.inside_first.get())  ))
917
918        header.append('(k40_whisperer_set comb_engrave  %s )'  %( int(self.comb_engrave.get())  ))
919        header.append('(k40_whisperer_set comb_vector   %s )'  %( int(self.comb_vector.get())   ))
920        header.append('(k40_whisperer_set zoom2image    %s )'  %( int(self.zoom2image.get())    ))
921        header.append('(k40_whisperer_set rotary        %s )'  %( int(self.rotary.get())        ))
922
923        header.append('(k40_whisperer_set trace_w_laser %s )'  %( int(self.trace_w_laser.get()) ))
924
925        # STRING.get()
926        header.append('(k40_whisperer_set board_name    %s )'  %( self.board_name.get()     ))
927        header.append('(k40_whisperer_set units         %s )'  %( self.units.get()          ))
928        header.append('(k40_whisperer_set Reng_feed     %s )'  %( self.Reng_feed.get()      ))
929        header.append('(k40_whisperer_set Veng_feed     %s )'  %( self.Veng_feed.get()      ))
930        header.append('(k40_whisperer_set Vcut_feed     %s )'  %( self.Vcut_feed.get()      ))
931        header.append('(k40_whisperer_set jog_step      %s )'  %( self.jog_step.get()       ))
932
933        header.append('(k40_whisperer_set Reng_passes   %s )'  %( self.Reng_passes.get()    ))
934        header.append('(k40_whisperer_set Veng_passes   %s )'  %( self.Veng_passes.get()    ))
935        header.append('(k40_whisperer_set Vcut_passes   %s )'  %( self.Vcut_passes.get()    ))
936        header.append('(k40_whisperer_set Gcde_passes   %s )'  %( self.Gcde_passes.get()    ))
937
938        header.append('(k40_whisperer_set rast_step     %s )'  %( self.rast_step.get()      ))
939        header.append('(k40_whisperer_set ht_size       %s )'  %( self.ht_size.get()        ))
940
941        header.append('(k40_whisperer_set LaserXsize    %s )'  %( self.LaserXsize.get()     ))
942        header.append('(k40_whisperer_set LaserYsize    %s )'  %( self.LaserYsize.get()     ))
943        header.append('(k40_whisperer_set LaserXscale   %s )'  %( self.LaserXscale.get()    ))
944        header.append('(k40_whisperer_set LaserYscale   %s )'  %( self.LaserYscale.get()    ))
945        header.append('(k40_whisperer_set LaserRscale   %s )'  %( self.LaserRscale.get()    ))
946        header.append('(k40_whisperer_set rapid_feed   %s )'  %( self.rapid_feed.get()      ))
947
948        header.append('(k40_whisperer_set gotoX         %s )'  %( self.gotoX.get()          ))
949        header.append('(k40_whisperer_set gotoY         %s )'  %( self.gotoY.get()          ))
950
951        header.append('(k40_whisperer_set bezier_M1     %s )'  %( self.bezier_M1.get()      ))
952        header.append('(k40_whisperer_set bezier_M2     %s )'  %( self.bezier_M2.get()      ))
953        header.append('(k40_whisperer_set bezier_weight %s )'  %( self.bezier_weight.get()  ))
954
955        header.append('(k40_whisperer_set trace_gap     %s )'  %( self.trace_gap.get()      ))
956        header.append('(k40_whisperer_set trace_speed   %s )'  %( self.trace_speed.get()    ))
957
958##        header.append('(k40_whisperer_set unsharp_flag  %s )'  %( int(self.unsharp_flag.get())  ))
959##        header.append('(k40_whisperer_set unsharp_r     %s )'  %( self.unsharp_r.get()      ))
960##        header.append('(k40_whisperer_set unsharp_p     %s )'  %( self.unsharp_p.get()      ))
961##        header.append('(k40_whisperer_set unsharp_t     %s )'  %( self.unsharp_t.get()      ))
962
963        header.append('(k40_whisperer_set t_timeout     %s )'  %( self.t_timeout.get()      ))
964        header.append('(k40_whisperer_set n_timeouts    %s )'  %( self.n_timeouts.get()     ))
965
966        header.append('(k40_whisperer_set ink_timeout   %s )'  %( self.ink_timeout.get()    ))
967
968
969        header.append('(k40_whisperer_set designfile    \042%s\042 )' %( self.DESIGN_FILE   ))
970        header.append('(k40_whisperer_set inkscape_path \042%s\042 )' %( self.inkscape_path.get() ))
971        header.append('(k40_whisperer_set batch_path    \042%s\042 )' %( self.batch_path.get() ))
972
973
974        self.jog_step
975        header.append("(=========================================================)")
976
977        return header
978        ######################################################
979
980    def Quit_Click(self, event):
981        self.statusMessage.set("Exiting!")
982        self.Release_USB
983        root.destroy()
984
985    def mousePanStart(self,event):
986        self.panx = event.x
987        self.pany = event.y
988        self.move_start_x = event.x
989        self.move_start_y = event.y
990
991    def mousePan(self,event):
992        all = self.PreviewCanvas.find_all()
993        dx = event.x-self.panx
994        dy = event.y-self.pany
995
996        self.PreviewCanvas.move('LaserTag', dx, dy)
997        self.lastx = self.lastx + dx
998        self.lasty = self.lasty + dy
999        self.panx = event.x
1000        self.pany = event.y
1001
1002    def mousePanStop(self,event):
1003        Xold = round(self.laserX,3)
1004        Yold = round(self.laserY,3)
1005
1006        can_dx = event.x-self.move_start_x
1007        can_dy = -(event.y-self.move_start_y)
1008
1009        dx = can_dx*self.PlotScale
1010        dy = can_dy*self.PlotScale
1011        if self.HomeUR.get():
1012            dx = -dx
1013        self.laserX,self.laserY = self.XY_in_bounds(dx,dy)
1014        DXmils = round((self.laserX - Xold)*1000.0,0)
1015        DYmils = round((self.laserY - Yold)*1000.0,0)
1016
1017        if self.Send_Rapid_Move(DXmils,DYmils):
1018            self.menu_View_Refresh()
1019
1020    def right_mousePanStart(self,event):
1021        self.s_panx = event.x
1022        self.s_pany = event.y
1023        self.s_move_start_x = event.x
1024        self.s_move_start_y = event.y
1025
1026    def right_mousePan(self,event):
1027        all = self.PreviewCanvas.find_all()
1028        dx = event.x-self.s_panx
1029        dy = event.y-self.s_pany
1030
1031        self.PreviewCanvas.move('LaserDot', dx, dy)
1032        self.s_lastx = self.lastx + dx
1033        self.s_lasty = self.lasty + dy
1034        self.s_panx = event.x
1035        self.s_pany = event.y
1036
1037    def right_mousePanStop(self,event):
1038        Xold = round(self.laserX,3)
1039        Yold = round(self.laserY,3)
1040        can_dx =   event.x-self.s_move_start_x
1041        can_dy = -(event.y-self.s_move_start_y)
1042
1043        dx = can_dx*self.PlotScale
1044        dy = can_dy*self.PlotScale
1045
1046        DX =  round(dx*1000)
1047        DY =  round(dy*1000)
1048        self.Move_Arbitrary(DX,DY)
1049        self.menu_View_Refresh()
1050
1051    def LASER_Size(self):
1052        MINX = 0.0
1053        MAXY = 0.0
1054        if self.units.get()=="in":
1055            MAXX =  float(self.LaserXsize.get())
1056            MINY = -float(self.LaserYsize.get())
1057        else:
1058            MAXX =  float(self.LaserXsize.get())/25.4
1059            MINY = -float(self.LaserYsize.get())/25.4
1060
1061        return (MAXX-MINX,MAXY-MINY)
1062
1063
1064    def XY_in_bounds(self,dx_inches,dy_inches, no_size=False):
1065        MINX = 0.0
1066        MAXY = 0.0
1067        if self.units.get()=="in":
1068            MAXX =  float(self.LaserXsize.get())
1069            MINY = -float(self.LaserYsize.get())
1070        else:
1071            MAXX =  float(self.LaserXsize.get())/25.4
1072            MINY = -float(self.LaserYsize.get())/25.4
1073
1074        if (self.inputCSYS.get() and self.RengData.image == None) or no_size:
1075            xmin,xmax,ymin,ymax = 0.0,0.0,0.0,0.0
1076        else:
1077            xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
1078
1079        X = self.laserX + dx_inches
1080        Y = self.laserY + dy_inches
1081        ################
1082        dx=xmax-xmin
1083        dy=ymax-ymin
1084        if X < MINX:
1085            X = MINX
1086        if X+dx > MAXX:
1087            X = MAXX-dx
1088
1089        if Y-dy < MINY:
1090            Y = MINY+dy
1091        if Y > MAXY:
1092            Y = MAXY
1093        ################
1094        if not no_size:
1095            XOFF = self.pos_offset[0]/1000.0
1096            YOFF = self.pos_offset[1]/1000.0
1097            if X+XOFF < MINX:
1098                X= X +(MINX-(X+XOFF))
1099            if X+XOFF > MAXX:
1100                X= X -((X+XOFF)-MAXX)
1101            if Y+YOFF < MINY:
1102                Y= Y + (MINY-(Y+YOFF))
1103            if Y+YOFF > MAXY:
1104                Y= Y -((Y+YOFF)-MAXY)
1105        ################
1106        X = round(X,3)
1107        Y = round(Y,3)
1108        return X,Y
1109
1110##    def computeAccurateVeng(self):
1111##        self.update_gui("Optimize vector engrave.")
1112##        self.VengData.set_ecoords(self.optimize_paths(self.VengData.ecoords),data_sorted=True)
1113##        self.refreshTime()
1114##
1115##    def computeAccurateVcut(self):
1116##        self.update_gui("Optimize vector cut.")
1117##        self.VcutData.set_ecoords(self.optimize_paths(self.VcutData.ecoords),data_sorted=True)
1118##        self.refreshTime()
1119##
1120##    def computeAccurateReng(self):
1121##        self.update_gui("Calculating Raster engrave.")
1122##        if self.RengData.image != None:
1123##            if self.RengData.ecoords == []:
1124##                self.make_raster_coords()
1125##        self.RengData.sorted = True
1126##        self.refreshTime()
1127
1128
1129    def format_time(self,time_in_seconds):
1130        # format the duration from seconds to something human readable
1131        if time_in_seconds !=None and time_in_seconds >=0 :
1132            s = round(time_in_seconds)
1133            m,s=divmod(s,60)
1134            h,m=divmod(m,60)
1135            res = ""
1136            if h > 0:
1137                res =  "%dh " %(h)
1138            if m > 0:
1139                res += "%dm " %(m)
1140            if h == 0:
1141                res += "%ds " %(s)
1142            #L=len(res)
1143            #for i in range(L,8):
1144            #    res =  res+" "
1145            return res
1146        else :
1147            return "?"
1148
1149    def refreshTime(self):
1150        if not self.include_Time.get():
1151            return
1152        if self.units.get() == 'in':
1153            factor =  60.0
1154        else :
1155            factor = 25.4
1156
1157        Raster_eng_feed = float(self.Reng_feed.get()) / factor
1158        Vector_eng_feed = float(self.Veng_feed.get()) / factor
1159        Vector_cut_feed = float(self.Vcut_feed.get()) / factor
1160
1161        Raster_eng_passes = float(self.Reng_passes.get())
1162        Vector_eng_passes = float(self.Veng_passes.get())
1163        Vector_cut_passes = float(self.Vcut_passes.get())
1164        Gcode_passes      = float(self.Gcde_passes.get())
1165
1166        rapid_feed = 100.0 / 25.4   # 100 mm/s move feed to be confirmed
1167
1168        if self.RengData.rpaths:
1169            Reng_time=0
1170        else:
1171            Reng_time  = None
1172        Veng_time  = 0
1173        Vcut_time  = 0
1174
1175        if self.RengData.len!=None:
1176            # these equations are a terrible hack based on measured raster engraving times
1177            # to be fixed someday
1178            if Raster_eng_feed*60.0 <= 300:
1179                accel_time=8.3264*(Raster_eng_feed*60.0)**(-0.7451)
1180            else:
1181                accel_time=2.5913*(Raster_eng_feed*60.0)**(-0.4795)
1182
1183            t_accel = self.RengData.n_scanlines * accel_time
1184            Reng_time  =  ( (self.RengData.len)/Raster_eng_feed ) * Raster_eng_passes + t_accel
1185        if self.VengData.len!=None:
1186            Veng_time  =  (self.VengData.len / Vector_eng_feed + self.VengData.move / rapid_feed) * Vector_eng_passes
1187        if self.VcutData.len!=None:
1188            Vcut_time  =  (self.VcutData.len / Vector_cut_feed + self.VcutData.move / rapid_feed) * Vector_cut_passes
1189
1190        Gcode_time =  self.GcodeData.gcode_time * Gcode_passes
1191
1192        self.Reng_time.set("Raster Engrave: %s" %(self.format_time(Reng_time)))
1193        self.Veng_time.set("Vector Engrave: %s" %(self.format_time(Veng_time)))
1194        self.Vcut_time.set("    Vector Cut: %s" %(self.format_time(Vcut_time)))
1195        self.Gcde_time.set("         Gcode: %s" %(self.format_time(Gcode_time)))
1196
1197        ##########################################
1198        cszw = int(self.PreviewCanvas.cget("width"))
1199        cszh = int(self.PreviewCanvas.cget("height"))
1200        HUD_vspace = 15
1201        HUD_X = cszw-5
1202        HUD_Y = cszh-5
1203
1204        w = int(self.master.winfo_width())
1205        h = int(self.master.winfo_height())
1206        HUD_X2 = w-20
1207        HUD_Y2 = h-75
1208
1209        self.PreviewCanvas.delete("HUD")
1210        self.calc_button.place_forget()
1211
1212        if self.GcodeData.ecoords == []:
1213            self.PreviewCanvas.create_text(HUD_X, HUD_Y             , fill = "red"  ,text =self.Vcut_time.get(), anchor="se",tags="HUD")
1214            self.PreviewCanvas.create_text(HUD_X, HUD_Y-HUD_vspace  , fill = "blue" ,text =self.Veng_time.get(), anchor="se",tags="HUD")
1215
1216            if (Reng_time==None):
1217                #try:
1218                #    self.calc_button.place_forget()
1219                #except:
1220                #    pass
1221                #self.calc_button = Button(self.master,text="Calculate Raster Time", command=self.menu_Calc_Raster_Time)
1222                self.calc_button.place(x=HUD_X2, y=HUD_Y2, width=120+20, height=17, anchor="se")
1223            else:
1224                self.calc_button.place_forget()
1225                self.PreviewCanvas.create_text(HUD_X, HUD_Y-HUD_vspace*2, fill = "black",
1226                                               text =self.Reng_time.get(), anchor="se",tags="HUD")
1227        else:
1228            self.PreviewCanvas.create_text(HUD_X, HUD_Y, fill = "black",text =self.Gcde_time.get(), anchor="se",tags="HUD")
1229        ##########################################
1230
1231
1232    def Settings_ReLoad_Click(self, event):
1233        win_id=self.grab_current()
1234
1235    def Close_Current_Window_Click(self,event=None):
1236        current_name = event.widget.winfo_parent()
1237        win_id = event.widget.nametowidget(current_name)
1238        win_id.destroy()
1239
1240    # Left Column #
1241    #############################
1242    def Entry_Reng_feed_Check(self):
1243        try:
1244            value = float(self.Reng_feed.get())
1245            vfactor=(25.4/60.0)/self.feed_factor()
1246            low_limit = self.min_raster_speed*vfactor
1247            if  value < low_limit:
1248                self.statusMessage.set(" Feed Rate should be greater than or equal to %f " %(low_limit))
1249                return 2 # Value is invalid number
1250        except:
1251            return 3     # Value not a number
1252        self.refreshTime()
1253        return 0         # Value is a valid number
1254    def Entry_Reng_feed_Callback(self, varName, index, mode):
1255        self.entry_set(self.Entry_Reng_feed, self.Entry_Reng_feed_Check(), new=1)
1256    #############################
1257    def Entry_Veng_feed_Check(self):
1258        try:
1259            value = float(self.Veng_feed.get())
1260            vfactor=(25.4/60.0)/self.feed_factor()
1261            low_limit = self.min_vector_speed*vfactor
1262            if  value < low_limit:
1263                self.statusMessage.set(" Feed Rate should be greater than or equal to %f " %(low_limit))
1264                return 2 # Value is invalid number
1265        except:
1266            return 3     # Value not a number
1267        self.refreshTime()
1268        return 0         # Value is a valid number
1269    def Entry_Veng_feed_Callback(self, varName, index, mode):
1270        self.entry_set(self.Entry_Veng_feed, self.Entry_Veng_feed_Check(), new=1)
1271    #############################
1272    def Entry_Vcut_feed_Check(self):
1273        try:
1274            value = float(self.Vcut_feed.get())
1275            vfactor=(25.4/60.0)/self.feed_factor()
1276            low_limit = self.min_vector_speed*vfactor
1277            if  value < low_limit:
1278                self.statusMessage.set(" Feed Rate should be greater than or equal to %f " %(low_limit))
1279                return 2 # Value is invalid number
1280        except:
1281            return 3     # Value not a number
1282        self.refreshTime()
1283        return 0         # Value is a valid number
1284    def Entry_Vcut_feed_Callback(self, varName, index, mode):
1285        self.entry_set(self.Entry_Vcut_feed, self.Entry_Vcut_feed_Check(), new=1)
1286
1287    #############################
1288    def Entry_Step_Check(self):
1289        try:
1290            value = float(self.jog_step.get())
1291            if  value <= 0.0:
1292                self.statusMessage.set(" Step should be greater than 0.0 ")
1293                return 2 # Value is invalid number
1294        except:
1295            return 3     # Value not a number
1296        return 0         # Value is a valid number
1297    def Entry_Step_Callback(self, varName, index, mode):
1298        self.entry_set(self.Entry_Step, self.Entry_Step_Check(), new=1)
1299
1300
1301    #############################
1302    def Entry_GoToX_Check(self):
1303        try:
1304            value = float(self.gotoX.get())
1305            if  (value < 0.0) and (not self.HomeUR.get()):
1306                self.statusMessage.set(" Value should be greater than 0.0 ")
1307                return 2 # Value is invalid number
1308            elif (value > 0.0) and self.HomeUR.get():
1309                self.statusMessage.set(" Value should be less than 0.0 ")
1310                return 2 # Value is invalid number
1311        except:
1312            return 3     # Value not a number
1313        return 0         # Value is a valid number
1314    def Entry_GoToX_Callback(self, varName, index, mode):
1315        self.entry_set(self.Entry_GoToX, self.Entry_GoToX_Check(), new=1)
1316
1317    #############################
1318    def Entry_GoToY_Check(self):
1319        try:
1320            value = float(self.gotoY.get())
1321            if  value > 0.0:
1322                self.statusMessage.set(" Value should be less than 0.0 ")
1323                return 2 # Value is invalid number
1324        except:
1325            return 3     # Value not a number
1326        return 0         # Value is a valid number
1327    def Entry_GoToY_Callback(self, varName, index, mode):
1328        self.entry_set(self.Entry_GoToY, self.Entry_GoToY_Check(), new=1)
1329
1330    #############################
1331    def Entry_Rstep_Check(self):
1332        try:
1333            value = self.get_raster_step_1000in()
1334            if  value <= 0 or value > 63:
1335                self.statusMessage.set(" Step should be between 0.001 and 0.063 in")
1336                return 2 # Value is invalid number
1337        except:
1338            return 3     # Value not a number
1339        return 0         # Value is a valid number
1340    def Entry_Rstep_Callback(self, varName, index, mode):
1341        self.RengData.reset_path()
1342        self.refreshTime()
1343        self.entry_set(self.Entry_Rstep, self.Entry_Rstep_Check(), new=1)
1344
1345##    #############################
1346##    def Entry_Unsharp_Radius_Check(self):
1347##        try:
1348##            value = float(self.unsharp_r.get())
1349##            if  value <= 0:
1350##                self.statusMessage.set(" Radius should be greater than zero.")
1351##                return 2 # Value is invalid number
1352##        except:
1353##            return 3     # Value not a number
1354##        self.menu_View_Refresh_Callback()
1355##        return 0         # Value is a valid number
1356##    def Entry_Unsharp_Radius_Callback(self, varName, index, mode):
1357##        self.entry_set(self.Entry_Unsharp_Radius, self.Entry_Unsharp_Radius_Check(), new=1)
1358##
1359##
1360##    #############################
1361##    def Entry_Unsharp_Percent_Check(self):
1362##        try:
1363##            value = float(self.unsharp_p.get())
1364##            if  value <= 0:
1365##                self.statusMessage.set(" Percent should be greater than zero.")
1366##                return 2 # Value is invalid number
1367##        except:
1368##            return 3     # Value not a number
1369##        self.menu_View_Refresh_Callback()
1370##        return 0         # Value is a valid number
1371##    def Entry_Unsharp_Percent_Callback(self, varName, index, mode):
1372##        self.entry_set(self.Entry_Unsharp_Percent, self.Entry_Unsharp_Percent_Check(), new=1)
1373##
1374##    #############################
1375##    def Entry_Unsharp_Threshold_Check(self):
1376##        try:
1377##            value = float(self.unsharp_t.get())
1378##            if  value < 0:
1379##                self.statusMessage.set(" Threshold should be greater than or equal to zero.")
1380##                return 2 # Value is invalid number
1381##        except:
1382##            return 3     # Value not a number
1383##        self.menu_View_Refresh_Callback()
1384##        return 0         # Value is a valid number
1385##    def Entry_Unsharp_Threshold_Callback(self, varName, index, mode):
1386##        self.entry_set(self.Entry_Unsharp_Threshold, self.Entry_Unsharp_Threshold_Check(), new=1)
1387
1388    #############################
1389    # End Left Column #
1390    #############################
1391    def bezier_weight_Callback(self, varName=None, index=None, mode=None):
1392        self.Reset_RasterPath_and_Update_Time()
1393        self.bezier_plot()
1394
1395    def bezier_M1_Callback(self, varName=None, index=None, mode=None):
1396        self.Reset_RasterPath_and_Update_Time()
1397        self.bezier_plot()
1398
1399    def bezier_M2_Callback(self, varName=None, index=None, mode=None):
1400        self.Reset_RasterPath_and_Update_Time()
1401        self.bezier_plot()
1402
1403    def bezier_plot(self):
1404        self.BezierCanvas.delete('bez')
1405
1406        #self.BezierCanvas.create_line( 5,260-0,260,260-255,fill="black", capstyle="round", width = 2, tags='bez')
1407        M1 = float(self.bezier_M1.get())
1408        M2 = float(self.bezier_M2.get())
1409        w  = float(self.bezier_weight.get())
1410        num = 10
1411        x,y = self.generate_bezier(M1,M2,w,n=num)
1412        for i in range(0,num):
1413            self.BezierCanvas.create_line( 5+x[i],260-y[i],5+x[i+1],260-y[i+1],fill="black", \
1414                                           capstyle="round", width = 2, tags='bez')
1415        self.BezierCanvas.create_text(128, 0, text="Output Level vs. Input Level",anchor="n", tags='bez')
1416
1417
1418    #############################
1419    def Entry_Ink_Timeout_Check(self):
1420        try:
1421            value = float(self.ink_timeout.get())
1422            if  value < 0.0:
1423                self.statusMessage.set(" Timeout should be 0 or greater")
1424                return 2 # Value is invalid number
1425        except:
1426            return 3     # Value not a number
1427        return 0         # Value is a valid number
1428    def Entry_Ink_Timeout_Callback(self, varName, index, mode):
1429        self.entry_set(self.Entry_Ink_Timeout,self.Entry_Ink_Timeout_Check(), new=1)
1430
1431
1432    #############################
1433    def Entry_Timeout_Check(self):
1434        try:
1435            value = float(self.t_timeout.get())
1436            if  value <= 0.0:
1437                self.statusMessage.set(" Timeout should be greater than 0 ")
1438                return 2 # Value is invalid number
1439        except:
1440            return 3     # Value not a number
1441        return 0         # Value is a valid number
1442    def Entry_Timeout_Callback(self, varName, index, mode):
1443        self.entry_set(self.Entry_Timeout,self.Entry_Timeout_Check(), new=1)
1444
1445    #############################
1446    def Entry_N_Timeouts_Check(self):
1447        try:
1448            value = float(self.n_timeouts.get())
1449            if  value <= 0.0:
1450                self.statusMessage.set(" N_Timeouts should be greater than 0 ")
1451                return 2 # Value is invalid number
1452        except:
1453            return 3     # Value not a number
1454        return 0         # Value is a valid number
1455    def Entry_N_Timeouts_Callback(self, varName, index, mode):
1456        self.entry_set(self.Entry_N_Timeouts,self.Entry_N_Timeouts_Check(), new=1)
1457
1458    #############################
1459    def Entry_N_EGV_Passes_Check(self):
1460        try:
1461            value = int(self.n_egv_passes.get())
1462            if  value < 1:
1463                self.statusMessage.set(" EGV passes should be 1 or higher")
1464                return 2 # Value is invalid number
1465        except:
1466            return 3     # Value not a number
1467        return 0         # Value is a valid number
1468    def Entry_N_EGV_Passes_Callback(self, varName, index, mode):
1469        self.entry_set(self.Entry_N_EGV_Passes,self.Entry_N_EGV_Passes_Check(), new=1)
1470
1471    #############################
1472    def Entry_Laser_Area_Width_Check(self):
1473        try:
1474            value = float(self.LaserXsize.get())
1475            if  value <= 0.0:
1476                self.statusMessage.set(" Width should be greater than 0 ")
1477                return 2 # Value is invalid number
1478        except:
1479            return 3     # Value not a number
1480        return 0         # Value is a valid number
1481    def Entry_Laser_Area_Width_Callback(self, varName, index, mode):
1482        self.entry_set(self.Entry_Laser_Area_Width,self.Entry_Laser_Area_Width_Check(), new=1)
1483
1484    #############################
1485    def Entry_Laser_Area_Height_Check(self):
1486        try:
1487            value = float(self.LaserYsize.get())
1488            if  value <= 0.0:
1489                self.statusMessage.set(" Height should be greater than 0 ")
1490                return 2 # Value is invalid number
1491        except:
1492            return 3     # Value not a number
1493        return 0         # Value is a valid number
1494    def Entry_Laser_Area_Height_Callback(self, varName, index, mode):
1495        self.entry_set(self.Entry_Laser_Area_Height,self.Entry_Laser_Area_Height_Check(), new=1)
1496
1497
1498    #############################
1499    def Entry_Laser_X_Scale_Check(self):
1500        try:
1501            value = float(self.LaserXscale.get())
1502            if  value <= 0.0:
1503                self.statusMessage.set(" X scale factor should be greater than 0 ")
1504                return 2 # Value is invalid number
1505        except:
1506            return 3     # Value not a number
1507        self.Reset_RasterPath_and_Update_Time()
1508        return 0         # Value is a valid number
1509    def Entry_Laser_X_Scale_Callback(self, varName, index, mode):
1510        self.entry_set(self.Entry_Laser_X_Scale,self.Entry_Laser_X_Scale_Check(), new=1)
1511    #############################
1512    def Entry_Laser_Y_Scale_Check(self):
1513        try:
1514            value = float(self.LaserYscale.get())
1515            if  value <= 0.0:
1516                self.statusMessage.set(" Y scale factor should be greater than 0 ")
1517                return 2 # Value is invalid number
1518        except:
1519            return 3     # Value not a number
1520        self.Reset_RasterPath_and_Update_Time()
1521        return 0         # Value is a valid number
1522    def Entry_Laser_Y_Scale_Callback(self, varName, index, mode):
1523        self.entry_set(self.Entry_Laser_Y_Scale,self.Entry_Laser_Y_Scale_Check(), new=1)
1524
1525    #############################
1526    def Entry_Laser_R_Scale_Check(self):
1527        try:
1528            value = float(self.LaserRscale.get())
1529            if  value <= 0.0:
1530                self.statusMessage.set(" Rotary scale factor should be greater than 0 ")
1531                return 2 # Value is invalid number
1532        except:
1533            return 3     # Value not a number
1534        self.Reset_RasterPath_and_Update_Time()
1535        return 0         # Value is a valid number
1536    def Entry_Laser_R_Scale_Callback(self, varName, index, mode):
1537        self.entry_set(self.Entry_Laser_R_Scale,self.Entry_Laser_R_Scale_Check(), new=1)
1538
1539    #############################
1540    def Entry_Laser_Rapid_Feed_Check(self):
1541        try:
1542            value = float(self.rapid_feed.get())
1543            vfactor=(25.4/60.0)/self.feed_factor()
1544            low_limit = 1.0*vfactor
1545            if  value !=0 and value < low_limit:
1546                self.statusMessage.set(" Rapid feed should be greater than or equal to %f (or 0 for default speed) " %(low_limit))
1547                return 2 # Value is invalid number
1548        except:
1549            return 3     # Value not a number
1550        return 0         # Value is a valid number
1551    def Entry_Laser_Rapid_Feed_Callback(self, varName, index, mode):
1552        self.entry_set(self.Entry_Laser_Rapid_Feed,self.Entry_Laser_Rapid_Feed_Check(), new=1)
1553
1554    # Advanced Column #
1555    #############################
1556    def Entry_Reng_passes_Check(self):
1557        try:
1558            value = int(self.Reng_passes.get())
1559            if  value < 1:
1560                self.statusMessage.set(" Number of passes should be greater than 0 ")
1561                return 2 # Value is invalid number
1562        except:
1563            return 3     # Value not a number
1564        self.refreshTime()
1565        return 0         # Value is a valid number
1566    def Entry_Reng_passes_Callback(self, varName, index, mode):
1567        self.entry_set(self.Entry_Reng_passes, self.Entry_Reng_passes_Check(), new=1)
1568    #############################
1569    def Entry_Veng_passes_Check(self):
1570        try:
1571            value = int(self.Veng_passes.get())
1572            if  value < 1:
1573                self.statusMessage.set(" Number of passes should be greater than 0 ")
1574                return 2 # Value is invalid number
1575        except:
1576            return 3     # Value not a number
1577        self.refreshTime()
1578        return 0         # Value is a valid number
1579    def Entry_Veng_passes_Callback(self, varName, index, mode):
1580        self.entry_set(self.Entry_Veng_passes, self.Entry_Veng_passes_Check(), new=1)
1581    #############################
1582    def Entry_Vcut_passes_Check(self):
1583        try:
1584            value = int(self.Vcut_passes.get())
1585            if  value < 1:
1586                self.statusMessage.set(" Number of passes should be greater than 0 ")
1587                return 2 # Value is invalid number
1588        except:
1589            return 3     # Value not a number
1590        self.refreshTime()
1591        return 0         # Value is a valid number
1592    def Entry_Vcut_passes_Callback(self, varName, index, mode):
1593        self.entry_set(self.Entry_Vcut_passes, self.Entry_Vcut_passes_Check(), new=1)
1594
1595    #############################
1596    def Entry_Gcde_passes_Check(self):
1597        try:
1598            value = int(self.Gcde_passes.get())
1599            if  value < 1:
1600                self.statusMessage.set(" Number of passes should be greater than 0 ")
1601                return 2 # Value is invalid number
1602        except:
1603            return 3     # Value not a number
1604        self.refreshTime()
1605        return 0         # Value is a valid number
1606    def Entry_Gcde_passes_Callback(self, varName, index, mode):
1607        self.entry_set(self.Entry_Gcde_passes, self.Entry_Gcde_passes_Check(), new=1)
1608
1609    #############################
1610
1611    def Entry_Trace_Gap_Check(self):
1612        try:
1613            value = float(self.trace_gap.get())
1614        except:
1615            return 3     # Value not a number
1616        self.menu_View_Refresh()
1617        return 0         # Value is a valid number
1618    def Entry_Trace_Gap_Callback(self, varName, index, mode):
1619        self.entry_set(self.Entry_Trace_Gap, self.Entry_Trace_Gap_Check(), new=1)
1620
1621    #############################
1622
1623    def Entry_Trace_Speed_Check(self):
1624        try:
1625            value = float(self.trace_speed.get())
1626            vfactor=(25.4/60.0)/self.feed_factor()
1627            low_limit = self.min_vector_speed*vfactor
1628            if  value < low_limit:
1629                self.statusMessage.set(" Feed Rate should be greater than or equal to %f " %(low_limit))
1630                return 2 # Value is invalid number
1631        except:
1632            return 3     # Value not a number
1633        self.refreshTime()
1634        return 0         # Value is a valid number
1635    def Entry_Trace_Speed_Callback(self, varName, index, mode):
1636        self.entry_set(self.Entry_Trace_Speed, self.Entry_Trace_Speed_Check(), new=1)
1637
1638    #############################
1639    def Inkscape_Path_Click(self, event):
1640        self.Inkscape_Path_Message()
1641        win_id=self.grab_current()
1642        newfontdir = askopenfilename(filetypes=[("Executable Files",("inkscape.exe","*inkscape*")),\
1643                                                ("All Files","*")],\
1644                                                 initialdir=self.inkscape_path.get())
1645        if newfontdir != "" and newfontdir != ():
1646            if type(newfontdir) is not str:
1647                newfontdir = newfontdir.encode("utf-8")
1648            self.inkscape_path.set(newfontdir)
1649
1650        try:
1651            win_id.withdraw()
1652            win_id.deiconify()
1653        except:
1654            pass
1655
1656    def Inkscape_Path_Message(self, event=None):
1657        if self.inkscape_warning == False:
1658            self.inkscape_warning = True
1659            msg1 = "Beware:"
1660            msg2 = "Most people should leave the 'Inkscape Executable' entry field blank. "
1661            msg3 = "K40 Whisperer will find Inkscape in one of the the standard locations after you install Inkscape."
1662            message_box(msg1, msg2+msg3)
1663
1664
1665    def Entry_units_var_Callback(self):
1666        if (self.units.get() == 'in') and (self.funits.get()=='mm/s'):
1667            self.funits.set('in/min')
1668            self.Scale_Linear_Inputs('in')
1669        elif (self.units.get() == 'mm') and (self.funits.get()=='in/min'):
1670            self.funits.set('mm/s')
1671            self.Scale_Linear_Inputs('mm')
1672
1673    def Scale_Linear_Inputs(self, new_units=None):
1674        if new_units=='in':
1675            self.units_scale = 1.0
1676            factor  = 1/25.4
1677            vfactor = 60.0/25.4
1678        elif new_units=='mm':
1679            factor  = 25.4
1680            vfactor = 25.4/60.0
1681            self.units_scale = 25.4
1682        else:
1683            return
1684        self.LaserXsize.set ( self.Scale_Text_Value('%.2f',self.LaserXsize.get()  ,factor ) )
1685        self.LaserYsize.set ( self.Scale_Text_Value('%.2f',self.LaserYsize.get()  ,factor ) )
1686        self.jog_step.set   ( self.Scale_Text_Value('%.3f',self.jog_step.get()    ,factor ) )
1687        self.gotoX.set      ( self.Scale_Text_Value('%.3f',self.gotoX.get()       ,factor ) )
1688        self.gotoY.set      ( self.Scale_Text_Value('%.3f',self.gotoY.get()       ,factor ) )
1689        self.Reng_feed.set  ( self.Scale_Text_Value('%.1f',self.Reng_feed.get()   ,vfactor) )
1690        self.Veng_feed.set  ( self.Scale_Text_Value('%.1f',self.Veng_feed.get()   ,vfactor) )
1691        self.Vcut_feed.set  ( self.Scale_Text_Value('%.1f',self.Vcut_feed.get()   ,vfactor) )
1692        self.trace_speed.set( self.Scale_Text_Value('%.1f',self.trace_speed.get() ,vfactor) )
1693        self.rapid_feed.set ( self.Scale_Text_Value('%.1f',self.rapid_feed.get()  ,vfactor) )
1694
1695    def Scale_Text_Value(self,format_txt,Text_Value,factor):
1696        try:
1697            return format_txt %(float(Text_Value)*factor )
1698        except:
1699            return ''
1700
1701    def menu_File_Open_Settings_File(self,event=None):
1702        init_dir = os.path.dirname(self.DESIGN_FILE)
1703        if ( not os.path.isdir(init_dir) ):
1704            init_dir = self.HOME_DIR
1705        fileselect = askopenfilename(filetypes=[("Settings Files","*.txt"),\
1706                                                ("All Files","*")],\
1707                                                 initialdir=init_dir)
1708        if fileselect != '' and fileselect != ():
1709            self.Open_Settings_File(fileselect)
1710
1711
1712    def menu_Reload_Design(self,event=None):
1713        if self.GUI_Disabled:
1714            return
1715        file_full = self.DESIGN_FILE
1716        file_name = os.path.basename(file_full)
1717        if ( os.path.isfile(file_full) ):
1718            filename = file_full
1719        elif ( os.path.isfile( file_name ) ):
1720            filename = file_name
1721        elif ( os.path.isfile( self.HOME_DIR+"/"+file_name ) ):
1722            filename = self.HOME_DIR+"/"+file_name
1723        else:
1724            self.statusMessage.set("file not found: %s" %(os.path.basename(file_full)) )
1725            self.statusbar.configure( bg = 'red' )
1726            return
1727
1728        Name, fileExtension = os.path.splitext(filename)
1729        TYPE=fileExtension.upper()
1730        if TYPE=='.DXF':
1731            self.Open_DXF(filename)
1732        elif TYPE=='.SVG':
1733            self.Open_SVG(filename)
1734        elif TYPE=='.EGV':
1735            self.EGV_Send_Window(filename)
1736        else:
1737            self.Open_G_Code(filename)
1738        self.menu_View_Refresh()
1739
1740
1741
1742    def menu_File_Open_Design(self,event=None):
1743        if self.GUI_Disabled:
1744            return
1745        init_dir = os.path.dirname(self.DESIGN_FILE)
1746        if ( not os.path.isdir(init_dir) ):
1747            init_dir = self.HOME_DIR
1748
1749        design_types = ("Design Files", ("*.svg","*.dxf"))
1750        gcode_types  = ("G-Code Files", ("*.ngc","*.gcode","*.g","*.tap"))
1751
1752        Name, fileExtension = os.path.splitext(self.DESIGN_FILE)
1753        TYPE=fileExtension.upper()
1754        if TYPE != '.DXF' and TYPE!='.SVG' and TYPE!='.EGV' and TYPE!='':
1755            default_types = gcode_types
1756        else:
1757            default_types = design_types
1758
1759        fileselect = askopenfilename(filetypes=[default_types,
1760                                            ("G-Code Files ", ("*.ngc","*.gcode","*.g","*.tap")),\
1761                                            ("DXF Files ","*.dxf"),\
1762                                            ("SVG Files ","*.svg"),\
1763                                            ("All Files ","*"),\
1764                                            ("Design Files ", ("*.svg","*.dxf"))],\
1765                                            initialdir=init_dir)
1766
1767        if fileselect == () or (not os.path.isfile(fileselect)):
1768            return
1769
1770        Name, fileExtension = os.path.splitext(fileselect)
1771        self.update_gui("Opening '%s'" % fileselect )
1772        TYPE=fileExtension.upper()
1773        if TYPE=='.DXF':
1774            self.Open_DXF(fileselect)
1775        elif TYPE=='.SVG':
1776            self.Open_SVG(fileselect)
1777        else:
1778            self.Open_G_Code(fileselect)
1779
1780
1781        self.DESIGN_FILE = fileselect
1782        self.menu_View_Refresh()
1783
1784    def menu_File_Raster_Engrave(self):
1785        self.menu_File_save_EGV(operation_type="Raster_Eng")
1786
1787    def menu_File_Vector_Engrave(self):
1788        self.menu_File_save_EGV(operation_type="Vector_Eng")
1789
1790    def menu_File_Vector_Cut(self):
1791        self.menu_File_save_EGV(operation_type="Vector_Cut")
1792
1793    def menu_File_G_Code(self):
1794        self.menu_File_save_EGV(operation_type="Gcode_Cut")
1795
1796    def menu_File_Raster_Vector_Engrave(self):
1797        self.menu_File_save_EGV(operation_type="Raster_Eng-Vector_Eng")
1798
1799    def menu_File_Vector_Engrave_Cut(self):
1800        self.menu_File_save_EGV(operation_type="Vector_Eng-Vector_Cut")
1801
1802    def menu_File_Raster_Vector_Cut(self):
1803        self.menu_File_save_EGV(operation_type="Raster_Eng-Vector_Eng-Vector_Cut")
1804
1805    def menu_File_save_EGV(self,operation_type=None,default_name="out.EGV"):
1806        self.stop[0]=False
1807        if DEBUG:
1808            start=time()
1809        fileName, fileExtension = os.path.splitext(self.DESIGN_FILE)
1810        init_file=os.path.basename(fileName)
1811        default_name = init_file+"_"+operation_type
1812
1813        if self.EGV_FILE != None:
1814            init_dir = os.path.dirname(self.EGV_FILE)
1815        else:
1816            init_dir = os.path.dirname(self.DESIGN_FILE)
1817
1818        if ( not os.path.isdir(init_dir) ):
1819            init_dir = self.HOME_DIR
1820
1821        fileName, fileExtension = os.path.splitext(default_name)
1822        init_file=os.path.basename(fileName)
1823
1824        filename = asksaveasfilename(defaultextension='.EGV', \
1825                                     filetypes=[("EGV File","*.EGV")],\
1826                                     initialdir=init_dir,\
1827                                     initialfile= init_file )
1828
1829        if filename != '' and filename != ():
1830
1831            if operation_type.find("Raster_Eng") > -1:
1832                self.make_raster_coords()
1833            else:
1834                self.statusbar.configure( bg = 'yellow' )
1835                self.statusMessage.set("No raster data to engrave")
1836
1837            self.send_data(operation_type=operation_type, output_filename=filename)
1838            self.EGV_FILE = filename
1839        if DEBUG:
1840            print("time = %d seconds" %(int(time()-start)))
1841        self.stop[0]=True
1842
1843
1844
1845    def menu_File_Open_EGV(self):
1846        init_dir = os.path.dirname(self.DESIGN_FILE)
1847        if ( not os.path.isdir(init_dir) ):
1848            init_dir = self.HOME_DIR
1849        fileselect = askopenfilename(filetypes=[("Engraver Files", ("*.egv","*.EGV")),\
1850                                                    ("All Files","*")],\
1851                                                     initialdir=init_dir)
1852        if fileselect != '' and fileselect != ():
1853            self.resetPath()
1854            self.DESIGN_FILE = fileselect
1855            self.EGV_Send_Window(fileselect)
1856
1857    def Open_EGV(self,filemname,n_passes=1):
1858        self.stop[0]=False
1859        EGV_data=[]
1860        value1 = ""
1861        value2 = ""
1862        value3 = ""
1863        value4 = ""
1864        data=""
1865        #value1 and value2 are the absolute y and x starting positions
1866        #value3 and value4 are the absolute y and x end positions
1867        with open(filemname) as f:
1868            while True:
1869                ## Skip header
1870                c = f.read(1)
1871                while c!="%" and c:
1872                    c = f.read(1)
1873                ## Read 1st Value
1874                c = f.read(1)
1875                while c!="%" and c:
1876                    value1 = value1 + c
1877                    c = f.read(1)
1878                y_start_mils = int(value1)
1879                ## Read 2nd Value
1880                c = f.read(1)
1881                while c!="%" and c:
1882                    value2 = value2 + c
1883                    c = f.read(1)
1884                x_start_mils = int(value2)
1885                ## Read 3rd Value
1886                c = f.read(1)
1887                while c!="%" and c:
1888                    value3 = value3 + c
1889                    c = f.read(1)
1890                y_end_mils = int(value3)
1891                ## Read 4th Value
1892                c = f.read(1)
1893                while c!="%" and c:
1894                    value4 = value4 + c
1895                    c = f.read(1)
1896                x_end_mils = int(value4)
1897                break
1898
1899            ## Read Data
1900            while True:
1901                c = f.read(1)
1902                if not c:
1903                    break
1904                if c=='\n' or c==' ' or c=='\r':
1905                    pass
1906                else:
1907                    data=data+"%c" %c
1908                    EGV_data.append(ord(c))
1909
1910        if ( (x_end_mils != 0) or (y_end_mils != 0) ):
1911            n_passes=1
1912        else:
1913            x_start_mils = 0
1914            y_start_mils = 0
1915
1916        try:
1917            self.send_egv_data(EGV_data,n_passes)
1918        except MemoryError as e:
1919            msg1 = "Memory Error:"
1920            msg2 = "Memory Error:  Out of Memory."
1921            self.statusMessage.set(msg2)
1922            self.statusbar.configure( bg = 'red' )
1923            message_box(msg1, msg2)
1924            debug_message(traceback.format_exc())
1925
1926        except Exception as e:
1927            msg1 = "Sending Data Stopped: "
1928            msg2 = "%s" %(e)
1929            if msg2 == "":
1930                formatted_lines = traceback.format_exc().splitlines()
1931            self.statusMessage.set((msg1+msg2).split("\n")[0] )
1932            self.statusbar.configure( bg = 'red' )
1933            message_box(msg1, msg2)
1934            debug_message(traceback.format_exc())
1935
1936        #rapid move back to starting position
1937        dxmils = -(x_end_mils - x_start_mils)
1938        dymils =   y_end_mils - y_start_mils
1939        self.Send_Rapid_Move(dxmils,dxmils)
1940        self.stop[0]=True
1941
1942
1943    def Open_SVG(self,filemname):
1944        self.resetPath()
1945
1946        self.SVG_FILE = filemname
1947        svg_reader =  SVG_READER()
1948        svg_reader.set_inkscape_path(self.inkscape_path.get())
1949        self.input_dpi = 1000
1950        svg_reader.image_dpi = self.input_dpi
1951        svg_reader.timout = int(float( self.ink_timeout.get())*60.0)
1952        dialog_pxpi    = None
1953        dialog_viewbox = None
1954        try:
1955            try:
1956                try:
1957                    svg_reader.parse_svg(self.SVG_FILE)
1958                    svg_reader.make_paths()
1959                except SVG_PXPI_EXCEPTION as e:
1960                    pxpi_dialog = pxpiDialog(root,
1961                                           self.units.get(),
1962                                           svg_reader.SVG_Size,
1963                                           svg_reader.SVG_ViewBox,
1964                                           svg_reader.SVG_inkscape_version)
1965
1966                    svg_reader = SVG_READER()
1967                    svg_reader.set_inkscape_path(self.inkscape_path.get())
1968                    if pxpi_dialog.result == None:
1969                        return
1970
1971                    dialog_pxpi,dialog_viewbox = pxpi_dialog.result
1972                    svg_reader.parse_svg(self.SVG_FILE)
1973                    svg_reader.set_size(dialog_pxpi,dialog_viewbox)
1974                    svg_reader.make_paths()
1975
1976            except SVG_TEXT_EXCEPTION as e:
1977                svg_reader = SVG_READER()
1978                svg_reader.set_inkscape_path(self.inkscape_path.get())
1979                self.statusMessage.set("Converting TEXT to PATHS.")
1980                self.master.update()
1981                svg_reader.parse_svg(self.SVG_FILE)
1982                if dialog_pxpi != None and dialog_viewbox != None:
1983                    svg_reader.set_size(dialog_pxpi,dialog_viewbox)
1984                svg_reader.make_paths(txt2paths=True)
1985
1986        except Exception as e:
1987            msg1 = "SVG Error: "
1988            msg2 = "%s" %(e)
1989            self.statusMessage.set((msg1+msg2).split("\n")[0] )
1990            self.statusbar.configure( bg = 'red' )
1991            message_box(msg1, msg2)
1992            debug_message(traceback.format_exc())
1993            return
1994        except:
1995            self.statusMessage.set("Unable To open SVG File: %s" %(filemname))
1996            debug_message(traceback.format_exc())
1997            return
1998        xmax = svg_reader.Xsize/25.4
1999        ymax = svg_reader.Ysize/25.4
2000        xmin = 0
2001        ymin = 0
2002
2003        self.Design_bounds = (xmin,xmax,ymin,ymax)
2004
2005        ##########################
2006        ###   Create ECOORDS   ###
2007        ##########################
2008        self.VcutData.make_ecoords(svg_reader.cut_lines,scale=1/25.4)
2009        self.VengData.make_ecoords(svg_reader.eng_lines,scale=1/25.4)
2010
2011        ##########################
2012        ###   Load Image       ###
2013        ##########################
2014        self.RengData.set_image(svg_reader.raster_PIL)
2015
2016        if (self.RengData.image != None):
2017            self.wim, self.him = self.RengData.image.size
2018            self.aspect_ratio =  float(self.wim-1) / float(self.him-1)
2019            #self.make_raster_coords()
2020        self.refreshTime()
2021        margin=0.0625 # A bit of margin to prevent the warningwindow for designs that are close to being within the bounds
2022        if self.Design_bounds[0] > self.VengData.bounds[0]+margin or\
2023           self.Design_bounds[0] > self.VcutData.bounds[0]+margin or\
2024           self.Design_bounds[1] < self.VengData.bounds[1]-margin or\
2025           self.Design_bounds[1] < self.VcutData.bounds[1]-margin or\
2026           self.Design_bounds[2] > self.VengData.bounds[2]+margin or\
2027           self.Design_bounds[2] > self.VcutData.bounds[2]+margin or\
2028           self.Design_bounds[3] < self.VengData.bounds[3]-margin or\
2029           self.Design_bounds[3] < self.VcutData.bounds[3]-margin:
2030            line1 = "Warning:\n"
2031            line2 = "There is vector cut or vector engrave data located outside of the SVG page bounds.\n\n"
2032            line3 = "K40 Whisperer will attempt to use all of the vector data.  "
2033            line4 = "Please verify that the vector data is not outside of your lasers working area before engraving."
2034            message_box("Warning", line1+line2+line3+line4)
2035
2036
2037    #####################################################################
2038    def make_raster_coords(self):
2039        if self.RengData.rpaths:
2040            return
2041        try:
2042            hcoords=[]
2043            if (self.RengData.image != None and self.RengData.ecoords==[]):
2044                ecoords=[]
2045                cutoff=128
2046                image_temp = self.RengData.image.convert("L")
2047##                if self.unsharp_flag.get():
2048##                    from PIL import ImageFilter
2049##                    #image_temp = image_temp.filter(UnsharpMask(radius=self.unsharp_r, percent=self.unsharp_p, threshold=self.unsharp_t))
2050##                    filter = ImageFilter.UnsharpMask()
2051##                    filter.radius    = float(self.unsharp_r.get())      # radius 3-5 pixels
2052##                    filter.percent   = int(float(self.unsharp_p.get())) # precent 500%
2053##                    filter.threshold = int(float(self.unsharp_t.get())) # Threshold 0
2054##                    image_temp = image_temp.filter(filter)
2055
2056                if self.negate.get():
2057                    image_temp = ImageOps.invert(image_temp)
2058
2059                if self.mirror.get():
2060                    image_temp = ImageOps.mirror(image_temp)
2061
2062                if self.rotate.get():
2063                    #image_temp = image_temp.rotate(90,expand=True)
2064                    image_temp = self.rotate_raster(image_temp)
2065
2066                Xscale = float(self.LaserXscale.get())
2067                Yscale = float(self.LaserYscale.get())
2068                if self.rotary.get():
2069                    Rscale = float(self.LaserRscale.get())
2070                    Yscale = Yscale*Rscale
2071
2072                if Xscale != 1.0 or Yscale != 1.0:
2073                    wim,him = image_temp.size
2074                    nw = int(wim*Xscale)
2075                    nh = int(him*Yscale)
2076                    image_temp = image_temp.resize((nw,nh))
2077
2078
2079                if self.halftone.get():
2080                    #start = time()
2081                    ht_size_mils =  round( 1000.0 / float(self.ht_size.get()) ,1)
2082                    npixels = int( round(ht_size_mils,1) )
2083                    if npixels == 0:
2084                        return
2085                    wim,him = image_temp.size
2086                    # Convert to Halftoning and save
2087                    nw=int(wim / npixels)
2088                    nh=int(him / npixels)
2089                    image_temp = image_temp.resize((nw,nh))
2090
2091                    image_temp = self.convert_halftoning(image_temp)
2092                    image_temp = image_temp.resize((wim,him))
2093                    #print time()-start
2094                else:
2095                    image_temp = image_temp.point(lambda x: 0 if x<128 else 255, '1')
2096                    #image_temp = image_temp.convert('1',dither=Image.NONE)
2097
2098
2099                if DEBUG:
2100                    image_name = os.path.expanduser("~")+"/IMAGE.png"
2101                    image_temp.save(image_name,"PNG")
2102
2103                Reng_np = image_temp.load()
2104                wim,him = image_temp.size
2105                del image_temp
2106                #######################################
2107                x=0
2108                y=0
2109                loop=1
2110                LENGTH=0
2111                n_scanlines = 0
2112
2113                my_hull = hull2D()
2114                bignumber = 9999999;
2115                Raster_step = self.get_raster_step_1000in()
2116                timestamp=0
2117                for i in range(0,him,Raster_step):
2118                    stamp=int(3*time()) #update every 1/3 of a second
2119                    if (stamp != timestamp):
2120                        timestamp=stamp #interlock
2121                        self.statusMessage.set("Creating Scan Lines: %.1f %%" %( (100.0*i)/him ) )
2122                        self.master.update()
2123                    if self.stop[0]==True:
2124                        raise Exception("Action stopped by User.")
2125                    line = []
2126                    cnt=1
2127                    LEFT  = bignumber;
2128                    RIGHT =-bignumber;
2129                    for j in range(1,wim):
2130                        if (Reng_np[j,i] == Reng_np[j-1,i]):
2131                            cnt = cnt+1
2132                        else:
2133                            #laser = "U" if Reng_np[j-1,i] > cutoff else "D"
2134                            if Reng_np[j-1,i]:
2135                                laser = "U"
2136                            else:
2137                                laser = "D"
2138                                LEFT  = min(j-cnt,LEFT)
2139                                RIGHT = max(j,RIGHT)
2140
2141                            line.append((cnt,laser))
2142                            cnt=1
2143                    #laser = "U" if Reng_np[j-1,i] > cutoff else "D"
2144                    if Reng_np[j-1,i] > cutoff:
2145                        laser = "U"
2146                    else:
2147                        laser = "D"
2148                        LEFT  = min(j-cnt,LEFT)
2149                        RIGHT = max(j,RIGHT)
2150
2151                    line.append((cnt,laser))
2152                    if LEFT != bignumber and RIGHT != -bignumber:
2153                        LENGTH = LENGTH + (RIGHT - LEFT)/1000.0
2154                        n_scanlines = n_scanlines + 1
2155
2156                    y=(him-i)/1000.0
2157                    x=0
2158                    if LEFT != bignumber:
2159                        hcoords.append([LEFT/1000.0,y])
2160                    if RIGHT != -bignumber:
2161                        hcoords.append([RIGHT/1000.0,y])
2162                    if hcoords!=[]:
2163                        hcoords = my_hull.convexHullecoords(hcoords)
2164
2165                    #rng = range(0,len(line),1)
2166                    rng = list(range(0,len(line),1))
2167
2168                    for i in rng:
2169                        seg = line[i]
2170                        delta = seg[0]/1000.0
2171                        if seg[1]=="D":
2172                            loop=loop+1
2173                            ecoords.append([x      ,y,loop])
2174                            ecoords.append([x+delta,y,loop])
2175                        x = x + delta
2176                #if ecoords!=[]:
2177                self.RengData.set_ecoords(ecoords,data_sorted=True)
2178                self.RengData.len=LENGTH
2179                self.RengData.n_scanlines = n_scanlines
2180            #Set Flag indicating raster paths have been calculated
2181            self.RengData.rpaths = True
2182            self.RengData.hull_coords = hcoords
2183
2184        except MemoryError as e:
2185            msg1 = "Memory Error:"
2186            msg2 = "Memory Error:  Out of Memory."
2187            self.statusMessage.set(msg2)
2188            self.statusbar.configure( bg = 'red' )
2189            message_box(msg1, msg2)
2190            debug_message(traceback.format_exc())
2191
2192        except Exception as e:
2193            msg1 = "Making Raster Coords Stopped: "
2194            msg2 = "%s" %(e)
2195            self.statusMessage.set((msg1+msg2).split("\n")[0] )
2196            self.statusbar.configure( bg = 'red' )
2197            message_box(msg1, msg2)
2198            debug_message(traceback.format_exc())
2199    #######################################################################
2200
2201
2202    def rotate_raster(self,image_in):
2203        wim,him = image_in.size
2204        im_rotated = Image.new("L", (him, wim), "white")
2205
2206        image_in_np   = image_in.load()
2207        im_rotated_np = im_rotated.load()
2208
2209        for i in range(1,him):
2210            for j in range(1,wim):
2211                im_rotated_np[i,wim-j] = image_in_np[j,i]
2212        return im_rotated
2213
2214    def get_raster_step_1000in(self):
2215        val_in = float(self.rast_step.get())
2216        value = int(round(val_in*1000.0,1))
2217        return value
2218
2219
2220    def generate_bezier(self,M1,M2,w,n=100):
2221        if (M1==M2):
2222            x1=0
2223            y1=0
2224        else:
2225            x1 = 255*(1-M2)/(M1-M2)
2226            y1 = M1*x1
2227        x=[]
2228        y=[]
2229        # Calculate Bezier Curve
2230        for step in range(0,n+1):
2231            t    = float(step)/float(n)
2232            Ct   = 1 / ( pow(1-t,2)+2*(1-t)*t*w+pow(t,2) )
2233            x.append( Ct*( 2*(1-t)*t*w*x1+pow(t,2)*255) )
2234            y.append( Ct*( 2*(1-t)*t*w*y1+pow(t,2)*255) )
2235        return x,y
2236
2237    '''This Example opens an Image and transform the image into halftone.  -Isai B. Cicourel'''
2238    # Create a Half-tone version of the image
2239    def convert_halftoning(self,image):
2240        image = image.convert('L')
2241        x_lim, y_lim = image.size
2242        pixel = image.load()
2243
2244        M1 = float(self.bezier_M1.get())
2245        M2 = float(self.bezier_M2.get())
2246        w  = float(self.bezier_weight.get())
2247
2248        if w > 0:
2249            x,y = self.generate_bezier(M1,M2,w)
2250
2251            interp = interpolate(x, y) # Set up interpolate class
2252            val_map=[]
2253            # Map Bezier Curve to values between 0 and 255
2254            for val in range(0,256):
2255                val_out = int(round(interp[val])) # Get the interpolated value at each value
2256                val_map.append(val_out)
2257            # Adjust image
2258            timestamp=0
2259            for y in range(1, y_lim):
2260                stamp=int(3*time()) #update every 1/3 of a second
2261                if (stamp != timestamp):
2262                    timestamp=stamp #interlock
2263                    self.statusMessage.set("Adjusting Image Darkness: %.1f %%" %( (100.0*y)/y_lim ) )
2264                    self.master.update()
2265                for x in range(1, x_lim):
2266                    pixel[x, y] = val_map[ pixel[x, y] ]
2267
2268        self.statusMessage.set("Creating Halftone Image." )
2269        self.master.update()
2270        image = image.convert('1')
2271        return image
2272
2273    #######################################################################
2274
2275    def gcode_error_message(self,message):
2276        error_report = Toplevel(width=525,height=60)
2277        error_report.title("G-Code Reading Errors/Warnings")
2278        error_report.iconname("G-Code Errors")
2279        error_report.grab_set()
2280        return_value =  StringVar()
2281        return_value.set("none")
2282
2283
2284        def Close_Click(event):
2285            return_value.set("close")
2286            error_report.destroy()
2287
2288        #Text Box
2289        Error_Frame = Frame(error_report)
2290        scrollbar = Scrollbar(Error_Frame, orient=VERTICAL)
2291        Error_Text = Text(Error_Frame, width="80", height="20",yscrollcommand=scrollbar.set,bg='white')
2292        for line in message:
2293            Error_Text.insert(END,line+"\n")
2294        scrollbar.config(command=Error_Text.yview)
2295        scrollbar.pack(side=RIGHT,fill=Y)
2296        #End Text Box
2297
2298        Button_Frame = Frame(error_report)
2299        close_button = Button(Button_Frame,text=" Close ")
2300        close_button.bind("<ButtonRelease-1>", Close_Click)
2301        close_button.pack(side=RIGHT,fill=X)
2302
2303        Error_Text.pack(side=LEFT,fill=BOTH,expand=1)
2304        Button_Frame.pack(side=BOTTOM)
2305        Error_Frame.pack(side=LEFT,fill=BOTH,expand=1)
2306
2307        root.wait_window(error_report)
2308        return return_value.get()
2309
2310    def Open_G_Code(self,filename):
2311        self.resetPath()
2312
2313        g_rip = G_Code_Rip()
2314        try:
2315            MSG = g_rip.Read_G_Code(filename, XYarc2line = True, arc_angle=2, units="in", Accuracy="")
2316            Error_Text = ""
2317            if MSG!=[]:
2318                self.gcode_error_message(MSG)
2319
2320        #except StandardError as e:
2321        except Exception as e:
2322            msg1 = "G-Code Load Failed:  "
2323            msg2 = "Filename: %s" %(filename)
2324            msg3 = "%s" %(e)
2325            self.statusMessage.set((msg1+msg3).split("\n")[0] )
2326            self.statusbar.configure( bg = 'red' )
2327            message_box(msg1, "%s\n%s" %(msg2,msg3))
2328            debug_message(traceback.format_exc())
2329
2330
2331        ecoords= g_rip.generate_laser_paths(g_rip.g_code_data)
2332        self.GcodeData.set_ecoords(ecoords,data_sorted=True)
2333        self.Design_bounds = self.GcodeData.bounds
2334
2335
2336    def Open_DXF(self,filemname):
2337        self.resetPath()
2338
2339        self.DXF_FILE = filemname
2340        dxf_import=DXF_CLASS()
2341        tolerance = .0005
2342        try:
2343            fd = open(self.DXF_FILE)
2344            dxf_import.GET_DXF_DATA(fd,lin_tol = tolerance,get_units=True,units=None)
2345            fd.seek(0)
2346
2347            dxf_units = dxf_import.units
2348            if dxf_units=="Unitless":
2349                d = UnitsDialog(root)
2350                dxf_units = d.result
2351            if dxf_units=="Inches":
2352                dxf_scale = 1.0
2353            elif dxf_units=="Feet":
2354                dxf_scale = 12.0
2355            elif dxf_units=="Miles":
2356                dxf_scale = 5280.0*12.0
2357            elif dxf_units=="Millimeters":
2358                dxf_scale = 1.0/25.4
2359            elif dxf_units=="Centimeters":
2360                dxf_scale = 1.0/2.54
2361            elif dxf_units=="Meters":
2362                dxf_scale = 1.0/254.0
2363            elif dxf_units=="Kilometers":
2364                dxf_scale = 1.0/254000.0
2365            elif dxf_units=="Microinches":
2366                dxf_scale = 1.0/1000000.0
2367            elif dxf_units=="Mils":
2368                dxf_scale = 1.0/1000.0
2369            else:
2370                return
2371
2372            lin_tol = tolerance / dxf_scale
2373            dxf_import.GET_DXF_DATA(fd,lin_tol=lin_tol,get_units=False,units=None)
2374            fd.close()
2375        #except StandardError as e:
2376        except Exception as e:
2377            msg1 = "DXF Load Failed:"
2378            msg2 = "%s" %(e)
2379            self.statusMessage.set((msg1+msg2).split("\n")[0] )
2380            self.statusbar.configure( bg = 'red' )
2381            message_box(msg1, msg2)
2382            debug_message(traceback.format_exc())
2383        except:
2384            fmessage("Unable To open Drawing Exchange File (DXF) file.")
2385            debug_message(traceback.format_exc())
2386            return
2387
2388        new_origin=False
2389        dxf_engrave_coords = dxf_import.DXF_COORDS_GET_TYPE(engrave=True, new_origin=False)
2390        dxf_cut_coords     = dxf_import.DXF_COORDS_GET_TYPE(engrave=False,new_origin=False)
2391##        if DEBUG:
2392##            dxf_code = dxf_import.WriteDXF(close_loops=False)
2393##            fout = open('Z:\\out.dxf','w')
2394##            for line in dxf_code:
2395##                fout.write(line+'\n')
2396##            fout.close
2397
2398        if dxf_import.dxf_messages != "":
2399            msg_split=dxf_import.dxf_messages.split("\n")
2400            msg_split.sort()
2401            msg_split.append("")
2402            mcnt=1
2403            msg_out = ""
2404            for i in range(1,len(msg_split)):
2405                if msg_split[i-1]==msg_split[i]:
2406                    mcnt=mcnt+1
2407                else:
2408                    if msg_split[i-1]!="":
2409                        msg_line = "%s (%d places)\n" %(msg_split[i-1],mcnt)
2410                        msg_out = msg_out + msg_line
2411                    mcnt=1
2412            message_box("DXF Import:",msg_out)
2413
2414        ##########################
2415        ###   Create ECOORDS   ###
2416        ##########################
2417        self.VcutData.make_ecoords(dxf_cut_coords    ,scale=dxf_scale)
2418        self.VengData.make_ecoords(dxf_engrave_coords,scale=dxf_scale)
2419
2420        xmin = min(self.VcutData.bounds[0],self.VengData.bounds[0])
2421        xmax = max(self.VcutData.bounds[1],self.VengData.bounds[1])
2422        ymin = min(self.VcutData.bounds[2],self.VengData.bounds[2])
2423        ymax = max(self.VcutData.bounds[3],self.VengData.bounds[3])
2424        self.Design_bounds = (xmin,xmax,ymin,ymax)
2425
2426
2427    def Open_Settings_File(self,filename):
2428        try:
2429            fin = open(filename,'r')
2430        except:
2431            fmessage("Unable to open file: %s" %(filename))
2432            return
2433
2434        text_codes=[]
2435        ident = "k40_whisperer_set"
2436        for line in fin:
2437            try:
2438                if ident in line:
2439                    # BOOL
2440                    if "include_Reng"  in line:
2441                        self.include_Reng.set(line[line.find("include_Reng"):].split()[1])
2442                    elif "include_Veng"  in line:
2443                        self.include_Veng.set(line[line.find("include_Veng"):].split()[1])
2444                    elif "include_Vcut"  in line:
2445                        self.include_Vcut.set(line[line.find("include_Vcut"):].split()[1])
2446                    elif "include_Gcde"  in line:
2447                        self.include_Gcde.set(line[line.find("include_Gcde"):].split()[1])
2448                    elif "include_Time"  in line:
2449                        self.include_Time.set(line[line.find("include_Time"):].split()[1])
2450                    elif "halftone"  in line:
2451                        self.halftone.set(line[line.find("halftone"):].split()[1])
2452                    elif "negate"  in line:
2453                        self.negate.set(line[line.find("negate"):].split()[1])
2454                    elif "HomeUR"  in line:
2455                        self.HomeUR.set(line[line.find("HomeUR"):].split()[1])
2456                    elif "inputCSYS"  in line:
2457                        self.inputCSYS.set(line[line.find("inputCSYS"):].split()[1])
2458                    elif "advanced"  in line:
2459                        self.advanced.set(line[line.find("advanced"):].split()[1])
2460                    elif "mirror"  in line:
2461                        self.mirror.set(line[line.find("mirror"):].split()[1])
2462                    elif "rotate"  in line:
2463                        self.rotate.set(line[line.find("rotate"):].split()[1])
2464                    elif "engraveUP"  in line:
2465                        self.engraveUP.set(line[line.find("engraveUP"):].split()[1])
2466                    elif "init_home"  in line:
2467                        self.init_home.set(line[line.find("init_home"):].split()[1])
2468                    elif "post_home"  in line:
2469                        self.post_home.set(line[line.find("post_home"):].split()[1])
2470                    elif "post_beep"  in line:
2471                        self.post_beep.set(line[line.find("post_beep"):].split()[1])
2472                    elif "post_disp"  in line:
2473                        self.post_disp.set(line[line.find("post_disp"):].split()[1])
2474                    elif "post_exec"  in line:
2475                        self.post_exec.set(line[line.find("post_exec"):].split()[1])
2476
2477                    elif "pre_pr_crc"  in line:
2478                        self.pre_pr_crc.set(line[line.find("pre_pr_crc"):].split()[1])
2479                    elif "inside_first"  in line:
2480                        self.inside_first.set(line[line.find("inside_first"):].split()[1])
2481                    elif "comb_engrave"  in line:
2482                        self.comb_engrave.set(line[line.find("comb_engrave"):].split()[1])
2483                    elif "comb_vector"  in line:
2484                        self.comb_vector.set(line[line.find("comb_vector"):].split()[1])
2485                    elif "zoom2image"  in line:
2486                        self.zoom2image.set(line[line.find("zoom2image"):].split()[1])
2487
2488                    elif "rotary"  in line:
2489                         self.rotary.set(line[line.find("rotary"):].split()[1])
2490                    elif "trace_w_laser"  in line:
2491                         self.trace_w_laser.set(line[line.find("trace_w_laser"):].split()[1])
2492
2493                    # STRING.set()
2494                    elif "board_name" in line:
2495                        self.board_name.set(line[line.find("board_name"):].split()[1])
2496                    elif "units"    in line:
2497                        self.units.set(line[line.find("units"):].split()[1])
2498                    elif "Reng_feed"    in line:
2499                         self.Reng_feed .set(line[line.find("Reng_feed"):].split()[1])
2500                    elif "Veng_feed"    in line:
2501                         self.Veng_feed .set(line[line.find("Veng_feed"):].split()[1])
2502                    elif "Vcut_feed"    in line:
2503                         self.Vcut_feed.set(line[line.find("Vcut_feed"):].split()[1])
2504                    elif "jog_step"    in line:
2505                         self.jog_step.set(line[line.find("jog_step"):].split()[1])
2506
2507                    elif "Reng_passes"    in line:
2508                         self.Reng_passes.set(line[line.find("Reng_passes"):].split()[1])
2509                    elif "Veng_passes"    in line:
2510                         self.Veng_passes.set(line[line.find("Veng_passes"):].split()[1])
2511                    elif "Vcut_passes"    in line:
2512                         self.Vcut_passes.set(line[line.find("Vcut_passes"):].split()[1])
2513                    elif "Gcde_passes"    in line:
2514                         self.Gcde_passes.set(line[line.find("Gcde_passes"):].split()[1])
2515
2516                    elif "rast_step"    in line:
2517                         self.rast_step.set(line[line.find("rast_step"):].split()[1])
2518                    elif "ht_size"    in line:
2519                         self.ht_size.set(line[line.find("ht_size"):].split()[1])
2520
2521                    elif "LaserXsize"    in line:
2522                         self.LaserXsize.set(line[line.find("LaserXsize"):].split()[1])
2523                    elif "LaserYsize"    in line:
2524                         self.LaserYsize.set(line[line.find("LaserYsize"):].split()[1])
2525
2526                    elif "LaserXscale"    in line:
2527                         self.LaserXscale.set(line[line.find("LaserXscale"):].split()[1])
2528                    elif "LaserYscale"    in line:
2529                         self.LaserYscale.set(line[line.find("LaserYscale"):].split()[1])
2530                    elif "LaserRscale"    in line:
2531                         self.LaserRscale.set(line[line.find("LaserRscale"):].split()[1])
2532
2533                    elif "rapid_feed"    in line:
2534                         self.rapid_feed.set(line[line.find("rapid_feed"):].split()[1])
2535
2536                    elif "gotoX"    in line:
2537                         self.gotoX.set(line[line.find("gotoX"):].split()[1])
2538                    elif "gotoY"    in line:
2539                         self.gotoY.set(line[line.find("gotoY"):].split()[1])
2540
2541                    elif "bezier_M1"    in line:
2542                         self.bezier_M1.set(line[line.find("bezier_M1"):].split()[1])
2543                    elif "bezier_M2"    in line:
2544                         self.bezier_M2.set(line[line.find("bezier_M2"):].split()[1])
2545                    elif "bezier_weight"    in line:
2546                         self.bezier_weight.set(line[line.find("bezier_weight"):].split()[1])
2547                    elif "trace_gap"    in line:
2548                         self.trace_gap.set(line[line.find("trace_gap"):].split()[1])
2549                    elif "trace_speed"    in line:
2550                         self.trace_speed.set(line[line.find("trace_speed"):].split()[1])
2551
2552    ##                elif "unsharp_flag"    in line:
2553    ##                     self.unsharp_flag.set(line[line.find("unsharp_flag"):].split()[1])
2554    ##                elif "unsharp_r"    in line:
2555    ##                     self.unsharp_r.set(line[line.find("unsharp_r"):].split()[1])
2556    ##                elif "unsharp_p"    in line:
2557    ##                     self.unsharp_p.set(line[line.find("unsharp_p"):].split()[1])
2558    ##                elif "unsharp_t"    in line:
2559    ##                     self.unsharp_t.set(line[line.find("unsharp_t"):].split()[1])
2560
2561                    elif "t_timeout"    in line:
2562                         self.t_timeout.set(line[line.find("t_timeout"):].split()[1])
2563                    elif "n_timeouts"    in line:
2564                         self.n_timeouts.set(line[line.find("n_timeouts"):].split()[1])
2565
2566                    elif "ink_timeout"    in line:
2567                         self.ink_timeout.set(line[line.find("ink_timeout"):].split()[1])
2568
2569                    elif "designfile"    in line:
2570                           self.DESIGN_FILE=(line[line.find("designfile"):].split("\042")[1])
2571                    elif "inkscape_path"    in line:
2572                         self.inkscape_path.set(line[line.find("inkscape_path"):].split("\042")[1])
2573                    elif "batch_path"    in line:
2574                         self.batch_path.set(line[line.find("batch_path"):].split("\042")[1])
2575
2576
2577            except:
2578                #Ignoring exeptions during reading data from line
2579                pass
2580
2581        fin.close()
2582
2583        fileName, fileExtension = os.path.splitext(self.DESIGN_FILE)
2584        init_file=os.path.basename(fileName)
2585
2586        if init_file != "None":
2587            if ( os.path.isfile(self.DESIGN_FILE) ):
2588                pass
2589            else:
2590                self.statusMessage.set("Image file not found: %s " %(self.DESIGN_FILE))
2591
2592        if self.units.get() == 'in':
2593            self.funits.set('in/min')
2594            self.units_scale = 1.0
2595        else:
2596            self.units.set('mm')
2597            self.funits.set('mm/s')
2598            self.units_scale = 25.4
2599
2600        temp_name, fileExtension = os.path.splitext(filename)
2601        file_base=os.path.basename(temp_name)
2602
2603        if self.initComplete == 1:
2604            self.menu_Mode_Change()
2605            self.DESIGN_FILE = filename
2606
2607    ##########################################################################
2608    ##########################################################################
2609    def menu_File_Save(self):
2610        settings_data = self.WriteConfig()
2611        init_dir = os.path.dirname(self.DESIGN_FILE)
2612        if ( not os.path.isdir(init_dir) ):
2613            init_dir = self.HOME_DIR
2614
2615        fileName, fileExtension = os.path.splitext(self.DESIGN_FILE)
2616        init_file=os.path.basename(fileName)
2617
2618        filename = asksaveasfilename(defaultextension='.txt', \
2619                                     filetypes=[("Text File","*.txt")],\
2620                                     initialdir=init_dir,\
2621                                     initialfile= init_file )
2622
2623        if filename != '' and filename != ():
2624            try:
2625                fout = open(filename,'w')
2626            except:
2627                self.statusMessage.set("Unable to open file for writing: %s" %(filename))
2628                self.statusbar.configure( bg = 'red' )
2629                return
2630
2631            for line in settings_data:
2632                try:
2633                    fout.write(line+'\n')
2634                except:
2635                    fout.write('(skipping line)\n')
2636                    debug_message(traceback.format_exc())
2637            fout.close
2638            self.statusMessage.set("File Saved: %s" %(filename))
2639            self.statusbar.configure( bg = 'white' )
2640
2641    def Get_Design_Bounds(self):
2642        if self.rotate.get():
2643            ymin =  self.Design_bounds[0]
2644            ymax =  self.Design_bounds[1]
2645            xmin = -self.Design_bounds[3]
2646            xmax = -self.Design_bounds[2]
2647        else:
2648            xmin,xmax,ymin,ymax = self.Design_bounds
2649        return (xmin,xmax,ymin,ymax)
2650
2651    def Move_UL(self,dummy=None):
2652        xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
2653        if self.HomeUR.get():
2654            Xnew = self.laserX + (xmax-xmin)
2655            DX = round((xmax-xmin)*1000.0)
2656        else:
2657            Xnew = self.laserX
2658            DX = 0
2659
2660        (Xsize,Ysize)=self.LASER_Size()
2661        if Xnew <= Xsize+.001:
2662            self.move_head_window_temporary([DX,0.0])
2663        else:
2664            pass
2665
2666    def Move_UR(self,dummy=None):
2667        xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
2668        if self.HomeUR.get():
2669            Xnew = self.laserX
2670            DX = 0
2671        else:
2672            Xnew = self.laserX + (xmax-xmin)
2673            DX = round((xmax-xmin)*1000.0)
2674
2675        (Xsize,Ysize)=self.LASER_Size()
2676        if Xnew <= Xsize+.001:
2677            self.move_head_window_temporary([DX,0.0])
2678        else:
2679            pass
2680
2681    def Move_LR(self,dummy=None):
2682        xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
2683        if self.HomeUR.get():
2684            Xnew = self.laserX
2685            DX = 0
2686        else:
2687            Xnew = self.laserX + (xmax-xmin)
2688            DX = round((xmax-xmin)*1000.0)
2689
2690        Ynew = self.laserY - (ymax-ymin)
2691        (Xsize,Ysize)=self.LASER_Size()
2692        if Xnew <= Xsize+.001 and Ynew >= -Ysize-.001:
2693            DY = round((ymax-ymin)*1000.0)
2694            self.move_head_window_temporary([DX,-DY])
2695        else:
2696            pass
2697
2698    def Move_LL(self,dummy=None):
2699        xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
2700        if self.HomeUR.get():
2701            Xnew = self.laserX + (xmax-xmin)
2702            DX = round((xmax-xmin)*1000.0)
2703        else:
2704            Xnew = self.laserX
2705            DX = 0
2706
2707        Ynew = self.laserY - (ymax-ymin)
2708        (Xsize,Ysize)=self.LASER_Size()
2709        if Xnew <= Xsize+.001 and Ynew >= -Ysize-.001:
2710            DY = round((ymax-ymin)*1000.0)
2711            self.move_head_window_temporary([DX,-DY])
2712        else:
2713            pass
2714
2715    def Move_CC(self,dummy=None):
2716        xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
2717        if self.HomeUR.get():
2718            Xnew = self.laserX + (xmax-xmin)/2.0
2719            DX = round((xmax-xmin)/2.0*1000.0)
2720        else:
2721            Xnew = self.laserX + (xmax-xmin)/2.0
2722            DX = round((xmax-xmin)/2.0*1000.0)
2723
2724
2725        Ynew = self.laserY - (ymax-ymin)/2.0
2726        (Xsize,Ysize)=self.LASER_Size()
2727        if Xnew <= Xsize+.001 and Ynew >= -Ysize-.001:
2728            DY = round((ymax-ymin)/2.0*1000.0)
2729            self.move_head_window_temporary([DX,-DY])
2730        else:
2731            pass
2732
2733    def Move_Arbitrary(self,MoveX,MoveY,dummy=None):
2734        if self.GUI_Disabled:
2735            return
2736        if self.HomeUR.get():
2737            DX = -MoveX
2738        else:
2739            DX = MoveX
2740        DY = MoveY
2741        NewXpos = self.pos_offset[0]+DX
2742        NewYpos = self.pos_offset[1]+DY
2743        self.move_head_window_temporary([NewXpos,NewYpos])
2744
2745    def Move_Arb_Step(self,dx,dy):
2746        if self.GUI_Disabled:
2747            return
2748        if self.units.get()=="in":
2749            dx_inches = round(dx*1000)
2750            dy_inches = round(dy*1000)
2751        else:
2752            dx_inches = round(dx/25.4*1000)
2753            dy_inches = round(dy/25.4*1000)
2754        self.Move_Arbitrary( dx_inches,dy_inches )
2755
2756    def Move_Arb_Right(self,dummy=None):
2757        JOG_STEP = float( self.jog_step.get() )
2758        self.Move_Arb_Step( JOG_STEP,0 )
2759
2760    def Move_Arb_Left(self,dummy=None):
2761        JOG_STEP = float( self.jog_step.get() )
2762        self.Move_Arb_Step( -JOG_STEP,0 )
2763
2764    def Move_Arb_Up(self,dummy=None):
2765        JOG_STEP = float( self.jog_step.get() )
2766        self.Move_Arb_Step( 0,JOG_STEP )
2767
2768    def Move_Arb_Down(self,dummy=None):
2769        JOG_STEP = float( self.jog_step.get() )
2770        self.Move_Arb_Step( 0,-JOG_STEP )
2771
2772    ####################################################
2773
2774    def Move_Right(self,dummy=None):
2775        JOG_STEP = float( self.jog_step.get() )
2776        self.Rapid_Move( JOG_STEP,0 )
2777
2778    def Move_Left(self,dummy=None):
2779        JOG_STEP = float( self.jog_step.get() )
2780        self.Rapid_Move( -JOG_STEP,0 )
2781
2782    def Move_Up(self,dummy=None):
2783        JOG_STEP = float( self.jog_step.get() )
2784        self.Rapid_Move( 0,JOG_STEP )
2785
2786    def Move_Down(self,dummy=None):
2787        JOG_STEP = float( self.jog_step.get() )
2788        self.Rapid_Move( 0,-JOG_STEP )
2789
2790    def Rapid_Move(self,dx,dy):
2791        if self.GUI_Disabled:
2792            return
2793        if self.units.get()=="in":
2794            dx_inches = round(dx,3)
2795            dy_inches = round(dy,3)
2796        else:
2797            dx_inches = round(dx/25.4,3)
2798            dy_inches = round(dy/25.4,3)
2799
2800        if (self.HomeUR.get()):
2801            dx_inches = -dx_inches
2802
2803        Xnew,Ynew = self.XY_in_bounds(dx_inches,dy_inches)
2804        dxmils = (Xnew - self.laserX)*1000.0
2805        dymils = (Ynew - self.laserY)*1000.0
2806
2807        if self.k40 == None:
2808            self.laserX  = Xnew
2809            self.laserY  = Ynew
2810            self.menu_View_Refresh()
2811        elif self.Send_Rapid_Move(dxmils,dymils):
2812            self.laserX  = Xnew
2813            self.laserY  = Ynew
2814            self.menu_View_Refresh()
2815
2816
2817    def Send_Rapid_Move(self,dxmils,dymils):
2818        try:
2819            if self.k40 != None:
2820                Xscale = float(self.LaserXscale.get())
2821                Yscale = float(self.LaserYscale.get())
2822                if self.rotary.get():
2823                    Rscale = float(self.LaserRscale.get())
2824                    Yscale = Yscale*Rscale
2825
2826                if Xscale != 1.0 or Yscale != 1.0:
2827                    dxmils = int(round(dxmils *Xscale))
2828                    dymils = int(round(dymils *Yscale))
2829                self.k40.n_timeouts = 10
2830
2831                if self.rotary.get() and float(self.rapid_feed.get()):
2832                    self.slow_jog(int(dxmils),int(dymils))
2833                else:
2834                    self.k40.rapid_move(int(dxmils),int(dymils))
2835
2836                return True
2837            else:
2838                return True
2839        #except StandardError as e:
2840        except Exception as e:
2841            msg1 = "Rapid Move Failed: "
2842            msg2 = "%s" %(e)
2843            if msg2 == "":
2844                formatted_lines = traceback.format_exc().splitlines()
2845            self.statusMessage.set((msg1+msg2).split("\n")[0] )
2846            self.statusbar.configure( bg = 'red' )
2847            debug_message(traceback.format_exc())
2848            return False
2849
2850
2851    def slow_jog(self,dxmils,dymils):
2852        if int(dxmils)==0 and int(dymils)==0:
2853            return
2854        self.stop[0]=False
2855        Rapid_data=[]
2856        Rapid_inst = egv(target=lambda s:Rapid_data.append(s))
2857        Rapid_feed = float(self.rapid_feed.get())*self.feed_factor()
2858        Rapid_inst.make_egv_rapid(dxmils,dymils,Feed=Rapid_feed,board_name=self.board_name.get())
2859        self.send_egv_data(Rapid_data, 1, None)
2860        self.stop[0]=True
2861
2862    def update_gui(self, message=None, bgcolor='white'):
2863        if message!=None:
2864            self.statusMessage.set(message)
2865            self.statusbar.configure( bg = bgcolor )
2866        self.master.update()
2867        return True
2868
2869    def set_gui(self,new_state="normal"):
2870        if new_state=="normal":
2871            self.GUI_Disabled=False
2872        else:
2873            self.GUI_Disabled=True
2874
2875        try:
2876            self.menuBar.entryconfigure("File"    , state=new_state)
2877            self.menuBar.entryconfigure("View"    , state=new_state)
2878            self.menuBar.entryconfigure("Tools"     , state=new_state)
2879            self.menuBar.entryconfigure("Settings", state=new_state)
2880            self.menuBar.entryconfigure("Help"    , state=new_state)
2881            self.PreviewCanvas.configure(state=new_state)
2882
2883            for w in self.master.winfo_children():
2884                try:
2885                    w.configure(state=new_state)
2886                except:
2887                    pass
2888            self.Stop_Button.configure(state="normal")
2889            self.statusbar.configure(state="normal")
2890            self.master.update()
2891        except:
2892            if DEBUG:
2893                debug_message(traceback.format_exc())
2894
2895    def Vector_Cut(self, output_filename=None):
2896        self.Prepare_for_laser_run("Vector Cut: Processing Vector Data.")
2897        if self.VcutData.ecoords!=[]:
2898            self.send_data("Vector_Cut", output_filename)
2899        else:
2900            self.statusbar.configure( bg = 'yellow' )
2901            self.statusMessage.set("No vector data to cut")
2902        self.Finish_Job()
2903
2904    def Vector_Eng(self, output_filename=None):
2905        self.Prepare_for_laser_run("Vector Engrave: Processing Vector Data.")
2906        if self.VengData.ecoords!=[]:
2907            self.send_data("Vector_Eng", output_filename)
2908        else:
2909            self.statusbar.configure( bg = 'yellow' )
2910            self.statusMessage.set("No vector data to engrave")
2911        self.Finish_Job()
2912
2913    def Trace_Eng(self, output_filename=None):
2914        self.Prepare_for_laser_run("Boundary Trace: Processing Data.")
2915        self.trace_coords = self.make_trace_path()
2916
2917        if self.trace_coords!=[]:
2918            self.send_data("Trace_Eng", output_filename)
2919        else:
2920            self.statusbar.configure( bg = 'yellow' )
2921            self.statusMessage.set("No trace data to follow")
2922        self.Finish_Job()
2923
2924    def Raster_Eng(self, output_filename=None):
2925        self.Prepare_for_laser_run("Raster Engraving: Processing Image Data.")
2926        try:
2927            self.make_raster_coords()
2928            if self.RengData.ecoords!=[]:
2929                self.send_data("Raster_Eng", output_filename)
2930            else:
2931                self.statusbar.configure( bg = 'yellow' )
2932                self.statusMessage.set("No raster data to engrave")
2933
2934        except MemoryError as e:
2935            msg1 = "Memory Error:"
2936            msg2 = "Memory Error:  Out of Memory."
2937            self.statusMessage.set(msg2)
2938            self.statusbar.configure( bg = 'red' )
2939            message_box(msg1, msg2)
2940            debug_message(traceback.format_exc())
2941
2942        except Exception as e:
2943            msg1 = "Making Raster Data Stopped: "
2944            msg2 = "%s" %(e)
2945            self.statusMessage.set((msg1+msg2).split("\n")[0] )
2946            self.statusbar.configure( bg = 'red' )
2947            message_box(msg1, msg2)
2948            debug_message(traceback.format_exc())
2949        self.Finish_Job()
2950
2951    def Raster_Vector_Eng(self, output_filename=None):
2952        self.Prepare_for_laser_run("Raster Engraving: Processing Image and Vector Data.")
2953        try:
2954            self.make_raster_coords()
2955            if self.RengData.ecoords!=[] or self.VengData.ecoords!=[]:
2956                self.send_data("Raster_Eng+Vector_Eng", output_filename)
2957            else:
2958                self.statusbar.configure( bg = 'yellow' )
2959                self.statusMessage.set("No data to engrave")
2960        except Exception as e:
2961            msg1 = "Preparing Data Stopped: "
2962            msg2 = "%s" %(e)
2963            self.statusMessage.set((msg1+msg2).split("\n")[0] )
2964            self.statusbar.configure( bg = 'red' )
2965            message_box(msg1, msg2)
2966            debug_message(traceback.format_exc())
2967        self.Finish_Job()
2968
2969    def Vector_Eng_Cut(self, output_filename=None):
2970        self.Prepare_for_laser_run("Vector Cut: Processing Vector Data.")
2971        if self.VcutData.ecoords!=[] or self.VengData.ecoords!=[]:
2972            self.send_data("Vector_Eng+Vector_Cut", output_filename)
2973        else:
2974            self.statusbar.configure( bg = 'yellow' )
2975            self.statusMessage.set("No vector data.")
2976        self.Finish_Job()
2977
2978    def Raster_Vector_Cut(self, output_filename=None):
2979        self.Prepare_for_laser_run("Raster Engraving: Processing Image and Vector Data.")
2980        try:
2981            self.make_raster_coords()
2982            if self.RengData.ecoords!=[] or self.VengData.ecoords!=[] or self.VcutData.ecoords!=[]:
2983                self.send_data("Raster_Eng+Vector_Eng+Vector_Cut", output_filename)
2984            else:
2985                self.statusbar.configure( bg = 'yellow' )
2986                self.statusMessage.set("No data to engrave/cut")
2987        except Exception as e:
2988            msg1 = "Preparing Data Stopped: "
2989            msg2 = "%s" %(e)
2990            self.statusMessage.set((msg1+msg2).split("\n")[0] )
2991            self.statusbar.configure( bg = 'red' )
2992            message_box(msg1, msg2)
2993            debug_message(traceback.format_exc())
2994        self.Finish_Job()
2995
2996    def Gcode_Cut(self, output_filename=None):
2997        self.Prepare_for_laser_run("G Code Cutting.")
2998        if self.GcodeData.ecoords!=[]:
2999            self.send_data("Gcode_Cut", output_filename)
3000        else:
3001            self.statusbar.configure( bg = 'yellow' )
3002            self.statusMessage.set("No g-code data to cut")
3003        self.Finish_Job()
3004
3005    def Prepare_for_laser_run(self,msg):
3006        self.stop[0]=False
3007        self.move_head_window_temporary([0,0])
3008        self.set_gui("disabled")
3009        self.statusbar.configure( bg = 'green' )
3010        self.statusMessage.set(msg)
3011        self.master.update()
3012
3013    def Finish_Job(self, event=None):
3014        self.set_gui("normal")
3015        self.stop[0]=True
3016        if self.post_home.get():
3017            self.Unlock()
3018
3019        if self.post_beep.get():
3020            self.master.bell()
3021
3022        stderr = ''
3023        stdout = ''
3024        if self.post_exec.get():
3025            cmd = [self.batch_path.get()]
3026            from subprocess import Popen, PIPE
3027            startupinfo=None
3028            proc = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE, startupinfo=startupinfo)
3029            stdout,stderr = proc.communicate()
3030
3031        if self.post_disp.get() or stderr != '':
3032            msg1 = ''
3033            minutes = floor(self.run_time / 60)
3034            seconds = self.run_time - minutes*60
3035            msg2 = "Job Ended.\nRun Time = %02d:%02d" %(minutes,seconds)
3036            if stdout != '':
3037                msg2=msg2+'\n\nBatch File Output:\n'+stdout
3038            if stderr != '':
3039                msg2=msg2+'\n\nBatch File Errors:\n'+stderr
3040            self.run_time = 0
3041            message_box(msg1, msg2)
3042
3043
3044    def make_trace_path(self):
3045        my_hull = hull2D()
3046        if self.inputCSYS.get() and self.RengData.image == None:
3047            xmin,xmax,ymin,ymax = 0.0,0.0,0.0,0.0
3048        else:
3049            xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
3050
3051        startx = xmin
3052        starty = ymax
3053
3054        #######################################
3055        Vcut_coords = self.VcutData.ecoords
3056        Veng_coords = self.VengData.ecoords
3057        Gcode_coords= self.GcodeData.ecoords
3058        if self.mirror.get() or self.rotate.get():
3059            Vcut_coords = self.mirror_rotate_vector_coords(Vcut_coords)
3060            Veng_coords = self.mirror_rotate_vector_coords(Veng_coords)
3061            Gcode_coords= self.mirror_rotate_vector_coords(Gcode_coords)
3062
3063        #######################################
3064        if self.RengData.ecoords==[]:
3065            if self.stop[0] == True:
3066                self.stop[0]=False
3067                self.make_raster_coords()
3068                self.stop[0]=True
3069            else:
3070                self.make_raster_coords()
3071
3072        RengHullCoords = []
3073        Xscale = 1/float(self.LaserXscale.get())
3074        Yscale = 1/float(self.LaserYscale.get())
3075        if self.rotary.get():
3076            Rscale = 1/float(self.LaserRscale.get())
3077            Yscale = Yscale*Rscale
3078
3079        for point in self.RengData.hull_coords:
3080            RengHullCoords.append([point[0]*Xscale+xmin, point[1]*Yscale, point[2]])
3081
3082        all_coords = []
3083        all_coords.extend(Vcut_coords)
3084        all_coords.extend(Veng_coords)
3085        all_coords.extend(Gcode_coords)
3086        all_coords.extend(RengHullCoords)
3087
3088        trace_coords=[]
3089        if all_coords != []:
3090            trace_coords = my_hull.convexHullecoords(all_coords)
3091            gap = float(self.trace_gap.get())/self.units_scale
3092            trace_coords = self.offset_eccords(trace_coords,gap)
3093
3094        trace_coords,startx,starty = self.scale_vector_coords(trace_coords,startx,starty)
3095        return trace_coords
3096
3097
3098    ################################################################################
3099    def Sort_Paths(self,ecoords,i_loop=2):
3100        ##########################
3101        ###   find loop ends   ###
3102        ##########################
3103        Lbeg=[]
3104        Lend=[]
3105        if len(ecoords)>0:
3106            Lbeg.append(0)
3107            loop_old=ecoords[0][i_loop]
3108            for i in range(1,len(ecoords)):
3109                loop = ecoords[i][i_loop]
3110                if loop != loop_old:
3111                    Lbeg.append(i)
3112                    Lend.append(i-1)
3113                loop_old=loop
3114            Lend.append(i)
3115
3116        #######################################################
3117        # Find new order based on distance to next beg or end #
3118        #######################################################
3119        order_out = []
3120        use_beg=0
3121        if len(ecoords)>0:
3122            order_out.append([Lbeg[0],Lend[0]])
3123        inext = 0
3124        total=len(Lbeg)
3125        for i in range(total-1):
3126            if use_beg==1:
3127                ii=Lbeg.pop(inext)
3128                Lend.pop(inext)
3129            else:
3130                ii=Lend.pop(inext)
3131                Lbeg.pop(inext)
3132
3133            Xcur = ecoords[ii][0]
3134            Ycur = ecoords[ii][1]
3135
3136            dx = Xcur - ecoords[ Lbeg[0] ][0]
3137            dy = Ycur - ecoords[ Lbeg[0] ][1]
3138            min_dist = dx*dx + dy*dy
3139
3140            dxe = Xcur - ecoords[ Lend[0] ][0]
3141            dye = Ycur - ecoords[ Lend[0] ][1]
3142            min_diste = dxe*dxe + dye*dye
3143
3144            inext=0
3145            inexte=0
3146            for j in range(1,len(Lbeg)):
3147                dx = Xcur - ecoords[ Lbeg[j] ][0]
3148                dy = Ycur - ecoords[ Lbeg[j] ][1]
3149                dist = dx*dx + dy*dy
3150                if dist < min_dist:
3151                    min_dist=dist
3152                    inext=j
3153                ###
3154                dxe = Xcur - ecoords[ Lend[j] ][0]
3155                dye = Ycur - ecoords[ Lend[j] ][1]
3156                diste = dxe*dxe + dye*dye
3157                if diste < min_diste:
3158                    min_diste=diste
3159                    inexte=j
3160                ###
3161            if min_diste < min_dist:
3162                inext=inexte
3163                order_out.append([Lend[inexte],Lbeg[inexte]])
3164                use_beg=1
3165            else:
3166                order_out.append([Lbeg[inext],Lend[inext]])
3167                use_beg=0
3168        ###########################################################
3169        return order_out
3170
3171    #####################################################
3172    # determine if a point is inside a given polygon or not
3173    # Polygon is a list of (x,y) pairs.
3174    # http://www.ariel.com.au/a/python-point-int-poly.html
3175    #####################################################
3176    def point_inside_polygon(self,x,y,poly):
3177        n = len(poly)
3178        inside = -1
3179        p1x = poly[0][0]
3180        p1y = poly[0][1]
3181        for i in range(n+1):
3182            p2x = poly[i%n][0]
3183            p2y = poly[i%n][1]
3184            if y > min(p1y,p2y):
3185                if y <= max(p1y,p2y):
3186                    if x <= max(p1x,p2x):
3187                        if p1y != p2y:
3188                            xinters = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
3189                        if p1x == p2x or x <= xinters:
3190                            inside = inside * -1
3191            p1x,p1y = p2x,p2y
3192
3193        return inside
3194
3195    def optimize_paths(self,ecoords,inside_check=True):
3196        order_out = self.Sort_Paths(ecoords)
3197        lastx=-999
3198        lasty=-999
3199        Acc=0.004
3200        cuts=[]
3201
3202        for line in order_out:
3203            temp=line
3204            if temp[0] > temp[1]:
3205                step = -1
3206            else:
3207                step = 1
3208
3209            loop_old = -1
3210
3211            for i in range(temp[0],temp[1]+step,step):
3212                x1   = ecoords[i][0]
3213                y1   = ecoords[i][1]
3214                loop = ecoords[i][2]
3215                # check and see if we need to move to a new discontinuous start point
3216                if (loop != loop_old):
3217                    dx = x1-lastx
3218                    dy = y1-lasty
3219                    dist = sqrt(dx*dx + dy*dy)
3220                    if dist > Acc:
3221                        cuts.append([[x1,y1]])
3222                    else:
3223                        cuts[-1].append([x1,y1])
3224                else:
3225                    cuts[-1].append([x1,y1])
3226                lastx = x1
3227                lasty = y1
3228                loop_old = loop
3229
3230        if inside_check:
3231            #####################################################
3232            # For each loop determine if other loops are inside #
3233            #####################################################
3234            Nloops=len(cuts)
3235            self.LoopTree=[]
3236            for iloop in range(Nloops):
3237                self.LoopTree.append([])
3238    ##            CUR_PCT=float(iloop)/Nloops*100.0
3239    ##            if (not self.batch.get()):
3240    ##                self.statusMessage.set('Determining Which Side of Loop to Cut: %d of %d' %(iloop+1,Nloops))
3241    ##                self.master.update()
3242                ipoly = cuts[iloop]
3243                ## Check points in other loops (could just check one) ##
3244                if ipoly != []:
3245                    for jloop in range(Nloops):
3246                        if jloop != iloop:
3247                            inside = 0
3248                            inside = inside + self.point_inside_polygon(cuts[jloop][0][0],cuts[jloop][0][1],ipoly)
3249                            if inside > 0:
3250                                self.LoopTree[iloop].append(jloop)
3251            #####################################################
3252            for i in range(Nloops):
3253                lns=[]
3254                lns.append(i)
3255                self.remove_self_references(lns,self.LoopTree[i])
3256
3257            self.order=[]
3258            self.loops = list(range(Nloops))
3259            for i in range(Nloops):
3260                if self.LoopTree[i]!=[]:
3261                    self.addlist(self.LoopTree[i])
3262                    self.LoopTree[i]=[]
3263                if self.loops[i]!=[]:
3264                    self.order.append(self.loops[i])
3265                    self.loops[i]=[]
3266        #END inside_check
3267            ecoords_out = []
3268            for i in self.order:
3269                line = cuts[i]
3270                for coord in line:
3271                    ecoords_out.append([coord[0],coord[1],i])
3272        #END inside_check
3273        else:
3274            ecoords_out = []
3275            for i in range(len(cuts)):
3276                line = cuts[i]
3277                for coord in line:
3278                    ecoords_out.append([coord[0],coord[1],i])
3279
3280        return ecoords_out
3281
3282    def remove_self_references(self,loop_numbers,loops):
3283        for i in range(0,len(loops)):
3284            for j in range(0,len(loop_numbers)):
3285                if loops[i]==loop_numbers[j]:
3286                    loops.pop(i)
3287                    return
3288            if self.LoopTree[loops[i]]!=[]:
3289                loop_numbers.append(loops[i])
3290                self.remove_self_references(loop_numbers,self.LoopTree[loops[i]])
3291
3292    def addlist(self,list):
3293        for i in list:
3294            try: #this try/except is a bad hack fix to a recursion error. It should be fixed properly later.
3295                if self.LoopTree[i]!=[]:
3296                    self.addlist(self.LoopTree[i]) #too many recursions here causes cmp error
3297                    self.LoopTree[i]=[]
3298            except:
3299                pass
3300            if self.loops[i]!=[]:
3301                self.order.append(self.loops[i])
3302                self.loops[i]=[]
3303
3304
3305    def mirror_rotate_vector_coords(self,coords):
3306        xmin = self.Design_bounds[0]
3307        xmax = self.Design_bounds[1]
3308        coords_rotate_mirror=[]
3309
3310        for i in range(len(coords)):
3311            coords_rotate_mirror.append(coords[i][:])
3312            if self.mirror.get():
3313                if self.inputCSYS.get() and self.RengData.image == None:
3314                    coords_rotate_mirror[i][0]=-coords_rotate_mirror[i][0]
3315                else:
3316                    coords_rotate_mirror[i][0]=xmin+xmax-coords_rotate_mirror[i][0]
3317
3318
3319            if self.rotate.get():
3320                x = coords_rotate_mirror[i][0]
3321                y = coords_rotate_mirror[i][1]
3322                coords_rotate_mirror[i][0] = -y
3323                coords_rotate_mirror[i][1] =  x
3324
3325        return coords_rotate_mirror
3326
3327    def scale_vector_coords(self,coords,startx,starty):
3328
3329        Xscale = float(self.LaserXscale.get())
3330        Yscale = float(self.LaserYscale.get())
3331        if self.rotary.get():
3332            Rscale = float(self.LaserRscale.get())
3333            Yscale = Yscale*Rscale
3334
3335        coords_scale=[]
3336        if Xscale != 1.0 or Yscale != 1.0:
3337            for i in range(len(coords)):
3338                coords_scale.append(coords[i][:])
3339                x = coords_scale[i][0]
3340                y = coords_scale[i][1]
3341                coords_scale[i][0] = x*Xscale
3342                coords_scale[i][1] = y*Yscale
3343            scaled_startx = startx*Xscale
3344            scaled_starty = starty*Yscale
3345        else:
3346            coords_scale = coords
3347            scaled_startx = startx
3348            scaled_starty = starty
3349
3350        return coords_scale,scaled_startx,scaled_starty
3351
3352
3353    def feed_factor(self):
3354        if self.units.get()=='in':
3355            feed_factor = 25.4/60.0
3356        else:
3357            feed_factor = 1.0
3358        return feed_factor
3359
3360    def send_data(self,operation_type=None, output_filename=None):
3361        num_passes=0
3362        if self.k40 == None and output_filename == None:
3363            self.statusMessage.set("Laser Cutter is not Initialized...")
3364            self.statusbar.configure( bg = 'red' )
3365            return
3366        try:
3367            feed_factor=self.feed_factor()
3368
3369            if self.inputCSYS.get() and self.RengData.image == None:
3370                xmin,xmax,ymin,ymax = 0.0,0.0,0.0,0.0
3371            else:
3372                xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
3373
3374            startx = xmin
3375            starty = ymax
3376
3377            if self.HomeUR.get():
3378                Xscale = float(self.LaserXscale.get())
3379                FlipXoffset = Xscale*abs(xmax-xmin)
3380                if self.rotate.get():
3381                    startx = -xmin
3382            else:
3383                FlipXoffset = 0
3384
3385            if self.rotary.get():
3386                Rapid_Feed = float(self.rapid_feed.get())*feed_factor
3387            else:
3388                Rapid_Feed = 0.0
3389
3390            Raster_Eng_data=[]
3391            Vector_Eng_data=[]
3392            Trace_Eng_data=[]
3393            Vector_Cut_data=[]
3394            G_code_Cut_data=[]
3395
3396            if (operation_type.find("Vector_Cut") > -1) and  (self.VcutData.ecoords!=[]):
3397                Feed_Rate = float(self.Vcut_feed.get())*feed_factor
3398                self.statusMessage.set("Vector Cut: Determining Cut Order....")
3399                self.master.update()
3400                if not self.VcutData.sorted and self.inside_first.get():
3401                    self.VcutData.set_ecoords(self.optimize_paths(self.VcutData.ecoords),data_sorted=True)
3402
3403
3404##                DEBUG_PLOT=False
3405##                test_ecoords=self.VcutData.ecoords
3406##                if DEBUG_PLOT:
3407##                    import matplotlib.pyplot as plt
3408##                    plt.ion()
3409##                    plt.clf()
3410##                    X=[]
3411##                    Y=[]
3412##                    LOOP_OLD = test_ecoords[0][2]
3413##                    for i in range(len(test_ecoords)):
3414##                        LOOP = test_ecoords[i][2]
3415##                        if LOOP != LOOP_OLD:
3416##                            plt.plot(X,Y)
3417##                            plt.pause(.5)
3418##                            X=[]
3419##                            Y=[]
3420##                            LOOP_OLD=LOOP
3421##                        X.append(test_ecoords[i][0])
3422##                        Y.append(test_ecoords[i][1])
3423##                    plt.plot(X,Y)
3424
3425
3426                self.statusMessage.set("Generating EGV data...")
3427                self.master.update()
3428
3429                Vcut_coords = self.VcutData.ecoords
3430                if self.mirror.get() or self.rotate.get():
3431                    Vcut_coords = self.mirror_rotate_vector_coords(Vcut_coords)
3432
3433                Vcut_coords,startx,starty = self.scale_vector_coords(Vcut_coords,startx,starty)
3434                Vector_Cut_egv_inst = egv(target=lambda s:Vector_Cut_data.append(s))
3435                Vector_Cut_egv_inst.make_egv_data(
3436                                                Vcut_coords,                      \
3437                                                startX=startx,                    \
3438                                                startY=starty,                    \
3439                                                Feed = Feed_Rate,                 \
3440                                                board_name=self.board_name.get(), \
3441                                                Raster_step = 0,                  \
3442                                                update_gui=self.update_gui,       \
3443                                                stop_calc=self.stop,              \
3444                                                FlipXoffset=FlipXoffset,          \
3445                                                Rapid_Feed_Rate = Rapid_Feed,     \
3446                                                use_laser=True
3447                                                )
3448
3449            if (operation_type.find("Vector_Eng") > -1) and  (self.VengData.ecoords!=[]):
3450                Feed_Rate = float(self.Veng_feed.get())*feed_factor
3451                self.statusMessage.set("Vector Engrave: Determining Cut Order....")
3452                self.master.update()
3453                if not self.VengData.sorted and self.inside_first.get():
3454                    self.VengData.set_ecoords(self.optimize_paths(self.VengData.ecoords,inside_check=False),data_sorted=True)
3455                self.statusMessage.set("Generating EGV data...")
3456                self.master.update()
3457
3458                Veng_coords = self.VengData.ecoords
3459                if self.mirror.get() or self.rotate.get():
3460                    Veng_coords = self.mirror_rotate_vector_coords(Veng_coords)
3461
3462                Veng_coords,startx,starty = self.scale_vector_coords(Veng_coords,startx,starty)
3463                Vector_Eng_egv_inst = egv(target=lambda s:Vector_Eng_data.append(s))
3464                Vector_Eng_egv_inst.make_egv_data(
3465                                                Veng_coords,                      \
3466                                                startX=startx,                    \
3467                                                startY=starty,                    \
3468                                                Feed = Feed_Rate,                 \
3469                                                board_name=self.board_name.get(), \
3470                                                Raster_step = 0,                  \
3471                                                update_gui=self.update_gui,       \
3472                                                stop_calc=self.stop,              \
3473                                                FlipXoffset=FlipXoffset,          \
3474                                                Rapid_Feed_Rate = Rapid_Feed,     \
3475                                                use_laser=True
3476                                                )
3477
3478
3479            if (operation_type.find("Trace_Eng") > -1) and (self.trace_coords!=[]):
3480                Feed_Rate = float(self.trace_speed.get())*feed_factor
3481                laser_on = self.trace_w_laser.get()
3482                self.statusMessage.set("Generating EGV data...")
3483                self.master.update()
3484                Trace_Eng_egv_inst = egv(target=lambda s:Trace_Eng_data.append(s))
3485                Trace_Eng_egv_inst.make_egv_data(
3486                                                self.trace_coords,                \
3487                                                startX=startx,                    \
3488                                                startY=starty,                    \
3489                                                Feed = Feed_Rate,                 \
3490                                                board_name=self.board_name.get(), \
3491                                                Raster_step = 0,                  \
3492                                                update_gui=self.update_gui,       \
3493                                                stop_calc=self.stop,              \
3494                                                FlipXoffset=FlipXoffset,          \
3495                                                Rapid_Feed_Rate = Rapid_Feed,     \
3496                                                use_laser=laser_on
3497                                                )
3498
3499
3500            if (operation_type.find("Raster_Eng") > -1) and  (self.RengData.ecoords!=[]):
3501                Feed_Rate = float(self.Reng_feed.get())*feed_factor
3502                Raster_step = self.get_raster_step_1000in()
3503                if not self.engraveUP.get():
3504                    Raster_step = -Raster_step
3505
3506                raster_startx = 0
3507
3508                Yscale = float(self.LaserYscale.get())
3509                if self.rotary.get():
3510                    Rscale = float(self.LaserRscale.get())
3511                    Yscale = Yscale*Rscale
3512                raster_starty = Yscale*starty
3513
3514                self.statusMessage.set("Generating EGV data...")
3515                self.master.update()
3516                Raster_Eng_egv_inst = egv(target=lambda s:Raster_Eng_data.append(s))
3517                Raster_Eng_egv_inst.make_egv_data(
3518                                                self.RengData.ecoords,            \
3519                                                startX=raster_startx,             \
3520                                                startY=raster_starty,             \
3521                                                Feed = Feed_Rate,                 \
3522                                                board_name=self.board_name.get(), \
3523                                                Raster_step = Raster_step,        \
3524                                                update_gui=self.update_gui,       \
3525                                                stop_calc=self.stop,              \
3526                                                FlipXoffset=FlipXoffset,          \
3527                                                Rapid_Feed_Rate = Rapid_Feed,     \
3528                                                use_laser=True
3529                                                )
3530                #self.RengData.reset_path()
3531
3532            if (operation_type.find("Gcode_Cut") > -1) and (self.GcodeData.ecoords!=[]):
3533                self.statusMessage.set("Generating EGV data...")
3534                self.master.update()
3535                Gcode_coords = self.GcodeData.ecoords
3536                if self.mirror.get() or self.rotate.get():
3537                    Gcode_coords = self.mirror_rotate_vector_coords(Gcode_coords)
3538
3539                Gcode_coords,startx,starty = self.scale_vector_coords(Gcode_coords,startx,starty)
3540                G_code_Cut_egv_inst = egv(target=lambda s:G_code_Cut_data.append(s))
3541                G_code_Cut_egv_inst.make_egv_data(
3542                                                Gcode_coords,                     \
3543                                                startX=startx,                    \
3544                                                startY=starty,                    \
3545                                                Feed = None,                      \
3546                                                board_name=self.board_name.get(), \
3547                                                Raster_step = 0,                  \
3548                                                update_gui=self.update_gui,       \
3549                                                stop_calc=self.stop,              \
3550                                                FlipXoffset=FlipXoffset,          \
3551                                                Rapid_Feed_Rate = Rapid_Feed,     \
3552                                                use_laser=True
3553                                                )
3554
3555            ### Join Resulting Data together ###
3556            data=[]
3557            data.append(ord("I"))
3558            if Trace_Eng_data!=[]:
3559                trace_passes=1
3560                for k in range(trace_passes):
3561                    if len(data)> 4:
3562                        data[-4]=ord("@")
3563                    data.extend(Trace_Eng_data)
3564            if Raster_Eng_data!=[]:
3565                num_passes = int(float(self.Reng_passes.get()))
3566                for k in range(num_passes):
3567                    if len(data)> 4:
3568                        data[-4]=ord("@")
3569                    data.extend(Raster_Eng_data)
3570            if Vector_Eng_data!=[]:
3571                num_passes = int(float(self.Veng_passes.get()))
3572                for k in range(num_passes):
3573                    if len(data)> 4:
3574                        data[-4]=ord("@")
3575                    data.extend(Vector_Eng_data)
3576            if Vector_Cut_data!=[]:
3577                num_passes = int(float(self.Vcut_passes.get()))
3578                for k in range(num_passes):
3579                    if len(data)> 4:
3580                        data[-4]=ord("@")
3581                    data.extend(Vector_Cut_data)
3582            if G_code_Cut_data!=[]:
3583                num_passes = int(float(self.Gcde_passes.get()))
3584                for k in range(num_passes):
3585                    if len(data)> 4:
3586                        data[-4]=ord("@")
3587                    data.extend(G_code_Cut_data)
3588            if len(data)< 4:
3589                raise Exception("No laser data was generated.")
3590
3591            self.master.update()
3592            if output_filename != None:
3593                self.write_egv_to_file(data,output_filename)
3594            else:
3595                self.send_egv_data(data, 1, output_filename)
3596                self.menu_View_Refresh()
3597
3598        except MemoryError as e:
3599            msg1 = "Memory Error:"
3600            msg2 = "Memory Error:  Out of Memory."
3601            self.statusMessage.set(msg2)
3602            self.statusbar.configure( bg = 'red' )
3603            message_box(msg1, msg2)
3604            debug_message(traceback.format_exc())
3605
3606        except Exception as e:
3607            msg1 = "Sending Data Stopped: "
3608            msg2 = "%s" %(e)
3609            if msg2 == "":
3610                formatted_lines = traceback.format_exc().splitlines()
3611            self.statusMessage.set((msg1+msg2).split("\n")[0] )
3612            self.statusbar.configure( bg = 'red' )
3613            message_box(msg1, msg2)
3614            debug_message(traceback.format_exc())
3615
3616    def send_egv_data(self,data,num_passes=1,output_filename=None):
3617        pre_process_CRC        = self.pre_pr_crc.get()
3618        if self.k40 != None:
3619            self.k40.timeout       = int(float( self.t_timeout.get()  ))
3620            self.k40.n_timeouts    = int(float( self.n_timeouts.get() ))
3621            time_start = time()
3622            self.k40.send_data(data,self.update_gui,self.stop,num_passes,pre_process_CRC, wait_for_laser=True)
3623            self.run_time = time()-time_start
3624            if DEBUG:
3625                print(("Elapsed Time: %.6f" %(time()-time_start)))
3626
3627        else:
3628            self.statusMessage.set("Laser is not initialized.")
3629            self.statusbar.configure( bg = 'yellow' )
3630            return
3631        self.menu_View_Refresh()
3632
3633    ##########################################################################
3634    ##########################################################################
3635    def write_egv_to_file(self,data,fname):
3636        if len(data) == 0:
3637            raise Exception("No data available to write to file.")
3638        try:
3639            fout = open(fname,'w')
3640        except:
3641            raise Exception("Unable to open file ( %s ) for writing." %(fname))
3642        fout.write("Document type : LHYMICRO-GL file\n")
3643        fout.write("Creator-Software: K40 Whisperer\n")
3644
3645        fout.write("\n")
3646        fout.write("%0%0%0%0%")
3647        for char_val in data:
3648            char = chr(char_val)
3649            fout.write("%s" %(char))
3650
3651        #fout.write("\n")
3652        fout.close
3653        self.menu_View_Refresh()
3654        self.statusMessage.set("Data saved to: %s" %(fname))
3655
3656    def Home(self, event=None):
3657        if self.GUI_Disabled:
3658            return
3659        if self.k40 != None:
3660            self.k40.home_position()
3661        self.laserX  = 0.0
3662        self.laserY  = 0.0
3663        self.pos_offset = [0.0,0.0]
3664        self.menu_View_Refresh()
3665
3666    def GoTo(self):
3667        xpos = float(self.gotoX.get())
3668        ypos = float(self.gotoY.get())
3669        if self.k40 != None:
3670            self.k40.home_position()
3671        self.laserX  = 0.0
3672        self.laserY  = 0.0
3673        self.Rapid_Move(xpos,ypos)
3674        self.menu_View_Refresh()
3675
3676    def Reset(self):
3677        if self.k40 != None:
3678            try:
3679                self.k40.reset_usb()
3680                self.statusMessage.set("USB Reset Succeeded")
3681            except:
3682                debug_message(traceback.format_exc())
3683                pass
3684
3685    def Stop(self,event=None):
3686        if self.stop[0]==True:
3687            return
3688        line1 = "Sending data to the laser from K40 Whisperer is currently Paused."
3689        line2 = "Press \"OK\" to abort any jobs currently running."
3690        line3 = "Press \"Cancel\" to resume."
3691        if self.k40 != None:
3692            self.k40.pause_un_pause()
3693
3694        if message_ask_ok_cancel("Stop Laser Job.", "%s\n\n%s\n%s" %(line1,line2,line3)):
3695            self.stop[0]=True
3696        else:
3697            if self.k40 != None:
3698                self.k40.pause_un_pause()
3699
3700    def Hide_Advanced(self,event=None):
3701        self.advanced.set(0)
3702        self.menu_View_Refresh()
3703
3704    def Release_USB(self):
3705        if self.k40 != None:
3706            try:
3707                self.k40.release_usb()
3708                self.statusMessage.set("USB Release Succeeded")
3709            except:
3710                debug_message(traceback.format_exc())
3711                pass
3712            self.k40=None
3713
3714    def Initialize_Laser(self,event=None):
3715        if self.GUI_Disabled:
3716            return
3717        self.stop[0]=True
3718        self.Release_USB()
3719        self.k40=None
3720        self.move_head_window_temporary([0.0,0.0])
3721        self.k40=K40_CLASS()
3722        try:
3723            self.k40.initialize_device()
3724            self.k40.say_hello()
3725            if self.init_home.get():
3726                self.Home()
3727            else:
3728                self.Unlock()
3729
3730        except Exception as e:
3731            error_text = "%s" %(e)
3732            if "BACKEND" in error_text.upper():
3733                error_text = error_text + " (libUSB driver not installed)"
3734            self.statusMessage.set("USB Error: %s" %(error_text))
3735            self.statusbar.configure( bg = 'red' )
3736            self.k40=None
3737            debug_message(traceback.format_exc())
3738
3739        except:
3740            self.statusMessage.set("Unknown USB Error")
3741            self.statusbar.configure( bg = 'red' )
3742            self.k40=None
3743            debug_message(traceback.format_exc())
3744
3745    def Unlock(self,event=None):
3746        if self.GUI_Disabled:
3747            return
3748        if self.k40 != None:
3749            try:
3750                self.k40.unlock_rail()
3751                self.statusMessage.set("Rail Unlock Succeeded")
3752                self.statusbar.configure( bg = 'white' )
3753            except:
3754                self.statusMessage.set("Rail Unlock Failed.")
3755                self.statusbar.configure( bg = 'red' )
3756                debug_message(traceback.format_exc())
3757                pass
3758
3759    ##########################################################################
3760    ##########################################################################
3761
3762    def menu_File_Quit(self):
3763        if message_ask_ok_cancel("Exit", "Exiting...."):
3764            self.Quit_Click(None)
3765
3766    def Reset_RasterPath_and_Update_Time(self, varName=0, index=0, mode=0):
3767        self.RengData.reset_path()
3768        self.refreshTime()
3769
3770    def View_Refresh_and_Reset_RasterPath(self, varName=0, index=0, mode=0):
3771        self.RengData.reset_path()
3772        self.SCALE = 0
3773        self.menu_View_Refresh()
3774
3775    def menu_View_inputCSYS_Refresh_Callback(self, varName, index, mode):
3776        self.move_head_window_temporary([0.0,0.0])
3777        self.SCALE = 0
3778        self.menu_View_Refresh()
3779
3780    def menu_View_Refresh_Callback(self, varName=0, index=0, mode=0):
3781        self.SCALE = 0
3782        self.menu_View_Refresh()
3783
3784        if DEBUG:
3785            curframe = inspect.currentframe()
3786            calframe = inspect.getouterframes(curframe, 2)
3787            print('menu_View_Refresh_Callback called by: %s' %(calframe[1][3]))
3788
3789    def menu_View_Refresh(self):
3790        if DEBUG:
3791            curframe = inspect.currentframe()
3792            calframe = inspect.getouterframes(curframe, 2)
3793            print('menu_View_Refresh called by: %s' %(calframe[1][3]))
3794
3795        try:
3796            app.master.title(title_text+"   "+ self.DESIGN_FILE)
3797        except:
3798            pass
3799        dummy_event = Event()
3800        dummy_event.widget=self.master
3801        self.Master_Configure(dummy_event,1)
3802        self.Plot_Data()
3803        xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
3804        W = xmax-xmin
3805        H = ymax-ymin
3806
3807        if self.units.get()=="in":
3808            X_display = self.laserX + self.pos_offset[0]/1000.0
3809            Y_display = self.laserY + self.pos_offset[1]/1000.0
3810            W_display = W
3811            H_display = H
3812            U_display = self.units.get()
3813        else:
3814            X_display = (self.laserX + self.pos_offset[0]/1000.0)*self.units_scale
3815            Y_display = (self.laserY + self.pos_offset[1]/1000.0)*self.units_scale
3816            W_display = W*self.units_scale
3817            H_display = H*self.units_scale
3818            U_display = self.units.get()
3819        if self.HomeUR.get():
3820            X_display = -X_display
3821
3822        self.statusMessage.set(" Current Position: X=%.3f Y=%.3f    ( W X H )=( %.3f%s X %.3f%s ) "
3823                                %(X_display,
3824                                  Y_display,
3825                                  W_display,
3826                                  U_display,
3827                                  H_display,
3828                                  U_display))
3829
3830        self.statusbar.configure( bg = 'white' )
3831
3832    def menu_Inside_First_Callback(self, varName, index, mode):
3833        if self.GcodeData.ecoords != []:
3834            if self.VcutData.sorted == True:
3835                self.menu_Reload_Design()
3836            elif self.VengData.sorted == True:
3837                self.menu_Reload_Design()
3838
3839    def menu_Mode_Change(self):
3840        dummy_event = Event()
3841        dummy_event.widget=self.master
3842        self.Master_Configure(dummy_event,1)
3843
3844    def menu_Calc_Raster_Time(self,event=None):
3845        self.set_gui("disabled")
3846        self.stop[0]=False
3847        self.make_raster_coords()
3848        self.stop[0]=True
3849        self.refreshTime()
3850        self.set_gui("normal")
3851        self.menu_View_Refresh()
3852
3853
3854    def menu_Help_About(self):
3855        application="K40 Whisperer"
3856        about = "%s Version %s\n\n" %(application,version)
3857        about = about + "By Scorch.\n"
3858        about = about + "\163\143\157\162\143\150\100\163\143\157\162"
3859        about = about + "\143\150\167\157\162\153\163\056\143\157\155\n"
3860        about = about + "https://www.scorchworks.com/\n\n"
3861        try:
3862            python_version = "%d.%d.%d" %(sys.version_info.major,sys.version_info.minor,sys.version_info.micro)
3863        except:
3864            python_version = ""
3865        about = about + "Python "+python_version+" (%d bit)" %(struct.calcsize("P") * 8)
3866        message_box("About %s" %(application),about)
3867
3868    def menu_Help_Web(self):
3869        webbrowser.open_new(r"https://www.scorchworks.com/K40whisperer/k40whisperer.html")
3870
3871    def menu_Help_Manual(self):
3872        webbrowser.open_new(r"https://www.scorchworks.com/K40whisperer/k40w_manual.html")
3873
3874    def KEY_F1(self, event):
3875        if self.GUI_Disabled:
3876            return
3877        self.menu_Help_About()
3878
3879    def KEY_F2(self, event):
3880        if self.GUI_Disabled:
3881            return
3882        self.GEN_Settings_Window()
3883
3884    def KEY_F3(self, event):
3885        if self.GUI_Disabled:
3886            return
3887        self.RASTER_Settings_Window()
3888
3889    def KEY_F4(self, event):
3890        if self.GUI_Disabled:
3891            return
3892        self.ROTARY_Settings_Window()
3893        self.menu_View_Refresh()
3894
3895    def KEY_F5(self, event):
3896        if self.GUI_Disabled:
3897            return
3898        self.menu_View_Refresh()
3899
3900    def KEY_F6(self, event):
3901        if self.GUI_Disabled:
3902            return
3903        self.advanced.set(not self.advanced.get())
3904        self.menu_View_Refresh()
3905
3906    def bindConfigure(self, event):
3907        if not self.initComplete:
3908            self.initComplete = 1
3909            self.menu_Mode_Change()
3910
3911    def Master_Configure(self, event, update=0):
3912        if event.widget != self.master:
3913            return
3914        x = int(self.master.winfo_x())
3915        y = int(self.master.winfo_y())
3916        w = int(self.master.winfo_width())
3917        h = int(self.master.winfo_height())
3918        if (self.x, self.y) == (-1,-1):
3919            self.x, self.y = x,y
3920        if abs(self.w-w)>10 or abs(self.h-h)>10 or update==1:
3921            ###################################################
3922            #  Form changed Size (resized) adjust as required #
3923            ###################################################
3924            self.w=w
3925            self.h=h
3926
3927            if True:
3928                # Left Column #
3929                w_label=90
3930                w_entry=48
3931                w_units=52
3932
3933                x_label_L=10
3934                x_entry_L=x_label_L+w_label+20-5
3935                x_units_L=x_entry_L+w_entry+2
3936
3937                Yloc=10
3938                self.Initialize_Button.place (x=12, y=Yloc, width=100*2, height=23)
3939                Yloc=Yloc+33
3940
3941                self.Open_Button.place (x=12, y=Yloc, width=100, height=40)
3942                self.Reload_Button.place(x=12+100, y=Yloc, width=100, height=40)
3943                if h>=560:
3944                    Yloc=Yloc+50
3945                    self.separator1.place(x=x_label_L, y=Yloc,width=w_label+75+40, height=2)
3946                    Yloc=Yloc+6
3947                    self.Label_Position_Control.place(x=x_label_L, y=Yloc, width=w_label*2, height=21)
3948
3949                    Yloc=Yloc+25
3950                    self.Home_Button.place (x=12, y=Yloc, width=100, height=23)
3951                    self.UnLock_Button.place(x=12+100, y=Yloc, width=100, height=23)
3952
3953                    Yloc=Yloc+33
3954                    self.Label_Step.place(x=x_label_L, y=Yloc, width=w_label, height=21)
3955                    self.Label_Step_u.place(x=x_units_L, y=Yloc, width=w_units, height=21)
3956                    self.Entry_Step.place(x=x_entry_L, y=Yloc, width=w_entry, height=23)
3957
3958                    ###########################################################################
3959                    Yloc=Yloc+30
3960                    bsz=40
3961                    xoffst=35
3962                    self.UL_Button.place    (x=xoffst+12      ,  y=Yloc, width=bsz, height=bsz)
3963                    self.Up_Button.place    (x=xoffst+12+bsz  ,  y=Yloc, width=bsz, height=bsz)
3964                    self.UR_Button.place    (x=xoffst+12+bsz*2,  y=Yloc, width=bsz, height=bsz)
3965                    Yloc=Yloc+bsz
3966                    self.Left_Button.place  (x=xoffst+12      ,y=Yloc, width=bsz, height=bsz)
3967                    self.CC_Button.place    (x=xoffst+12+bsz  ,y=Yloc, width=bsz, height=bsz)
3968                    self.Right_Button.place (x=xoffst+12+bsz*2,y=Yloc, width=bsz, height=bsz)
3969                    Yloc=Yloc+bsz
3970                    self.LL_Button.place    (x=xoffst+12      ,  y=Yloc, width=bsz, height=bsz)
3971                    self.Down_Button.place  (x=xoffst+12+bsz  ,  y=Yloc, width=bsz, height=bsz)
3972                    self.LR_Button.place    (x=xoffst+12+bsz*2,  y=Yloc, width=bsz, height=bsz)
3973
3974
3975                    Yloc=Yloc+bsz
3976                    ###########################################################################
3977                    self.Label_GoToX.place(x=x_entry_L, y=Yloc, width=w_entry, height=23)
3978                    self.Label_GoToY.place(x=x_units_L, y=Yloc, width=w_entry, height=23)
3979                    Yloc=Yloc+25
3980                    self.GoTo_Button.place (x=12, y=Yloc, width=100, height=23)
3981                    self.Entry_GoToX.place(x=x_entry_L, y=Yloc, width=w_entry, height=23)
3982                    self.Entry_GoToY.place(x=x_units_L, y=Yloc, width=w_entry, height=23)
3983                    ###########################################################################
3984                else:
3985                    ###########################################################################
3986                    self.separator1.place_forget()
3987                    self.Label_Position_Control.place_forget()
3988                    ##
3989                    Yloc=Yloc+50
3990                    self.separator1.place(x=x_label_L, y=Yloc,width=w_label+75+40, height=2)
3991                    Yloc=Yloc+6
3992                    self.Home_Button.place (x=12, y=Yloc, width=100, height=23)
3993                    self.UnLock_Button.place(x=12+100, y=Yloc, width=100, height=23)
3994                    ##
3995                    self.Label_Step.place_forget()
3996                    self.Label_Step_u.place_forget()
3997                    self.Entry_Step.place_forget()
3998                    self.UL_Button.place_forget()
3999                    self.Up_Button.place_forget()
4000                    self.UR_Button.place_forget()
4001                    self.Left_Button.place_forget()
4002                    self.CC_Button.place_forget()
4003                    self.Right_Button.place_forget()
4004                    self.LL_Button.place_forget()
4005                    self.Down_Button.place_forget()
4006                    self.LR_Button.place_forget()
4007                    self.Label_GoToX.place_forget()
4008                    self.Label_GoToY.place_forget()
4009                    self.GoTo_Button.place_forget()
4010                    self.Entry_GoToX.place_forget()
4011                    self.Entry_GoToY.place_forget()
4012                    ###########################################################################
4013
4014                #From Bottom up
4015                BUinit = self.h-70
4016                Yloc = BUinit
4017                self.Stop_Button.place (x=12, y=Yloc, width=100*2, height=30)
4018
4019                self.Stop_Button.configure(bg='light coral')
4020                Yloc=Yloc-10+10
4021
4022                wadv       = 220 #200
4023                wadv_use   = wadv-20
4024                Xvert_sep  = 220
4025                Xadvanced  = Xvert_sep+10
4026                w_label_adv= wadv-80 #  110 w_entry
4027
4028                if self.GcodeData.ecoords == []:
4029                    self.Grun_Button.place_forget()
4030                    self.Reng_Veng_Vcut_Button.place_forget()
4031                    self.Reng_Veng_Button.place_forget()
4032                    self.Veng_Vcut_Button.place_forget()
4033
4034                    Yloc=Yloc-30
4035                    self.Vcut_Button.place      (x=12, y=Yloc, width=100, height=23)
4036                    self.Entry_Vcut_feed.place  (x=x_entry_L, y=Yloc, width=w_entry, height=23)
4037                    self.Label_Vcut_feed_u.place(x=x_units_L, y=Yloc, width=w_units, height=23)
4038                    Y_Vcut=Yloc
4039
4040                    Yloc=Yloc-30
4041                    self.Veng_Button.place  (x=12, y=Yloc, width=100, height=23)
4042                    self.Entry_Veng_feed.place(  x=x_entry_L, y=Yloc, width=w_entry, height=23)
4043                    self.Label_Veng_feed_u.place(x=x_units_L, y=Yloc, width=w_units, height=23)
4044                    Y_Veng=Yloc
4045
4046                    Yloc=Yloc-30
4047                    self.Reng_Button.place  (x=12, y=Yloc, width=100, height=23)
4048                    self.Entry_Reng_feed.place(  x=x_entry_L, y=Yloc, width=w_entry, height=23)
4049                    self.Label_Reng_feed_u.place(x=x_units_L, y=Yloc, width=w_units, height=23)
4050                    Y_Reng=Yloc
4051
4052                    if self.comb_vector.get() or self.comb_engrave.get():
4053                        if self.comb_engrave.get():
4054                            self.Veng_Button.place_forget()
4055                            self.Reng_Button.place_forget()
4056                        if self.comb_vector.get():
4057                            self.Vcut_Button.place_forget()
4058                            self.Veng_Button.place_forget()
4059
4060                        if self.comb_engrave.get():
4061                            if self.comb_vector.get():
4062                                self.Reng_Veng_Vcut_Button.place(x=12, y=Y_Reng, width=100, height=23*3+14)
4063                            else:
4064                                self.Reng_Veng_Button.place(x=12, y=Y_Reng, width=100, height=23*2+7)
4065                        elif self.comb_vector.get():
4066                            self.Veng_Vcut_Button.place(x=12, y=Y_Veng, width=100, height=23*2+7)
4067
4068
4069                else:
4070                    self.Vcut_Button.place_forget()
4071                    self.Entry_Vcut_feed.place_forget()
4072                    self.Label_Vcut_feed_u.place_forget()
4073
4074                    self.Veng_Button.place_forget()
4075                    self.Entry_Veng_feed.place_forget()
4076                    self.Label_Veng_feed_u.place_forget()
4077
4078                    self.Reng_Button.place_forget()
4079                    self.Entry_Reng_feed.place_forget()
4080                    self.Label_Reng_feed_u.place_forget()
4081
4082                    self.Reng_Veng_Vcut_Button.place_forget()
4083                    self.Reng_Veng_Button.place_forget()
4084                    self.Veng_Vcut_Button.place_forget()
4085
4086                    Yloc=Yloc-30
4087                    self.Grun_Button.place  (x=12, y=Yloc, width=100*2, height=23)
4088
4089                if h>=560:
4090                    Yloc=Yloc-15
4091                    self.separator2.place(x=x_label_L, y=Yloc,width=w_label+75+40, height=2)
4092                else:
4093                    self.separator2.place_forget()
4094
4095                # End Left Column #
4096
4097                if self.advanced.get():
4098
4099                    self.PreviewCanvas.configure( width = self.w-240-wadv, height = self.h-50 )
4100                    self.PreviewCanvas_frame.place(x=220+wadv, y=10)
4101                    self.separator_vert.place(x=220, y=10,width=2, height=self.h-50)
4102
4103                    adv_Yloc=25-10 #15
4104                    self.Label_Advanced_column.place(x=Xadvanced, y=adv_Yloc, width=wadv_use, height=21)
4105                    adv_Yloc=adv_Yloc+25
4106                    self.separator_adv.place(x=Xadvanced, y=adv_Yloc,width=wadv_use, height=2)
4107
4108                    if h>=560:
4109                        adv_Yloc=adv_Yloc+25-20 #15
4110                        self.Label_Halftone_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4111                        self.Checkbutton_Halftone_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4112
4113                        adv_Yloc=adv_Yloc+25
4114                        self.Label_Negate_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4115                        self.Checkbutton_Negate_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4116
4117                        adv_Yloc=adv_Yloc+25
4118                        self.separator_adv2.place(x=Xadvanced, y=adv_Yloc,width=wadv_use, height=2)
4119
4120                        adv_Yloc=adv_Yloc+25-20
4121                        self.Label_Mirror_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4122                        self.Checkbutton_Mirror_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4123
4124                        adv_Yloc=adv_Yloc+25
4125                        self.Label_Rotate_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4126                        self.Checkbutton_Rotate_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4127
4128                        adv_Yloc=adv_Yloc+25
4129                        self.Label_inputCSYS_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4130                        self.Checkbutton_inputCSYS_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4131
4132                        adv_Yloc=adv_Yloc+25
4133                        self.separator_adv3.place(x=Xadvanced, y=adv_Yloc,width=wadv_use, height=2)
4134
4135                        adv_Yloc=adv_Yloc+25-20
4136                        self.Label_Inside_First_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4137                        self.Checkbutton_Inside_First_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4138
4139                        adv_Yloc=adv_Yloc+25
4140                        self.Label_Rotary_Enable_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4141                        self.Checkbutton_Rotary_Enable_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4142                    else:
4143                        #self.Label_Advanced_column.place_forget()
4144                        #self.separator_adv.place_forget()
4145                        self.Label_Halftone_adv.place_forget()
4146                        self.Checkbutton_Halftone_adv.place_forget()
4147                        self.Label_Negate_adv.place_forget()
4148                        self.Checkbutton_Negate_adv.place_forget()
4149                        self.separator_adv2.place_forget()
4150                        self.Label_Mirror_adv.place_forget()
4151                        self.Checkbutton_Mirror_adv.place_forget()
4152                        self.Label_Rotate_adv.place_forget()
4153                        self.Checkbutton_Rotate_adv.place_forget()
4154                        self.Label_inputCSYS_adv.place_forget()
4155                        self.Checkbutton_inputCSYS_adv.place_forget()
4156                        self.separator_adv3.place_forget()
4157                        self.Label_Inside_First_adv.place_forget()
4158                        self.Checkbutton_Inside_First_adv.place_forget()
4159                        self.Label_Rotary_Enable_adv.place_forget()
4160                        self.Checkbutton_Rotary_Enable_adv.place_forget()
4161
4162                    adv_Yloc = BUinit
4163                    self.Hide_Adv_Button.place (x=Xadvanced, y=adv_Yloc, width=wadv_use, height=30)
4164
4165                    if self.RengData.image != None:
4166                        self.Label_inputCSYS_adv.configure(state="disabled")
4167                        self.Checkbutton_inputCSYS_adv.place_forget()
4168                    else:
4169                        self.Label_inputCSYS_adv.configure(state="normal")
4170
4171                    if self.GcodeData.ecoords == []:
4172                        #adv_Yloc = adv_Yloc-40
4173                        self.Label_Vcut_passes.place(x=Xadvanced, y=Y_Vcut, width=w_label_adv, height=21)
4174                        self.Entry_Vcut_passes.place(x=Xadvanced+w_label_adv+2, y=Y_Vcut, width=w_entry, height=23)
4175
4176                        #adv_Yloc=adv_Yloc-30
4177                        self.Label_Veng_passes.place(x=Xadvanced, y=Y_Veng, width=w_label_adv, height=21)
4178                        self.Entry_Veng_passes.place(x=Xadvanced+w_label_adv+2, y=Y_Veng, width=w_entry, height=23)
4179
4180                        #adv_Yloc=adv_Yloc-30
4181                        self.Label_Reng_passes.place(x=Xadvanced, y=Y_Reng, width=w_label_adv, height=21)
4182                        self.Entry_Reng_passes.place(x=Xadvanced+w_label_adv+2, y=Y_Reng, width=w_entry, height=23)
4183                        self.Label_Gcde_passes.place_forget()
4184                        self.Entry_Gcde_passes.place_forget()
4185                        adv_Yloc = Y_Reng
4186
4187                       ####
4188                        adv_Yloc=adv_Yloc-15
4189                        self.separator_comb.place(x=Xadvanced-1, y=adv_Yloc, width=wadv_use, height=2)
4190
4191                        adv_Yloc=adv_Yloc-25
4192                        self.Label_Comb_Vector_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4193                        self.Checkbutton_Comb_Vector_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4194
4195                        adv_Yloc=adv_Yloc-25
4196                        self.Label_Comb_Engrave_adv.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4197                        self.Checkbutton_Comb_Engrave_adv.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=25, height=23)
4198                        ####
4199
4200                    else:
4201                        adv_Yloc=adv_Yloc-40
4202                        self.Label_Gcde_passes.place(x=Xadvanced, y=adv_Yloc, width=w_label_adv, height=21)
4203                        self.Entry_Gcde_passes.place(x=Xadvanced+w_label_adv+2, y=adv_Yloc, width=w_entry, height=23)
4204                        self.Label_Vcut_passes.place_forget()
4205                        self.Entry_Vcut_passes.place_forget()
4206                        self.Label_Veng_passes.place_forget()
4207                        self.Entry_Veng_passes.place_forget()
4208                        self.Label_Reng_passes.place_forget()
4209                        self.Entry_Reng_passes.place_forget()
4210
4211                else:
4212                    self.PreviewCanvas_frame.place_forget()
4213                    self.separator_vert.place_forget()
4214                    self.Label_Advanced_column.place_forget()
4215                    self.separator_adv.place_forget()
4216                    self.Label_Halftone_adv.place_forget()
4217                    self.Checkbutton_Halftone_adv.place_forget()
4218                    self.Label_Negate_adv.place_forget()
4219                    self.Checkbutton_Negate_adv.place_forget()
4220                    self.separator_adv2.place_forget()
4221                    self.Label_Mirror_adv.place_forget()
4222                    self.Checkbutton_Mirror_adv.place_forget()
4223                    self.Label_Rotate_adv.place_forget()
4224                    self.Checkbutton_Rotate_adv.place_forget()
4225                    self.Label_inputCSYS_adv.place_forget()
4226                    self.Checkbutton_inputCSYS_adv.place_forget()
4227                    self.separator_adv3.place_forget()
4228                    self.Label_Inside_First_adv.place_forget()
4229                    self.Checkbutton_Inside_First_adv.place_forget()
4230
4231                    self.Label_Rotary_Enable_adv.place_forget()
4232                    self.Checkbutton_Rotary_Enable_adv.place_forget()
4233
4234                    self.separator_comb.place_forget()
4235                    self.Label_Comb_Engrave_adv.place_forget()
4236                    self.Checkbutton_Comb_Engrave_adv.place_forget()
4237                    self.Label_Comb_Vector_adv.place_forget()
4238                    self.Checkbutton_Comb_Vector_adv.place_forget()
4239
4240
4241                    self.Entry_Vcut_passes.place_forget()
4242                    self.Label_Vcut_passes.place_forget()
4243                    self.Entry_Veng_passes.place_forget()
4244                    self.Label_Veng_passes.place_forget()
4245                    self.Entry_Reng_passes.place_forget()
4246                    self.Label_Reng_passes.place_forget()
4247                    self.Label_Gcde_passes.place_forget()
4248                    self.Entry_Gcde_passes.place_forget()
4249                    self.Hide_Adv_Button.place_forget()
4250
4251                    self.PreviewCanvas.configure( width = self.w-240, height = self.h-50 )
4252                    self.PreviewCanvas_frame.place(x=Xvert_sep, y=10)
4253                    self.separator_vert.place_forget()
4254
4255                self.Set_Input_States()
4256
4257            self.Plot_Data()
4258
4259    def Recalculate_RQD_Click(self, event):
4260        self.menu_View_Refresh()
4261
4262    def Set_Input_States(self):
4263        pass
4264
4265    def Set_Input_States_Event(self,event):
4266        self.Set_Input_States()
4267
4268    def Set_Input_States_RASTER(self,event=None):
4269        if self.halftone.get():
4270            self.Label_Halftone_DPI.configure(state="normal")
4271            self.Halftone_DPI_OptionMenu.configure(state="normal")
4272            self.Label_Halftone_u.configure(state="normal")
4273            self.Label_bezier_M1.configure(state="normal")
4274            self.bezier_M1_Slider.configure(state="normal")
4275            self.Label_bezier_M2.configure(state="normal")
4276            self.bezier_M2_Slider.configure(state="normal")
4277            self.Label_bezier_weight.configure(state="normal")
4278            self.bezier_weight_Slider.configure(state="normal")
4279        else:
4280            self.Label_Halftone_DPI.configure(state="disabled")
4281            self.Halftone_DPI_OptionMenu.configure(state="disabled")
4282            self.Label_Halftone_u.configure(state="disabled")
4283            self.Label_bezier_M1.configure(state="disabled")
4284            self.bezier_M1_Slider.configure(state="disabled")
4285            self.Label_bezier_M2.configure(state="disabled")
4286            self.bezier_M2_Slider.configure(state="disabled")
4287            self.Label_bezier_weight.configure(state="disabled")
4288            self.bezier_weight_Slider.configure(state="disabled")
4289
4290    def Set_Input_States_BATCH(self):
4291        if self.post_exec.get():
4292            self.Entry_Batch_Path.configure(state="normal")
4293        else:
4294            self.Entry_Batch_Path.configure(state="disabled")
4295##    def Set_Input_States_Unsharp(self,event=None):
4296##        if self.unsharp_flag.get():
4297##            self.Label_Unsharp_Radius.configure(state="normal")
4298##            self.Label_Unsharp_Radius_u.configure(state="normal")
4299##            self.Entry_Unsharp_Radius.configure(state="normal")
4300##            self.Label_Unsharp_Percent.configure(state="normal")
4301##            self.Label_Unsharp_Percent_u.configure(state="normal")
4302##            self.Entry_Unsharp_Percent.configure(state="normal")
4303##            self.Label_Unsharp_Threshold.configure(state="normal")
4304##            self.Entry_Unsharp_Threshold.configure(state="normal")
4305##
4306##        else:
4307##            self.Label_Unsharp_Radius.configure(state="disabled")
4308##            self.Label_Unsharp_Radius_u.configure(state="disabled")
4309##            self.Entry_Unsharp_Radius.configure(state="disabled")
4310##            self.Label_Unsharp_Percent.configure(state="disabled")
4311##            self.Label_Unsharp_Percent_u.configure(state="disabled")
4312##            self.Entry_Unsharp_Percent.configure(state="disabled")
4313##            self.Label_Unsharp_Threshold.configure(state="disabled")
4314##            self.Entry_Unsharp_Threshold.configure(state="disabled")
4315
4316    def Set_Input_States_Rotary(self,event=None):
4317        if self.rotary.get():
4318            self.Label_Laser_R_Scale.configure(state="normal")
4319            self.Entry_Laser_R_Scale.configure(state="normal")
4320            self.Label_Laser_Rapid_Feed.configure(state="normal")
4321            self.Label_Laser_Rapid_Feed_u.configure(state="normal")
4322            self.Entry_Laser_Rapid_Feed.configure(state="normal")
4323        else:
4324            self.Label_Laser_R_Scale.configure(state="disabled")
4325            self.Entry_Laser_R_Scale.configure(state="disabled")
4326            self.Label_Laser_Rapid_Feed.configure(state="disabled")
4327            self.Label_Laser_Rapid_Feed_u.configure(state="disabled")
4328            self.Entry_Laser_Rapid_Feed.configure(state="disabled")
4329
4330#    def Set_Input_States_RASTER_Event(self,event):
4331#        self.Set_Input_States_RASTER()
4332
4333    def Imaging_Free(self,image_in,bg="#ffffff"):
4334        image_in = image_in.convert('L')
4335        wim,him = image_in.size
4336        image_out=PhotoImage(width=wim,height=him)
4337        pixel=image_in.load()
4338        if bg!=None:
4339            image_out.put(bg, to=(0,0,wim,him))
4340        for y in range(0,him):
4341            for x in range(0,wim):
4342                val=pixel[x,y]
4343                if val!=255:
4344                    image_out.put("#%02x%02x%02x" %(val,val,val),(x,y))
4345        return image_out
4346
4347    ##########################################
4348    #        CANVAS PLOTTING STUFF           #
4349    ##########################################
4350    def Plot_Data(self):
4351        self.PreviewCanvas.delete(ALL)
4352        self.calc_button.place_forget()
4353
4354        for seg in self.segID:
4355            self.PreviewCanvas.delete(seg)
4356        self.segID = []
4357
4358        cszw = int(self.PreviewCanvas.cget("width"))
4359        cszh = int(self.PreviewCanvas.cget("height"))
4360        buff=10
4361        wc = float(cszw/2)
4362        hc = float(cszh/2)
4363
4364        maxx = float(self.LaserXsize.get()) / self.units_scale
4365        minx = 0.0
4366        maxy = 0.0
4367        miny = -float(self.LaserYsize.get()) / self.units_scale
4368        midx=(maxx+minx)/2
4369        midy=(maxy+miny)/2
4370
4371
4372        if self.inputCSYS.get() and self.RengData.image == None:
4373            xmin,xmax,ymin,ymax = 0.0,0.0,0.0,0.0
4374        else:
4375            xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
4376
4377        if (self.HomeUR.get()):
4378            XlineShift = maxx - self.laserX - (xmax-xmin)
4379        else:
4380            XlineShift = self.laserX
4381        YlineShift = self.laserY
4382
4383        if min((xmax-xmin),(ymax-ymin)) > 0 and self.zoom2image.get():
4384            self.PlotScale = max((xmax-xmin)/(cszw-buff), (ymax-ymin)/(cszh-buff))
4385            x_lft =  minx / self.PlotScale - self.laserX / self.PlotScale + (cszw-(xmax-xmin)/self.PlotScale)/2
4386            x_rgt =  maxx / self.PlotScale - self.laserX / self.PlotScale + (cszw-(xmax-xmin)/self.PlotScale)/2
4387            y_bot = -miny / self.PlotScale + self.laserY / self.PlotScale + (cszh-(ymax-ymin)/self.PlotScale)/2
4388            y_top = -maxy / self.PlotScale + self.laserY / self.PlotScale + (cszh-(ymax-ymin)/self.PlotScale)/2
4389            self.segID.append( self.PreviewCanvas.create_rectangle(
4390                            x_lft, y_bot, x_rgt, y_top, fill="gray80", outline="gray80", width = 0) )
4391        else:
4392            self.PlotScale = max((maxx-minx)/(cszw-buff), (maxy-miny)/(cszh-buff))
4393            x_lft = cszw/2 + (minx-midx) / self.PlotScale
4394            x_rgt = cszw/2 + (maxx-midx) / self.PlotScale
4395            y_bot = cszh/2 + (maxy-midy) / self.PlotScale
4396            y_top = cszh/2 + (miny-midy) / self.PlotScale
4397            self.segID.append( self.PreviewCanvas.create_rectangle(
4398                            x_lft, y_bot, x_rgt, y_top, fill="gray80", outline="gray80", width = 0) )
4399
4400
4401        ######################################
4402        ###       Plot Raster Image        ###
4403        ######################################
4404        if self.RengData.image != None:
4405            if self.include_Reng.get():
4406                try:
4407                    new_SCALE = (1.0/self.PlotScale)/self.input_dpi
4408                    if new_SCALE != self.SCALE:
4409                        self.SCALE = new_SCALE
4410                        nw=int(self.SCALE*self.wim)
4411                        nh=int(self.SCALE*self.him)
4412
4413                        plot_im = self.RengData.image.convert("L")
4414##                        if self.unsharp_flag.get():
4415##                            from PIL import ImageFilter
4416##                            filter = ImageFilter.UnsharpMask()
4417##                            filter.radius    = float(self.unsharp_r.get())
4418##                            filter.percent   = int(float(self.unsharp_p.get()))
4419##                            filter.threshold = int(float(self.unsharp_t.get()))
4420##                            plot_im = plot_im.filter(filter)
4421
4422                        if self.negate.get():
4423                            plot_im = ImageOps.invert(plot_im)
4424
4425                        if self.halftone.get() == False:
4426                            plot_im = plot_im.point(lambda x: 0 if x<128 else 255, '1')
4427                            plot_im = plot_im.convert("L")
4428
4429                        if self.mirror.get():
4430                            plot_im = ImageOps.mirror(plot_im)
4431
4432                        if self.rotate.get():
4433                            plot_im = plot_im.rotate(90,expand=True)
4434                            nh=int(self.SCALE*self.wim)
4435                            nw=int(self.SCALE*self.him)
4436
4437                        try:
4438                            self.UI_image = ImageTk.PhotoImage(plot_im.resize((nw,nh), Image.ANTIALIAS))
4439                        except:
4440                            debug_message("Imaging_Free Used.")
4441                            self.UI_image = self.Imaging_Free(plot_im.resize((nw,nh), Image.ANTIALIAS))
4442                except:
4443                    self.SCALE = 1
4444                    debug_message(traceback.format_exc())
4445
4446                self.Plot_Raster(self.laserX+.001, self.laserY-.001, x_lft,y_top,self.PlotScale,im=self.UI_image)
4447        else:
4448            self.UI_image = None
4449
4450
4451        ######################################
4452        ###       Plot Reng Coords         ###
4453        ######################################
4454        if self.include_Rpth.get() and self.RengData.ecoords!=[]:
4455            loop_old = -1
4456
4457            #####
4458            Xscale = 1/float(self.LaserXscale.get())
4459            Yscale = 1/float(self.LaserYscale.get())
4460            if self.rotary.get():
4461                Rscale = 1/float(self.LaserRscale.get())
4462                Yscale = Yscale*Rscale
4463            ######
4464
4465            for line in self.RengData.ecoords:
4466                XY    = line
4467                x1    = XY[0]*Xscale
4468                y1    = XY[1]*Yscale-ymax
4469                loop  = XY[2]
4470                color = "black"
4471                # check and see if we need to move to a new discontinuous start point
4472                if (loop == loop_old):
4473                    self.Plot_Line(xold, yold, x1, y1, x_lft, y_top, XlineShift, YlineShift, self.PlotScale, color)
4474                loop_old = loop
4475                xold=x1
4476                yold=y1
4477
4478
4479        ######################################
4480        ###       Plot Veng Coords         ###
4481        ######################################
4482        if self.include_Veng.get():
4483            loop_old = -1
4484
4485
4486            plot_coords = self.VengData.ecoords
4487            if self.mirror.get() or self.rotate.get():
4488                plot_coords = self.mirror_rotate_vector_coords(plot_coords)
4489
4490            for line in plot_coords:
4491                XY    = line
4492                x1    = (XY[0]-xmin)
4493                y1    = (XY[1]-ymax)
4494                loop  = XY[2]
4495                # check and see if we need to move to a new discontinuous start point
4496                if (loop == loop_old):
4497                    self.Plot_Line(xold, yold, x1, y1, x_lft, y_top, XlineShift, YlineShift, self.PlotScale, "blue")
4498                loop_old = loop
4499                xold=x1
4500                yold=y1
4501
4502        ######################################
4503        ###       Plot Vcut Coords         ###
4504        ######################################
4505        if self.include_Vcut.get():
4506            loop_old = -1
4507
4508            plot_coords = self.VcutData.ecoords
4509            if self.mirror.get() or self.rotate.get():
4510                    plot_coords = self.mirror_rotate_vector_coords(plot_coords)
4511
4512            for line in plot_coords:
4513                XY    = line
4514                x1    = (XY[0]-xmin)
4515                y1    = (XY[1]-ymax)
4516                loop  = XY[2]
4517                # check and see if we need to move to a new discontinuous start point
4518                if (loop == loop_old):
4519                    self.Plot_Line(xold, yold, x1, y1, x_lft, y_top, XlineShift, YlineShift, self.PlotScale, "red")
4520                loop_old = loop
4521                xold=x1
4522                yold=y1
4523
4524        ######################################
4525        ###       Plot Gcode Coords        ###
4526        ######################################
4527        if self.include_Gcde.get():
4528            loop_old = -1
4529            scale=1
4530
4531            plot_coords = self.GcodeData.ecoords
4532            if self.mirror.get() or self.rotate.get():
4533                    plot_coords = self.mirror_rotate_vector_coords(plot_coords)
4534
4535            for line in plot_coords:
4536                XY    = line
4537                x1    = (XY[0]-xmin)*scale
4538                y1    = (XY[1]-ymax)*scale
4539
4540                loop  = XY[2]
4541                # check and see if we need to move to a new discontinuous start point
4542                if (loop == loop_old):
4543                    self.Plot_Line(xold, yold, x1, y1, x_lft, y_top, XlineShift, YlineShift, self.PlotScale, "white")
4544                loop_old = loop
4545                xold=x1
4546                yold=y1
4547
4548
4549        ######################################
4550        ###       Plot Trace Coords        ###
4551        ######################################
4552        if self.trace_window.winfo_exists():  # or DEBUG:
4553            #####
4554            Xscale = 1/float(self.LaserXscale.get())
4555            Yscale = 1/float(self.LaserYscale.get())
4556            if self.rotary.get():
4557                Rscale = 1/float(self.LaserRscale.get())
4558                Yscale = Yscale*Rscale
4559            ######
4560            trace_coords = self.make_trace_path()
4561            for i in range(len(trace_coords)):
4562                trace_coords[i]=[trace_coords[i][0]*Xscale,trace_coords[i][1]*Yscale,trace_coords[i][2]]
4563
4564            for line in trace_coords:
4565                XY    = line
4566                x1    = (XY[0]-xmin)*scale
4567                y1    = (XY[1]-ymax)*scale
4568                loop  = XY[2]
4569                # check and see if we need to move to a new discontinuous start point
4570                if (loop == loop_old):
4571                    green = "#%02x%02x%02x" % (0, 200, 0)
4572                    self.Plot_Line(xold, yold, x1, y1, x_lft, y_top, XlineShift, YlineShift,
4573                                   self.PlotScale, green, thick=2,tag_value=('LaserTag', 'trace'))
4574                loop_old = loop
4575                xold=x1
4576                yold=y1
4577
4578
4579        ######################################
4580        self.refreshTime()
4581        dot_col = "grey50"
4582        xoff = self.pos_offset[0]/1000.0
4583        yoff = self.pos_offset[1]/1000.0
4584
4585        if abs(self.pos_offset[0])+abs(self.pos_offset[1]) > 0:
4586            head_offset=True
4587        else:
4588            head_offset=False
4589
4590        self.Plot_circle(self.laserX+xoff,self.laserY+yoff,x_lft,y_top,self.PlotScale,dot_col,radius=5,cross_hair=head_offset)
4591
4592    def Plot_Raster(self, XX, YY, Xleft, Ytop, PlotScale, im):
4593        if (self.HomeUR.get()):
4594            maxx = float(self.LaserXsize.get()) / self.units_scale
4595            xmin,xmax,ymin,ymax = self.Get_Design_Bounds()
4596            xplt = Xleft + ( maxx-XX-(xmax-xmin) )/PlotScale
4597        else:
4598            xplt = Xleft +  XX/PlotScale
4599
4600        yplt = Ytop  - YY/PlotScale
4601        self.segID.append(
4602            self.PreviewCanvas.create_image(xplt, yplt, anchor=NW, image=self.UI_image,tags='LaserTag')
4603            )
4604
4605
4606    def offset_eccords(self,ecoords_in,offset_val):
4607        if not PYCLIPPER:
4608            return ecoords_in
4609
4610        loop_num = ecoords_in[0][2]
4611        pco = pyclipper.PyclipperOffset()
4612        ecoords_out=[]
4613        pyclip_path = []
4614        for i in range(0,len(ecoords_in)):
4615            pyclip_path.append([ecoords_in[i][0]*1000,ecoords_in[i][1]*1000])
4616
4617        pco.AddPath(pyclip_path, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
4618        try:
4619            plot_coords = pco.Execute(offset_val*1000.0)[0]
4620            plot_coords.append(plot_coords[0])
4621        except:
4622            plot_coords=[]
4623
4624        for i in range(0,len(plot_coords)):
4625            ecoords_out.append([plot_coords[i][0]/1000.0,plot_coords[i][1]/1000.0,loop_num])
4626        return ecoords_out
4627
4628
4629    def Plot_circle(self, XX, YY, Xleft, Ytop, PlotScale, col, radius=0, cross_hair=False):
4630        circle_tags = ('LaserTag','LaserDot')
4631        if (self.HomeUR.get()):
4632            maxx = float(self.LaserXsize.get()) / self.units_scale
4633            xplt = Xleft + maxx/PlotScale - XX/PlotScale
4634        else:
4635            xplt = Xleft + XX/PlotScale
4636        yplt = Ytop  - YY/PlotScale
4637
4638
4639        if cross_hair:
4640            radius=radius*2
4641            leg = int(radius*.707)
4642            self.segID.append(
4643                self.PreviewCanvas.create_polygon(
4644                                                xplt-radius,
4645                                                yplt,
4646                                                xplt-leg,
4647                                                yplt+leg,
4648                                                xplt,
4649                                                yplt+radius,
4650                                                xplt+leg,
4651                                                yplt+leg,
4652                                                xplt+radius,
4653                                                yplt,
4654                                                xplt+leg,
4655                                                yplt-leg,
4656                                                xplt,
4657                                                yplt-radius,
4658                                                xplt-leg,
4659                                                yplt-leg,
4660                                                fill=col,  outline=col, width = 1, stipple='gray12',tags=circle_tags ))
4661
4662            self.segID.append(
4663                self.PreviewCanvas.create_line( xplt-radius,
4664                                                yplt,
4665                                                xplt+radius,
4666                                                yplt,
4667                                                fill=col, capstyle="round", width = 1, tags=circle_tags ))
4668            self.segID.append(
4669                self.PreviewCanvas.create_line( xplt,
4670                                                yplt-radius,
4671                                                xplt,
4672                                                yplt+radius,
4673                                                fill=col, capstyle="round", width = 1, tags=circle_tags ))
4674        else:
4675            self.segID.append(
4676                self.PreviewCanvas.create_oval(
4677                                                xplt-radius,
4678                                                yplt-radius,
4679                                                xplt+radius,
4680                                                yplt+radius,
4681                                                fill=col,  outline=col, width = 0, stipple='gray50',tags=circle_tags ))
4682
4683
4684    def Plot_Line(self, XX1, YY1, XX2, YY2, Xleft, Ytop, XlineShift, YlineShift, PlotScale, col, thick=0, tag_value='LaserTag'):
4685        xplt1 = Xleft + (XX1 + XlineShift )/PlotScale
4686        xplt2 = Xleft + (XX2 + XlineShift )/PlotScale
4687        yplt1 = Ytop  - (YY1 + YlineShift )/PlotScale
4688        yplt2 = Ytop  - (YY2 + YlineShift )/PlotScale
4689
4690        self.segID.append(
4691            self.PreviewCanvas.create_line( xplt1,
4692                                            yplt1,
4693                                            xplt2,
4694                                            yplt2,
4695                                            fill=col, capstyle="round", width = thick, tags=tag_value) )
4696
4697    ################################################################################
4698    #                         Temporary Move Window                                #
4699    ################################################################################
4700    def move_head_window_temporary(self,new_pos_offset):
4701        if self.GUI_Disabled:
4702            return
4703        dx_inches = round(new_pos_offset[0]/1000.0,3)
4704        dy_inches = round(new_pos_offset[1]/1000.0,3)
4705        Xnew,Ynew = self.XY_in_bounds(dx_inches,dy_inches,no_size=True)
4706
4707        pos_offset_X = round((Xnew-self.laserX)*1000.0)
4708        pos_offset_Y = round((Ynew-self.laserY)*1000.0)
4709        new_pos_offset = [pos_offset_X,pos_offset_Y]
4710
4711        if self.inputCSYS.get() and self.RengData.image == None:
4712            new_pos_offset = [0,0]
4713            xdist = -self.pos_offset[0]
4714            ydist = -self.pos_offset[1]
4715        else:
4716            xdist = -self.pos_offset[0] + new_pos_offset[0]
4717            ydist = -self.pos_offset[1] + new_pos_offset[1]
4718
4719        if self.k40 != None:
4720            if self.Send_Rapid_Move( xdist,ydist ):
4721                self.pos_offset = new_pos_offset
4722                self.menu_View_Refresh()
4723        else:
4724            self.pos_offset = new_pos_offset
4725            self.menu_View_Refresh()
4726
4727    ################################################################################
4728    #                         General Settings Window                              #
4729    ################################################################################
4730    def GEN_Settings_Window(self):
4731        gen_width = 560
4732        gen_settings = Toplevel(width=gen_width, height=560) #460+75)
4733        gen_settings.grab_set() # Use grab_set to prevent user input in the main window
4734        gen_settings.focus_set()
4735        gen_settings.resizable(0,0)
4736        gen_settings.title('General Settings')
4737        gen_settings.iconname("General Settings")
4738
4739        D_Yloc  = 6
4740        D_dY = 26
4741        xd_label_L = 12
4742
4743        w_label=150
4744        w_entry=40
4745        w_units=45
4746        xd_entry_L=xd_label_L+w_label+10
4747        xd_units_L=xd_entry_L+w_entry+5
4748        sep_border=10
4749
4750        #Radio Button
4751        D_Yloc=D_Yloc+D_dY
4752        self.Label_Units = Label(gen_settings,text="Units")
4753        self.Label_Units.place(x=xd_label_L, y=D_Yloc, width=113, height=21)
4754        self.Radio_Units_IN = Radiobutton(gen_settings,text="inch", value="in",
4755                                         width="100", anchor=W)
4756        self.Radio_Units_IN.place(x=w_label+22, y=D_Yloc, width=75, height=23)
4757        self.Radio_Units_IN.configure(variable=self.units, command=self.Entry_units_var_Callback )
4758        self.Radio_Units_MM = Radiobutton(gen_settings,text="mm", value="mm",
4759                                         width="100", anchor=W)
4760        self.Radio_Units_MM.place(x=w_label+110, y=D_Yloc, width=75, height=23)
4761        self.Radio_Units_MM.configure(variable=self.units, command=self.Entry_units_var_Callback )
4762
4763        D_Yloc=D_Yloc+D_dY
4764        self.Label_init_home = Label(gen_settings,text="Home Upon Initialize")
4765        self.Label_init_home.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4766        self.Checkbutton_init_home = Checkbutton(gen_settings,text="", anchor=W)
4767        self.Checkbutton_init_home.place(x=xd_entry_L, y=D_Yloc, width=75, height=23)
4768        self.Checkbutton_init_home.configure(variable=self.init_home)
4769
4770
4771        D_Yloc=D_Yloc+D_dY
4772        self.Label_post_home = Label(gen_settings,text="After Job Finishes:")
4773        self.Label_post_home.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4774
4775        Xoption_width = 120
4776        Xoption_col1  = xd_entry_L
4777        Xoption_col2  = xd_entry_L+Xoption_width
4778        Xoption_col3  = xd_entry_L+Xoption_width*2
4779
4780        self.Checkbutton_post_home = Checkbutton(gen_settings,text="Unlock Rail", anchor=W)
4781        self.Checkbutton_post_home.place(x=Xoption_col1, y=D_Yloc, width=Xoption_width, height=23)
4782        self.Checkbutton_post_home.configure(variable=self.post_home)
4783
4784        self.Checkbutton_post_beep = Checkbutton(gen_settings,text="Beep", anchor=W)
4785        self.Checkbutton_post_beep.place(x=Xoption_col2, y=D_Yloc, width=Xoption_width, height=23)
4786        self.Checkbutton_post_beep.configure(variable=self.post_beep)
4787
4788        D_Yloc=D_Yloc+D_dY
4789        self.Checkbutton_post_disp = Checkbutton(gen_settings,text="Popup Report", anchor=W)
4790        self.Checkbutton_post_disp.place(x=Xoption_col1, y=D_Yloc, width=Xoption_width, height=23)
4791        self.Checkbutton_post_disp.configure(variable=self.post_disp)
4792
4793        self.Checkbutton_post_exec = Checkbutton(gen_settings,text="Run Batch File:", anchor=W, command=self.Set_Input_States_BATCH)
4794        self.Checkbutton_post_exec.place(x=Xoption_col2, y=D_Yloc, width=Xoption_width, height=23)
4795        self.Checkbutton_post_exec.configure(variable=self.post_exec)
4796
4797
4798        self.Entry_Batch_Path = Entry(gen_settings)
4799        self.Entry_Batch_Path.place(x=Xoption_col3, y=D_Yloc, width=Xoption_width, height=23)
4800        self.Entry_Batch_Path.configure(textvariable=self.batch_path)
4801
4802
4803        D_Yloc=D_Yloc+D_dY
4804        self.Label_Preprocess_CRC = Label(gen_settings,text="Preprocess CRC Data")
4805        self.Label_Preprocess_CRC.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4806        self.Checkbutton_Preprocess_CRC = Checkbutton(gen_settings,text="", anchor=W)
4807        self.Checkbutton_Preprocess_CRC.place(x=xd_entry_L, y=D_Yloc, width=75, height=23)
4808        self.Checkbutton_Preprocess_CRC.configure(variable=self.pre_pr_crc)
4809
4810        #D_Yloc=D_Yloc+D_dY
4811        #self.Label_Timeout = Label(gen_settings,text="USB Timeout")
4812        #self.Label_Timeout.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4813        #self.Label_Timeout_u = Label(gen_settings,text="ms", anchor=W)
4814        #self.Label_Timeout_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
4815        #self.Entry_Timeout = Entry(gen_settings,width="15")
4816        #self.Entry_Timeout.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4817        #self.Entry_Timeout.configure(textvariable=self.t_timeout)
4818        #self.t_timeout.trace_variable("w", self.Entry_Timeout_Callback)
4819        #self.entry_set(self.Entry_Timeout,self.Entry_Timeout_Check(),2)
4820
4821        #D_Yloc=D_Yloc+D_dY
4822        #self.Label_N_Timeouts = Label(gen_settings,text="Number of Timeouts")
4823        #self.Label_N_Timeouts.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4824        #self.Entry_N_Timeouts = Entry(gen_settings,width="15")
4825        #self.Entry_N_Timeouts.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4826        #self.Entry_N_Timeouts.configure(textvariable=self.n_timeouts)
4827        #self.n_timeouts.trace_variable("w", self.Entry_N_Timeouts_Callback)
4828        #self.entry_set(self.Entry_N_Timeouts,self.Entry_N_Timeouts_Check(),2)
4829
4830        D_Yloc=D_Yloc+D_dY*1.25
4831        self.gen_separator1 = Frame(gen_settings, height=2, bd=1, relief=SUNKEN)
4832        self.gen_separator1.place(x=xd_label_L, y=D_Yloc,width=gen_width-40, height=2)
4833
4834        D_Yloc=D_Yloc+D_dY*.25
4835        self.Label_Inkscape_title = Label(gen_settings,text="Inkscape Options")
4836        self.Label_Inkscape_title.place(x=xd_label_L, y=D_Yloc, width=gen_width-40, height=21)
4837
4838        D_Yloc=D_Yloc+D_dY
4839        font_entry_width=215
4840        self.Label_Inkscape_Path = Label(gen_settings,text="Inkscape Executable")
4841        self.Label_Inkscape_Path.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4842        self.Entry_Inkscape_Path = Entry(gen_settings,width="15")
4843        self.Entry_Inkscape_Path.place(x=xd_entry_L, y=D_Yloc, width=font_entry_width, height=23)
4844        self.Entry_Inkscape_Path.configure(textvariable=self.inkscape_path)
4845        self.Entry_Inkscape_Path.bind('<FocusIn>', self.Inkscape_Path_Message)
4846        self.Inkscape_Path = Button(gen_settings,text="Find Inkscape")
4847        self.Inkscape_Path.place(x=xd_entry_L+font_entry_width+10, y=D_Yloc, width=110, height=23)
4848        self.Inkscape_Path.bind("<ButtonRelease-1>", self.Inkscape_Path_Click)
4849
4850        D_Yloc=D_Yloc+D_dY
4851        self.Label_Ink_Timeout = Label(gen_settings,text="Inkscape Timeout")
4852        self.Label_Ink_Timeout.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4853        self.Label_Ink_Timeout_u = Label(gen_settings,text="minutes", anchor=W)
4854        self.Label_Ink_Timeout_u.place(x=xd_units_L, y=D_Yloc, width=w_units*2, height=21)
4855        self.Entry_Ink_Timeout = Entry(gen_settings,width="15")
4856        self.Entry_Ink_Timeout.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4857        self.Entry_Ink_Timeout.configure(textvariable=self.ink_timeout)
4858        self.ink_timeout.trace_variable("w", self.Entry_Ink_Timeout_Callback)
4859        self.entry_set(self.Entry_Ink_Timeout,self.Entry_Ink_Timeout_Check(),2)
4860
4861        D_Yloc=D_Yloc+D_dY*1.25
4862        self.gen_separator2 = Frame(gen_settings, height=2, bd=1, relief=SUNKEN)
4863        self.gen_separator2.place(x=xd_label_L, y=D_Yloc,width=gen_width-40, height=2)
4864
4865        D_Yloc=D_Yloc+D_dY*.5
4866        self.Label_no_com = Label(gen_settings,text="Home in Upper Right")
4867        self.Label_no_com.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4868        self.Checkbutton_no_com = Checkbutton(gen_settings,text="", anchor=W)
4869        self.Checkbutton_no_com.place(x=xd_entry_L, y=D_Yloc, width=75, height=23)
4870        self.Checkbutton_no_com.configure(variable=self.HomeUR)
4871        self.HomeUR.trace_variable("w",self.menu_View_Refresh_Callback)
4872
4873        D_Yloc=D_Yloc+D_dY
4874        self.Label_Board_Name      = Label(gen_settings,text="Board Name", anchor=CENTER )
4875        self.Board_Name_OptionMenu = OptionMenu(gen_settings, self.board_name,
4876                                            "LASER-M2",
4877                                            "LASER-M1",
4878                                            "LASER-M",
4879                                            "LASER-B2",
4880                                            "LASER-B1",
4881                                            "LASER-B",
4882                                            "LASER-A")
4883        self.Label_Board_Name.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4884        self.Board_Name_OptionMenu.place(x=xd_entry_L, y=D_Yloc, width=w_entry*3, height=23)
4885
4886        D_Yloc=D_Yloc+D_dY
4887        self.Label_Laser_Area_Width = Label(gen_settings,text="Laser Area Width")
4888        self.Label_Laser_Area_Width.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4889        self.Label_Laser_Area_Width_u = Label(gen_settings,textvariable=self.units, anchor=W)
4890        self.Label_Laser_Area_Width_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
4891        self.Entry_Laser_Area_Width = Entry(gen_settings,width="15")
4892        self.Entry_Laser_Area_Width.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4893        self.Entry_Laser_Area_Width.configure(textvariable=self.LaserXsize)
4894        self.LaserXsize.trace_variable("w", self.Entry_Laser_Area_Width_Callback)
4895        self.entry_set(self.Entry_Laser_Area_Width,self.Entry_Laser_Area_Width_Check(),2)
4896
4897        D_Yloc=D_Yloc+D_dY
4898        self.Label_Laser_Area_Height = Label(gen_settings,text="Laser Area Height")
4899        self.Label_Laser_Area_Height.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4900        self.Label_Laser_Area_Height_u = Label(gen_settings,textvariable=self.units, anchor=W)
4901        self.Label_Laser_Area_Height_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
4902        self.Entry_Laser_Area_Height = Entry(gen_settings,width="15")
4903        self.Entry_Laser_Area_Height.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4904        self.Entry_Laser_Area_Height.configure(textvariable=self.LaserYsize)
4905        self.LaserYsize.trace_variable("w", self.Entry_Laser_Area_Height_Callback)
4906        self.entry_set(self.Entry_Laser_Area_Height,self.Entry_Laser_Area_Height_Check(),2)
4907
4908        D_Yloc=D_Yloc+D_dY
4909        self.Label_Laser_X_Scale = Label(gen_settings,text="X Scale Factor")
4910        self.Label_Laser_X_Scale.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4911        self.Entry_Laser_X_Scale = Entry(gen_settings,width="15")
4912        self.Entry_Laser_X_Scale.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4913        self.Entry_Laser_X_Scale.configure(textvariable=self.LaserXscale)
4914        self.LaserXscale.trace_variable("w", self.Entry_Laser_X_Scale_Callback)
4915        self.entry_set(self.Entry_Laser_X_Scale,self.Entry_Laser_X_Scale_Check(),2)
4916
4917        D_Yloc=D_Yloc+D_dY
4918        self.Label_Laser_Y_Scale = Label(gen_settings,text="Y Scale Factor")
4919        self.Label_Laser_Y_Scale.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4920        self.Entry_Laser_Y_Scale = Entry(gen_settings,width="15")
4921        self.Entry_Laser_Y_Scale.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4922        self.Entry_Laser_Y_Scale.configure(textvariable=self.LaserYscale)
4923        self.LaserYscale.trace_variable("w", self.Entry_Laser_Y_Scale_Callback)
4924        self.entry_set(self.Entry_Laser_Y_Scale,self.Entry_Laser_Y_Scale_Check(),2)
4925
4926        D_Yloc=D_Yloc+D_dY+10
4927        self.Label_SaveConfig = Label(gen_settings,text="Configuration File")
4928        self.Label_SaveConfig.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4929
4930        self.GEN_SaveConfig = Button(gen_settings,text="Save")
4931        self.GEN_SaveConfig.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=21, anchor="nw")
4932        self.GEN_SaveConfig.bind("<ButtonRelease-1>", self.Write_Config_File)
4933
4934        ## Buttons ##
4935        gen_settings.update_idletasks()
4936        Ybut=int(gen_settings.winfo_height())-30
4937        Xbut=int(gen_settings.winfo_width()/2)
4938
4939        self.GEN_Close = Button(gen_settings,text="Close")
4940        self.GEN_Close.place(x=Xbut, y=Ybut, width=130, height=30, anchor="center")
4941        self.GEN_Close.bind("<ButtonRelease-1>", self.Close_Current_Window_Click)
4942
4943        self.Set_Input_States_BATCH()
4944
4945    ################################################################################
4946    #                          Raster Settings Window                              #
4947    ################################################################################
4948    def RASTER_Settings_Window(self):
4949        Wset=425+280
4950        Hset=330 #260
4951        raster_settings = Toplevel(width=Wset, height=Hset)
4952        raster_settings.grab_set() # Use grab_set to prevent user input in the main window
4953        raster_settings.focus_set()
4954        raster_settings.resizable(0,0)
4955        raster_settings.title('Raster Settings')
4956        raster_settings.iconname("Raster Settings")
4957
4958        D_Yloc  = 6
4959        D_dY = 24
4960        xd_label_L = 12
4961
4962        w_label=155
4963        w_entry=60
4964        w_units=35
4965        xd_entry_L=xd_label_L+w_label+10
4966        xd_units_L=xd_entry_L+w_entry+5
4967
4968        D_Yloc=D_Yloc+D_dY
4969        self.Label_Rstep   = Label(raster_settings,text="Scanline Step", anchor=CENTER )
4970        self.Label_Rstep.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4971        self.Label_Rstep_u = Label(raster_settings,text="in", anchor=W)
4972        self.Label_Rstep_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
4973        self.Entry_Rstep   = Entry(raster_settings,width="15")
4974        self.Entry_Rstep.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
4975        self.Entry_Rstep.configure(textvariable=self.rast_step)
4976        self.rast_step.trace_variable("w", self.Entry_Rstep_Callback)
4977
4978        D_Yloc=D_Yloc+D_dY
4979        self.Label_EngraveUP = Label(raster_settings,text="Engrave Bottom Up")
4980        self.Checkbutton_EngraveUP = Checkbutton(raster_settings,text=" ", anchor=W)
4981        self.Checkbutton_EngraveUP.configure(variable=self.engraveUP)
4982        self.Label_EngraveUP.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4983        self.Checkbutton_EngraveUP.place(x=w_label+22, y=D_Yloc, width=75, height=23)
4984
4985        D_Yloc=D_Yloc+D_dY
4986        self.Label_Halftone = Label(raster_settings,text="Halftone (Dither)")
4987        self.Label_Halftone.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
4988        self.Checkbutton_Halftone = Checkbutton(raster_settings,text=" ", anchor=W, command=self.Set_Input_States_RASTER)
4989        self.Checkbutton_Halftone.place(x=w_label+22, y=D_Yloc, width=75, height=23)
4990        self.Checkbutton_Halftone.configure(variable=self.halftone)
4991        self.halftone.trace_variable("w", self.menu_View_Refresh_Callback)
4992
4993        ############
4994        D_Yloc=D_Yloc+D_dY
4995        self.Label_Halftone_DPI      = Label(raster_settings,text="Halftone Resolution", anchor=CENTER )
4996        self.Halftone_DPI_OptionMenu = OptionMenu(raster_settings, self.ht_size,
4997                                            "1000",
4998                                            "500",
4999                                            "333",
5000                                            "250",
5001                                            "200",
5002                                            "167",
5003                                            "143",
5004                                            "125")
5005        self.Label_Halftone_DPI.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5006        self.Halftone_DPI_OptionMenu.place(x=xd_entry_L, y=D_Yloc, width=w_entry+30, height=23)
5007
5008
5009        self.Label_Halftone_u = Label(raster_settings,text="dpi", anchor=W)
5010        self.Label_Halftone_u.place(x=xd_units_L+30, y=D_Yloc, width=w_units, height=21)
5011
5012        ############
5013        D_Yloc=D_Yloc+D_dY+5
5014        self.Label_bezier_M1  = Label(raster_settings,
5015                                text="Slope, Black (%.1f)"%(self.bezier_M1_default),
5016                                anchor=CENTER )
5017        self.bezier_M1_Slider = Scale(raster_settings, from_=1, to=50, resolution=0.1, \
5018                                orient=HORIZONTAL, variable=self.bezier_M1)
5019        self.bezier_M1_Slider.place(x=xd_entry_L, y=D_Yloc, width=(Wset-xd_entry_L-25-280 ))
5020        D_Yloc=D_Yloc+21
5021        self.Label_bezier_M1.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5022        self.bezier_M1.trace_variable("w", self.bezier_M1_Callback)
5023
5024        D_Yloc=D_Yloc+D_dY-8
5025        self.Label_bezier_M2  = Label(raster_settings,
5026                                text="Slope, White (%.2f)"%(self.bezier_M2_default),
5027                                anchor=CENTER )
5028        self.bezier_M2_Slider = Scale(raster_settings, from_=0.0, to=1, \
5029                                orient=HORIZONTAL,resolution=0.01, variable=self.bezier_M2)
5030        self.bezier_M2_Slider.place(x=xd_entry_L, y=D_Yloc, width=(Wset-xd_entry_L-25-280 ))
5031        D_Yloc=D_Yloc+21
5032        self.Label_bezier_M2.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5033        self.bezier_M2.trace_variable("w", self.bezier_M2_Callback)
5034
5035        D_Yloc=D_Yloc+D_dY-8
5036        self.Label_bezier_weight   = Label(raster_settings,
5037                                     text="Transition (%.1f)"%(self.bezier_M1_default),
5038                                     anchor=CENTER )
5039        self.bezier_weight_Slider = Scale(raster_settings, from_=0, to=10, resolution=0.1, \
5040                                    orient=HORIZONTAL, variable=self.bezier_weight)
5041        self.bezier_weight_Slider.place(x=xd_entry_L, y=D_Yloc, width=(Wset-xd_entry_L-25-280 ))
5042        D_Yloc=D_Yloc+21
5043        self.Label_bezier_weight.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5044        self.bezier_weight.trace_variable("w", self.bezier_weight_Callback)
5045
5046##        show_unsharp = False
5047##        if DEBUG and show_unsharp:
5048##            D_Yloc=D_Yloc+D_dY
5049##            self.Label_UnsharpMask = Label(raster_settings,text="Unsharp Mask")
5050##            self.Label_UnsharpMask.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5051##            self.Checkbutton_UnsharpMask = Checkbutton(raster_settings,text=" ", anchor=W, command=self.Set_Input_States_Unsharp)
5052##            self.Checkbutton_UnsharpMask.place(x=w_label+22, y=D_Yloc, width=75, height=23)
5053##            self.Checkbutton_UnsharpMask.configure(variable=self.unsharp_flag)
5054##            self.unsharp_flag.trace_variable("w", self.menu_View_Refresh_Callback)
5055##
5056##            D_Yloc=D_Yloc+D_dY
5057##            self.Label_Unsharp_Radius   = Label(raster_settings,text="Unsharp Mask Radius", anchor=CENTER )
5058##            self.Label_Unsharp_Radius.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5059##            self.Label_Unsharp_Radius_u = Label(raster_settings,text="Pixels", anchor=W)
5060##            self.Label_Unsharp_Radius_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
5061##            self.Entry_Unsharp_Radius   = Entry(raster_settings,width="15")
5062##            self.Entry_Unsharp_Radius.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5063##            self.Entry_Unsharp_Radius.configure(textvariable=self.unsharp_r)
5064##            self.unsharp_r.trace_variable("w", self.Entry_Unsharp_Radius_Callback)
5065##
5066##            D_Yloc=D_Yloc+D_dY
5067##            self.Label_Unsharp_Percent   = Label(raster_settings,text="Unsharp Mask Percent", anchor=CENTER )
5068##            self.Label_Unsharp_Percent.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5069##            self.Label_Unsharp_Percent_u = Label(raster_settings,text="%", anchor=W)
5070##            self.Label_Unsharp_Percent_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
5071##            self.Entry_Unsharp_Percent   = Entry(raster_settings,width="15")
5072##            self.Entry_Unsharp_Percent.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5073##            self.Entry_Unsharp_Percent.configure(textvariable=self.unsharp_p)
5074##            self.unsharp_p.trace_variable("w", self.Entry_Unsharp_Percent_Callback)
5075##
5076##            D_Yloc=D_Yloc+D_dY
5077##            self.Label_Unsharp_Threshold   = Label(raster_settings,text="Unsharp Mask Threshold", anchor=CENTER )
5078##            self.Label_Unsharp_Threshold.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5079##            #self.Label_Unsharp_Threshold_u = Label(raster_settings,text="Pixels", anchor=W)
5080##            #self.Label_Unsharp_Threshold_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
5081##            self.Entry_Unsharp_Threshold   = Entry(raster_settings,width="15")
5082##            self.Entry_Unsharp_Threshold.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5083##            self.Entry_Unsharp_Threshold.configure(textvariable=self.unsharp_t)
5084##            self.unsharp_t.trace_variable("w", self.Entry_Unsharp_Threshold_Callback)
5085
5086        # Bezier Canvas
5087        self.Bezier_frame = Frame(raster_settings, bd=1, relief=SUNKEN)
5088        self.Bezier_frame.place(x=Wset-280, y=10, height=265, width=265)
5089        self.BezierCanvas = Canvas(self.Bezier_frame, background="white")
5090        self.BezierCanvas.pack(side=LEFT, fill=BOTH, expand=1)
5091        self.BezierCanvas.create_line( 5,260-0,260,260-255,fill="grey75", capstyle="round", width = 2, tags='perm')
5092
5093
5094        M1 = self.bezier_M1_default
5095        M2 = self.bezier_M2_default
5096        w  = self.bezier_weight_default
5097        num = 10
5098        x,y = self.generate_bezier(M1,M2,w,n=num)
5099        for i in range(0,num):
5100            self.BezierCanvas.create_line( 5+x[i],260-y[i],5+x[i+1],260-y[i+1],fill="grey85", stipple='gray25',\
5101                                           capstyle="round", width = 2, tags='perm')
5102
5103
5104        ## Buttons ##
5105        raster_settings.update_idletasks()
5106        Ybut=int(raster_settings.winfo_height())-30
5107        Xbut=int(raster_settings.winfo_width()/2)
5108
5109        self.RASTER_Close = Button(raster_settings,text="Close")
5110        self.RASTER_Close.place(x=Xbut, y=Ybut, width=130, height=30, anchor="center")
5111        self.RASTER_Close.bind("<ButtonRelease-1>", self.Close_Current_Window_Click)
5112
5113        self.bezier_M1_Callback()
5114        self.Set_Input_States_RASTER()
5115        #if DEBUG and show_unsharp:
5116        #    self.Set_Input_States_Unsharp()
5117
5118
5119    ################################################################################
5120    #                         Rotary Settings Window                               #
5121    ################################################################################
5122    def ROTARY_Settings_Window(self):
5123        rotary_settings = Toplevel(width=350, height=175)
5124        rotary_settings.grab_set() # Use grab_set to prevent user input in the main window
5125        rotary_settings.focus_set()
5126        rotary_settings.resizable(0,0)
5127        rotary_settings.title('Rotary Settings')
5128        rotary_settings.iconname("Rotary Settings")
5129
5130        D_Yloc  = 6
5131        D_dY = 30
5132        xd_label_L = 12
5133
5134        w_label=180
5135        w_entry=40
5136        w_units=45
5137        xd_entry_L=xd_label_L+w_label+10
5138        xd_units_L=xd_entry_L+w_entry+5
5139        sep_border=10
5140
5141
5142        D_Yloc=D_Yloc+D_dY-15
5143        self.Label_Rotary_Enable = Label(rotary_settings,text="Use Rotary Settings")
5144        self.Label_Rotary_Enable.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5145        self.Checkbutton_Rotary_Enable = Checkbutton(rotary_settings,text="", anchor=W, command=self.Set_Input_States_Rotary)
5146        self.Checkbutton_Rotary_Enable.place(x=xd_entry_L, y=D_Yloc, width=75, height=23)
5147        self.Checkbutton_Rotary_Enable.configure(variable=self.rotary)
5148
5149        D_Yloc=D_Yloc+D_dY
5150        self.Label_Laser_R_Scale = Label(rotary_settings,text="Rotary Scale Factor (Y axis)")
5151        self.Label_Laser_R_Scale.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5152        self.Entry_Laser_R_Scale = Entry(rotary_settings,width="15")
5153        self.Entry_Laser_R_Scale.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5154        self.Entry_Laser_R_Scale.configure(textvariable=self.LaserRscale)
5155        self.LaserRscale.trace_variable("w", self.Entry_Laser_R_Scale_Callback)
5156        self.entry_set(self.Entry_Laser_R_Scale,self.Entry_Laser_R_Scale_Check(),2)
5157
5158        D_Yloc=D_Yloc+D_dY
5159        self.Label_Laser_Rapid_Feed = Label(rotary_settings,text="Rapid Speed (default=0)")
5160        self.Label_Laser_Rapid_Feed.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5161        self.Label_Laser_Rapid_Feed_u = Label(rotary_settings,textvariable=self.funits, anchor=W)
5162        self.Label_Laser_Rapid_Feed_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
5163        self.Entry_Laser_Rapid_Feed = Entry(rotary_settings,width="15")
5164        self.Entry_Laser_Rapid_Feed.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5165        self.Entry_Laser_Rapid_Feed.configure(textvariable=self.rapid_feed)
5166        self.rapid_feed.trace_variable("w", self.Entry_Laser_Rapid_Feed_Callback)
5167        self.entry_set(self.Entry_Laser_Rapid_Feed,self.Entry_Laser_Rapid_Feed_Check(),2)
5168
5169        ## Buttons ##
5170        rotary_settings.update_idletasks()
5171        Ybut=int(rotary_settings.winfo_height())-30
5172        Xbut=int(rotary_settings.winfo_width()/2)
5173
5174        self.GEN_Close = Button(rotary_settings,text="Close")
5175        self.GEN_Close.place(x=Xbut, y=Ybut, width=130, height=30, anchor="center")
5176        self.GEN_Close.bind("<ButtonRelease-1>", self.Close_Current_Window_Click)
5177
5178        self.Set_Input_States_Rotary()
5179
5180    ################################################################################
5181    #                            Trace Send Window                                 #
5182    ################################################################################
5183
5184    def TRACE_Settings_Window(self, dummy=None):
5185        if self.GUI_Disabled:
5186            return
5187        trace_window = Toplevel(width=350, height=180)
5188        self.trace_window=trace_window
5189        trace_window.grab_set() # Use grab_set to prevent user input in the main window during calculations
5190        trace_window.resizable(0,0)
5191        trace_window.title('Trace Boundary')
5192        trace_window.iconname("Trace Boundary")
5193
5194        def Close_Click():
5195            win_id=self.grab_current()
5196            self.PreviewCanvas.delete('trace')
5197            win_id.destroy()
5198
5199        def Close_and_Send_Click():
5200            win_id=self.grab_current()
5201            self.PreviewCanvas.delete('trace')
5202            win_id.destroy()
5203            self.Trace_Eng()
5204
5205        D_Yloc  = 0
5206        D_dY = 28
5207        xd_label_L = 12
5208
5209        w_label=225
5210        w_entry=40
5211        w_units=50
5212        xd_entry_L=xd_label_L+w_label+10
5213        xd_units_L=xd_entry_L+w_entry+5
5214
5215        D_Yloc=D_Yloc+D_dY
5216        self.Label_Laser_Trace = Label(trace_window,text="Laser 'On' During Trace")
5217        self.Label_Laser_Trace.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5218        self.Checkbutton_Laser_Trace = Checkbutton(trace_window,text="", anchor=W)
5219        self.Checkbutton_Laser_Trace.place(x=xd_entry_L, y=D_Yloc, width=75, height=23)
5220        self.Checkbutton_Laser_Trace.configure(variable=self.trace_w_laser)
5221
5222        D_Yloc=D_Yloc+D_dY
5223        self.Label_Trace_Gap = Label(trace_window,text="Gap Between Design and Trace")
5224        self.Label_Trace_Gap.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5225        self.Entry_Trace_Gap = Entry(trace_window,width="15")
5226        self.Entry_Trace_Gap.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5227        self.Label_Trace_Gap_u = Label(trace_window,textvariable=self.units, anchor=W)
5228        self.Label_Trace_Gap_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
5229        self.Entry_Trace_Gap.configure(textvariable=self.trace_gap,justify='center')
5230        self.trace_gap.trace_variable("w", self.Entry_Trace_Gap_Callback)
5231        self.entry_set(self.Entry_Trace_Gap,self.Entry_Trace_Gap_Check(),2)
5232        if not PYCLIPPER:
5233            self.Label_Trace_Gap.configure(state="disabled")
5234            self.Label_Trace_Gap_u.configure(state="disabled")
5235            self.Entry_Trace_Gap.configure(state="disabled")
5236
5237        D_Yloc=D_Yloc+D_dY
5238        self.Trace_Button = Button(trace_window,text="Trace Boundary With Laser Head",command=Close_and_Send_Click)
5239        self.Trace_Button.place(x=xd_label_L, y=D_Yloc, width=w_label, height=23)
5240
5241        self.Entry_Trace_Speed = Entry(trace_window,width="15")
5242        self.Entry_Trace_Speed.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5243        green = "#%02x%02x%02x" % (0, 200, 0)
5244        self.Entry_Trace_Speed.configure(textvariable=self.trace_speed,justify='center',fg=green)
5245        self.trace_speed.trace_variable("w", self.Entry_Trace_Speed_Callback)
5246        self.entry_set(self.Entry_Trace_Speed,self.Entry_Trace_Speed_Check(),2)
5247        self.Label_Trace_Speed_u = Label(trace_window,textvariable=self.funits, anchor=W)
5248        self.Label_Trace_Speed_u.place(x=xd_units_L, y=D_Yloc, width=w_units, height=21)
5249
5250
5251        ## Buttons ##
5252        trace_window.update_idletasks()
5253        Ybut=int(trace_window.winfo_height())-30
5254        Xbut=int(trace_window.winfo_width()/2)
5255
5256        self.Trace_Close = Button(trace_window,text="Cancel",command=Close_Click)
5257        self.Trace_Close.place(x=Xbut, y=Ybut, width=130, height=30, anchor="center")
5258        ################################################################################
5259
5260    ################################################################################
5261    #                            EGV Send Window                                   #
5262    ################################################################################
5263    def EGV_Send_Window(self,EGV_filename):
5264
5265        egv_send = Toplevel(width=400, height=180)
5266        egv_send.grab_set() # Use grab_set to prevent user input in the main window during calculations
5267        egv_send.resizable(0,0)
5268        egv_send.title('EGV Send')
5269        egv_send.iconname("EGV Send")
5270
5271        D_Yloc  = 0
5272        D_dY = 28
5273        xd_label_L = 12
5274
5275        w_label=150
5276        w_entry=40
5277        w_units=35
5278        xd_entry_L=xd_label_L+w_label+10
5279        xd_units_L=xd_entry_L+w_entry+5
5280
5281        D_Yloc=D_Yloc+D_dY
5282        self.Label_Preprocess_CRC = Label(egv_send,text="Preprocess CRC Data")
5283        self.Label_Preprocess_CRC.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5284        self.Checkbutton_Preprocess_CRC = Checkbutton(egv_send,text="", anchor=W)
5285        self.Checkbutton_Preprocess_CRC.place(x=xd_entry_L, y=D_Yloc, width=75, height=23)
5286        self.Checkbutton_Preprocess_CRC.configure(variable=self.pre_pr_crc)
5287
5288        D_Yloc=D_Yloc+D_dY
5289        self.Label_N_EGV_Passes = Label(egv_send,text="Number of EGV Passes")
5290        self.Label_N_EGV_Passes.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5291        self.Entry_N_EGV_Passes = Entry(egv_send,width="15")
5292        self.Entry_N_EGV_Passes.place(x=xd_entry_L, y=D_Yloc, width=w_entry, height=23)
5293        self.Entry_N_EGV_Passes.configure(textvariable=self.n_egv_passes)
5294        self.n_egv_passes.trace_variable("w", self.Entry_N_EGV_Passes_Callback)
5295        self.entry_set(self.Entry_N_EGV_Passes,self.Entry_N_EGV_Passes_Check(),2)
5296
5297        D_Yloc=D_Yloc+D_dY
5298        font_entry_width=215
5299        self.Label_Inkscape_Path = Label(egv_send,text="EGV File:")
5300        self.Label_Inkscape_Path.place(x=xd_label_L, y=D_Yloc, width=w_label, height=21)
5301
5302        EGV_Name = os.path.basename(EGV_filename)
5303        self.Label_Inkscape_Path = Label(egv_send,text=EGV_Name,anchor="w") #,bg="yellow")
5304        self.Label_Inkscape_Path.place(x=xd_entry_L, y=D_Yloc, width=200, height=21,anchor="nw")
5305
5306        ## Buttons ##
5307        egv_send.update_idletasks()
5308        Ybut=int(egv_send.winfo_height())-30
5309        Xbut=int(egv_send.winfo_width()/2)
5310
5311        self.EGV_Close = Button(egv_send,text="Cancel")
5312        self.EGV_Close.place(x=Xbut, y=Ybut, width=130, height=30, anchor="e")
5313        self.EGV_Close.bind("<ButtonRelease-1>", self.Close_Current_Window_Click)
5314
5315        def Close_and_Send_Click():
5316            win_id=self.grab_current()
5317            win_id.destroy()
5318            self.Open_EGV(EGV_filename, n_passes=int( float(self.n_egv_passes.get()) ))
5319
5320        self.EGV_Send = Button(egv_send,text="Send EGV Data",command=Close_and_Send_Click)
5321        self.EGV_Send.place(x=Xbut, y=Ybut, width=130, height=30, anchor="w")
5322        ################################################################################
5323
5324
5325################################################################################
5326#             Function for outputting messages to different locations          #
5327#            depending on what options are enabled                             #
5328################################################################################
5329def fmessage(text,newline=True):
5330    global QUIET
5331    if (not QUIET):
5332        if newline==True:
5333            try:
5334                sys.stdout.write(text)
5335                sys.stdout.write("\n")
5336                debug_message(traceback.format_exc())
5337            except:
5338                debug_message(traceback.format_exc())
5339                pass
5340        else:
5341            try:
5342                sys.stdout.write(text)
5343                debug_message(traceback.format_exc())
5344            except:
5345                debug_message(traceback.format_exc())
5346                pass
5347
5348################################################################################
5349#                               Message Box                                    #
5350################################################################################
5351def message_box(title,message):
5352    title = "%s (K40 Whisperer V%s)" %(title,version)
5353    if VERSION == 3:
5354        tkinter.messagebox.showinfo(title,message)
5355    else:
5356        tkMessageBox.showinfo(title,message)
5357        pass
5358
5359################################################################################
5360#                          Message Box ask OK/Cancel                           #
5361################################################################################
5362def message_ask_ok_cancel(title, mess):
5363    if VERSION == 3:
5364        result=tkinter.messagebox.askokcancel(title, mess)
5365    else:
5366        result=tkMessageBox.askokcancel(title, mess)
5367    return result
5368
5369################################################################################
5370#                         Debug Message Box                                    #
5371################################################################################
5372def debug_message(message):
5373    global DEBUG
5374    title = "Debug Message"
5375    if DEBUG:
5376        if VERSION == 3:
5377            tkinter.messagebox.showinfo(title,message)
5378        else:
5379            tkMessageBox.showinfo(title,message)
5380            pass
5381
5382################################################################################
5383#                         Choose Units Dialog                                  #
5384################################################################################
5385if VERSION < 3:
5386    import tkSimpleDialog
5387else:
5388    import tkinter.simpledialog as tkSimpleDialog
5389
5390class UnitsDialog(tkSimpleDialog.Dialog):
5391    def body(self, master):
5392        self.resizable(0,0)
5393        self.title('Units')
5394        self.iconname("Units")
5395
5396        self.uom = StringVar()
5397        self.uom.set("Millimeters")
5398
5399        Label(master, text="Select DXF Import Units:").grid(row=0)
5400        Radio_Units_IN = Radiobutton(master,text="Inches",        value="Inches")
5401        Radio_Units_MM = Radiobutton(master,text="Millimeters",   value="Millimeters")
5402        Radio_Units_CM = Radiobutton(master,text="Centimeters",   value="Centimeters")
5403
5404        Radio_Units_IN.grid(row=1, sticky=W)
5405        Radio_Units_MM.grid(row=2, sticky=W)
5406        Radio_Units_CM.grid(row=3, sticky=W)
5407
5408        Radio_Units_IN.configure(variable=self.uom)
5409        Radio_Units_MM.configure(variable=self.uom)
5410        Radio_Units_CM.configure(variable=self.uom)
5411
5412    def apply(self):
5413        self.result = self.uom.get()
5414        return
5415
5416
5417class toplevel_dummy():
5418    def winfo_exists(self):
5419        return False
5420
5421class pxpiDialog(tkSimpleDialog.Dialog):
5422
5423    def __init__(self,
5424                 parent,
5425                 units = "mm",
5426                 SVG_Size            =None,
5427                 SVG_ViewBox         =None,
5428                 SVG_inkscape_version=None):
5429
5430        self.result = None
5431        self.svg_pxpi   = StringVar()
5432        self.other      = StringVar()
5433        self.svg_width  = StringVar()
5434        self.svg_height = StringVar()
5435        self.svg_units  = StringVar()
5436        self.fixed_size = False
5437        self.svg_units.set(units)
5438        if units=="mm":
5439            self.scale=1.0
5440        else:
5441            self.scale=1/25.4
5442
5443
5444        ###################################
5445        ##       Set initial pxpi          #
5446        ###################################
5447        pxpi = 72.0
5448        if SVG_inkscape_version != None:
5449            if SVG_inkscape_version >=.92:
5450                pxpi = 96.0
5451            else:
5452                pxpi = 90.0
5453
5454        self.svg_pxpi.set("%d"%(pxpi))
5455        self.other.set("%d"%(pxpi))
5456
5457        ###################################
5458        ##       Set minx/miny            #
5459        ###################################
5460        if SVG_ViewBox!=None and SVG_ViewBox[0]!=None and SVG_ViewBox[1]!=None:
5461            self.minx_pixels = SVG_ViewBox[0]
5462            self.miny_pixels = SVG_ViewBox[1]
5463        else:
5464            self.minx_pixels = 0.0
5465            self.miny_pixels = 0.0
5466
5467        ###################################
5468        ##       Set Initial Size         #
5469        ###################################
5470        if SVG_Size!=None and SVG_Size[2]!=None and SVG_Size[3]!=None:
5471            self.width_pixels = SVG_Size[2]
5472            self.height_pixels = SVG_Size[3]
5473        elif SVG_ViewBox!=None and SVG_ViewBox[2]!=None and SVG_ViewBox[3]!=None:
5474            self.width_pixels = SVG_ViewBox[2]
5475            self.height_pixels = SVG_ViewBox[3]
5476        else:
5477            self.width_pixels  = 500.0
5478            self.height_pixels = 500.0
5479        ###################################
5480        ##       Set Initial Size         #
5481        ###################################
5482        if SVG_Size[0]!=None and SVG_Size[1]!=None:
5483            width  = SVG_Size[0]
5484            height = SVG_Size[1]
5485            self.fixed_size=True
5486        else:
5487            width  = self.width_pixels/float(self.svg_pxpi.get())*25.4
5488            height = self.height_pixels/float(self.svg_pxpi.get())*25.4
5489
5490        self.svg_width.set("%f" %(width*self.scale))
5491        self.svg_height.set("%f" %(height*self.scale))
5492        ###################################
5493        tkSimpleDialog.Dialog.__init__(self, parent)
5494
5495
5496    def body(self, master):
5497        self.resizable(0,0)
5498        self.title('SVG Import Scale:')
5499        self.iconname("SVG Scale")
5500
5501        ###########################################################################
5502        def Entry_custom_Check():
5503            try:
5504                value = float(self.other.get())
5505                if  value <= 0.0:
5506                    return 2 # Value is invalid number
5507            except:
5508                return 3     # Value not a number
5509            return 0         # Value is a valid number
5510        def Entry_custom_Callback(varName, index, mode):
5511            if Entry_custom_Check() > 0:
5512                Entry_Custom_pxpi.configure( bg = 'red' )
5513            else:
5514                Entry_Custom_pxpi.configure( bg = 'white' )
5515                pxpi = float(self.other.get())
5516                width  = self.width_pixels/pxpi*25.4
5517                height = self.height_pixels/pxpi*25.4
5518                if self.fixed_size:
5519                    pass
5520                else:
5521                    Set_Value(width=width*self.scale,height=height*self.scale)
5522                self.svg_pxpi.set("custom")
5523        ###################################################
5524        def Entry_Width_Check():
5525            try:
5526                value = float(self.svg_width.get())/self.scale
5527                if  value <= 0.0:
5528                    return 2 # Value is invalid number
5529            except:
5530                return 3     # Value not a number
5531            return 0         # Value is a valid number
5532        def Entry_Width_Callback(varName, index, mode):
5533            if Entry_Width_Check() > 0:
5534                Entry_Custom_Width.configure( bg = 'red' )
5535            else:
5536                Entry_Custom_Width.configure( bg = 'white' )
5537                width = float(self.svg_width.get())/self.scale
5538                pxpi = self.width_pixels*25.4/width
5539                height = self.height_pixels/pxpi*25.4
5540                Set_Value(other=pxpi,height=height*self.scale)
5541                self.svg_pxpi.set("custom")
5542        ###################################################
5543        def Entry_Height_Check():
5544            try:
5545                value = float(self.svg_height.get())
5546                if  value <= 0.0:
5547                    return 2 # Value is invalid number
5548            except:
5549                return 3     # Value not a number
5550            return 0         # Value is a valid number
5551        def Entry_Height_Callback(varName, index, mode):
5552            if Entry_Height_Check() > 0:
5553                Entry_Custom_Height.configure( bg = 'red' )
5554            else:
5555                Entry_Custom_Height.configure( bg = 'white' )
5556                height = float(self.svg_height.get())/self.scale
5557                pxpi = self.height_pixels*25.4/height
5558                width = self.width_pixels/pxpi*25.4
5559                Set_Value(other=pxpi,width=width*self.scale)
5560                self.svg_pxpi.set("custom")
5561        ###################################################
5562        def SVG_pxpi_callback(varName, index, mode):
5563            if self.svg_pxpi.get() == "custom":
5564                try:
5565                    pxpi=float(self.other.get())
5566                except:
5567                    pass
5568            else:
5569                pxpi=float(self.svg_pxpi.get())
5570                width  = self.width_pixels/pxpi*25.4
5571                height = self.height_pixels/pxpi*25.4
5572                if self.fixed_size:
5573                    Set_Value(other=pxpi)
5574                else:
5575                    Set_Value(other=pxpi,width=width*self.scale,height=height*self.scale)
5576
5577        ###########################################################################
5578
5579        def Set_Value(other=None,width=None,height=None):
5580            self.svg_pxpi.trace_vdelete("w",self.trace_id_svg_pxpi)
5581            self.other.trace_vdelete("w",self.trace_id_pxpi)
5582            self.svg_width.trace_vdelete("w",self.trace_id_width)
5583            self.svg_height.trace_vdelete("w",self.trace_id_height)
5584            self.update_idletasks()
5585
5586            if other != None:
5587                self.other.set("%f" %(other))
5588            if width != None:
5589                self.svg_width.set("%f" %(width))
5590            if height != None:
5591                self.svg_height.set("%f" %(height))
5592
5593            self.trace_id_svg_pxpi = self.svg_pxpi.trace_variable("w", SVG_pxpi_callback)
5594            self.trace_id_pxpi     = self.other.trace_variable("w", Entry_custom_Callback)
5595            self.trace_id_width   = self.svg_width.trace_variable("w", Entry_Width_Callback)
5596            self.trace_id_height  = self.svg_height.trace_variable("w", Entry_Height_Callback)
5597            self.update_idletasks()
5598
5599        ###########################################################################
5600        t0="This dialog opens if the SVG file you are opening\n"
5601        t1="does not contain enough information to determine\n"
5602        t2="the intended physical size of the design.\n"
5603        t3="Select an SVG Import Scale:\n"
5604        Title_Text0 = Label(master, text=t0+t1+t2, anchor=W)
5605        Title_Text1 = Label(master, text=t3, anchor=W)
5606
5607        Radio_SVG_pxpi_96   = Radiobutton(master,text=" 96 units/in", value="96")
5608        Label_SVG_pxpi_96   = Label(master,text="(File saved with Inkscape v0.92 or newer)", anchor=W)
5609
5610        Radio_SVG_pxpi_90   = Radiobutton(master,text=" 90 units/in", value="90")
5611        Label_SVG_pxpi_90   = Label(master,text="(File saved with Inkscape v0.91 or older)", anchor=W)
5612
5613        Radio_SVG_pxpi_72   = Radiobutton(master,text=" 72 units/in", value="72")
5614        Label_SVG_pxpi_72   = Label(master,text="(File saved with Adobe Illustrator)", anchor=W)
5615
5616        Radio_Res_Custom = Radiobutton(master,text=" Custom:", value="custom")
5617        Bottom_row       = Label(master, text=" ")
5618
5619
5620        Entry_Custom_pxpi   = Entry(master,width="10")
5621        Entry_Custom_pxpi.configure(textvariable=self.other)
5622        Label_pxpi_units =  Label(master,text="units/in", anchor=W)
5623        self.trace_id_pxpi = self.other.trace_variable("w", Entry_custom_Callback)
5624
5625        Label_Width =  Label(master,text="Width", anchor=W)
5626        Entry_Custom_Width   = Entry(master,width="10")
5627        Entry_Custom_Width.configure(textvariable=self.svg_width)
5628        Label_Width_units =  Label(master,textvariable=self.svg_units, anchor=W)
5629        self.trace_id_width = self.svg_width.trace_variable("w", Entry_Width_Callback)
5630
5631        Label_Height =  Label(master,text="Height", anchor=W)
5632        Entry_Custom_Height   = Entry(master,width="10")
5633        Entry_Custom_Height.configure(textvariable=self.svg_height)
5634        Label_Height_units =  Label(master,textvariable=self.svg_units, anchor=W)
5635        self.trace_id_height = self.svg_height.trace_variable("w", Entry_Height_Callback)
5636
5637        if self.fixed_size == True:
5638             Entry_Custom_Width.configure(state="disabled")
5639             Entry_Custom_Height.configure(state="disabled")
5640        ###########################################################################
5641        rn=0
5642        Title_Text0.grid(row=rn,column=0,columnspan=5, sticky=W)
5643
5644        rn=rn+1
5645        Title_Text1.grid(row=rn,column=0,columnspan=5, sticky=W)
5646
5647        rn=rn+1
5648        Radio_SVG_pxpi_96.grid(    row=rn, sticky=W)
5649        Label_SVG_pxpi_96.grid(    row=rn, column=1,columnspan=50, sticky=W)
5650
5651        rn=rn+1
5652        Radio_SVG_pxpi_90.grid(    row=rn, sticky=W)
5653        Label_SVG_pxpi_90.grid(    row=rn, column=1,columnspan=50, sticky=W)
5654
5655        rn=rn+1
5656        Radio_SVG_pxpi_72.grid(    row=rn, column=0, sticky=W)
5657        Label_SVG_pxpi_72.grid(    row=rn, column=1,columnspan=50, sticky=W)
5658
5659        rn=rn+1
5660        Radio_Res_Custom.grid(    row=rn, column=0, sticky=W)
5661        Entry_Custom_pxpi.grid(    row=rn, column=1, sticky=E)
5662        Label_pxpi_units.grid(     row=rn, column=2, sticky=W)
5663
5664        rn=rn+1
5665        Label_Width.grid(         row=rn, column=0, sticky=E)
5666        Entry_Custom_Width.grid(  row=rn, column=1, sticky=E)
5667        Label_Width_units.grid(   row=rn, column=2, sticky=W)
5668
5669        rn=rn+1
5670        Label_Height.grid(        row=rn, column=0, sticky=E)
5671        Entry_Custom_Height.grid( row=rn, column=1, sticky=E)
5672        Label_Height_units.grid(  row=rn, column=2, sticky=W)
5673
5674        rn=rn+1
5675        Bottom_row.grid(row=rn,columnspan=50)
5676
5677        Radio_SVG_pxpi_96.configure  (variable=self.svg_pxpi)
5678        Radio_SVG_pxpi_90.configure  (variable=self.svg_pxpi)
5679        Radio_SVG_pxpi_72.configure  (variable=self.svg_pxpi)
5680        Radio_Res_Custom.configure  (variable=self.svg_pxpi)
5681        self.trace_id_svg_pxpi = self.svg_pxpi.trace_variable("w", SVG_pxpi_callback)
5682        ###########################################################################
5683
5684    def apply(self):
5685        width  = float(self.svg_width.get())/self.scale
5686        height = float(self.svg_height.get())/self.scale
5687        pxpi    = float(self.other.get())
5688        viewbox = [self.minx_pixels, self.miny_pixels, width/25.4*pxpi, height/25.4*pxpi]
5689        self.result = pxpi,viewbox
5690        return
5691
5692################################################################################
5693#                          Startup Application                                 #
5694################################################################################
5695
5696root = Tk()
5697app = Application(root)
5698app.master.title(title_text)
5699app.master.iconname("K40")
5700app.master.minsize(800,560)
5701app.master.geometry("800x560")
5702try:
5703    try:
5704        import tkFont
5705        default_font = tkFont.nametofont("TkDefaultFont")
5706    except:
5707        import tkinter.font
5708        default_font = tkinter.font.nametofont("TkDefaultFont")
5709
5710    default_font.configure(size=9)
5711    default_font.configure(family='arial')
5712    #print(default_font.cget("size"))
5713    #print(default_font.cget("family"))
5714except:
5715    debug_message("Font Set Failed.")
5716
5717################################## Set Icon  ########################################
5718Icon_Set=False
5719
5720try:
5721    debug_message("Icon set %s" %(sys.argv[0]))
5722    root.iconbitmap(default="emblem")
5723    debug_message("Icon set worked %s" %(sys.argv[0]))
5724    Icon_Set=True
5725except:
5726    debug_message(traceback.format_exc())
5727    Icon_Set=False
5728
5729if not Icon_Set:
5730    try:
5731        scorch_ico_B64=b'R0lGODlhEAAQAIYAAA\
5732        AAABAQEBYWFhcXFxsbGyUlJSYmJikpKSwsLC4uLi8vLzExMTMzMzc3Nzg4ODk5OTs7Oz4+PkJCQkRERE\
5733        VFRUtLS0xMTE5OTlNTU1dXV1xcXGBgYGVlZWhoaGtra3FxcXR0dHh4eICAgISEhI+Pj5mZmZ2dnaKioq\
5734        Ojo62tra6urrS0tLi4uLm5ub29vcLCwsbGxsjIyMzMzM/Pz9PT09XV1dbW1tjY2Nzc3OHh4eLi4uXl5e\
5735        fn5+jo6Ovr6+/v7/Hx8fLy8vT09PX19fn5+fv7+/z8/P7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
5736        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
5737        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
5738        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEKAEkALAAAAAAQABAAQAj/AJMIFBhBQYAACRIkWbgwAA\
5739        4kEFEECACAxBAkGH8ESEKgBZIiAIQECBAjAA8kNwIkScKgQhAkRggAIJACCZIaJxgk2clgAY4OAAoEAO\
5740        ABCIIDSZIwkIHEBw0YFAAA6IGDCBIkLAhMyICka9cAKZCIRTLEBIMkaA0MSNGjSBEVIgpESEK3LgMCI1\
5741        aAWCFDA4EDSQInwaDACBEAImLwCAFARw4HFJJcgGADyZEAL3YQcMGBBpIjHx4EeIGkRoMFJgakWADABx\
5742        IkPwIgcIGkdm0AMJDo1g3jQBIBRZAINyKAwxEkyHEUSMIcwYYbEgwYmQGgyI8SD5Jo327hgIIAAQ5cBs\
5743        CQpHySgAA7'
5744        icon_im =PhotoImage(data=scorch_ico_B64, format='gif')
5745        root.call('wm', 'iconphoto', root._w, '-default', icon_im)
5746    except:
5747        pass
5748#####################################################################################
5749
5750
5751if LOAD_MSG != "":
5752    message_box("K40 Whisperer",LOAD_MSG)
5753
5754opts, args = None, None
5755try:
5756    opts, args = getopt.getopt(sys.argv[1:], "hpd",["help", "pi", "debug"])
5757except:
5758    print('Unable interpret command line options')
5759    sys.exit()
5760
5761for option, value in opts:
5762    if option in ('-h','--help'):
5763        print(' ')
5764        print('Usage: python k40_whisperer.py [-h -p]')
5765        print('-h    : print this help (also --help)')
5766        print('-p    : Small screen option (for small raspberry pi display) (also --pi)')
5767        sys.exit()
5768    elif option in ('-p','--pi'):
5769        print("pi mode")
5770        app.master.minsize(480,320)
5771        app.master.geometry("480x320")
5772    elif option in ('-d','--debug'):
5773        DEBUG=True
5774
5775if DEBUG:
5776    import inspect
5777debug_message("Debuging is turned on.")
5778
5779root.mainloop()
5780