kopia lustrzana https://gitlab.com/markol/embroiderino
526 wiersze
24 KiB
Python
526 wiersze
24 KiB
Python
#!/usr/bin/python3
|
|
|
|
from tkinter import Tk, Label, Button, Entry, Menu, filedialog, messagebox, colorchooser, Canvas, Frame, LabelFrame, Scale, Toplevel, PhotoImage
|
|
from tkinter.font import Font
|
|
import tkinter.ttk as ttk
|
|
from tkinter import LEFT, TOP, BOTTOM, N, YES, W,SUNKEN,X, HORIZONTAL, DISABLED, NORMAL, RAISED, FLAT, RIDGE, END
|
|
from path_preview import ResizingCanvas, load_gcode_file, save_gcode_file, load_csv_file, translate_toolpath, rotate_toolpath, reflect_toolpath, scale_toolpath, toolpath_border_points, toolpath_info, _from_rgb
|
|
from collections import namedtuple
|
|
import copy, re, math, time, pickle
|
|
|
|
import control_serial as serial
|
|
|
|
class ControlAppGUI:
|
|
def __init__(self, master):
|
|
self.master = master
|
|
# GUI layout setup
|
|
self.menu = Menu(self.master)
|
|
|
|
self.master.config(menu=self.menu)
|
|
master.grid_rowconfigure(0, weight=1)
|
|
master.grid_columnconfigure(0, weight=1)
|
|
filemenu = Menu(self.menu)
|
|
self.menu.add_cascade(label="File", menu=filemenu)
|
|
filemenu.add_command(label="New", command=self.NewFile)
|
|
openmenu = Menu(self.menu)
|
|
openmenu.add_command(label="Gcode", command=self.OpenGcodeFile)
|
|
openmenu.add_command(label="CSV", command=self.OpenCsvFile)
|
|
savemenu = Menu(self.menu)
|
|
savemenu.add_command(label="Gcode", command=self.SaveGcodeFile)
|
|
savemenu.add_command(label="CSV", command=self.SaveCsvFile)
|
|
filemenu.add_cascade(label='Open...', menu=openmenu, underline=0)
|
|
filemenu.add_cascade(label="Save...", menu=savemenu, underline=0)
|
|
filemenu.add_command(label="Set color", command=self.AskColor)
|
|
filemenu.add_separator()
|
|
filemenu.add_command(label="Exit", command=self.Quit)
|
|
|
|
editmenu = Menu(self.menu)
|
|
self.menu.add_cascade(label="Edit", menu=editmenu)
|
|
editmenu.add_command(label="Settings", command=self.Settings)
|
|
|
|
helpmenu = Menu(self.menu)
|
|
self.menu.add_cascade(label="Help", menu=helpmenu)
|
|
helpmenu.add_command(label="About...", command=self.About)
|
|
master.title("Embroiderino frontend")
|
|
|
|
self.controls = ttk.Notebook(master)
|
|
tab1 = Frame(self.controls)
|
|
tab2 = Frame(self.controls)
|
|
self.controls.add(tab1, text = "Machine control")
|
|
self.controls.add(tab2, text = "Path manipulation")
|
|
self.controls.grid(row=0,column=1, sticky=N)
|
|
self.controls.grid_rowconfigure(0, weight=1)
|
|
self.controls.grid_columnconfigure(0, weight=1)
|
|
|
|
# MACHINE TAB
|
|
self.portCombo = ttk.Combobox(tab1, values=serial.serial_ports())
|
|
self.portCombo.current(0)
|
|
self.portCombo.grid(row=1,column=0)
|
|
self.baudCombo = ttk.Combobox(tab1,state='readonly', values=("115200", "9600"), width=10)
|
|
self.baudCombo.current(0)
|
|
self.baudCombo.grid(row=1,column=1)
|
|
self.connectButton = Button(tab1, text="Connect", command=self.ToggleConnect, width=10)
|
|
self.connectButton.grid(row=1,column=2)
|
|
|
|
self.startButton = Button(tab1, text="Start job", command=self.ToggleStart, state=DISABLED)
|
|
self.startButton.grid(row=2,column=1)
|
|
self.homeButton = Button(tab1, text="Home machine", command=lambda: serial.queue_command("G28\n"), state=DISABLED)
|
|
self.homeButton.grid(row=2,column=0)
|
|
|
|
testNavigation = Frame(tab1)
|
|
leftButton = Button(testNavigation, text="<", command=lambda: serial.queue_command("G91\nG0 Y-2\nG90\n"), state=DISABLED)
|
|
leftButton.grid(row=1,column=0)
|
|
rightButton = Button(testNavigation, text=">", command=lambda: serial.queue_command("G91\nG0 Y2\nG90\n"), state=DISABLED)
|
|
rightButton.grid(row=1,column=2)
|
|
upButton = Button(testNavigation, text="/\\", command=lambda: serial.queue_command("G91\nG0 X2\nG90\n"), state=DISABLED)
|
|
upButton.grid(row=0,column=1)
|
|
downButton = Button(testNavigation, text="\\/", command=lambda: serial.queue_command("G91\nG0 X-2\nG90\n"), state=DISABLED)
|
|
downButton.grid(row=2,column=1)
|
|
testNavigation.grid(row=3,column=0)
|
|
self.navigationButtons = [leftButton, rightButton, upButton, downButton]
|
|
|
|
self.testButton = Button(tab1, text="Test border path", command=self.TestBorder, state=DISABLED)
|
|
self.testButton.grid(row=3,column=1)
|
|
|
|
self.gotoButton = Button(tab1, text="Go to", command=self.GoTo, state=DISABLED, relief=RAISED)
|
|
self.gotoButton.grid(row=3,column=2)
|
|
|
|
self.stopButton = Button(tab1, text="STOP", command=self.StopAll, state=DISABLED)
|
|
self.stopButton.grid(row=4,column=0)
|
|
|
|
progressFrame = Frame(tab1)
|
|
Label(progressFrame, text="Tool changes: ", bd=1).grid(row=0,column=0)
|
|
self.toolChangesLabel = Label(progressFrame, text="0/0", bd=1, relief=SUNKEN)
|
|
self.toolChangesLabel.grid(row=1,column=0)
|
|
|
|
Label(progressFrame, text="Tool points: ", bd=1).grid(row=0,column=2)
|
|
self.toolPointsLabel = Label(progressFrame, text="0/0", bd=1, relief=SUNKEN)
|
|
self.toolPointsLabel.grid(row=1,column=2)
|
|
|
|
Label(progressFrame, text="Estimated endtime: ", bd=1).grid(row=0,column=4)
|
|
self.timeLabel = Label(progressFrame, text="0/0", bd=1, relief=SUNKEN)
|
|
self.timeLabel.grid(row=1,column=4)
|
|
progressFrame.grid(row=5,column=0, columnspan=3)
|
|
|
|
# PATH TAB
|
|
tab2.grid_columnconfigure(0, weight=1)
|
|
Label(tab2, text="Display progress: ", bd=1).grid(row=0)
|
|
self.slider = Scale(tab2, from_=0, to=0, command=self.UpdatePath, orient=HORIZONTAL,length=300)
|
|
self.slider.grid(row=1)
|
|
|
|
toolbar = Frame(tab2, bd=1, relief=RAISED)
|
|
toolbar.grid(row=2)
|
|
self.panButton = Button(toolbar, relief=RAISED, command=self.TogglePan, text="Move path")
|
|
self.panButton.pack(side=LEFT, padx=2, pady=2)
|
|
|
|
self.rotateButton = Button(toolbar, relief=RAISED, command=self.ToggleRotate, text="Rotate path")
|
|
self.rotateButton.pack(side=LEFT, padx=2, pady=2)
|
|
|
|
self.mirrorButton = Button(toolbar, relief=RAISED, command=self.ToggleMirror, text="Mirror path")
|
|
self.mirrorButton.pack(side=LEFT, padx=2, pady=2)
|
|
|
|
self.scaleButton = Button(toolbar, relief=RAISED, command=self.ToggleScale, text="Scale path")
|
|
self.scaleButton.pack(side=LEFT, padx=2, pady=2)
|
|
|
|
# CANVAS
|
|
canvasFrame = Frame(master)
|
|
canvasFrame.grid(row=0, column=0, sticky='NWES')
|
|
self.canvas = ResizingCanvas(canvasFrame,width=400, height=400, bg="white", highlightthickness=0)
|
|
self.canvas.bind("<B1-Motion>", self.CanvasDrag)
|
|
self.canvas.bind("<Button-1>", self.CanvasClick)
|
|
self.canvas.bind("<ButtonRelease-1>", self.CanvasRelease)
|
|
self.canvas.pack(expand=YES, anchor=N+W)
|
|
|
|
#STATUS BAR
|
|
self.status = Label(master, text="Not connected", bd=1, relief=SUNKEN, anchor=W)
|
|
self.status.grid(row=2, columnspan=2, sticky='WE')
|
|
|
|
# PROGRAM VARIABLES
|
|
self.SETTINGSFNAME = "settings.pickle"
|
|
self.commands = []
|
|
self.transform = (0,0)
|
|
self.isConnected = False
|
|
self.isJobRunning = False
|
|
self.isJobPaused = False
|
|
self.lastSendCommandIndex = -1
|
|
self.lastMove = None
|
|
self.currentColor = 'black'
|
|
self.currentToolChange = 0
|
|
self.toolChangesTotal = 0
|
|
self.currentToolPoint = 0
|
|
self.toolPointsTotal = 0
|
|
self.distancesList = []
|
|
self.distanceTraveled = 0
|
|
self.positionResponseRegex = re.compile("X:(\-?\d+\.\d+),Y:(\-?\d+\.\d+)")
|
|
self.workAreaSize = [100,100]
|
|
|
|
# LOAD SOME SETTIGS
|
|
self.loadSettings()
|
|
self.canvas.setArea(self.workAreaSize[0], self.workAreaSize[1])
|
|
|
|
# UI LOGIC
|
|
def Quit(self):
|
|
if messagebox.askyesno('Confirm', 'Really quit?'):
|
|
self.master.quit()
|
|
def AskColor(self):
|
|
color = colorchooser.askcolor(title = "Colour Chooser")
|
|
def NewFile(self):
|
|
if self.isJobRunning:
|
|
return
|
|
self.toolChangesTotal = 0
|
|
self.toolPointsTotal = 0
|
|
self.distancesList = []
|
|
self.lastSendCommandIndex = -1
|
|
self.lastMove = None
|
|
self.commands = []
|
|
self.canvas.clear()
|
|
self.slider.config(to=0)
|
|
def OpenGcodeFile(self):
|
|
if self.isJobRunning:
|
|
return
|
|
with filedialog.askopenfile(filetypes = (("Machine G-code","*.gcode"),)) as f:
|
|
self.commands = load_gcode_file(f)
|
|
self.FinishLoading()
|
|
def SaveGcodeFile(self):
|
|
if not self.commands:
|
|
return
|
|
with filedialog.asksaveasfile(filetypes = (("Machine G-code","*.gcode"),), defaultextension='.gcode') as f:
|
|
save_gcode_file(f, self.commands)
|
|
def OpenCsvFile(self):
|
|
if self.isJobRunning:
|
|
return
|
|
with filedialog.askopenfile(filetypes = (("Comma separated values","*.csv"),) ) as f:
|
|
self.commands = load_csv_file(f)
|
|
self.FinishLoading()
|
|
def SaveCsvFile(self):
|
|
pass
|
|
def FinishLoading(self):
|
|
points_count = len(self.commands)
|
|
# file loaded
|
|
if points_count > 2:
|
|
self.testButton.config(state=NORMAL)
|
|
self.startButton.config(state=NORMAL)
|
|
# center loaded path
|
|
rectangle = toolpath_border_points(self.commands[1:])
|
|
center = (rectangle[2][0] - (rectangle[2][0] - rectangle[0][0])/2, rectangle[2][1] - (rectangle[2][1] - rectangle[0][1])/2)
|
|
transform = (self.workAreaSize[0]/2 - center[0], self.workAreaSize[1]/2 - center[1])
|
|
self.commands = translate_toolpath(self.commands, transform)
|
|
|
|
self.slider.config(to=points_count)
|
|
self.slider.set(points_count)
|
|
self.toolPointsTotal, self.toolChangesTotal, self.distancesList = toolpath_info(self.commands)
|
|
self.toolPointsLabel.config(text="%d/%d" % (self.currentToolPoint, self.toolPointsTotal))
|
|
self.toolChangesLabel.config(text="%d/%d" % (self.currentToolChange, self.toolChangesTotal))
|
|
self.timeLabel.config(text="%d/%d" % (self.distancesList[self.currentToolChange]- self.distanceTraveled, self.distancesList[-1]-self.distanceTraveled))
|
|
self.canvas.draw_toolpath(self.commands)
|
|
|
|
def About(self):
|
|
#self.ToolChangePopup()
|
|
messagebox.showinfo('About this software', 'This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nWritten in 2018 by markol.')
|
|
def Settings(self):
|
|
tl = Toplevel(root)
|
|
tl.title("Global settings")
|
|
|
|
frame = Frame(tl)
|
|
frame.grid()
|
|
|
|
machineFrame = LabelFrame(frame, text="Machine hoop workarea (mm)", relief=RIDGE)
|
|
machineFrame.grid()
|
|
Label(machineFrame, text="width " ).grid(row=0, column=0, sticky=N)
|
|
workareaWidth = Entry(machineFrame, text="1")
|
|
workareaWidth.grid(row=0,column=1)
|
|
workareaWidth.delete(0, END)
|
|
workareaWidth.insert(0, str(self.workAreaSize[0]))
|
|
Label(machineFrame, text="height " ).grid(row=2, column=0, sticky=N)
|
|
workareaHeight = Entry(machineFrame, text="2")
|
|
workareaHeight.grid(row=2,column=1)
|
|
workareaHeight.delete(0, END)
|
|
workareaHeight.insert(0, str(self.workAreaSize[1]))
|
|
|
|
def saveSettings():
|
|
try:
|
|
self.workAreaSize = (int(workareaWidth.get()), int(workareaHeight.get()))
|
|
except:
|
|
messagebox.showerror("Invalid numeric values","Please provide correct workarea values!")
|
|
return
|
|
|
|
self.canvas.setArea(self.workAreaSize[0], self.workAreaSize[1])
|
|
self.storeSettings()
|
|
TmpDim = namedtuple('TmpDim', 'width height')
|
|
tmp = TmpDim(self.canvas.width, self.canvas.height)
|
|
self.canvas.on_resize(tmp)
|
|
|
|
Button(frame, text="Save", command=saveSettings, width=10).grid(row=2, column=3)
|
|
Button(frame, text="Cancel", command=lambda: tl.destroy(), width=10).grid(row=2, column=2)
|
|
|
|
def loadSettings(self):
|
|
try:
|
|
with open(self.SETTINGSFNAME, "rb") as f:
|
|
data = pickle.load(f)
|
|
self.workAreaSize = data["workAreaSize"]
|
|
except Exception as e:
|
|
print ("Unable to restore program settings:", str(e))
|
|
|
|
def storeSettings(self):
|
|
with open(self.SETTINGSFNAME, "wb") as f:
|
|
try:
|
|
data = {"workAreaSize": self.workAreaSize}
|
|
pickle.dump(data, f)
|
|
except Exception as e:
|
|
print ("error while saving settings:", str(e))
|
|
|
|
def ToggleConnect(self):
|
|
if self.isConnected:
|
|
serial.close_serial()
|
|
self.connectButton.config(text="Connect")
|
|
self.status.config(text="Not connected")
|
|
self.homeButton.config(state=DISABLED)
|
|
self.stopButton.config(state=DISABLED)
|
|
self.gotoButton.config(state=DISABLED)
|
|
self.SetNavButtonsState(False)
|
|
self.isConnected = False
|
|
else:
|
|
if serial.open_serial(self.portCombo.get(), self.baudCombo.get()):
|
|
self.connectButton.config(text="Disconnect")
|
|
self.status.config(text="Connected")
|
|
self.homeButton.config(state=NORMAL)
|
|
self.stopButton.config(state=NORMAL)
|
|
self.gotoButton.config(state=NORMAL)
|
|
self.SetNavButtonsState(True)
|
|
self.isConnected = True
|
|
self.GetPositionTimerTaks()
|
|
|
|
def TestBorder(self):
|
|
rectangle = toolpath_border_points(self.commands[1:])
|
|
for point in rectangle:
|
|
serial.queue_command("G0 X%f Y%f F5000\n" % point)
|
|
def ToggleStart(self):
|
|
if self.isJobPaused:
|
|
serial.queue.clear()
|
|
self.startButton.config(text="Resume job")
|
|
self.status.config(text="Job paused")
|
|
else:
|
|
self.startButton.config(text="Pause job")
|
|
self.status.config(text="Job in progress")
|
|
startInstructionIndex = self.lastSendCommandIndex + 1
|
|
# job launch
|
|
if not self.isJobRunning:
|
|
self.canvas.clear()
|
|
startInstructionIndex = 0
|
|
self.start = time.time()
|
|
# after every move command being sent, this callback is executed
|
|
def progressCallback(instruction_index):
|
|
point = self.commands[instruction_index]
|
|
if self.lastMove:
|
|
coord = (self.lastMove[1], self.lastMove[2], point[1], point[2])
|
|
color = self.currentColor
|
|
# set color for jump move
|
|
if "G0" in point[0]:
|
|
color = "snow2"
|
|
else:
|
|
self.currentToolPoint += 1
|
|
self.toolPointsLabel.config(text="%d/%d" % (self.currentToolPoint, self.toolPointsTotal))
|
|
self.distanceTraveled += math.hypot(coord[0] - coord[2], coord[1] - coord[3])
|
|
line = self.canvas.create_line(self.canvas.calc_coords(coord), fill=color)
|
|
self.canvas.lift(self.canvas.pointer, line)
|
|
self.timeLabel.config(text="%d/%d" % (self.distancesList[self.currentToolChange]- self.distanceTraveled, self.distancesList[-1]-self.distanceTraveled))
|
|
self.lastSendCommandIndex = instruction_index
|
|
self.lastMove = point
|
|
|
|
# this callback pauses
|
|
def progressToolChangeCallback(instruction_index):
|
|
point = self.commands[instruction_index]
|
|
self.lastSendCommandIndex = instruction_index
|
|
self.currentColor = _from_rgb((point[1], point[2], point[3]))
|
|
self.ToggleStart()
|
|
self.currentToolChange += 1
|
|
self.toolChangesLabel.config(text="%d/%d" % (self.currentToolChange, self.toolChangesTotal))
|
|
self.ToolChangePopup(self.currentColor)
|
|
|
|
self.isJobRunning = True
|
|
commandsCount = len(self.commands)
|
|
# all the commands until tool change command, are queued at once
|
|
for i in range(startInstructionIndex, commandsCount):
|
|
point = self.commands[i]
|
|
# pause on color change
|
|
if "M6" in point[0]:
|
|
serial.queue_command("G0 F25000\n", lambda _, index = i: progressToolChangeCallback(index))
|
|
break
|
|
else:
|
|
serial.queue_command("%s X%f Y%f\n" % (point[0],point[1], point[2]), lambda _, index = i: progressCallback(index))
|
|
# queue job finish callback, it is unnecessary added after every pause but is cleaned in pause callback
|
|
serial.queue_command("M114\n", self.JobFinished)
|
|
|
|
self.isJobPaused = not self.isJobPaused
|
|
|
|
def SetNavButtonsState(self, enabled = False):
|
|
newState = NORMAL if enabled else DISABLED
|
|
for b in self.navigationButtons:
|
|
b.config(state=newState)
|
|
def TogglePan(self):
|
|
self.rotateButton.config(relief=RAISED)
|
|
self.scaleButton.config(relief=RAISED)
|
|
if self.isJobRunning:
|
|
return
|
|
if self.panButton.config('relief')[-1] == SUNKEN:
|
|
self.panButton.config(relief=RAISED)
|
|
else:
|
|
self.panButton.config(relief=SUNKEN)
|
|
def ToggleRotate(self):
|
|
self.panButton.config(relief=RAISED)
|
|
self.scaleButton.config(relief=RAISED)
|
|
if self.isJobRunning:
|
|
return
|
|
if self.rotateButton.config('relief')[-1] == SUNKEN:
|
|
self.rotateButton.config(relief=RAISED)
|
|
else:
|
|
self.rotateButton.config(relief=SUNKEN)
|
|
def ToggleMirror(self):
|
|
self.panButton.config(relief=RAISED)
|
|
self.rotateButton.config(relief=RAISED)
|
|
self.scaleButton.config(relief=RAISED)
|
|
if self.isJobRunning:
|
|
return
|
|
self.commands = reflect_toolpath(self.commands, self.workAreaSize[0]/2)
|
|
self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())])
|
|
def ToggleScale(self):
|
|
self.panButton.config(relief=RAISED)
|
|
self.rotateButton.config(relief=RAISED)
|
|
self.mirrorButton.config(relief=RAISED)
|
|
if self.isJobRunning:
|
|
return
|
|
if self.scaleButton.config('relief')[-1] == SUNKEN:
|
|
self.scaleButton.config(relief=RAISED)
|
|
else:
|
|
self.scaleButton.config(relief=SUNKEN)
|
|
def GoTo(self):
|
|
if self.isJobRunning:
|
|
return
|
|
if self.gotoButton.config('relief')[-1] == SUNKEN:
|
|
self.gotoButton.config(relief=RAISED)
|
|
else:
|
|
self.gotoButton.config(relief=SUNKEN)
|
|
def StopAll(self):
|
|
serial.queue.clear()
|
|
self.JobFinished(False)
|
|
self.status.config(text="Job stopped on user demand")
|
|
|
|
def JobFinished(self, messagePopup = True):
|
|
self.isJobRunning = False
|
|
self.isJobPaused = False
|
|
self.lastSendCommandIndex = -1
|
|
self.lastMove = None
|
|
self.distanceTraveled = 0
|
|
self.currentToolChange = 0
|
|
self.currentToolPoint = 0
|
|
self.currentColor = 'black'
|
|
self.toolPointsLabel.config(text="%d/%d" % (self.currentToolPoint, self.toolPointsTotal))
|
|
self.toolChangesLabel.config(text="%d/%d" % (self.currentToolChange, self.toolChangesTotal))
|
|
self.timeLabel.config(text="%d/%d" % (self.distancesList[self.currentToolChange]- self.distanceTraveled, self.distancesList[-1]-self.distanceTraveled))
|
|
self.startButton.config(text="Start job")
|
|
self.status.config(text="Job finished")
|
|
timeTaken = time.time() - self.start
|
|
# non blocking popup messagebox
|
|
if messagePopup:
|
|
tl = Toplevel(root)
|
|
tl.title("Job finished")
|
|
frame = Frame(tl)
|
|
frame.grid()
|
|
Label(frame, text='Current job is finished and took %s.' % time.strftime("%H hours, %M minutes, %S seconds", time.gmtime(timeTaken)) ).grid(row=0, column=0, sticky=N)
|
|
Button(frame, text="OK", command=lambda: tl.destroy(), width=10).grid(row=1, column=0)
|
|
|
|
|
|
def CanvasClick(self, event):
|
|
if self.isJobRunning:
|
|
return
|
|
self.dragStart = [event.x, event.y]
|
|
#self.transform = math.atan2(event.x, event.y)
|
|
self.transform = 0
|
|
# go to
|
|
if self.gotoButton.config('relief')[-1] == SUNKEN:
|
|
point = self.canvas.canvas_point_to_machine(self.dragStart)
|
|
serial.queue_command("G0 X%f Y%f\n" % point)
|
|
#print("Clicked at: ", self.dragStart)
|
|
def CanvasRelease(self, event):
|
|
if self.isJobRunning:
|
|
return
|
|
|
|
print ("Applied transform", self.transform)
|
|
def CanvasDrag(self, event):
|
|
if self.isJobRunning:
|
|
return
|
|
vect = (self.dragStart[0]-event.x, self.dragStart[1]-event.y)
|
|
# move event
|
|
if self.panButton.config('relief')[-1] == SUNKEN:
|
|
self.transform = self.canvas.canvas_vector_to_machine(vect)
|
|
self.commands = translate_toolpath(self.commands, self.transform)
|
|
self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())])
|
|
self.dragStart[0] = event.x
|
|
self.dragStart[1] = event.y
|
|
# rotate event
|
|
if self.rotateButton.config('relief')[-1] == SUNKEN:
|
|
angle = math.atan2(vect[0], vect[1]) # atan2(y, x) or atan2(sin, cos)
|
|
self.commands = rotate_toolpath(self.commands, (self.workAreaSize[0]/2,self.workAreaSize[1]/2), -(self.transform-angle))
|
|
self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())])
|
|
self.transform = angle
|
|
# scale event
|
|
if self.scaleButton.config('relief')[-1] == SUNKEN:
|
|
factor = math.sqrt((vect[0])**2 + (vect[1])**2) / 500
|
|
f = factor - self.transform
|
|
if vect[0] < 0:
|
|
f = -f
|
|
self.commands = scale_toolpath(self.commands, f)
|
|
self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())])
|
|
self.transform = factor
|
|
def UpdatePath(self, val):
|
|
if self.isJobRunning:
|
|
return
|
|
self.canvas.draw_toolpath(self.commands[0:int(val)])
|
|
def GetPositionTimerTaks(self):
|
|
if self.isConnected:
|
|
def TimerCallback(response):
|
|
response = self.positionResponseRegex.search(response)
|
|
if response:
|
|
pos = (float(response.group(1)), float(response.group(2)))
|
|
self.canvas.move_pointer(pos)
|
|
|
|
serial.queue_command("M114\n", TimerCallback, priority = -1)
|
|
self.master.after(2000, self.GetPositionTimerTaks)
|
|
|
|
def CleanUp(self):
|
|
serial.close_serial()
|
|
|
|
def ToolChangePopup(self, newColor = "black"):
|
|
tl = Toplevel(root)
|
|
tl.title("Tool change")
|
|
|
|
frame = Frame(tl)
|
|
frame.grid()
|
|
|
|
canvas = Canvas(frame, width=100, height=130)
|
|
canvas.grid(row=1, column=0)
|
|
#imgvar = PhotoImage(file="pyrocket.png")
|
|
#canvas.create_image(50,70, image=imgvar)
|
|
#canvas.image = imgvar
|
|
|
|
msgbody1 = Label(frame, text="There is time to change tool for a " )
|
|
msgbody1.grid(row=1, column=1, sticky=N)
|
|
lang = Label(frame, text="next color", font=Font(size=20, weight="bold"), fg=newColor)
|
|
lang.grid(row=1, column=2, sticky=N)
|
|
msgbody2 = Label(frame, text="Resume the current job after change.")
|
|
msgbody2.grid(row=1, column=3, sticky=N)
|
|
|
|
okbttn = Button(frame, text="OK", command=lambda: tl.destroy(), width=10)
|
|
okbttn.grid(row=2, column=4)
|
|
|
|
root = Tk()
|
|
my_gui = ControlAppGUI(root)
|
|
def on_closing():
|
|
my_gui.CleanUp()
|
|
root.destroy()
|
|
|
|
root.protocol("WM_DELETE_WINDOW", on_closing)
|
|
|
|
root.mainloop()
|
|
|