jpcgt/flatcam/Beta слито с Beta

This commit is contained in:
Camellan 2019-10-20 17:39:42 +04:00
commit d083eb5f7b
77 changed files with 24991 additions and 25318 deletions

View File

@ -9,6 +9,8 @@
import urllib.request import urllib.request
import urllib.parse import urllib.parse
import urllib.error import urllib.error
import webbrowser
import getopt import getopt
import random import random
import simplejson as json import simplejson as json
@ -21,7 +23,7 @@ import subprocess
import ctypes import ctypes
import tkinter as tk import tkinter as tk
from PyQt5 import QtPrintSupport, QtNetwork from PyQt5 import QtPrintSupport
from contextlib import contextmanager from contextlib import contextmanager
import gc import gc
@ -40,10 +42,12 @@ import vispy.scene as scene
# ####################################### # #######################################
from ObjectCollection import * from ObjectCollection import *
from FlatCAMObj import * from FlatCAMObj import *
from camlib import to_dict, dict2obj, ET, ParseError
from flatcamGUI.PlotCanvas import * from flatcamGUI.PlotCanvas import *
from flatcamGUI.PlotCanvasLegacy import * from flatcamGUI.PlotCanvasLegacy import *
from flatcamGUI.FlatCAMGUI import * from flatcamGUI.FlatCAMGUI import *
from FlatCAMCommon import LoudDict from FlatCAMCommon import LoudDict
from FlatCAMPostProc import load_postprocessors from FlatCAMPostProc import load_postprocessors
@ -3061,7 +3065,7 @@ class App(QtCore.QObject):
separator=True) separator=True)
self.panelize_tool = Panelize(self) self.panelize_tool = Panelize(self)
self.panelize_tool.install(icon=QtGui.QIcon('share/panel16.png')) self.panelize_tool.install(icon=QtGui.QIcon('share/panelize16.png'))
self.film_tool = Film(self) self.film_tool = Film(self)
self.film_tool.install(icon=QtGui.QIcon('share/film16.png')) self.film_tool.install(icon=QtGui.QIcon('share/film16.png'))
@ -3192,6 +3196,7 @@ class App(QtCore.QObject):
self.ui.newexc_btn.triggered.connect(self.new_excellon_object) self.ui.newexc_btn.triggered.connect(self.new_excellon_object)
self.ui.editgeo_btn.triggered.connect(self.object2editor) self.ui.editgeo_btn.triggered.connect(self.object2editor)
self.ui.update_obj_btn.triggered.connect(lambda: self.editor2object()) self.ui.update_obj_btn.triggered.connect(lambda: self.editor2object())
self.ui.copy_btn.triggered.connect(self.on_copy_object)
self.ui.delete_btn.triggered.connect(self.on_delete) self.ui.delete_btn.triggered.connect(self.on_delete)
self.ui.distance_btn.triggered.connect(lambda: self.distance_tool.run(toggle=True)) self.ui.distance_btn.triggered.connect(lambda: self.distance_tool.run(toggle=True))
@ -3228,6 +3233,9 @@ class App(QtCore.QObject):
""" """
self.report_usage("object2editor()") self.report_usage("object2editor()")
# disable the objects menu as it may interfere with the Editors
self.ui.menuobjects.setDisabled(True)
edited_object = self.collection.get_active() edited_object = self.collection.get_active()
if isinstance(edited_object, FlatCAMGerber) or isinstance(edited_object, FlatCAMGeometry) or \ if isinstance(edited_object, FlatCAMGerber) or isinstance(edited_object, FlatCAMGeometry) or \
@ -3316,6 +3324,9 @@ class App(QtCore.QObject):
""" """
self.report_usage("editor2object()") self.report_usage("editor2object()")
# re-enable the objects menu that was disabled on entry in Editor mode
self.ui.menuobjects.setDisabled(False)
# do not update a geometry or excellon object unless it comes out of an editor # do not update a geometry or excellon object unless it comes out of an editor
if self.call_source != 'app': if self.call_source != 'app':
edited_obj = self.collection.get_active() edited_obj = self.collection.get_active()
@ -4527,10 +4538,10 @@ class App(QtCore.QObject):
attributions_label = QtWidgets.QLabel( attributions_label = QtWidgets.QLabel(
_( _(
'Some of the icons used are from the following sources: <BR>' 'Some of the icons used are from the following sources:<br>'
'<div>Icons made by <a href="https://www.flaticon.com/authors/freepik" ' '<div>Icons made by <a href="https://www.flaticon.com/authors/freepik" '
'title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" ' 'title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" '
'title="Flaticon">www.flaticon.com</a></div><br>' 'title="Flaticon">www.flaticon.com</a></div>'
'Icons by <a target="_blank" href="https://icons8.com">Icons8</a>' 'Icons by <a target="_blank" href="https://icons8.com">Icons8</a>'
) )
) )
@ -4650,6 +4661,10 @@ class App(QtCore.QObject):
self.prog_form_lay.addRow(QtWidgets.QLabel('%s' % "@mgix")) self.prog_form_lay.addRow(QtWidgets.QLabel('%s' % "@mgix"))
self.translator_grid_lay = QtWidgets.QGridLayout() self.translator_grid_lay = QtWidgets.QGridLayout()
self.translator_grid_lay.setColumnStretch(0, 0)
self.translator_grid_lay.setColumnStretch(1, 0)
self.translator_grid_lay.setColumnStretch(2, 1)
self.translator_grid_lay.setColumnStretch(3, 0)
# trans_widget = QtWidgets.QWidget() # trans_widget = QtWidgets.QWidget()
# trans_widget.setLayout(self.translator_grid_lay) # trans_widget.setLayout(self.translator_grid_lay)
@ -4667,25 +4682,29 @@ class App(QtCore.QObject):
self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Language")), 0, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Language")), 0, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Translator")), 0, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Translator")), 0, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 2) self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("Corrections")), 0, 2)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Brasilian - Portuguese"), 1, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('<b>%s</b>' % _("E-mail")), 0, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "BR - Portuguese"), 1, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Carlos Stein"), 1, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Carlos Stein"), 1, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 1, 2) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<carlos.stein@gmail.com>"), 1, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "French"), 2, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "French"), 2, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Translation)"), 2, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 2, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 2, 2) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "(Google-Translation)"), 2, 2)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 2, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 3, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 3, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Translation)"), 3, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 3, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 2) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jens Karstedt"), 3, 2)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Romanian"), 4, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Romanian"), 4, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 4, 2) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 4, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Russian"), 5, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Russian"), 5, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 5, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Andrey Kultyapov"), 5, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<camellan@yandex.ru>"), 5, 2) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<camellan@yandex.ru>"), 5, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Spanish"), 6, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Spanish"), 6, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Translation)"), 6, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 6, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 6, 2) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "(Google-Translation)"), 6, 2)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 6, 3)
self.translator_grid_lay.setColumnStretch(0, 0) self.translator_grid_lay.setColumnStretch(0, 0)
self.translators_tab_layout.addStretch() self.translators_tab_layout.addStretch()
@ -7922,9 +7941,12 @@ class App(QtCore.QObject):
def add_act(name): def add_act(name):
obj_for_icon = self.collection.get_by_name(name) obj_for_icon = self.collection.get_by_name(name)
add_action = QtWidgets.QAction(parent=self.ui.menuobjects) add_action = QtWidgets.QAction(parent=self.ui.menuobjects)
add_action.setCheckable(True)
add_action.setText(name) add_action.setText(name)
add_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind])) add_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind]))
add_action.triggered.connect(lambda: self.collection.set_exclusive_active(name)) add_action.triggered.connect(
lambda: self.collection.set_active(name) if add_action.isChecked() is True else
self.collection.set_inactive(name))
self.ui.menuobjects.addAction(add_action) self.ui.menuobjects.addAction(add_action)
for name in gerber_list: for name in gerber_list:
@ -7950,6 +7972,17 @@ class App(QtCore.QObject):
for name in doc_list: for name in doc_list:
add_act(name) add_act(name)
self.ui.menuobjects.addSeparator()
self.ui.menuobjects_selall = self.ui.menuobjects.addAction(
QtGui.QIcon('share/select_all.png'),
_('Select All')
)
self.ui.menuobjects_unselall = self.ui.menuobjects.addAction(
QtGui.QIcon('share/deselect_all32.png'),
_('Deselect All')
)
self.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True))
self.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False))
elif state == 'delete': elif state == 'delete':
for act in self.ui.menuobjects.actions(): for act in self.ui.menuobjects.actions():
@ -7966,7 +7999,9 @@ class App(QtCore.QObject):
add_action = QtWidgets.QAction(parent=self.ui.menuobjects) add_action = QtWidgets.QAction(parent=self.ui.menuobjects)
add_action.setText(obj.options['name']) add_action.setText(obj.options['name'])
add_action.setIcon(QtGui.QIcon(icon_files[obj.kind])) add_action.setIcon(QtGui.QIcon(icon_files[obj.kind]))
add_action.triggered.connect(lambda: self.collection.set_exclusive_active(obj.options['name'])) add_action.triggered.connect(
lambda: self.collection.set_active(obj.options['name']) if add_action.isChecked() is True else
self.collection.set_inactive(obj.options['name']))
self.ui.menuobjects.insertAction(act, add_action) self.ui.menuobjects.insertAction(act, add_action)
@ -7984,6 +8019,39 @@ class App(QtCore.QObject):
pass pass
self.ui.menuobjects.clear() self.ui.menuobjects.clear()
self.ui.menuobjects.addSeparator()
self.ui.menuobjects_selall = self.ui.menuobjects.addAction(
QtGui.QIcon('share/select_all.png'),
_('Select All')
)
self.ui.menuobjects_unselall = self.ui.menuobjects.addAction(
QtGui.QIcon('share/deselect_all32.png'),
_('Deselect All')
)
self.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True))
self.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False))
def on_objects_selection(self, on_off):
obj_list = self.collection.get_names()
if on_off is True:
self.collection.set_all_active()
for act in self.ui.menuobjects.actions():
try:
act.setChecked(True)
except:
pass
if obj_list:
self.inform.emit('[selected] %s' % _("All objects are selected."))
else:
self.collection.set_all_inactive()
for act in self.ui.menuobjects.actions():
try:
act.setChecked(False)
except:
pass
self.inform.emit('%s' % _("Objects selection is cleared."))
def grid_status(self): def grid_status(self):
if self.ui.grid_snap_btn.isChecked(): if self.ui.grid_snap_btn.isChecked():
return True return True
@ -8426,93 +8494,40 @@ class App(QtCore.QObject):
objects_under_the_click_list.append(obj.options['name']) objects_under_the_click_list.append(obj.options['name'])
try: try:
# If there is no element in the overlapped objects list then make everyone inactive if objects_under_the_click_list:
# because we selected "nothing" curr_sel_obj = self.collection.get_active()
self.collection.set_all_inactive()
# delete the possible selection box around a possible selected object
self.delete_selection_shape()
if not objects_under_the_click_list:
# and as a convenience move the focus to the Project tab because Selected tab is now empty but
# only when working on App
if self.call_source == 'app':
if self.click_noproject is False:
self.ui.notebook.setCurrentWidget(self.ui.project_tab)
else:
# restore auto open the Project Tab
self.click_noproject = False
# delete any text in the status bar, implicitly the last object name that was selected
self.inform.emit("")
else:
self.call_source = 'app'
else:
# case when there is only an object under the click and we toggle it # case when there is only an object under the click and we toggle it
if len(objects_under_the_click_list) == 1: if len(objects_under_the_click_list) == 1:
if self.collection.get_active() is None: if curr_sel_obj is None:
self.collection.set_active(objects_under_the_click_list[0]) self.collection.set_active(objects_under_the_click_list[0])
# create the selection box around the selected object
curr_sel_obj = self.collection.get_active() curr_sel_obj = self.collection.get_active()
# create the selection box around the selected object
if self.defaults['global_selection_shape'] is True: if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(curr_sel_obj) self.draw_selection_shape(curr_sel_obj)
# self.inform.emit('[selected] %s: %s selected' %
# (str(curr_sel_obj.kind).capitalize(), str(curr_sel_obj.options['name'])))
if curr_sel_obj.kind == 'gerber':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='green', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'excellon':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='brown', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'cncjob':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='blue', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'geometry':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='red', name=str(curr_sel_obj.options['name'])))
elif self.collection.get_active().options['name'] not in objects_under_the_click_list: elif self.collection.get_active().options['name'] not in objects_under_the_click_list:
self.collection.set_all_inactive() self.on_objects_selection(False)
self.delete_selection_shape() self.delete_selection_shape()
self.collection.set_active(objects_under_the_click_list[0]) self.collection.set_active(objects_under_the_click_list[0])
# create the selection box around the selected object
curr_sel_obj = self.collection.get_active() curr_sel_obj = self.collection.get_active()
# create the selection box around the selected object
if self.defaults['global_selection_shape'] is True: if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(curr_sel_obj) self.draw_selection_shape(curr_sel_obj)
# self.inform.emit('[selected] %s: %s selected' % self.selected_message(curr_sel_obj=curr_sel_obj)
# (str(curr_sel_obj.kind).capitalize(), str(curr_sel_obj.options['name'])))
if curr_sel_obj.kind == 'gerber':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='green', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'excellon':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='brown', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'cncjob':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='blue', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'geometry':
self.inform.emit(
_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='red', name=str(curr_sel_obj.options['name'])))
else: else:
self.collection.set_all_inactive() self.on_objects_selection(False)
self.delete_selection_shape() self.delete_selection_shape()
if self.call_source == 'app':
# delete any text in the status bar, implicitly the last object name that was selected if self.call_source != 'app':
self.inform.emit("")
else:
self.call_source = 'app' self.call_source = 'app'
self.selected_message(curr_sel_obj=curr_sel_obj)
else: else:
# If there is no selected object # If there is no selected object
# make active the first element of the overlapped objects list # make active the first element of the overlapped objects list
@ -8527,9 +8542,9 @@ class App(QtCore.QObject):
name_sel_obj = objects_under_the_click_list[0] name_sel_obj = objects_under_the_click_list[0]
self.collection.set_active(name_sel_obj) self.collection.set_active(name_sel_obj)
else: else:
name_sel_obj_idx = objects_under_the_click_list.index(name_sel_obj) sel_idx = objects_under_the_click_list.index(name_sel_obj)
self.collection.set_all_inactive() self.collection.set_all_inactive()
self.collection.set_active(objects_under_the_click_list[(name_sel_obj_idx + 1) % self.collection.set_active(objects_under_the_click_list[(sel_idx + 1) %
len(objects_under_the_click_list)]) len(objects_under_the_click_list)])
curr_sel_obj = self.collection.get_active() curr_sel_obj = self.collection.get_active()
@ -8539,30 +8554,44 @@ class App(QtCore.QObject):
if self.defaults['global_selection_shape'] is True: if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(curr_sel_obj) self.draw_selection_shape(curr_sel_obj)
# self.inform.emit('[selected] %s: %s selected' % self.selected_message(curr_sel_obj=curr_sel_obj)
# (str(curr_sel_obj.kind).capitalize(), str(curr_sel_obj.options['name'])))
if curr_sel_obj.kind == 'gerber':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='green', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'excellon':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='brown', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'cncjob':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='blue', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'geometry':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='red', name=str(curr_sel_obj.options['name'])))
# for obj in self.collection.get_list(): else:
# obj.plot() # deselect everything
# curr_sel_obj.plot(color=self.FC_dark_blue, face_color=self.FC_light_blue) self.on_objects_selection(False)
# delete the possible selection box around a possible selected object
self.delete_selection_shape()
# TODO: on selected objects change the object colors and do not draw the selection box # and as a convenience move the focus to the Project tab because Selected tab is now empty but
# self.plotcanvas.update() # this updates the canvas # only when working on App
if self.call_source == 'app':
if self.click_noproject is False:
self.ui.notebook.setCurrentWidget(self.ui.project_tab)
else:
# restore auto open the Project Tab
self.click_noproject = False
# delete any text in the status bar, implicitly the last object name that was selected
# self.inform.emit("")
else:
self.call_source = 'app'
except Exception as e: except Exception as e:
log.error("[ERROR] Something went bad. %s" % str(e)) log.error("[ERROR] Something went bad in App.select_objects(). %s" % str(e))
return
def selected_message(self, curr_sel_obj):
if curr_sel_obj:
if curr_sel_obj.kind == 'gerber':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='green', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'excellon':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='brown', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'cncjob':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='blue', name=str(curr_sel_obj.options['name'])))
elif curr_sel_obj.kind == 'geometry':
self.inform.emit(_('[selected]<span style="color:{color};">{name}</span> selected').format(
color='red', name=str(curr_sel_obj.options['name'])))
def delete_hover_shape(self): def delete_hover_shape(self):
self.hover_shapes.clear() self.hover_shapes.clear()
@ -8621,6 +8650,9 @@ class App(QtCore.QObject):
:return: :return:
""" """
if sel_obj is None:
return
pt1 = (float(sel_obj.options['xmin']), float(sel_obj.options['ymin'])) pt1 = (float(sel_obj.options['xmin']), float(sel_obj.options['ymin']))
pt2 = (float(sel_obj.options['xmax']), float(sel_obj.options['ymin'])) pt2 = (float(sel_obj.options['xmax']), float(sel_obj.options['ymin']))
pt3 = (float(sel_obj.options['xmax']), float(sel_obj.options['ymax'])) pt3 = (float(sel_obj.options['xmax']), float(sel_obj.options['ymax']))
@ -8634,13 +8666,6 @@ class App(QtCore.QObject):
sel_rect = sel_rect.buffer(-0.00393) sel_rect = sel_rect.buffer(-0.00393)
sel_rect = sel_rect.buffer(0.00787) sel_rect = sel_rect.buffer(0.00787)
# if color:
# face = Color(color, alpha=0.2)
# outline = Color(color, alpha=0.8)
# else:
# face = Color(self.defaults['global_sel_fill'], alpha=0.2)
# outline = Color(self.defaults['global_sel_line'], alpha=0.8)
if color: if color:
face = color[:-2] + str(hex(int(0.2 * 255)))[2:] face = color[:-2] + str(hex(int(0.2 * 255)))[2:]
outline = color[:-2] + str(hex(int(0.8 * 255)))[2:] outline = color[:-2] + str(hex(int(0.8 * 255)))[2:]
@ -9044,7 +9069,7 @@ class App(QtCore.QObject):
try: try:
filename, _f = QtWidgets.QFileDialog.getSaveFileName( filename, _f = QtWidgets.QFileDialog.getSaveFileName(
caption=_("Export SVG"), caption=_("Export SVG"),
directory=self.get_last_save_folder() + '/' + str(name), directory=self.get_last_save_folder() + '/' + str(name) + '_svg',
filter=_filter) filter=_filter)
except TypeError: except TypeError:
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export SVG"), filter=_filter) filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export SVG"), filter=_filter)

View File

@ -10,24 +10,33 @@
# File modified by: Marius Stanciu # # File modified by: Marius Stanciu #
# ########################################################## # ##########################################################
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextDocument from shapely.geometry import Point, Polygon, MultiPolygon, MultiLineString, LineString, LinearRing
from shapely.ops import cascaded_union
import shapely.affinity as affinity
import copy import copy
from copy import deepcopy
from io import StringIO
import traceback
import inspect # TODO: For debugging only. import inspect # TODO: For debugging only.
from datetime import datetime from datetime import datetime
from flatcamEditors.FlatCAMTextEditor import TextEditor from flatcamEditors.FlatCAMTextEditor import TextEditor
from flatcamGUI.ObjectUI import * from flatcamGUI.ObjectUI import *
from FlatCAMCommon import LoudDict from FlatCAMCommon import LoudDict
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
from camlib import *
from flatcamParsers.ParseExcellon import Excellon from flatcamParsers.ParseExcellon import Excellon
from flatcamParsers.ParseGerber import Gerber from flatcamParsers.ParseGerber import Gerber
from camlib import Geometry, CNCjob
import FlatCAMApp
import itertools
import tkinter as tk import tkinter as tk
import sys import os, sys, itertools
import ezdxf
import math
import numpy as np
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -66,7 +75,7 @@ class FlatCAMObj(QtCore.QObject):
app = None app = None
# signal to plot a single object # signal to plot a single object
plot_single_object = pyqtSignal() plot_single_object = QtCore.pyqtSignal()
def __init__(self, name): def __init__(self, name):
""" """
@ -2804,10 +2813,13 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
for tool in tools: for tool in tools:
if tooldia > self.tools[tool]["C"]: if tooldia > self.tools[tool]["C"]:
self.app.inform.emit('[ERROR_NOTCL] %s %s: %s' % ( self.app.inform.emit(
_("Milling tool for DRILLS is larger than hole size. Cancelled.", '[ERROR_NOTCL] %s %s: %s' % (
str(tool)) _("Milling tool for DRILLS is larger than hole size. Cancelled."),
)) _("Tool"),
str(tool)
)
)
return False, "Error: Milling tool is larger than hole." return False, "Error: Milling tool is larger than hole."
def geo_init(geo_obj, app_obj): def geo_init(geo_obj, app_obj):
@ -3510,21 +3522,21 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
offset_item = QtWidgets.QComboBox() offset_item = QtWidgets.QComboBox()
for item in self.offset_item_options: for item in self.offset_item_options:
offset_item.addItem(item) offset_item.addItem(item)
offset_item.setStyleSheet('background-color: rgb(255,255,255)') # offset_item.setStyleSheet('background-color: rgb(255,255,255)')
idx = offset_item.findText(tooluid_value['offset']) idx = offset_item.findText(tooluid_value['offset'])
offset_item.setCurrentIndex(idx) offset_item.setCurrentIndex(idx)
type_item = QtWidgets.QComboBox() type_item = QtWidgets.QComboBox()
for item in self.type_item_options: for item in self.type_item_options:
type_item.addItem(item) type_item.addItem(item)
type_item.setStyleSheet('background-color: rgb(255,255,255)') # type_item.setStyleSheet('background-color: rgb(255,255,255)')
idx = type_item.findText(tooluid_value['type']) idx = type_item.findText(tooluid_value['type'])
type_item.setCurrentIndex(idx) type_item.setCurrentIndex(idx)
tool_type_item = QtWidgets.QComboBox() tool_type_item = QtWidgets.QComboBox()
for item in self.tool_type_item_options: for item in self.tool_type_item_options:
tool_type_item.addItem(item) tool_type_item.addItem(item)
tool_type_item.setStyleSheet('background-color: rgb(255,255,255)') # tool_type_item.setStyleSheet('background-color: rgb(255,255,255)')
idx = tool_type_item.findText(tooluid_value['tool_type']) idx = tool_type_item.findText(tooluid_value['tool_type'])
tool_type_item.setCurrentIndex(idx) tool_type_item.setCurrentIndex(idx)

View File

@ -9,7 +9,6 @@
from importlib.machinery import SourceFileLoader from importlib.machinery import SourceFileLoader
import os import os
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from datetime import datetime
import math import math
# module-root dictionary of postprocessors # module-root dictionary of postprocessors

View File

@ -11,13 +11,20 @@
# File modified by: Marius Stanciu # # File modified by: Marius Stanciu #
# ########################################################## # ##########################################################
# from PyQt5.QtCore import QModelIndex
from FlatCAMObj import *
import inspect # TODO: Remove
import FlatCAMApp
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QSettings from PyQt5.QtCore import Qt, QSettings
# import webbrowser from PyQt5.QtGui import QColor
# from PyQt5.QtCore import QModelIndex
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMExcellon, FlatCAMCNCjob, FlatCAMDocument, FlatCAMScript
import inspect # TODO: Remove
import FlatCAMApp
import re
import logging
import collections
from copy import deepcopy
from numpy import Inf
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -27,6 +34,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class KeySensitiveListView(QtWidgets.QTreeView): class KeySensitiveListView(QtWidgets.QTreeView):
""" """
@ -715,6 +724,16 @@ class ObjectCollection(QtCore.QAbstractItemModel):
log.error("[ERROR] Cause: %s" % str(e)) log.error("[ERROR] Cause: %s" % str(e))
raise raise
def set_all_active(self):
"""
Select all objects from the project list. This triggers the
list_selection_changed event and call on_list_selection_changed.
:return: None
"""
for name in self.get_names():
self.set_active(name)
def set_exclusive_active(self, name): def set_exclusive_active(self, name):
""" """
Make the object with the name in parameters the only selected object Make the object with the name in parameters the only selected object

View File

@ -9,6 +9,40 @@ CAD program, and create G-Code for Isolation routing.
================================================= =================================================
18.10.2019
- finished the update on the Google translated Spanish translation.
- updated the new objects icons for Gerber, Geometry and Excellon
- small import problem fixed
- RELEASE 8.98
17.10.2019
- fixed a bug in milling holes due of a message wrongly formatted
- added an translator email address
- finished the update on German Google translation. Part of it was corrected by Jens Karstedt
- finished the update of the Romanian translation.
- finished the Objects menu by adding the ability of actions to be checked so they will show the selected status of the objects and by adding to actions to (de)select all objects
- fixed and optimized the click selection on canvas
- fixed Gerber parsing for very simple Gerber files that have only one Polygon but many LPC zones
- fixed SVG export; fix bug #327
- finished the update on French Google translation.
16.10.2019
- small update to Romanian translation files
15.10.2019
- adjusted the layout in NCC Tool
- fixed bug in Panelization Tool for which in case of Excellon objects, the panel kept a reference to the source object which created issues when moving or disabling/enabling the plots
- cleaned up the module imports throughout the app (the TclCommands are not yet verified)
- removed the styling on the comboboxes cellWidget's in the Tool Tables
- replaced some of the icons that did not looked Ok on the dark theme
- added a new toolbar button for the Copy object functionality
- changed the Panelize tool icon
- corrected some strings
14.10.2019 14.10.2019
- modified the result highlight color in Check Rules Tool - modified the result highlight color in Check Rules Tool
@ -189,7 +223,7 @@ CAD program, and create G-Code for Isolation routing.
- fixed the ToolMeasurement geometry not being displayed - fixed the ToolMeasurement geometry not being displayed
- fixed a bug in Excellon Editor that crashed the app when editing the first tool added automatically into a new black Excellon file - fixed a bug in Excellon Editor that crashed the app when editing the first tool added automatically into a new black Excellon file
- made sure that if the big mouse cursor is selected, the utility geometry in Excellon Editor has a thicker line width (2 pixels now) so it is visible over the geometry of the mouse cursor - made sure that if the big mouse cursor is selected, the utility geometry in Excellon Editor has a thicker line width (2 pixels now) so it is visible over the geometry of the mouse cursor
- fixed issue #319 where generating a CNCJob from a geometry made with NCC Tool made the app crash - fixed issue #319 where generating a CNCJob from a geometry made with NCC Tool made the app crash; also #328 which is the same
- replaced in FlatCAM Tools and in FLatCAMObj.py and in Editors all references to hardcoded decimals in string formats for tools with a variable declared in the __init__() - replaced in FlatCAM Tools and in FLatCAMObj.py and in Editors all references to hardcoded decimals in string formats for tools with a variable declared in the __init__()
- fixed a small bug that made app crash when the splash screen is disabled: it was trying to close it without being open - fixed a small bug that made app crash when the splash screen is disabled: it was trying to close it without being open

106
camlib.py
View File

@ -11,12 +11,9 @@ from PyQt5 import QtWidgets
from io import StringIO from io import StringIO
import numpy as np import numpy as np
from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos, dot, float32, \
transpose
from numpy.linalg import solve, norm from numpy.linalg import solve, norm
import re, sys, os, platform import platform
import math
from copy import deepcopy from copy import deepcopy
import traceback import traceback
@ -26,8 +23,8 @@ from rtree import index as rtindex
from lxml import etree as ET from lxml import etree as ET
# See: http://toblerity.org/shapely/manual.html # See: http://toblerity.org/shapely/manual.html
from shapely.geometry import Polygon, LineString, Point, LinearRing, MultiLineString from shapely.geometry import Polygon, LineString, Point, LinearRing, MultiLineString, MultiPoint, MultiPolygon
from shapely.geometry import MultiPoint, MultiPolygon
from shapely.geometry import box as shply_box from shapely.geometry import box as shply_box
from shapely.ops import cascaded_union, unary_union, polygonize from shapely.ops import cascaded_union, unary_union, polygonize
import shapely.affinity as affinity import shapely.affinity as affinity
@ -64,7 +61,6 @@ import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
fcTranslate.apply_language('strings') fcTranslate.apply_language('strings')
log = logging.getLogger('base2') log = logging.getLogger('base2')
@ -162,9 +158,9 @@ class ApertureMacro:
# ## Variables # ## Variables
# These are variables defined locally inside the macro. They can be # These are variables defined locally inside the macro. They can be
# numerical constant or defind in terms of previously define # numerical constant or defined in terms of previously define
# variables, which can be defined locally or in an aperture # variables, which can be defined locally or in an aperture
# definition. All replacements ocurr here. # definition. All replacements occur here.
match = ApertureMacro.amvar_re.search(part) match = ApertureMacro.amvar_re.search(part)
if match: if match:
var = match.group(1) var = match.group(1)
@ -336,8 +332,8 @@ class ApertureMacro:
points = [(0, 0)]*nverts points = [(0, 0)]*nverts
for i in range(nverts): for i in range(nverts):
points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts), points[i] = (x + 0.5 * dia * np.cos(2*np.pi * i/nverts),
y + 0.5 * dia * sin(2*pi * i/nverts)) y + 0.5 * dia * np.sin(2*np.pi * i/nverts))
poly = Polygon(points) poly = Polygon(points)
poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) poly_rotated = affinity.rotate(poly, angle, origin=(0, 0))
@ -637,10 +633,10 @@ class Geometry(object):
def bounds_rec(obj): def bounds_rec(obj):
if type(obj) is list: if type(obj) is list:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in obj: for k in obj:
if type(k) is dict: if type(k) is dict:
@ -1155,7 +1151,7 @@ class Geometry(object):
log.debug("Image import as monochrome.") log.debug("Image import as monochrome.")
else: else:
mask_setting = (red <= mask[1]) + (green <= mask[2]) + (blue <= mask[3]) mask_setting = (red <= mask[1]) + (green <= mask[2]) + (blue <= mask[3])
total = np.zeros(red.shape, dtype=float32) total = np.zeros(red.shape, dtype=np.float32)
for band in red, green, blue: for band in red, green, blue:
total += band total += band
total /= 3 total /= 3
@ -1847,8 +1843,6 @@ class Geometry(object):
:return: SVG Element :return: SVG Element
""" """
geom = None
# Make sure we see a Shapely Geometry class and not a list # Make sure we see a Shapely Geometry class and not a list
if str(type(self)) == "<class 'FlatCAMObj.FlatCAMGeometry'>": if str(type(self)) == "<class 'FlatCAMObj.FlatCAMGeometry'>":
flat_geo = [] flat_geo = []
@ -1873,6 +1867,8 @@ class Geometry(object):
elif skew_reference == 'bottomright': elif skew_reference == 'bottomright':
skew_ref = (xmax, ymin) skew_ref = (xmax, ymin)
geom = geom_svg
if scale_factor_x: if scale_factor_x:
geom = affinity.scale(geom_svg, scale_factor_x, 1.0) geom = affinity.scale(geom_svg, scale_factor_x, 1.0)
if scale_factor_y: if scale_factor_y:
@ -3298,10 +3294,10 @@ class CNCjob(Geometry):
def bounds_rec(obj): def bounds_rec(obj):
if type(obj) is list: if type(obj) is list:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in obj: for k in obj:
if type(k) is dict: if type(k) is dict:
@ -4049,9 +4045,9 @@ class CNCjob(Geometry):
arcdir = [None, None, "cw", "ccw"] arcdir = [None, None, "cw", "ccw"]
if current['G'] in [2, 3]: # arc if current['G'] in [2, 3]: # arc
center = [gobj['I'] + current['X'], gobj['J'] + current['Y']] center = [gobj['I'] + current['X'], gobj['J'] + current['Y']]
radius = sqrt(gobj['I']**2 + gobj['J']**2) radius = np.sqrt(gobj['I']**2 + gobj['J']**2)
start = arctan2(-gobj['J'], -gobj['I']) start = np.arctan2(-gobj['J'], -gobj['I'])
stop = arctan2(-center[1] + y, -center[0] + x) stop = np.arctan2(-center[1] + y, -center[0] + x)
path += arc(center, radius, start, stop, arcdir[current['G']], int(self.steps_per_circle / 4)) path += arc(center, radius, start, stop, arcdir[current['G']], int(self.steps_per_circle / 4))
# Update current instruction # Update current instruction
@ -4710,10 +4706,10 @@ class CNCjob(Geometry):
def bounds_rec(obj): def bounds_rec(obj):
if type(obj) is list: if type(obj) is list:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in obj: for k in obj:
if type(k) is dict: if type(k) is dict:
@ -4742,15 +4738,15 @@ class CNCjob(Geometry):
bounds_coords = bounds_rec(self.solid_geometry) bounds_coords = bounds_rec(self.solid_geometry)
else: else:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k, v in self.cnc_tools.items(): for k, v in self.cnc_tools.items():
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
try: try:
for k in v['solid_geometry']: for k in v['solid_geometry']:
minx_, miny_, maxx_, maxy_ = bounds_rec(k) minx_, miny_, maxx_, maxy_ = bounds_rec(k)
@ -5186,10 +5182,10 @@ class CNCjob(Geometry):
def get_bounds(geometry_list): def get_bounds(geometry_list):
xmin = Inf xmin = np.Inf
ymin = Inf ymin = np.Inf
xmax = -Inf xmax = -np.Inf
ymax = -Inf ymax = -np.Inf
for gs in geometry_list: for gs in geometry_list:
try: try:
@ -5229,33 +5225,33 @@ def arc(center, radius, start, stop, direction, steps_per_circ):
da_sign = {"cw": -1.0, "ccw": 1.0} da_sign = {"cw": -1.0, "ccw": 1.0}
points = [] points = []
if direction == "ccw" and stop <= start: if direction == "ccw" and stop <= start:
stop += 2 * pi stop += 2 * np.pi
if direction == "cw" and stop >= start: if direction == "cw" and stop >= start:
stop -= 2 * pi stop -= 2 * np.pi
angle = abs(stop - start) angle = abs(stop - start)
# angle = stop-start # angle = stop-start
steps = max([int(ceil(angle / (2 * pi) * steps_per_circ)), 2]) steps = max([int(np.ceil(angle / (2 * np.pi) * steps_per_circ)), 2])
delta_angle = da_sign[direction] * angle * 1.0 / steps delta_angle = da_sign[direction] * angle * 1.0 / steps
for i in range(steps + 1): for i in range(steps + 1):
theta = start + delta_angle * i theta = start + delta_angle * i
points.append((center[0] + radius * cos(theta), center[1] + radius * sin(theta))) points.append((center[0] + radius * np.cos(theta), center[1] + radius * np.sin(theta)))
return points return points
def arc2(p1, p2, center, direction, steps_per_circ): def arc2(p1, p2, center, direction, steps_per_circ):
r = sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2) r = np.sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2)
start = arctan2(p1[1] - center[1], p1[0] - center[0]) start = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stop = arctan2(p2[1] - center[1], p2[0] - center[0]) stop = np.arctan2(p2[1] - center[1], p2[0] - center[0])
return arc(center, r, start, stop, direction, steps_per_circ) return arc(center, r, start, stop, direction, steps_per_circ)
def arc_angle(start, stop, direction): def arc_angle(start, stop, direction):
if direction == "ccw" and stop <= start: if direction == "ccw" and stop <= start:
stop += 2 * pi stop += 2 * np.pi
if direction == "cw" and stop >= start: if direction == "cw" and stop >= start:
stop -= 2 * pi stop -= 2 * np.pi
angle = abs(stop - start) angle = abs(stop - start)
return angle return angle
@ -5665,12 +5661,12 @@ def three_point_circle(p1, p2, p3):
a2 = (p2 + p3) / 2.0 a2 = (p2 + p3) / 2.0
# Normals # Normals
b1 = dot((p2 - p1), array([[0, -1], [1, 0]], dtype=float32)) b1 = np.dot((p2 - p1), np.array([[0, -1], [1, 0]], dtype=np.float32))
b2 = dot((p3 - p2), array([[0, 1], [-1, 0]], dtype=float32)) b2 = np.dot((p3 - p2), np.array([[0, 1], [-1, 0]], dtype=np.float32))
# Params # Params
try: try:
T = solve(transpose(array([-b1, b2])), a1 - a2) T = solve(np.transpose(np.array([-b1, b2])), a1 - a2)
except Exception as e: except Exception as e:
log.debug("camlib.three_point_circle() --> %s" % str(e)) log.debug("camlib.three_point_circle() --> %s" % str(e))
return return
@ -5685,11 +5681,11 @@ def three_point_circle(p1, p2, p3):
def distance(pt1, pt2): def distance(pt1, pt2):
return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) return np.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
def distance_euclidian(x1, y1, x2, y2): def distance_euclidian(x1, y1, x2, y2):
return sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) return np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
class FlatCAMRTree(object): class FlatCAMRTree(object):

View File

@ -12,14 +12,17 @@ class Polygon(object):
self.context = context self.context = context
else: else:
self.context = getattr(context, '__geo_interface__', context) self.context = getattr(context, '__geo_interface__', context)
@property @property
def geom_type(self): def geom_type(self):
return (getattr(self.context, 'geom_type', None) return (getattr(self.context, 'geom_type', None)
or self.context['type']) or self.context['type'])
@property @property
def exterior(self): def exterior(self):
return (getattr(self.context, 'exterior', None) return (getattr(self.context, 'exterior', None)
or self.context['coordinates'][0]) or self.context['coordinates'][0])
@property @property
def interiors(self): def interiors(self):
value = getattr(self.context, 'interiors', None) value = getattr(self.context, 'interiors', None)

View File

@ -8,19 +8,23 @@
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QSettings from PyQt5.QtCore import Qt, QSettings
from shapely.geometry import LineString, LinearRing, MultiLineString from camlib import distance, arc, FlatCAMRTreeStorage
from shapely.ops import cascaded_union
import shapely.affinity as affinity
from numpy import arctan2, Inf, array, sqrt, sign, dot
from rtree import index as rtindex
from camlib import *
from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, SpinBoxDelegate from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, SpinBoxDelegate
from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
from flatcamParsers.ParseExcellon import Excellon from flatcamParsers.ParseExcellon import Excellon
import FlatCAMApp
from copy import copy, deepcopy from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point
import shapely.affinity as affinity
import numpy as np
from rtree import index as rtindex
import traceback
import math
import logging
from copy import deepcopy
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -30,6 +34,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class FCDrillAdd(FCShapeTool): class FCDrillAdd(FCShapeTool):
""" """
@ -556,7 +562,6 @@ class FCSlotArray(FCShapeTool):
_("To add an Slot Array first select a tool in Tool Table")) _("To add an Slot Array first select a tool in Tool Table"))
return return
try: try:
QtGui.QGuiApplication.restoreOverrideCursor() QtGui.QGuiApplication.restoreOverrideCursor()
except Exception as e: except Exception as e:
@ -1006,7 +1011,6 @@ class FCDrillResize(FCShapeTool):
sel_shapes_to_be_deleted.append(select_shape) sel_shapes_to_be_deleted.append(select_shape)
# a hack to make the tool_table display more drills/slots per diameter when shape(drill/slot) # a hack to make the tool_table display more drills/slots per diameter when shape(drill/slot)
# is added. # is added.
# self.points_edit it's only useful first time when we load the data into the storage # self.points_edit it's only useful first time when we load the data into the storage
@ -3084,7 +3088,6 @@ class FlatCAMExcEditor(QtCore.QObject):
# element[1] of the tuple is a list of coordinates (a tuple themselves) # element[1] of the tuple is a list of coordinates (a tuple themselves)
ordered_edited_points = sorted(zip(edited_points.keys(), edited_points.values())) ordered_edited_points = sorted(zip(edited_points.keys(), edited_points.values()))
current_tool = 0 current_tool = 0
for tool_dia in ordered_edited_points: for tool_dia in ordered_edited_points:
current_tool += 1 current_tool += 1
@ -3208,14 +3211,13 @@ class FlatCAMExcEditor(QtCore.QObject):
except KeyError: except KeyError:
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("There are no Tools definitions in the file. Aborting Excellon creation.") _("There are no Tools definitions in the file. Aborting Excellon creation.")
) )
except: except:
msg = '[ERROR] %s' % \ msg = '[ERROR] %s' % \
_("An internal error has ocurred. See Shell.\n") _("An internal error has ocurred. See Shell.\n")
msg += traceback.format_exc() msg += traceback.format_exc()
app_obj.inform.emit(msg) app_obj.inform.emit(msg)
raise return
# raise
with self.app.proc_container.new(_("Creating Excellon.")): with self.app.proc_container.new(_("Creating Excellon.")):
@ -3320,7 +3322,7 @@ class FlatCAMExcEditor(QtCore.QObject):
self.pos = self.canvas.translate_coords(event_pos) self.pos = self.canvas.translate_coords(event_pos)
if self.app.grid_status() == True: if self.app.grid_status() == True:
self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1]) self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
else: else:
self.pos = (self.pos[0], self.pos[1]) self.pos = (self.pos[0], self.pos[1])
@ -3555,14 +3557,14 @@ class FlatCAMExcEditor(QtCore.QObject):
for storage in self.storage_dict: for storage in self.storage_dict:
for obj in self.storage_dict[storage].get_objects(): for obj in self.storage_dict[storage].get_objects():
if (sel_type is True and poly_selection.contains(obj.geo)) or \ if (sel_type is True and poly_selection.contains(obj.geo)) or \
(sel_type is False and poly_selection.intersects(obj.geo)): (sel_type is False and poly_selection.intersects(obj.geo)):
if obj in self.selected: if obj in self.selected:
# remove the shape object from the selected shapes storage # remove the shape object from the selected shapes storage
self.selected.remove(obj) self.selected.remove(obj)
else: else:
# add the shape object to the selected shapes storage # add the shape object to the selected shapes storage
self.selected.append(obj) self.selected.append(obj)
else: else:
# clear the selection shapes storage # clear the selection shapes storage
self.selected = [] self.selected = []
@ -3878,7 +3880,8 @@ class FlatCAMExcEditor(QtCore.QObject):
# self.points_edit it's only useful first time when we load the data into the storage # self.points_edit it's only useful first time when we load the data into the storage
# but is still used as referecen when building tool_table in self.build_ui() # but is still used as referecen when building tool_table in self.build_ui()
# the number of drills displayed in column 2 is just a len(self.points_edit) therefore # the number of drills displayed in column 2 is just a len(self.points_edit) therefore
# deleting self.points_edit elements (doesn't matter who but just the number) solved the display issue. # deleting self.points_edit elements (doesn't matter who but just the number)
# solved the display issue.
del self.points_edit[storage][0] del self.points_edit[storage][0]
else: else:
self.storage_dict[storage].remove(del_shape) self.storage_dict[storage].remove(del_shape)
@ -3998,10 +4001,10 @@ class FlatCAMExcEditor(QtCore.QObject):
def get_shapely_list_bounds(geometry_list): def get_shapely_list_bounds(geometry_list):
xmin = Inf xmin = np.Inf
ymin = Inf ymin = np.Inf
xmax = -Inf xmax = -np.Inf
ymax = -Inf ymax = -np.Inf
for gs in geometry_list: for gs in geometry_list:
try: try:

View File

@ -13,24 +13,26 @@
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QSettings from PyQt5.QtCore import Qt, QSettings
from camlib import *
from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from flatcamGUI.ObjectUI import LengthEntry, RadioSet from flatcamGUI.ObjectUI import RadioSet
from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog
from flatcamParsers.ParseFont import *
import FlatCAMApp
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
# from shapely.geometry import mapping
from shapely.ops import cascaded_union, unary_union from shapely.ops import cascaded_union, unary_union
import shapely.affinity as affinity import shapely.affinity as affinity
from shapely.geometry.polygon import orient from shapely.geometry.polygon import orient
from numpy import arctan2, Inf, array, sqrt, sign, dot import numpy as np
from numpy.linalg import norm as numpy_norm from numpy.linalg import norm as numpy_norm
from rtree import index as rtindex from rtree import index as rtindex
from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog
from flatcamParsers.ParseFont import *
from copy import deepcopy
# from vispy.io import read_png # from vispy.io import read_png
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -684,8 +686,8 @@ class TransformEditorTool(FlatCAMTool):
self.rotate_button.set_value(_("Rotate")) self.rotate_button.set_value(_("Rotate"))
self.rotate_button.setToolTip( self.rotate_button.setToolTip(
_("Rotate the selected shape(s).\n" _("Rotate the selected shape(s).\n"
"The point of reference is the middle of\n" "The point of reference is the middle of\n"
"the bounding box for all selected shapes.") "the bounding box for all selected shapes.")
) )
self.rotate_button.setFixedWidth(60) self.rotate_button.setFixedWidth(60)
@ -802,7 +804,7 @@ class TransformEditorTool(FlatCAMTool):
self.scale_link_cb.setText(_("Link")) self.scale_link_cb.setText(_("Link"))
self.scale_link_cb.setToolTip( self.scale_link_cb.setToolTip(
_("Scale the selected shape(s)\n" _("Scale the selected shape(s)\n"
"using the Scale Factor X for both axis.")) "using the Scale Factor X for both axis."))
self.scale_link_cb.setFixedWidth(50) self.scale_link_cb.setFixedWidth(50)
self.scale_zero_ref_cb = FCCheckBox() self.scale_zero_ref_cb = FCCheckBox()
@ -1060,7 +1062,6 @@ class TransformEditorTool(FlatCAMTool):
_("Transformation cancelled. No shape selected.")) _("Transformation cancelled. No shape selected."))
return return
self.draw_app.select_tool("select") self.draw_app.select_tool("select")
self.app.ui.notebook.setTabText(2, "Tools") self.app.ui.notebook.setTabText(2, "Tools")
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab) self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
@ -1081,10 +1082,7 @@ class TransformEditorTool(FlatCAMTool):
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Wrong value format entered, use a number.")) _("Wrong value format entered, use a number."))
return return
self.app.worker_task.emit({'fcn': self.on_rotate_action, self.app.worker_task.emit({'fcn': self.on_rotate_action, 'params': [value]})
'params': [value]})
# self.on_rotate_action(value)
return
def on_flipx(self): def on_flipx(self):
# self.on_flip("Y") # self.on_flip("Y")
@ -1205,13 +1203,9 @@ class TransformEditorTool(FlatCAMTool):
axis = 'Y' axis = 'Y'
point = (0, 0) point = (0, 0)
if self.scale_zero_ref_cb.get_value(): if self.scale_zero_ref_cb.get_value():
self.app.worker_task.emit({'fcn': self.on_scale, self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue, point]})
'params': [axis, xvalue, yvalue, point]})
# self.on_scale("Y", xvalue, yvalue, point=(0,0))
else: else:
# self.on_scale("Y", xvalue, yvalue) self.app.worker_task.emit({'fcn': self.on_scale, 'params': [axis, xvalue, yvalue]})
self.app.worker_task.emit({'fcn': self.on_scale,
'params': [axis, xvalue, yvalue]})
return return
@ -1304,7 +1298,7 @@ class TransformEditorTool(FlatCAMTool):
except Exception as e: except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
(_("Rotation action was not executed"),str(e))) (_("Rotation action was not executed"), str(e)))
return return
def on_flip(self, axis): def on_flip(self, axis):
@ -1664,10 +1658,10 @@ class DrawToolShape(object):
# now it can get bounds for nested lists of objects # now it can get bounds for nested lists of objects
def bounds_rec(shape_el): def bounds_rec(shape_el):
if type(shape_el) is list: if type(shape_el) is list:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in shape_el: for k in shape_el:
minx_, miny_, maxx_, maxy_ = bounds_rec(k) minx_, miny_, maxx_, maxy_ = bounds_rec(k)
@ -1904,10 +1898,10 @@ class DrawTool(object):
def bounds(self, obj): def bounds(self, obj):
def bounds_rec(o): def bounds_rec(o):
if type(o) is list: if type(o) is list:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in o: for k in o:
try: try:
@ -1977,7 +1971,7 @@ class FCCircle(FCShapeTool):
if len(self.points) == 1: if len(self.points) == 1:
p1 = self.points[0] p1 = self.points[0]
p2 = data p2 = data
radius = sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) radius = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
return DrawToolUtilityShape(Point(p1).buffer(radius, int(self.steps_per_circ / 4))) return DrawToolUtilityShape(Point(p1).buffer(radius, int(self.steps_per_circ / 4)))
return None return None
@ -2086,36 +2080,36 @@ class FCArc(FCShapeTool):
p1 = self.points[1] p1 = self.points[1]
p2 = data p2 = data
radius = sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2) radius = np.sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2)
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) stopangle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle, return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle,
self.direction, self.steps_per_circ)), self.direction, self.steps_per_circ)),
Point(center)]) Point(center)])
elif self.mode == '132': elif self.mode == '132':
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p3 = array(self.points[1]) p3 = np.array(self.points[1])
p2 = array(data) p2 = np.array(data)
try: try:
center, radius, t = three_point_circle(p1, p2, p3) center, radius, t = three_point_circle(p1, p2, p3)
except TypeError: except TypeError:
return return
direction = 'cw' if sign(t) > 0 else 'ccw' direction = 'cw' if np.sign(t) > 0 else 'ccw'
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p3[1] - center[1], p3[0] - center[0]) stopangle = np.arctan2(p3[1] - center[1], p3[0] - center[0])
return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle, return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle,
direction, self.steps_per_circ)), direction, self.steps_per_circ)),
Point(center), Point(p1), Point(p3)]) Point(center), Point(p1), Point(p3)])
else: # '12c' else: # '12c'
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p2 = array(self.points[1]) p2 = np.array(self.points[1])
# Midpoint # Midpoint
a = (p1 + p2) / 2.0 a = (p1 + p2) / 2.0
@ -2124,7 +2118,7 @@ class FCArc(FCShapeTool):
c = p2 - p1 c = p2 - p1
# Perpendicular vector # Perpendicular vector
b = dot(c, array([[0, -1], [1, 0]], dtype=float32)) b = np.dot(c, np.array([[0, -1], [1, 0]], dtype=np.float32))
b /= numpy_norm(b) b /= numpy_norm(b)
# Distance # Distance
@ -2133,14 +2127,14 @@ class FCArc(FCShapeTool):
# Which side? Cross product with c. # Which side? Cross product with c.
# cross(M-A, B-A), where line is AB and M is test point. # cross(M-A, B-A), where line is AB and M is test point.
side = (data[0] - p1[0]) * c[1] - (data[1] - p1[1]) * c[0] side = (data[0] - p1[0]) * c[1] - (data[1] - p1[1]) * c[0]
t *= sign(side) t *= np.sign(side)
# Center = a + bt # Center = a + bt
center = a + b * t center = a + b * t
radius = numpy_norm(center - p1) radius = numpy_norm(center - p1)
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) stopangle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle, return DrawToolUtilityShape([LineString(arc(center, radius, startangle, stopangle,
self.direction, self.steps_per_circ)), self.direction, self.steps_per_circ)),
@ -2156,29 +2150,29 @@ class FCArc(FCShapeTool):
p2 = self.points[2] p2 = self.points[2]
radius = distance(center, p1) radius = distance(center, p1)
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) stopangle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle, self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
self.direction, self.steps_per_circ))) self.direction, self.steps_per_circ)))
elif self.mode == '132': elif self.mode == '132':
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p3 = array(self.points[1]) p3 = np.array(self.points[1])
p2 = array(self.points[2]) p2 = np.array(self.points[2])
center, radius, t = three_point_circle(p1, p2, p3) center, radius, t = three_point_circle(p1, p2, p3)
direction = 'cw' if sign(t) > 0 else 'ccw' direction = 'cw' if np.sign(t) > 0 else 'ccw'
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p3[1] - center[1], p3[0] - center[0]) stopangle = np.arctan2(p3[1] - center[1], p3[0] - center[0])
self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle, self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
direction, self.steps_per_circ))) direction, self.steps_per_circ)))
else: # self.mode == '12c' else: # self.mode == '12c'
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p2 = array(self.points[1]) p2 = np.array(self.points[1])
pc = array(self.points[2]) pc = np.array(self.points[2])
# Midpoint # Midpoint
a = (p1 + p2) / 2.0 a = (p1 + p2) / 2.0
@ -2187,7 +2181,7 @@ class FCArc(FCShapeTool):
c = p2 - p1 c = p2 - p1
# Perpendicular vector # Perpendicular vector
b = dot(c, array([[0, -1], [1, 0]], dtype=float32)) b = np.dot(c, np.array([[0, -1], [1, 0]], dtype=np.float32))
b /= numpy_norm(b) b /= numpy_norm(b)
# Distance # Distance
@ -2196,14 +2190,14 @@ class FCArc(FCShapeTool):
# Which side? Cross product with c. # Which side? Cross product with c.
# cross(M-A, B-A), where line is AB and M is test point. # cross(M-A, B-A), where line is AB and M is test point.
side = (pc[0] - p1[0]) * c[1] - (pc[1] - p1[1]) * c[0] side = (pc[0] - p1[0]) * c[1] - (pc[1] - p1[1]) * c[0]
t *= sign(side) t *= np.sign(side)
# Center = a + bt # Center = a + bt
center = a + b * t center = a + b * t
radius = numpy_norm(center - p1) radius = numpy_norm(center - p1)
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) stopangle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle, self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
self.direction, self.steps_per_circ))) self.direction, self.steps_per_circ)))
@ -2228,7 +2222,7 @@ class FCRectangle(FCShapeTool):
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) QtGui.QGuiApplication.setOverrideCursor(self.cursor)
self.draw_app.app.inform.emit( _("Click on 1st corner ...")) self.draw_app.app.inform.emit(_("Click on 1st corner ..."))
def click(self, point): def click(self, point):
self.points.append(point) self.points.append(point)
@ -2705,7 +2699,6 @@ class FCText(FCShapeTool):
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_text.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_text.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) QtGui.QGuiApplication.setOverrideCursor(self.cursor)
# self.shape_buffer = self.draw_app.shape_buffer # self.shape_buffer = self.draw_app.shape_buffer
self.draw_app = draw_app self.draw_app = draw_app
self.app = draw_app.app self.app = draw_app.app
@ -2728,21 +2721,19 @@ class FCText(FCShapeTool):
log.debug("Font geometry is empty or incorrect: %s" % str(e)) log.debug("Font geometry is empty or incorrect: %s" % str(e))
self.draw_app.app.inform.emit('[ERROR] %s: %s' % self.draw_app.app.inform.emit('[ERROR] %s: %s' %
(_("Font not supported. Only Regular, Bold, Italic and BoldItalic are " (_("Font not supported. Only Regular, Bold, Italic and BoldItalic are "
"supported. Error"), str(e))) "supported. Error"), str(e)))
self.text_gui.text_path = [] self.text_gui.text_path = []
self.text_gui.hide_tool() self.text_gui.hide_tool()
self.draw_app.select_tool('select') self.draw_app.select_tool('select')
return return
else: else:
self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % _("No text to add."))
_("No text to add."))
return return
self.text_gui.text_path = [] self.text_gui.text_path = []
self.text_gui.hide_tool() self.text_gui.hide_tool()
self.complete = True self.complete = True
self.draw_app.app.inform.emit('[success]%s' % self.draw_app.app.inform.emit('[success]%s' % _(" Done. Adding Text completed."))
_(" Done. Adding Text completed."))
def utility_geometry(self, data=None): def utility_geometry(self, data=None):
""" """
@ -3060,7 +3051,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
"text": {"button": self.app.ui.geo_add_text_btn, "text": {"button": self.app.ui.geo_add_text_btn,
"constructor": FCText}, "constructor": FCText},
"buffer": {"button": self.app.ui.geo_add_buffer_btn, "buffer": {"button": self.app.ui.geo_add_buffer_btn,
"constructor": FCBuffer}, "constructor": FCBuffer},
"paint": {"button": self.app.ui.geo_add_paint_btn, "paint": {"button": self.app.ui.geo_add_paint_btn,
"constructor": FCPaint}, "constructor": FCPaint},
"eraser": {"button": self.app.ui.geo_eraser_btn, "eraser": {"button": self.app.ui.geo_eraser_btn,
@ -3068,11 +3059,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
"move": {"button": self.app.ui.geo_move_btn, "move": {"button": self.app.ui.geo_move_btn,
"constructor": FCMove}, "constructor": FCMove},
"transform": {"button": self.app.ui.geo_transform_btn, "transform": {"button": self.app.ui.geo_transform_btn,
"constructor": FCTransform}, "constructor": FCTransform},
"copy": {"button": self.app.ui.geo_copy_btn, "copy": {"button": self.app.ui.geo_copy_btn,
"constructor": FCCopy}, "constructor": FCCopy},
"explode": {"button": self.app.ui.geo_explode_btn, "explode": {"button": self.app.ui.geo_explode_btn,
"constructor": FCExplode} "constructor": FCExplode}
} }
# # ## Data # # ## Data
@ -3502,7 +3493,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
except (TypeError, AttributeError): except (TypeError, AttributeError):
pass pass
try: try:
self.app.ui.draw_text.triggered.disconnect() self.app.ui.draw_text.triggered.disconnect()
except (TypeError, AttributeError): except (TypeError, AttributeError):
@ -3561,12 +3551,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.add_shape(subshape) self.add_shape(subshape)
return return
assert isinstance(shape, DrawToolShape), \ assert isinstance(shape, DrawToolShape), "Expected a DrawToolShape, got %s" % type(shape)
"Expected a DrawToolShape, got %s" % type(shape) assert shape.geo is not None, "Shape object has empty geometry (None)"
assert shape.geo is not None, \
"Shape object has empty geometry (None)"
assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \ assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
not isinstance(shape.geo, list), "Shape objects has empty geometry ([])" not isinstance(shape.geo, list), "Shape objects has empty geometry ([])"
@ -4122,13 +4108,17 @@ class FlatCAMGeoEditor(QtCore.QObject):
continue continue
if shape in self.selected: if shape in self.selected:
self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_sel_draw_color'] + 'FF', linewidth=2) self.plot_shape(geometry=shape.geo,
color=self.app.defaults['global_sel_draw_color'] + 'FF',
linewidth=2)
continue continue
self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_draw_color'] + "FF") self.plot_shape(geometry=shape.geo,
color=self.app.defaults['global_draw_color'] + "FF")
for shape in self.utility: for shape in self.utility:
self.plot_shape(geometry=shape.geo, linewidth=1) self.plot_shape(geometry=shape.geo,
linewidth=1)
continue continue
self.shapes.redraw() self.shapes.redraw()
@ -4237,7 +4227,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
""" """
snap_x, snap_y = (x, y) snap_x, snap_y = (x, y)
snap_distance = Inf snap_distance = np.Inf
# # ## Object (corner?) snap # # ## Object (corner?) snap
# # ## No need for the objects, just the coordinates # # ## No need for the objects, just the coordinates
@ -4488,7 +4478,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
results = [] results = []
for t in selected: for t in selected:
if isinstance(t.geo, Polygon) and not t.geo.is_empty: if isinstance(t.geo, Polygon) and not t.geo.is_empty:
results.append((t.geo.exterior).buffer( results.append(t.geo.exterior.buffer(
buf_distance - 1e-10, buf_distance - 1e-10,
resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4), resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4),
join_style=join_style) join_style=join_style)
@ -4519,22 +4509,18 @@ class FlatCAMGeoEditor(QtCore.QObject):
selected = self.get_selected() selected = self.get_selected()
if buf_distance < 0: if buf_distance < 0:
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' % _("Negative buffer value is not accepted."))
_("Negative buffer value is not accepted.")
)
# deselect everything # deselect everything
self.selected = [] self.selected = []
self.replot() self.replot()
return 'fail' return 'fail'
if len(selected) == 0: if len(selected) == 0:
self.app.inform.emit('[WARNING_NOTCL] %s' % self.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected for buffering."))
_("Nothing selected for buffering."))
return 'fail' return 'fail'
if not isinstance(buf_distance, float): if not isinstance(buf_distance, float):
self.app.inform.emit('[WARNING_NOTCL] %s' % self.app.inform.emit('[WARNING_NOTCL] %s' % _("Invalid distance for buffering."))
_("Invalid distance for buffering."))
# deselect everything # deselect everything
self.selected = [] self.selected = []
self.replot() self.replot()
@ -4546,7 +4532,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
t.geo = Polygon(t.geo) t.geo = Polygon(t.geo)
if isinstance(t.geo, Polygon) and not t.geo.is_empty: if isinstance(t.geo, Polygon) and not t.geo.is_empty:
results.append((t.geo).buffer( results.append(t.geo.buffer(
-buf_distance + 1e-10, -buf_distance + 1e-10,
resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4), resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4),
join_style=join_style) join_style=join_style)
@ -4564,8 +4550,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.add_shape(DrawToolShape(sha)) self.add_shape(DrawToolShape(sha))
self.replot() self.replot()
self.app.inform.emit('[success] %s' % self.app.inform.emit('[success] %s' % _("Interior buffer geometry created."))
_("Interior buffer geometry created."))
def buffer_ext(self, buf_distance, join_style): def buffer_ext(self, buf_distance, join_style):
selected = self.get_selected() selected = self.get_selected()
@ -4598,7 +4583,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
t.geo = Polygon(t.geo) t.geo = Polygon(t.geo)
if isinstance(t.geo, Polygon) and not t.geo.is_empty: if isinstance(t.geo, Polygon) and not t.geo.is_empty:
results.append((t.geo).buffer( results.append(t.geo.buffer(
buf_distance, buf_distance,
resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4), resolution=int(int(self.app.defaults["geometry_circle_steps"]) / 4),
join_style=join_style) join_style=join_style)
@ -4616,68 +4601,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.add_shape(DrawToolShape(sha)) self.add_shape(DrawToolShape(sha))
self.replot() self.replot()
self.app.inform.emit('[success] %s' % self.app.inform.emit('[success] %s' % _("Exterior buffer geometry created."))
_("Exterior buffer geometry created."))
# def paint(self, tooldia, overlap, margin, method):
# selected = self.get_selected()
#
# if len(selected) == 0:
# self.app.inform.emit("[WARNING] Nothing selected for painting.")
# return
#
# for param in [tooldia, overlap, margin]:
# if not isinstance(param, float):
# param_name = [k for k, v in locals().items() if v is param][0]
# self.app.inform.emit("[WARNING] Invalid value for {}".format(param))
#
# # Todo: Check for valid method.
#
# # Todo: This is the 3rd implementation on painting polys... try to consolidate
#
# results = []
#
# def recurse(geo):
# try:
# for subg in geo:
# for subsubg in recurse(subg):
# yield subsubg
# except TypeError:
# if isinstance(geo, LinearRing):
# yield geo
#
# raise StopIteration
#
# for geo in selected:
# print(type(geo.geo))
#
# local_results = []
# for poly in recurse(geo.geo):
# if method == "seed":
# # Type(cp) == FlatCAMRTreeStorage | None
# cp = Geometry.clear_polygon2(poly.buffer(-margin),
# tooldia, overlap=overlap)
#
# else:
# # Type(cp) == FlatCAMRTreeStorage | None
# cp = Geometry.clear_polygon(poly.buffer(-margin),
# tooldia, overlap=overlap)
#
# if cp is not None:
# local_results += list(cp.get_objects())
#
# results.append(cascaded_union(local_results))
#
# # This is a dirty patch:
# for r in results:
# self.add_shape(DrawToolShape(r))
#
# self.replot()
def paint(self, tooldia, overlap, margin, connect, contour, method): def paint(self, tooldia, overlap, margin, connect, contour, method):
self.paint_tooldia = tooldia self.paint_tooldia = tooldia
selected = self.get_selected() selected = self.get_selected()
if len(selected) == 0: if len(selected) == 0:
@ -4814,11 +4742,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
def distance(pt1, pt2): def distance(pt1, pt2):
return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) return np.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
def mag(vec): def mag(vec):
return sqrt(vec[0] ** 2 + vec[1] ** 2) return np.sqrt(vec[0] ** 2 + vec[1] ** 2)
def poly2rings(poly): def poly2rings(poly):
@ -4826,10 +4754,10 @@ def poly2rings(poly):
def get_shapely_list_bounds(geometry_list): def get_shapely_list_bounds(geometry_list):
xmin = Inf xmin = np.Inf
ymin = Inf ymin = np.Inf
xmax = -Inf xmax = -np.Inf
ymax = -Inf ymax = -np.Inf
for gs in geometry_list: for gs in geometry_list:
try: try:

View File

@ -8,29 +8,28 @@
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QSettings from PyQt5.QtCore import Qt, QSettings
from shapely.geometry import LineString, LinearRing, MultiLineString from shapely.geometry import LineString, LinearRing, MultiLineString, Point, Polygon, MultiPolygon
# from shapely.geometry import mapping from shapely.ops import cascaded_union
from shapely.ops import cascaded_union, unary_union
import shapely.affinity as affinity import shapely.affinity as affinity
from numpy import arctan2, Inf, array, sqrt, sign, dot
from rtree import index as rtindex
import threading import threading
import time import time
from copy import copy, deepcopy from copy import copy, deepcopy
import logging
from camlib import * from camlib import distance, arc, three_point_circle
from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, \ from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, \
SpinBoxDelegate, EvalEntry, EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
from FlatCAMObj import FlatCAMGerber
from flatcamParsers.ParseGerber import Gerber
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
import FlatCAMApp
import numpy as np
from numpy.linalg import norm as numpy_norm from numpy.linalg import norm as numpy_norm
import math
# from vispy.io import read_png # from vispy.io import read_png
# import pngcanvas # import pngcanvas
import traceback
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -39,6 +38,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class DrawToolShape(object): class DrawToolShape(object):
""" """
@ -147,10 +148,10 @@ class DrawTool(object):
def bounds(obj): def bounds(obj):
def bounds_rec(o): def bounds_rec(o):
if type(o) is list: if type(o) is list:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in o: for k in o:
try: try:
@ -311,13 +312,13 @@ class FCPad(FCShapeTool):
p4 = (point_x - self.half_width, point_y + self.half_height - self.half_width) p4 = (point_x - self.half_width, point_y + self.half_height - self.half_width)
down_center = [point_x, point_y - self.half_height + self.half_width] down_center = [point_x, point_y - self.half_height + self.half_width]
d_start_angle = math.pi d_start_angle = np.pi
d_stop_angle = 0.0 d_stop_angle = 0.0
down_arc = arc(down_center, self.half_width, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ) down_arc = arc(down_center, self.half_width, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
up_center = [point_x, point_y + self.half_height - self.half_width] up_center = [point_x, point_y + self.half_height - self.half_width]
u_start_angle = 0.0 u_start_angle = 0.0
u_stop_angle = math.pi u_stop_angle = np.pi
up_arc = arc(up_center, self.half_width, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ) up_arc = arc(up_center, self.half_width, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
geo.append(p1) geo.append(p1)
@ -340,13 +341,13 @@ class FCPad(FCShapeTool):
p4 = (point_x - self.half_width + self.half_height, point_y + self.half_height) p4 = (point_x - self.half_width + self.half_height, point_y + self.half_height)
left_center = [point_x - self.half_width + self.half_height, point_y] left_center = [point_x - self.half_width + self.half_height, point_y]
d_start_angle = math.pi / 2 d_start_angle = np.pi / 2
d_stop_angle = 1.5 * math.pi d_stop_angle = 1.5 * np.pi
left_arc = arc(left_center, self.half_height, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ) left_arc = arc(left_center, self.half_height, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
right_center = [point_x + self.half_width - self.half_height, point_y] right_center = [point_x + self.half_width - self.half_height, point_y]
u_start_angle = 1.5 * math.pi u_start_angle = 1.5 * np.pi
u_stop_angle = math.pi / 2 u_stop_angle = np.pi / 2
right_arc = arc(right_center, self.half_height, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ) right_arc = arc(right_center, self.half_height, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
geo.append(p1) geo.append(p1)
@ -618,13 +619,13 @@ class FCPadArray(FCShapeTool):
p4 = (point_x - self.half_width, point_y + self.half_height - self.half_width) p4 = (point_x - self.half_width, point_y + self.half_height - self.half_width)
down_center = [point_x, point_y - self.half_height + self.half_width] down_center = [point_x, point_y - self.half_height + self.half_width]
d_start_angle = math.pi d_start_angle = np.pi
d_stop_angle = 0.0 d_stop_angle = 0.0
down_arc = arc(down_center, self.half_width, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ) down_arc = arc(down_center, self.half_width, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
up_center = [point_x, point_y + self.half_height - self.half_width] up_center = [point_x, point_y + self.half_height - self.half_width]
u_start_angle = 0.0 u_start_angle = 0.0
u_stop_angle = math.pi u_stop_angle = np.pi
up_arc = arc(up_center, self.half_width, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ) up_arc = arc(up_center, self.half_width, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
geo.append(p1) geo.append(p1)
@ -647,13 +648,13 @@ class FCPadArray(FCShapeTool):
p4 = (point_x - self.half_width + self.half_height, point_y + self.half_height) p4 = (point_x - self.half_width + self.half_height, point_y + self.half_height)
left_center = [point_x - self.half_width + self.half_height, point_y] left_center = [point_x - self.half_width + self.half_height, point_y]
d_start_angle = math.pi / 2 d_start_angle = np.pi / 2
d_stop_angle = 1.5 * math.pi d_stop_angle = 1.5 * np.pi
left_arc = arc(left_center, self.half_height, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ) left_arc = arc(left_center, self.half_height, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
right_center = [point_x + self.half_width - self.half_height, point_y] right_center = [point_x + self.half_width - self.half_height, point_y]
u_start_angle = 1.5 * math.pi u_start_angle = 1.5 * np.pi
u_stop_angle = math.pi / 2 u_stop_angle = np.pi / 2
right_arc = arc(right_center, self.half_height, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ) right_arc = arc(right_center, self.half_height, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
geo.append(p1) geo.append(p1)
@ -1296,7 +1297,7 @@ class FCTrack(FCRegion):
self.draw_app.bend_mode = 2 self.draw_app.bend_mode = 2
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_path2.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_path2.png'))
QtGui.QGuiApplication.setOverrideCursor(self.cursor) QtGui.QGuiApplication.setOverrideCursor(self.cursor)
msg = _('Track Mode 2: Reverse 45 degrees ...') msg = _('Track Mode 2: Reverse 45 degrees ...')
elif self.draw_app.bend_mode == 2: elif self.draw_app.bend_mode == 2:
self.draw_app.bend_mode = 3 self.draw_app.bend_mode = 3
self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_path3.png')) self.cursor = QtGui.QCursor(QtGui.QPixmap('share/aero_path3.png'))
@ -1415,7 +1416,7 @@ class FCDisc(FCShapeTool):
if len(self.points) == 1: if len(self.points) == 1:
p1 = self.points[0] p1 = self.points[0]
p2 = data p2 = data
radius = sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) radius = math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
new_geo_el['solid'] = Point(p1).buffer((radius + self.buf_val / 2), int(self.steps_per_circ / 4)) new_geo_el['solid'] = Point(p1).buffer((radius + self.buf_val / 2), int(self.steps_per_circ / 4))
return DrawToolUtilityShape(new_geo_el) return DrawToolUtilityShape(new_geo_el)
@ -1557,9 +1558,9 @@ class FCSemiDisc(FCShapeTool):
p1 = self.points[1] p1 = self.points[1]
p2 = data p2 = data
radius = sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2) + (self.buf_val / 2) radius = np.sqrt((center[0] - p1[0]) ** 2 + (center[1] - p1[1]) ** 2) + (self.buf_val / 2)
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) stopangle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
new_geo_el['solid'] = LineString( new_geo_el['solid'] = LineString(
arc(center, radius, startangle, stopangle, self.direction, self.steps_per_circ)) arc(center, radius, startangle, stopangle, self.direction, self.steps_per_circ))
@ -1567,20 +1568,20 @@ class FCSemiDisc(FCShapeTool):
return DrawToolUtilityShape([new_geo_el, new_geo_el_pt1]) return DrawToolUtilityShape([new_geo_el, new_geo_el_pt1])
elif self.mode == '132': elif self.mode == '132':
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p3 = array(self.points[1]) p3 = np.array(self.points[1])
p2 = array(data) p2 = np.array(data)
try: try:
center, radius, t = three_point_circle(p1, p2, p3) center, radius, t = three_point_circle(p1, p2, p3)
except TypeError: except TypeError:
return return
direction = 'cw' if sign(t) > 0 else 'ccw' direction = 'cw' if np.sign(t) > 0 else 'ccw'
radius += (self.buf_val / 2) radius += (self.buf_val / 2)
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p3[1] - center[1], p3[0] - center[0]) stopangle = np.arctan2(p3[1] - center[1], p3[0] - center[0])
new_geo_el['solid'] = LineString( new_geo_el['solid'] = LineString(
arc(center, radius, startangle, stopangle, direction, self.steps_per_circ)) arc(center, radius, startangle, stopangle, direction, self.steps_per_circ))
@ -1591,8 +1592,8 @@ class FCSemiDisc(FCShapeTool):
return DrawToolUtilityShape([new_geo_el, new_geo_el_pt2, new_geo_el_pt1, new_geo_el_pt3]) return DrawToolUtilityShape([new_geo_el, new_geo_el_pt2, new_geo_el_pt1, new_geo_el_pt3])
else: # '12c' else: # '12c'
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p2 = array(self.points[1]) p2 = np.array(self.points[1])
# Midpoint # Midpoint
a = (p1 + p2) / 2.0 a = (p1 + p2) / 2.0
@ -1600,7 +1601,7 @@ class FCSemiDisc(FCShapeTool):
c = p2 - p1 c = p2 - p1
# Perpendicular vector # Perpendicular vector
b = dot(c, array([[0, -1], [1, 0]], dtype=float32)) b = np.dot(c, np.array([[0, -1], [1, 0]], dtype=np.float32))
b /= numpy_norm(b) b /= numpy_norm(b)
# Distance # Distance
@ -1609,14 +1610,14 @@ class FCSemiDisc(FCShapeTool):
# Which side? Cross product with c. # Which side? Cross product with c.
# cross(M-A, B-A), where line is AB and M is test point. # cross(M-A, B-A), where line is AB and M is test point.
side = (data[0] - p1[0]) * c[1] - (data[1] - p1[1]) * c[0] side = (data[0] - p1[0]) * c[1] - (data[1] - p1[1]) * c[0]
t *= sign(side) t *= np.sign(side)
# Center = a + bt # Center = a + bt
center = a + b * t center = a + b * t
radius = numpy_norm(center - p1) + (self.buf_val / 2) radius = numpy_norm(center - p1) + (self.buf_val / 2)
startangle = arctan2(p1[1] - center[1], p1[0] - center[0]) startangle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stopangle = arctan2(p2[1] - center[1], p2[0] - center[0]) stopangle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
new_geo_el['solid'] = LineString( new_geo_el['solid'] = LineString(
arc(center, radius, startangle, stopangle, self.direction, self.steps_per_circ)) arc(center, radius, startangle, stopangle, self.direction, self.steps_per_circ))
@ -1636,8 +1637,8 @@ class FCSemiDisc(FCShapeTool):
p2 = self.points[2] p2 = self.points[2]
radius = distance(center, p1) + (self.buf_val / 2) radius = distance(center, p1) + (self.buf_val / 2)
start_angle = arctan2(p1[1] - center[1], p1[0] - center[0]) start_angle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stop_angle = arctan2(p2[1] - center[1], p2[0] - center[0]) stop_angle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
new_geo_el['solid'] = Polygon( new_geo_el['solid'] = Polygon(
arc(center, radius, start_angle, stop_angle, self.direction, self.steps_per_circ)) arc(center, radius, start_angle, stop_angle, self.direction, self.steps_per_circ))
new_geo_el['follow'] = Polygon( new_geo_el['follow'] = Polygon(
@ -1645,16 +1646,16 @@ class FCSemiDisc(FCShapeTool):
self.geometry = DrawToolShape(new_geo_el) self.geometry = DrawToolShape(new_geo_el)
elif self.mode == '132': elif self.mode == '132':
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p3 = array(self.points[1]) p3 = np.array(self.points[1])
p2 = array(self.points[2]) p2 = np.array(self.points[2])
center, radius, t = three_point_circle(p1, p2, p3) center, radius, t = three_point_circle(p1, p2, p3)
direction = 'cw' if sign(t) > 0 else 'ccw' direction = 'cw' if np.sign(t) > 0 else 'ccw'
radius += (self.buf_val / 2) radius += (self.buf_val / 2)
start_angle = arctan2(p1[1] - center[1], p1[0] - center[0]) start_angle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stop_angle = arctan2(p3[1] - center[1], p3[0] - center[0]) stop_angle = np.arctan2(p3[1] - center[1], p3[0] - center[0])
new_geo_el['solid'] = Polygon(arc(center, radius, start_angle, stop_angle, direction, self.steps_per_circ)) new_geo_el['solid'] = Polygon(arc(center, radius, start_angle, stop_angle, direction, self.steps_per_circ))
new_geo_el['follow'] = Polygon( new_geo_el['follow'] = Polygon(
@ -1662,9 +1663,9 @@ class FCSemiDisc(FCShapeTool):
self.geometry = DrawToolShape(new_geo_el) self.geometry = DrawToolShape(new_geo_el)
else: # self.mode == '12c' else: # self.mode == '12c'
p1 = array(self.points[0]) p1 = np.array(self.points[0])
p2 = array(self.points[1]) p2 = np.array(self.points[1])
pc = array(self.points[2]) pc = np.array(self.points[2])
# Midpoint # Midpoint
a = (p1 + p2) / 2.0 a = (p1 + p2) / 2.0
@ -1673,7 +1674,7 @@ class FCSemiDisc(FCShapeTool):
c = p2 - p1 c = p2 - p1
# Perpendicular vector # Perpendicular vector
b = dot(c, array([[0, -1], [1, 0]], dtype=float32)) b = np.dot(c, np.array([[0, -1], [1, 0]], dtype=np.float32))
b /= numpy_norm(b) b /= numpy_norm(b)
# Distance # Distance
@ -1682,14 +1683,14 @@ class FCSemiDisc(FCShapeTool):
# Which side? Cross product with c. # Which side? Cross product with c.
# cross(M-A, B-A), where line is AB and M is test point. # cross(M-A, B-A), where line is AB and M is test point.
side = (pc[0] - p1[0]) * c[1] - (pc[1] - p1[1]) * c[0] side = (pc[0] - p1[0]) * c[1] - (pc[1] - p1[1]) * c[0]
t *= sign(side) t *= np.sign(side)
# Center = a + bt # Center = a + bt
center = a + b * t center = a + b * t
radius = numpy_norm(center - p1) + (self.buf_val / 2) radius = numpy_norm(center - p1) + (self.buf_val / 2)
start_angle = arctan2(p1[1] - center[1], p1[0] - center[0]) start_angle = np.arctan2(p1[1] - center[1], p1[0] - center[0])
stop_angle = arctan2(p2[1] - center[1], p2[0] - center[0]) stop_angle = np.arctan2(p2[1] - center[1], p2[0] - center[0])
new_geo_el['solid'] = Polygon( new_geo_el['solid'] = Polygon(
arc(center, radius, start_angle, stop_angle, self.direction, self.steps_per_circ)) arc(center, radius, start_angle, stop_angle, self.direction, self.steps_per_circ))
@ -2437,9 +2438,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.apertures_box.addLayout(grid1) self.apertures_box.addLayout(grid1)
apcode_lbl = QtWidgets.QLabel('%s:' % _('Aperture Code')) apcode_lbl = QtWidgets.QLabel('%s:' % _('Aperture Code'))
apcode_lbl.setToolTip( apcode_lbl.setToolTip(_("Code for the new aperture"))
_("Code for the new aperture")
)
grid1.addWidget(apcode_lbl, 1, 0) grid1.addWidget(apcode_lbl, 1, 0)
self.apcode_entry = FCEntry() self.apcode_entry = FCEntry()
@ -2448,11 +2447,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
apsize_lbl = QtWidgets.QLabel('%s:' % _('Aperture Size')) apsize_lbl = QtWidgets.QLabel('%s:' % _('Aperture Size'))
apsize_lbl.setToolTip( apsize_lbl.setToolTip(
_("Size for the new aperture.\n" _("Size for the new aperture.\n"
"If aperture type is 'R' or 'O' then\n" "If aperture type is 'R' or 'O' then\n"
"this value is automatically\n" "this value is automatically\n"
"calculated as:\n" "calculated as:\n"
"sqrt(width**2 + height**2)") "sqrt(width**2 + height**2)")
) )
grid1.addWidget(apsize_lbl, 2, 0) grid1.addWidget(apsize_lbl, 2, 0)
@ -2462,10 +2461,10 @@ class FlatCAMGrbEditor(QtCore.QObject):
aptype_lbl = QtWidgets.QLabel('%s:' % _('Aperture Type')) aptype_lbl = QtWidgets.QLabel('%s:' % _('Aperture Type'))
aptype_lbl.setToolTip( aptype_lbl.setToolTip(
_("Select the type of new aperture. Can be:\n" _("Select the type of new aperture. Can be:\n"
"C = circular\n" "C = circular\n"
"R = rectangular\n" "R = rectangular\n"
"O = oblong") "O = oblong")
) )
grid1.addWidget(aptype_lbl, 3, 0) grid1.addWidget(aptype_lbl, 3, 0)
@ -2475,9 +2474,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.apdim_lbl = QtWidgets.QLabel('%s:' % _('Aperture Dim')) self.apdim_lbl = QtWidgets.QLabel('%s:' % _('Aperture Dim'))
self.apdim_lbl.setToolTip( self.apdim_lbl.setToolTip(
_("Dimensions for the new aperture.\n" _("Dimensions for the new aperture.\n"
"Active only for rectangular apertures (type R).\n" "Active only for rectangular apertures (type R).\n"
"The format is (width, height)") "The format is (width, height)")
) )
grid1.addWidget(self.apdim_lbl, 4, 0) grid1.addWidget(self.apdim_lbl, 4, 0)
@ -2495,12 +2494,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.addaperture_btn = QtWidgets.QPushButton(_('Add')) self.addaperture_btn = QtWidgets.QPushButton(_('Add'))
self.addaperture_btn.setToolTip( self.addaperture_btn.setToolTip(
_( "Add a new aperture to the aperture list.") _("Add a new aperture to the aperture list.")
) )
self.delaperture_btn = QtWidgets.QPushButton(_('Delete')) self.delaperture_btn = QtWidgets.QPushButton(_('Delete'))
self.delaperture_btn.setToolTip( self.delaperture_btn.setToolTip(
_( "Delete a aperture in the aperture list") _("Delete a aperture in the aperture list")
) )
hlay_ad.addWidget(self.addaperture_btn) hlay_ad.addWidget(self.addaperture_btn)
hlay_ad.addWidget(self.delaperture_btn) hlay_ad.addWidget(self.delaperture_btn)
@ -2677,8 +2676,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.array_type_combo = FCComboBox() self.array_type_combo = FCComboBox()
self.array_type_combo.setToolTip( self.array_type_combo.setToolTip(
_( "Select the type of pads array to create.\n" _("Select the type of pads array to create.\n"
"It can be Linear X(Y) or Circular") "It can be Linear X(Y) or Circular")
) )
self.array_type_combo.addItem(_("Linear")) self.array_type_combo.addItem(_("Linear"))
self.array_type_combo.addItem(_("Circular")) self.array_type_combo.addItem(_("Circular"))
@ -2733,10 +2732,10 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle')) self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
self.linear_angle_label.setToolTip( self.linear_angle_label.setToolTip(
_( "Angle at which the linear array is placed.\n" _("Angle at which the linear array is placed.\n"
"The precision is of max 2 decimals.\n" "The precision is of max 2 decimals.\n"
"Min value is: -359.99 degrees.\n" "Min value is: -359.99 degrees.\n"
"Max value is: 360.00 degrees.") "Max value is: 360.00 degrees.")
) )
self.linear_angle_label.setMinimumWidth(100) self.linear_angle_label.setMinimumWidth(100)
@ -2808,9 +2807,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
"scale": {"button": self.app.ui.aperture_scale_btn, "scale": {"button": self.app.ui.aperture_scale_btn,
"constructor": FCScale}, "constructor": FCScale},
"markarea": {"button": self.app.ui.aperture_markarea_btn, "markarea": {"button": self.app.ui.aperture_markarea_btn,
"constructor": FCMarkArea}, "constructor": FCMarkArea},
"eraser": {"button": self.app.ui.aperture_eraser_btn, "eraser": {"button": self.app.ui.aperture_eraser_btn,
"constructor": FCEraser}, "constructor": FCEraser},
"copy": {"button": self.app.ui.aperture_copy_btn, "copy": {"button": self.app.ui.aperture_copy_btn,
"constructor": FCApertureCopy}, "constructor": FCApertureCopy},
"transform": {"button": self.app.ui.grb_transform_btn, "transform": {"button": self.app.ui.grb_transform_btn,
@ -3245,7 +3244,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.storage_dict[ap_id]['width'] = dims[0] self.storage_dict[ap_id]['width'] = dims[0]
self.storage_dict[ap_id]['height'] = dims[1] self.storage_dict[ap_id]['height'] = dims[1]
size_val = math.sqrt((dims[0] ** 2) + (dims[1] ** 2)) size_val = np.sqrt((dims[0] ** 2) + (dims[1] ** 2))
self.apsize_entry.set_value(size_val) self.apsize_entry.set_value(size_val)
except Exception as e: except Exception as e:
@ -3613,7 +3612,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.app.ui.grb_draw_eraser.triggered.connect(self.on_eraser) self.app.ui.grb_draw_eraser.triggered.connect(self.on_eraser)
self.app.ui.grb_draw_transformations.triggered.connect(self.on_transform) self.app.ui.grb_draw_transformations.triggered.connect(self.on_transform)
def disconnect_canvas_event_handlers(self): def disconnect_canvas_event_handlers(self):
# we restore the key and mouse control to FlatCAMApp method # we restore the key and mouse control to FlatCAMApp method
@ -3803,7 +3801,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
# we subtract the big "negative" (clear) geometry from each solid polygon but only the part of clear geometry # we subtract the big "negative" (clear) geometry from each solid polygon but only the part of clear geometry
# that fits inside the solid. otherwise we may loose the solid # that fits inside the solid. otherwise we may loose the solid
for apid in self.gerber_obj.apertures: for apid in self.gerber_obj.apertures:
temp_solid_geometry= [] temp_solid_geometry = []
if 'geometry' in self.gerber_obj.apertures[apid]: if 'geometry' in self.gerber_obj.apertures[apid]:
# for elem in self.gerber_obj.apertures[apid]['geometry']: # for elem in self.gerber_obj.apertures[apid]['geometry']:
# if 'solid' in elem: # if 'solid' in elem:
@ -6032,10 +6030,10 @@ class TransformEditorTool(FlatCAMTool):
def get_shapely_list_bounds(geometry_list): def get_shapely_list_bounds(geometry_list):
xmin = Inf xmin = np.Inf
ymin = Inf ymin = np.Inf
xmax = -Inf xmax = -np.Inf
ymax = -Inf ymax = -np.Inf
for gs in geometry_list: for gs in geometry_list:
try: try:

View File

@ -12,8 +12,12 @@
# ########################################################## # ##########################################################
from flatcamGUI.PreferencesUI import * from flatcamGUI.PreferencesUI import *
from flatcamEditors.FlatCAMGeoEditor import FCShapeTool
from matplotlib.backend_bases import KeyEvent as mpl_key_event from matplotlib.backend_bases import KeyEvent as mpl_key_event
import webbrowser
from copy import deepcopy
from datetime import datetime
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -70,15 +74,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.menufilenew = self.menufile.addMenu(QtGui.QIcon('share/file16.png'), _('&New')) self.menufilenew = self.menufile.addMenu(QtGui.QIcon('share/file16.png'), _('&New'))
self.menufilenew.setToolTipsVisible(True) self.menufilenew.setToolTipsVisible(True)
self.menufilenewgeo = self.menufilenew.addAction(QtGui.QIcon('share/geometry16.png'), _('Geometry\tN')) self.menufilenewgeo = self.menufilenew.addAction(QtGui.QIcon('share/new_file_geo16.png'), _('Geometry\tN'))
self.menufilenewgeo.setToolTip( self.menufilenewgeo.setToolTip(
_("Will create a new, empty Geometry Object.") _("Will create a new, empty Geometry Object.")
) )
self.menufilenewgrb = self.menufilenew.addAction(QtGui.QIcon('share/flatcam_icon32.png'), _('Gerber\tB')) self.menufilenewgrb = self.menufilenew.addAction(QtGui.QIcon('share/new_file_grb16.png'), _('Gerber\tB'))
self.menufilenewgrb.setToolTip( self.menufilenewgrb.setToolTip(
_("Will create a new, empty Gerber Object.") _("Will create a new, empty Gerber Object.")
) )
self.menufilenewexc = self.menufilenew.addAction(QtGui.QIcon('share/drill16.png'), _('Excellon\tL')) self.menufilenewexc = self.menufilenew.addAction(QtGui.QIcon('share/new_file_exc16.png'), _('Excellon\tL'))
self.menufilenewexc.setToolTip( self.menufilenewexc.setToolTip(
_("Will create a new, empty Excellon Object.") _("Will create a new, empty Excellon Object.")
) )
@ -416,6 +420,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
# ########################## Objects # ################################### # ########################## Objects # ###################################
# ######################################################################## # ########################################################################
self.menuobjects = self.menu.addMenu(_('Objects')) self.menuobjects = self.menu.addMenu(_('Objects'))
self.menuobjects.addSeparator()
self.menuobjects_selall = self.menuobjects.addAction(QtGui.QIcon('share/select_all.png'), _('Select All'))
self.menuobjects_unselall = self.menuobjects.addAction(
QtGui.QIcon('share/deselect_all32.png'),
_('Deselect All')
)
# ######################################################################## # ########################################################################
# ########################## Tool # ###################################### # ########################## Tool # ######################################
@ -682,22 +692,23 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.file_open_excellon_btn = self.toolbarfile.addAction(QtGui.QIcon('share/drill32.png'), _("Open Excellon")) self.file_open_excellon_btn = self.toolbarfile.addAction(QtGui.QIcon('share/drill32.png'), _("Open Excellon"))
self.toolbarfile.addSeparator() self.toolbarfile.addSeparator()
self.file_open_btn = self.toolbarfile.addAction(QtGui.QIcon('share/folder32.png'), _("Open project")) self.file_open_btn = self.toolbarfile.addAction(QtGui.QIcon('share/folder32.png'), _("Open project"))
self.file_save_btn = self.toolbarfile.addAction(QtGui.QIcon('share/floppy32.png'), _("Save project")) self.file_save_btn = self.toolbarfile.addAction(QtGui.QIcon('share/project_save32.png'), _("Save project"))
# ######################################################################## # ########################################################################
# ########################## Edit Toolbar# ############################### # ########################## Edit Toolbar# ###############################
# ######################################################################## # ########################################################################
self.newgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_geo32_bis.png'), _("New Blank Geometry")) self.newgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_file_geo32.png'), _("New Blank Geometry"))
self.newgrb_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_geo32.png'), _("New Blank Gerber")) self.newgrb_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_file_grb32.png'), _("New Blank Gerber"))
self.newexc_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_exc32.png'), _("New Blank Excellon")) self.newexc_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_file_exc32.png'), _("New Blank Excellon"))
self.toolbargeo.addSeparator() self.toolbargeo.addSeparator()
self.editgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit32.png'), _("Editor")) self.editgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit_file32.png'), _("Editor"))
self.update_obj_btn = self.toolbargeo.addAction( self.update_obj_btn = self.toolbargeo.addAction(
QtGui.QIcon('share/edit_ok32_bis.png'), _("Save Object and close the Editor") QtGui.QIcon('share/close_edit_file32.png'), _("Save Object and close the Editor")
) )
self.toolbargeo.addSeparator() self.toolbargeo.addSeparator()
self.delete_btn = self.toolbargeo.addAction(QtGui.QIcon('share/cancel_edit32.png'), _("&Delete")) self.copy_btn = self.toolbargeo.addAction(QtGui.QIcon('share/copy_file32.png'), _("Copy"))
self.delete_btn = self.toolbargeo.addAction(QtGui.QIcon('share/delete_file32.png'), _("&Delete"))
self.toolbargeo.addSeparator() self.toolbargeo.addSeparator()
self.distance_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance32.png'), _("Distance Tool")) self.distance_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance32.png'), _("Distance Tool"))
self.distance_min_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance_min32.png'), self.distance_min_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance_min32.png'),
@ -705,7 +716,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.origin_btn = self.toolbargeo.addAction(QtGui.QIcon('share/origin32.png'), _('Set Origin')) self.origin_btn = self.toolbargeo.addAction(QtGui.QIcon('share/origin32.png'), _('Set Origin'))
self.jmp_btn = self.toolbargeo.addAction(QtGui.QIcon('share/jump_to16.png'), _('Jump to Location')) self.jmp_btn = self.toolbargeo.addAction(QtGui.QIcon('share/jump_to16.png'), _('Jump to Location'))
# ######################################################################## # ########################################################################
# ########################## View Toolbar# ############################### # ########################## View Toolbar# ###############################
# ######################################################################## # ########################################################################
@ -734,10 +744,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.paint_btn = self.toolbartools.addAction(QtGui.QIcon('share/paint20_1.png'), _("Paint Tool")) self.paint_btn = self.toolbartools.addAction(QtGui.QIcon('share/paint20_1.png'), _("Paint Tool"))
self.toolbartools.addSeparator() self.toolbartools.addSeparator()
self.panelize_btn = self.toolbartools.addAction(QtGui.QIcon('share/panel16.png'), _("Panel Tool")) self.panelize_btn = self.toolbartools.addAction(QtGui.QIcon('share/panelize32.png'), _("Panel Tool"))
self.film_btn = self.toolbartools.addAction(QtGui.QIcon('share/film16.png'), _("Film Tool")) self.film_btn = self.toolbartools.addAction(QtGui.QIcon('share/film16.png'), _("Film Tool"))
self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'), _("SolderPaste Tool")) self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'), _("SolderPaste Tool"))
self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Substract Tool")) self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Subtract Tool"))
self.rules_btn = self.toolbartools.addAction(QtGui.QIcon('share/rules32.png'), _("Rules Tool")) self.rules_btn = self.toolbartools.addAction(QtGui.QIcon('share/rules32.png'), _("Rules Tool"))
self.optimal_btn = self.toolbartools.addAction(QtGui.QIcon('share/open_excellon32.png'), _("Optimal Tool")) self.optimal_btn = self.toolbartools.addAction(QtGui.QIcon('share/open_excellon32.png'), _("Optimal Tool"))
@ -1869,7 +1879,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.draw_union = self.g_editor_cmenu.addAction(QtGui.QIcon('share/union32.png'), _("Union")) self.draw_union = self.g_editor_cmenu.addAction(QtGui.QIcon('share/union32.png'), _("Union"))
self.draw_intersect = self.g_editor_cmenu.addAction(QtGui.QIcon('share/intersection32.png'), _("Intersection")) self.draw_intersect = self.g_editor_cmenu.addAction(QtGui.QIcon('share/intersection32.png'), _("Intersection"))
self.draw_substract = self.g_editor_cmenu.addAction(QtGui.QIcon('share/subtract32.png'), _("Substraction")) self.draw_substract = self.g_editor_cmenu.addAction(QtGui.QIcon('share/subtract32.png'), _("Subtraction"))
self.draw_cut = self.g_editor_cmenu.addAction(QtGui.QIcon('share/cutpath32.png'), _("Cut")) self.draw_cut = self.g_editor_cmenu.addAction(QtGui.QIcon('share/cutpath32.png'), _("Cut"))
self.draw_transform = self.g_editor_cmenu.addAction(QtGui.QIcon('share/transform.png'), _("Transformations")) self.draw_transform = self.g_editor_cmenu.addAction(QtGui.QIcon('share/transform.png'), _("Transformations"))
@ -2106,19 +2116,21 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.file_open_excellon_btn = self.toolbarfile.addAction(QtGui.QIcon('share/drill32.png'), _("Open Excellon")) self.file_open_excellon_btn = self.toolbarfile.addAction(QtGui.QIcon('share/drill32.png'), _("Open Excellon"))
self.toolbarfile.addSeparator() self.toolbarfile.addSeparator()
self.file_open_btn = self.toolbarfile.addAction(QtGui.QIcon('share/folder32.png'), _("Open project")) self.file_open_btn = self.toolbarfile.addAction(QtGui.QIcon('share/folder32.png'), _("Open project"))
self.file_save_btn = self.toolbarfile.addAction(QtGui.QIcon('share/floppy32.png'), _("Save project")) self.file_save_btn = self.toolbarfile.addAction(QtGui.QIcon('share/project_save32.png'), _("Save project"))
# ## Edit Toolbar # ## # ## Edit Toolbar # ##
self.newgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_geo32_bis.png'), _("New Blank Geometry")) self.newgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_file_geo32.png'), _("New Blank Geometry"))
self.newexc_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_exc32.png'), _("New Blank Excellon")) self.newgrb_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_file_grb32.png'), _("New Blank Gerber"))
self.newexc_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_file_exc32.png'), _("New Blank Excellon"))
self.toolbargeo.addSeparator() self.toolbargeo.addSeparator()
self.editgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit32.png'), _("Editor")) self.editgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit32.png'), _("Editor"))
self.update_obj_btn = self.toolbargeo.addAction( self.update_obj_btn = self.toolbargeo.addAction(
QtGui.QIcon('share/edit_ok32_bis.png'), _("Save Object and close the Editor") QtGui.QIcon('share/close_edit_file32.png'), _("Save Object and close the Editor")
) )
self.toolbargeo.addSeparator() self.toolbargeo.addSeparator()
self.delete_btn = self.toolbargeo.addAction(QtGui.QIcon('share/cancel_edit32.png'), _("&Delete")) self.copy_btn = self.toolbargeo.addAction(QtGui.QIcon('share/copy_file32.png'), _("Copy"))
self.delete_btn = self.toolbargeo.addAction(QtGui.QIcon('share/delete_file32.png'), _("&Delete"))
self.toolbargeo.addSeparator() self.toolbargeo.addSeparator()
self.distance_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance32.png'), _("Distance Tool")) self.distance_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance32.png'), _("Distance Tool"))
self.distance_min_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance_min32.png'), self.distance_min_btn = self.toolbargeo.addAction(QtGui.QIcon('share/distance_min32.png'),
@ -2148,11 +2160,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.paint_btn = self.toolbartools.addAction(QtGui.QIcon('share/paint20_1.png'), _("Paint Tool")) self.paint_btn = self.toolbartools.addAction(QtGui.QIcon('share/paint20_1.png'), _("Paint Tool"))
self.toolbartools.addSeparator() self.toolbartools.addSeparator()
self.panelize_btn = self.toolbartools.addAction(QtGui.QIcon('share/panel16.png'), _("Panel Tool")) self.panelize_btn = self.toolbartools.addAction(QtGui.QIcon('share/panelize32.png'), _("Panel Tool"))
self.film_btn = self.toolbartools.addAction(QtGui.QIcon('share/film16.png'), _("Film Tool")) self.film_btn = self.toolbartools.addAction(QtGui.QIcon('share/film16.png'), _("Film Tool"))
self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'), self.solder_btn = self.toolbartools.addAction(QtGui.QIcon('share/solderpastebis32.png'),
_("SolderPaste Tool")) _("SolderPaste Tool"))
self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Substract Tool")) self.sub_btn = self.toolbartools.addAction(QtGui.QIcon('share/sub32.png'), _("Subtract Tool"))
self.toolbartools.addSeparator() self.toolbartools.addSeparator()
@ -3696,7 +3708,7 @@ class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon):
class BookmarkManager(QtWidgets.QWidget): class BookmarkManager(QtWidgets.QWidget):
mark_rows = pyqtSignal() mark_rows = QtCore.pyqtSignal()
def __init__(self, app, storage, parent=None): def __init__(self, app, storage, parent=None):
super(BookmarkManager, self).__init__(parent) super(BookmarkManager, self).__init__(parent)
@ -3754,7 +3766,7 @@ class BookmarkManager(QtWidgets.QWidget):
new_vlay = QtWidgets.QVBoxLayout() new_vlay = QtWidgets.QVBoxLayout()
layout.addLayout(new_vlay) layout.addLayout(new_vlay)
new_title_lbl = QtWidgets.QLabel(_("<b>New Bookmark</b>")) new_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("New Bookmark"))
new_vlay.addWidget(new_title_lbl) new_vlay.addWidget(new_title_lbl)
form0 = QtWidgets.QFormLayout() form0 = QtWidgets.QFormLayout()
@ -3958,8 +3970,9 @@ class BookmarkManager(QtWidgets.QWidget):
filter__ = "Text File (*.TXT);;All Files (*.*)" filter__ = "Text File (*.TXT);;All Files (*.*)"
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export FlatCAM Preferences"), filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export FlatCAM Preferences"),
directory=_('{l_save}/FlatCAM_Bookmarks_{date}').format( directory='{l_save}/FlatCAM_{n}_{date}'.format(
l_save=str(self.app.get_last_save_folder()), l_save=str(self.app.get_last_save_folder()),
n=_("Bookmarks"),
date=date), date=date),
filter=filter__) filter=filter__)
@ -3986,7 +3999,7 @@ class BookmarkManager(QtWidgets.QWidget):
self.app.log.error("Could not load defaults file.") self.app.log.error("Could not load defaults file.")
self.app.log.error(str(e)) self.app.log.error(str(e))
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Could not load bookamrks file.")) _("Could not load bookmarks file."))
return return
# Save update options # Save update options
@ -4019,7 +4032,7 @@ class BookmarkManager(QtWidgets.QWidget):
with open(filename) as f: with open(filename) as f:
bookmarks = f.readlines() bookmarks = f.readlines()
except IOError: except IOError:
self.app.log.error("Could not load bookamrks file.") self.app.log.error("Could not load bookmarks file.")
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Could not load bookmarks file.")) _("Could not load bookmarks file."))
return return

View File

@ -12,18 +12,14 @@
# ########################################################## # ##########################################################
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, pyqtSlot
from PyQt5.QtWidgets import QTextEdit, QCompleter, QAction from PyQt5.QtWidgets import QTextEdit, QCompleter, QAction
from PyQt5.QtGui import QColor, QKeySequence, QPalette, QTextCursor from PyQt5.QtGui import QKeySequence, QTextCursor
from copy import copy from copy import copy
import re import re
import logging import logging
import html import html
import webbrowser
from copy import deepcopy
import sys
from datetime import datetime
log = logging.getLogger('base') log = logging.getLogger('base')
@ -513,7 +509,7 @@ class EvalEntry2(QtWidgets.QLineEdit):
class FCSpinner(QtWidgets.QSpinBox): class FCSpinner(QtWidgets.QSpinBox):
returnPressed = pyqtSignal() returnPressed = QtCore.pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
super(FCSpinner, self).__init__(parent) super(FCSpinner, self).__init__(parent)
@ -580,7 +576,7 @@ class FCSpinner(QtWidgets.QSpinBox):
class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
returnPressed = pyqtSignal() returnPressed = QtCore.pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
super(FCDoubleSpinner, self).__init__(parent) super(FCDoubleSpinner, self).__init__(parent)
@ -869,16 +865,16 @@ class FCTextAreaExtended(QtWidgets.QTextEdit):
if self.textCursor().block().text().startswith(" "): if self.textCursor().block().text().startswith(" "):
# skip the white space # skip the white space
self.moveCursor(QtGui.QTextCursor.NextWord) self.moveCursor(QtGui.QTextCursor.NextWord)
self.moveCursor(QtGui.QTextCursor.NextCharacter,QtGui.QTextCursor.KeepAnchor) self.moveCursor(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor)
character = self.textCursor().selectedText() character = self.textCursor().selectedText()
if character == "#": if character == "#":
# delete # # delete #
self.textCursor().deletePreviousChar() self.textCursor().deletePreviousChar()
# delete white space # delete white space
self.moveCursor(QtGui.QTextCursor.NextWord,QtGui.QTextCursor.KeepAnchor) self.moveCursor(QtGui.QTextCursor.NextWord, QtGui.QTextCursor.KeepAnchor)
self.textCursor().removeSelectedText() self.textCursor().removeSelectedText()
else: else:
self.moveCursor(QtGui.QTextCursor.PreviousCharacter,QtGui.QTextCursor.KeepAnchor) self.moveCursor(QtGui.QTextCursor.PreviousCharacter, QtGui.QTextCursor.KeepAnchor)
self.textCursor().insertText("# ") self.textCursor().insertText("# ")
cursor = QtGui.QTextCursor(self.textCursor()) cursor = QtGui.QTextCursor(self.textCursor())
cursor.setPosition(pos) cursor.setPosition(pos)
@ -1261,7 +1257,6 @@ class FCDetachableTab(QtWidgets.QTabWidget):
attached = True attached = True
break break
# If the tab is not attached, close it's window and # If the tab is not attached, close it's window and
# remove the reference to it # remove the reference to it
if not attached: if not attached:
@ -1342,8 +1337,8 @@ class FCDetachableTab(QtWidgets.QTabWidget):
can be re-attached by closing the dialog or by dragging the window into the tab bar can be re-attached by closing the dialog or by dragging the window into the tab bar
""" """
onCloseSignal = pyqtSignal(QtWidgets.QWidget, str, QtGui.QIcon) onCloseSignal = QtCore.pyqtSignal(QtWidgets.QWidget, str, QtGui.QIcon)
onDropSignal = pyqtSignal(str, QtCore.QPoint) onDropSignal = QtCore.pyqtSignal(str, QtCore.QPoint)
def __init__(self, name, contentWidget): def __init__(self, name, contentWidget):
QtWidgets.QMainWindow.__init__(self, None) QtWidgets.QMainWindow.__init__(self, None)
@ -1384,7 +1379,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
An event filter class to detect a QMainWindow drop event An event filter class to detect a QMainWindow drop event
""" """
onDropSignal = pyqtSignal(QtCore.QPoint) onDropSignal = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self): def __init__(self):
QtCore.QObject.__init__(self) QtCore.QObject.__init__(self)
@ -1416,11 +1411,11 @@ class FCDetachableTab(QtWidgets.QTabWidget):
return False return False
class FCTabBar(QtWidgets.QTabBar): class FCTabBar(QtWidgets.QTabBar):
onDetachTabSignal = pyqtSignal(int, QtCore.QPoint) onDetachTabSignal = QtCore.pyqtSignal(int, QtCore.QPoint)
onMoveTabSignal = pyqtSignal(int, int) onMoveTabSignal = QtCore.pyqtSignal(int, int)
detachedTabDropSignal = pyqtSignal(str, int, QtCore.QPoint) detachedTabDropSignal = QtCore.pyqtSignal(str, int, QtCore.QPoint)
right_click = pyqtSignal(int) right_click = QtCore.pyqtSignal(int)
def __init__(self, parent=None): def __init__(self, parent=None):
QtWidgets.QTabBar.__init__(self, parent) QtWidgets.QTabBar.__init__(self, parent)
@ -1498,7 +1493,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
self.dragInitiated = True self.dragInitiated = True
# If the current movement is a drag initiated by the left button # If the current movement is a drag initiated by the left button
if (((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated and self.can_be_dragged): if ((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated and self.can_be_dragged:
# Stop the move event # Stop the move event
finishMoveEvent = QtGui.QMouseEvent( finishMoveEvent = QtGui.QMouseEvent(
@ -1591,7 +1586,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
class FCDetachableTab2(FCDetachableTab): class FCDetachableTab2(FCDetachableTab):
tab_closed_signal = pyqtSignal(object) tab_closed_signal = QtCore.pyqtSignal(object)
def __init__(self, protect=None, protect_by_name=None, parent=None): def __init__(self, protect=None, protect_by_name=None, parent=None):
super(FCDetachableTab2, self).__init__(protect=protect, protect_by_name=protect_by_name, parent=parent) super(FCDetachableTab2, self).__init__(protect=protect, protect_by_name=protect_by_name, parent=parent)
@ -1729,7 +1724,7 @@ class OptionalHideInputSection:
class FCTable(QtWidgets.QTableWidget): class FCTable(QtWidgets.QTableWidget):
drag_drop_sig = pyqtSignal() drag_drop_sig = QtCore.pyqtSignal()
def __init__(self, drag_drop=False, protected_rows=None, parent=None): def __init__(self, drag_drop=False, protected_rows=None, parent=None):
super(FCTable, self).__init__(parent) super(FCTable, self).__init__(parent)
@ -2024,8 +2019,8 @@ class _ExpandableTextEdit(QTextEdit):
Class implements edit line, which expands themselves automatically Class implements edit line, which expands themselves automatically
""" """
historyNext = pyqtSignal() historyNext = QtCore.pyqtSignal()
historyPrev = pyqtSignal() historyPrev = QtCore.pyqtSignal()
def __init__(self, termwidget, *args): def __init__(self, termwidget, *args):
QTextEdit.__init__(self, *args) QTextEdit.__init__(self, *args)
@ -2148,7 +2143,7 @@ class _ExpandableTextEdit(QTextEdit):
class MyCompleter(QCompleter): class MyCompleter(QCompleter):
insertText = pyqtSignal(str) insertText = QtCore.pyqtSignal(str)
def __init__(self, parent=None): def __init__(self, parent=None):
QCompleter.__init__(self) QCompleter.__init__(self)
@ -2166,4 +2161,3 @@ class MyCompleter(QCompleter):
def getSelected(self): def getSelected(self):
return self.lastSelected return self.lastSelected

View File

@ -11,8 +11,6 @@
# Date: 3/10/2019 # # Date: 3/10/2019 #
# ########################################################## # ##########################################################
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt
from flatcamGUI.GUIElements import * from flatcamGUI.GUIElements import *
import sys import sys

View File

@ -7,7 +7,7 @@
# Modified by Marius Stanciu 09/21/2019 # # Modified by Marius Stanciu 09/21/2019 #
############################################################ ############################################################
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import pyqtSignal
# needed for legacy mode # needed for legacy mode

View File

@ -8,11 +8,8 @@
from PyQt5.QtCore import QSettings from PyQt5.QtCore import QSettings
from flatcamGUI.GUIElements import * from flatcamGUI.GUIElements import *
import platform import platform
import webbrowser
import sys import sys
from flatcamEditors.FlatCAMGeoEditor import FCShapeTool
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -5121,7 +5118,7 @@ class Tools2RulesCheckPrefGroupUI(OptionsGroupUI):
self.crlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters")) self.crlabel = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
self.crlabel.setToolTip( self.crlabel.setToolTip(
_("A tool to check if Gerber files fir within a set\n" _("A tool to check if Gerber files are within a set\n"
"of Manufacturing Rules.") "of Manufacturing Rules.")
) )
self.layout.addWidget(self.crlabel) self.layout.addWidget(self.crlabel)

View File

@ -6,12 +6,15 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
import numpy as np
from PyQt5.QtGui import QPalette from PyQt5.QtGui import QPalette
from PyQt5.QtCore import QSettings from PyQt5.QtCore import QSettings
import numpy as np
import vispy.scene as scene import vispy.scene as scene
from vispy.scene.cameras.base_camera import BaseCamera from vispy.scene.cameras.base_camera import BaseCamera
from vispy.color import Color from vispy.color import Color
import time import time
white = Color("#ffffff") white = Color("#ffffff")

View File

@ -6,6 +6,8 @@
# ########################################################## # ##########################################################
from shapely.geometry import LineString from shapely.geometry import LineString
from shapely.affinity import rotate
import logging import logging
log = logging.getLogger('base2') log = logging.getLogger('base2')

View File

@ -9,7 +9,6 @@
# ########################################################## # ##########################################################
import math import math
import sys
def norm(v): def norm(v):

View File

@ -1,4 +1,14 @@
from camlib import *
from camlib import Geometry
import FlatCAMApp
import shapely.affinity as affinity
from shapely.geometry import Point, LineString
import numpy as np
import re
import logging
import traceback
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -8,6 +18,8 @@ import builtins
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class Excellon(Geometry): class Excellon(Geometry):
""" """
@ -1017,10 +1029,10 @@ class Excellon(Geometry):
def bounds_rec(obj): def bounds_rec(obj):
if type(obj) is list: if type(obj) is list:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in obj: for k in obj:
if type(k) is dict: if type(k) is dict:
@ -1087,8 +1099,10 @@ class Excellon(Geometry):
Scales geometry on the XY plane in the object by a given factor. Scales geometry on the XY plane in the object by a given factor.
Tool sizes, feedrates an Z-plane dimensions are untouched. Tool sizes, feedrates an Z-plane dimensions are untouched.
:param factor: Number by which to scale the object. :param xfactor: Number by which to scale the object.
:type factor: float :type xfactor: float
:param yfactor: Number by which to scale the object.
:type yfactor: float
:return: None :return: None
:rtype: NOne :rtype: NOne
""" """

View File

@ -11,12 +11,11 @@
# ###################################################################### # ######################################################################
import re, os, sys, glob import re, os, sys, glob
import itertools
from shapely.geometry import Point, Polygon from shapely.geometry import Point, Polygon
from shapely.affinity import translate, scale, rotate from shapely.affinity import translate, scale
from shapely.geometry import MultiPolygon from shapely.geometry import MultiPolygon
from shapely.geometry.base import BaseGeometry
import freetype as ft import freetype as ft
from fontTools import ttLib from fontTools import ttLib

View File

@ -1,4 +1,19 @@
from camlib import *
from camlib import Geometry, arc, arc_angle, ApertureMacro
import FlatCAMApp
import numpy as np
import re
import logging
import traceback
from copy import deepcopy
import sys
from shapely.ops import cascaded_union
from shapely.geometry import Polygon, MultiPolygon, LineString, Point
import shapely.affinity as affinity
from shapely.geometry import box as shply_box
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import gettext import gettext
@ -7,6 +22,8 @@ import builtins
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class Gerber(Geometry): class Gerber(Geometry):
""" """
@ -253,14 +270,14 @@ class Gerber(Geometry):
self.apertures[apid] = {"type": "R", self.apertures[apid] = {"type": "R",
"width": float(paramList[0]), "width": float(paramList[0]),
"height": float(paramList[1]), "height": float(paramList[1]),
"size": sqrt(float(paramList[0]) ** 2 + float(paramList[1]) ** 2)} # Hack "size": np.sqrt(float(paramList[0]) ** 2 + float(paramList[1]) ** 2)} # Hack
return apid return apid
if apertureType == "O": # Obround if apertureType == "O": # Obround
self.apertures[apid] = {"type": "O", self.apertures[apid] = {"type": "O",
"width": float(paramList[0]), "width": float(paramList[0]),
"height": float(paramList[1]), "height": float(paramList[1]),
"size": sqrt(float(paramList[0]) ** 2 + float(paramList[1]) ** 2)} # Hack "size": np.sqrt(float(paramList[0]) ** 2 + float(paramList[1]) ** 2)} # Hack
return apid return apid
if apertureType == "P": # Polygon (regular) if apertureType == "P": # Polygon (regular)
@ -1231,15 +1248,15 @@ class Gerber(Geometry):
if quadrant_mode == 'MULTI': if quadrant_mode == 'MULTI':
center = [i + current_x, j + current_y] center = [i + current_x, j + current_y]
radius = sqrt(i ** 2 + j ** 2) radius = np.sqrt(i ** 2 + j ** 2)
start = arctan2(-j, -i) # Start angle start = np.arctan2(-j, -i) # Start angle
# Numerical errors might prevent start == stop therefore # Numerical errors might prevent start == stop therefore
# we check ahead of time. This should result in a # we check ahead of time. This should result in a
# 360 degree arc. # 360 degree arc.
if current_x == circular_x and current_y == circular_y: if current_x == circular_x and current_y == circular_y:
stop = start stop = start
else: else:
stop = arctan2(-center[1] + circular_y, -center[0] + circular_x) # Stop angle stop = np.arctan2(-center[1] + circular_y, -center[0] + circular_x) # Stop angle
this_arc = arc(center, radius, start, stop, this_arc = arc(center, radius, start, stop,
arcdir[current_interpolation_mode], arcdir[current_interpolation_mode],
@ -1273,10 +1290,10 @@ class Gerber(Geometry):
valid = False valid = False
log.debug("I: %f J: %f" % (i, j)) log.debug("I: %f J: %f" % (i, j))
for center in center_candidates: for center in center_candidates:
radius = sqrt(i ** 2 + j ** 2) radius = np.sqrt(i ** 2 + j ** 2)
# Make sure radius to start is the same as radius to end. # Make sure radius to start is the same as radius to end.
radius2 = sqrt((center[0] - circular_x) ** 2 + (center[1] - circular_y) ** 2) radius2 = np.sqrt((center[0] - circular_x) ** 2 + (center[1] - circular_y) ** 2)
if radius2 < radius * 0.95 or radius2 > radius * 1.05: if radius2 < radius * 0.95 or radius2 > radius * 1.05:
continue # Not a valid center. continue # Not a valid center.
@ -1284,16 +1301,16 @@ class Gerber(Geometry):
i = center[0] - current_x i = center[0] - current_x
j = center[1] - current_y j = center[1] - current_y
start = arctan2(-j, -i) # Start angle start = np.arctan2(-j, -i) # Start angle
stop = arctan2(-center[1] + circular_y, -center[0] + circular_x) # Stop angle stop = np.arctan2(-center[1] + circular_y, -center[0] + circular_x) # Stop angle
angle = abs(arc_angle(start, stop, arcdir[current_interpolation_mode])) angle = abs(arc_angle(start, stop, arcdir[current_interpolation_mode]))
log.debug("ARC START: %f, %f CENTER: %f, %f STOP: %f, %f" % log.debug("ARC START: %f, %f CENTER: %f, %f STOP: %f, %f" %
(current_x, current_y, center[0], center[1], circular_x, circular_y)) (current_x, current_y, center[0], center[1], circular_x, circular_y))
log.debug("START Ang: %f, STOP Ang: %f, DIR: %s, ABS: %.12f <= %.12f: %s" % log.debug("START Ang: %f, STOP Ang: %f, DIR: %s, ABS: %.12f <= %.12f: %s" %
(start * 180 / pi, stop * 180 / pi, arcdir[current_interpolation_mode], (start * 180 / np.pi, stop * 180 / np.pi, arcdir[current_interpolation_mode],
angle * 180 / pi, pi / 2 * 180 / pi, angle <= (pi + 1e-6) / 2)) angle * 180 / np.pi, np.pi / 2 * 180 / np.pi, angle <= (np.pi + 1e-6) / 2))
if angle <= (pi + 1e-6) / 2: if angle <= (np.pi + 1e-6) / 2:
log.debug("########## ACCEPTING ARC ############") log.debug("########## ACCEPTING ARC ############")
this_arc = arc(center, radius, start, stop, this_arc = arc(center, radius, start, stop,
arcdir[current_interpolation_mode], arcdir[current_interpolation_mode],
@ -1367,7 +1384,7 @@ class Gerber(Geometry):
# this treats the case when we are storing geometry as solids # this treats the case when we are storing geometry as solids
if len(poly_buffer) == 0: if len(poly_buffer) == 0 and len(self.solid_geometry) == 0:
log.error("Object is not Gerber file or empty. Aborting Object creation.") log.error("Object is not Gerber file or empty. Aborting Object creation.")
return 'fail' return 'fail'
@ -1478,8 +1495,8 @@ class Gerber(Geometry):
n_vertices = aperture['nVertices'] n_vertices = aperture['nVertices']
points = [] points = []
for i in range(0, n_vertices): for i in range(0, n_vertices):
x = loc[0] + 0.5 * diam * (cos(2 * pi * i / n_vertices)) x = loc[0] + 0.5 * diam * (np.cos(2 * np.pi * i / n_vertices))
y = loc[1] + 0.5 * diam * (sin(2 * pi * i / n_vertices)) y = loc[1] + 0.5 * diam * (np.sin(2 * np.pi * i / n_vertices))
points.append((x, y)) points.append((x, y))
ply = Polygon(points) ply = Polygon(points)
if 'rotation' in aperture: if 'rotation' in aperture:
@ -1553,10 +1570,10 @@ class Gerber(Geometry):
def bounds_rec(obj): def bounds_rec(obj):
if type(obj) is list and type(obj) is not MultiPolygon: if type(obj) is list and type(obj) is not MultiPolygon:
minx = Inf minx = np.Inf
miny = Inf miny = np.Inf
maxx = -Inf maxx = -np.Inf
maxy = -Inf maxy = -np.Inf
for k in obj: for k in obj:
if type(k) is dict: if type(k) is dict:

View File

@ -23,7 +23,7 @@
from svg.path import Line, Arc, CubicBezier, QuadraticBezier, parse_path from svg.path import Line, Arc, CubicBezier, QuadraticBezier, parse_path
from svg.path.path import Move from svg.path.path import Move
from shapely.geometry import LineString from shapely.geometry import LineString
from shapely.affinity import skew, affine_transform from shapely.affinity import skew, affine_transform, rotate
import numpy as np import numpy as np
from flatcamParsers.ParseFont import * from flatcamParsers.ParseFont import *

View File

@ -5,8 +5,9 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from FlatCAMObj import * from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCEntry
import math import math
import gettext import gettext
@ -321,11 +322,11 @@ class ToolCalculator(FlatCAMTool):
def on_calculate_inch_units(self): def on_calculate_inch_units(self):
mm_val = float(self.mm_entry.get_value()) mm_val = float(self.mm_entry.get_value())
self.inch_entry.set_value('%.*f' % (self.decimals,(mm_val / 25.4))) self.inch_entry.set_value('%.*f' % (self.decimals, (mm_val / 25.4)))
def on_calculate_mm_units(self): def on_calculate_mm_units(self):
inch_val = float(self.inch_entry.get_value()) inch_val = float(self.inch_entry.get_value())
self.mm_entry.set_value('%.*f' % (self.decimals,(inch_val * 25.4))) self.mm_entry.set_value('%.*f' % (self.decimals, (inch_val * 25.4)))
def on_calculate_eplate(self): def on_calculate_eplate(self):
length = float(self.pcblength_entry.get_value()) length = float(self.pcblength_entry.get_value())

View File

@ -5,12 +5,21 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtGui, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from ObjectCollection import * from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox
from FlatCAMApp import * from FlatCAMObj import FlatCAMGerber
from shapely.geometry import box
from shapely.ops import cascaded_union, unary_union
from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing
from shapely.ops import cascaded_union, unary_union
import shapely.affinity as affinity
from matplotlib.backend_bases import KeyEvent as mpl_key_event
from numpy import Inf
from copy import deepcopy
import math
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -19,6 +28,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class CutOut(FlatCAMTool): class CutOut(FlatCAMTool):

View File

@ -1,9 +1,14 @@
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from FlatCAMObj import * from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry
from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon, FlatCAMGeometry
from shapely.geometry import Point from shapely.geometry import Point
from shapely import affinity from shapely import affinity
from PyQt5 import QtCore
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -12,6 +17,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class DblSidedTool(FlatCAMTool): class DblSidedTool(FlatCAMTool):

View File

@ -5,12 +5,15 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from FlatCAMObj import *
from flatcamGUI.VisPyVisuals import * from flatcamGUI.VisPyVisuals import *
from flatcamGUI.GUIElements import FCEntry
from math import sqrt import copy
import math
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -19,6 +22,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class Distance(FlatCAMTool): class Distance(FlatCAMTool):
@ -335,7 +340,7 @@ class Distance(FlatCAMTool):
elif len(self.points) == 2: elif len(self.points) == 2:
dx = self.points[1][0] - self.points[0][0] dx = self.points[1][0] - self.points[0][0]
dy = self.points[1][1] - self.points[0][1] dy = self.points[1][1] - self.points[0][1]
d = sqrt(dx ** 2 + dy ** 2) d = math.sqrt(dx ** 2 + dy ** 2)
self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1])) self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format( self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format(

View File

@ -5,14 +5,16 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from FlatCAMObj import *
from flatcamGUI.VisPyVisuals import * from flatcamGUI.VisPyVisuals import *
from flatcamGUI.GUIElements import FCEntry
from shapely.ops import nearest_points from shapely.ops import nearest_points
from shapely.geometry import Point
from math import sqrt import math
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -21,6 +23,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class DistanceMin(FlatCAMTool): class DistanceMin(FlatCAMTool):
@ -260,7 +264,7 @@ class DistanceMin(FlatCAMTool):
except Exception as e: except Exception as e:
pass pass
d = sqrt(dx ** 2 + dy ** 2) d = math.sqrt(dx ** 2 + dy ** 2)
self.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d))) self.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d)))
self.h_point = (min(first_pos.x, last_pos.x) + (abs(dx) / 2), min(first_pos.y, last_pos.y) + (abs(dy) / 2)) self.h_point = (min(first_pos.x, last_pos.x) + (abs(dx) / 2), min(first_pos.y, last_pos.y) + (abs(dy) / 2))

View File

@ -5,14 +5,15 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from FlatCAMTool import FlatCAMTool
from FlatCAMObj import *
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
OptionalHideInputSection, OptionalInputSection
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
OptionalHideInputSection, OptionalInputSection
from copy import deepcopy from copy import deepcopy
import logging
from shapely.geometry import Polygon, MultiPolygon, Point
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -22,6 +23,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class Film(FlatCAMTool): class Film(FlatCAMTool):
@ -166,7 +169,7 @@ class Film(FlatCAMTool):
self.ois_scale = OptionalInputSection(self.film_scale_cb, [self.film_scalex_label, self.film_scalex_entry, self.ois_scale = OptionalInputSection(self.film_scale_cb, [self.film_scalex_label, self.film_scalex_entry,
self.film_scaley_label, self.film_scaley_entry]) self.film_scaley_label, self.film_scaley_entry])
# Skew Geometry # Skew Geometry
self.film_skew_cb =FCCheckBox('%s' % _("Skew Film geometry")) self.film_skew_cb = FCCheckBox('%s' % _("Skew Film geometry"))
self.film_skew_cb.setToolTip( self.film_skew_cb.setToolTip(
_("Positive values will skew to the right\n" _("Positive values will skew to the right\n"
"while negative values will skew to the left.") "while negative values will skew to the left.")
@ -202,9 +205,9 @@ class Film(FlatCAMTool):
"It can be one of the four points of the geometry bounding box.") "It can be one of the four points of the geometry bounding box.")
) )
self.film_skew_reference = RadioSet([{'label': _('Bottom Left'), 'value': 'bottomleft'}, self.film_skew_reference = RadioSet([{'label': _('Bottom Left'), 'value': 'bottomleft'},
{'label': _('Top Left'), 'value': 'topleft'}, {'label': _('Top Left'), 'value': 'topleft'},
{'label': _('Bottom Right'), 'value': 'bottomright'}, {'label': _('Bottom Right'), 'value': 'bottomright'},
{'label': _('Top right'), 'value': 'topright'}], {'label': _('Top right'), 'value': 'topright'}],
orientation='vertical', orientation='vertical',
stretch=False) stretch=False)
@ -331,7 +334,7 @@ class Film(FlatCAMTool):
self.exc_label = QtWidgets.QLabel('%s:' % _("Excellon Obj")) self.exc_label = QtWidgets.QLabel('%s:' % _("Excellon Obj"))
self.exc_label.setToolTip( self.exc_label.setToolTip(
_("Remove the geometry of Excellon from the Film to create tge holes in pads.") _("Remove the geometry of Excellon from the Film to create the holes in pads.")
) )
self.exc_combo = QtWidgets.QComboBox() self.exc_combo = QtWidgets.QComboBox()
self.exc_combo.setModel(self.app.collection) self.exc_combo.setModel(self.app.collection)

View File

@ -5,11 +5,11 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCComboBox, FCSpinner
from PyQt5 import QtGui, QtWidgets from PyQt5 import QtGui, QtWidgets
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCComboBox, FCSpinner
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins

View File

@ -5,12 +5,13 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from FlatCAMObj import *
from flatcamGUI.VisPyVisuals import * from flatcamGUI.VisPyVisuals import *
from FlatCAMObj import FlatCAMGerber
from copy import copy from copy import copy
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -19,11 +20,13 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class ToolMove(FlatCAMTool): class ToolMove(FlatCAMTool):
toolName = _("Move") toolName = _("Move")
replot_signal = pyqtSignal(list) replot_signal = QtCore.pyqtSignal(list)
def __init__(self, app): def __init__(self, app):
FlatCAMTool.__init__(self, app) FlatCAMTool.__init__(self, app)

View File

@ -5,12 +5,23 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtCore, QtGui
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from copy import copy, deepcopy from flatcamGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog
from ObjectCollection import * from flatcamParsers.ParseGerber import Gerber
import time from FlatCAMObj import FlatCAMGeometry, FlatCAMGerber
from shapely.geometry import base import FlatCAMApp
from copy import deepcopy
import numpy as np
import math
from shapely.geometry import base
from shapely.ops import cascaded_union
from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing
import logging
import traceback
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -19,6 +30,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class NonCopperClear(FlatCAMTool, Gerber): class NonCopperClear(FlatCAMTool, Gerber):
@ -261,7 +274,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
) )
grid2.addWidget(self.addtool_btn, 0, 0) grid2.addWidget(self.addtool_btn, 0, 0)
# grid2.addWidget(self.copytool_btn, 0, 1)
grid2.addWidget(self.deltool_btn, 0, 2) grid2.addWidget(self.deltool_btn, 0, 2)
self.empty_label_0 = QtWidgets.QLabel('') self.empty_label_0 = QtWidgets.QLabel('')
@ -269,6 +281,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
grid3 = QtWidgets.QGridLayout() grid3 = QtWidgets.QGridLayout()
self.tools_box.addLayout(grid3) self.tools_box.addLayout(grid3)
grid3.setColumnStretch(0, 0)
grid3.setColumnStretch(1, 1)
e_lab_1 = QtWidgets.QLabel('<b>%s:</b>' % _("Parameters")) e_lab_1 = QtWidgets.QLabel('<b>%s:</b>' % _("Parameters"))
grid3.addWidget(e_lab_1, 0, 0) grid3.addWidget(e_lab_1, 0, 0)
@ -472,7 +486,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
"Add", self.on_add_tool_by_key, icon=QtGui.QIcon("share/plus16.png")) "Add", self.on_add_tool_by_key, icon=QtGui.QIcon("share/plus16.png"))
self.tools_table.addContextMenu( self.tools_table.addContextMenu(
"Delete", lambda: "Delete", lambda:
self.on_tool_delete(rows_to_delete=None, all=None), icon=QtGui.QIcon("share/delete32.png")) self.on_tool_delete(rows_to_delete=None, all_tools=None), icon=QtGui.QIcon("share/delete32.png"))
# ############################################################################# # #############################################################################
# ########################## VARIABLES ######################################## # ########################## VARIABLES ########################################
@ -739,7 +753,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
tool_type_item = QtWidgets.QComboBox() tool_type_item = QtWidgets.QComboBox()
for item in self.tool_type_item_options: for item in self.tool_type_item_options:
tool_type_item.addItem(item) tool_type_item.addItem(item)
tool_type_item.setStyleSheet('background-color: rgb(255,255,255)') # tool_type_item.setStyleSheet('background-color: rgb(255,255,255)')
idx = tool_type_item.findText(tooluid_value['tool_type']) idx = tool_type_item.findText(tooluid_value['tool_type'])
tool_type_item.setCurrentIndex(idx) tool_type_item.setCurrentIndex(idx)
@ -747,9 +761,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
operation_type = QtWidgets.QComboBox() operation_type = QtWidgets.QComboBox()
operation_type.addItem('iso_op') operation_type.addItem('iso_op')
operation_type.setStyleSheet('background-color: rgb(255,255,255)') # operation_type.setStyleSheet('background-color: rgb(255,255,255)')
operation_type.addItem('clear_op') operation_type.addItem('clear_op')
operation_type.setStyleSheet('background-color: rgb(255,255,255)') # operation_type.setStyleSheet('background-color: rgb(255,255,255)')
op_idx = operation_type.findText(tooluid_value['operation']) op_idx = operation_type.findText(tooluid_value['operation'])
operation_type.setCurrentIndex(op_idx) operation_type.setCurrentIndex(op_idx)
@ -1039,12 +1053,19 @@ class NonCopperClear(FlatCAMTool, Gerber):
"New diameter value is already in the Tool Table.")) "New diameter value is already in the Tool Table."))
self.build_ui() self.build_ui()
def on_tool_delete(self, rows_to_delete=None, all=None): def on_tool_delete(self, rows_to_delete=None, all_tools=None):
"""
Will delete a tool in the tool table
:param rows_to_delete: which rows to delete; can be a list
:param all_tools: delete all tools in the tool table
:return:
"""
self.ui_disconnect() self.ui_disconnect()
deleted_tools_list = [] deleted_tools_list = []
if all: if all_tools:
self.paint_tools.clear() self.paint_tools.clear()
self.build_ui() self.build_ui()
return return

View File

@ -5,13 +5,19 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from FlatCAMTool import FlatCAMTool from PyQt5 import QtWidgets, QtCore, QtGui
from FlatCAMObj import *
from shapely.geometry import Point
from shapely import affinity
from shapely.ops import nearest_points
from PyQt5 import QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import OptionalHideInputSection, FCTextArea, FCEntry, FCSpinner, FCCheckBox
from FlatCAMObj import FlatCAMGerber
import FlatCAMApp
from shapely.geometry import MultiPolygon
from shapely.ops import nearest_points
import numpy as np
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -20,13 +26,15 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class ToolOptimal(FlatCAMTool): class ToolOptimal(FlatCAMTool):
toolName = _("Optimal Tool") toolName = _("Optimal Tool")
update_text = pyqtSignal(list) update_text = QtCore.pyqtSignal(list)
update_sec_distances = pyqtSignal(dict) update_sec_distances = QtCore.pyqtSignal(dict)
def __init__(self, app): def __init__(self, app):
FlatCAMTool.__init__(self, app) FlatCAMTool.__init__(self, app)

View File

@ -5,19 +5,22 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from shapely.geometry import Point, Polygon, LineString import FlatCAMApp
from shapely.ops import cascaded_union, unary_union
from FlatCAMObj import * from shapely.geometry import Point, Polygon, LineString, MultiPolygon
from shapely.ops import unary_union
import math
from copy import copy, deepcopy from copy import copy, deepcopy
import numpy as np import numpy as np
import zlib import zlib
import re import re
import time import time
import logging
import traceback
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -27,6 +30,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class ToolPDF(FlatCAMTool): class ToolPDF(FlatCAMTool):
""" """

View File

@ -5,10 +5,25 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import Qt
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from copy import copy, deepcopy from copy import deepcopy
from ObjectCollection import * # from ObjectCollection import *
from shapely.geometry import base from flatcamParsers.ParseGerber import Gerber
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry
from camlib import Geometry
from flatcamGUI.GUIElements import FCTable, FCDoubleSpinner, FCCheckBox, FCInputDialog, RadioSet
import FlatCAMApp
from shapely.geometry import base, Polygon, MultiPolygon, LinearRing
from shapely.ops import cascaded_union
import numpy as np
from numpy import Inf
import traceback
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -18,6 +33,8 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class ToolPaint(FlatCAMTool, Gerber): class ToolPaint(FlatCAMTool, Gerber):
@ -374,6 +391,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.mm = None self.mm = None
self.mp = None self.mp = None
self.mr = None
self.sel_rect = [] self.sel_rect = []
@ -641,10 +659,10 @@ class ToolPaint(FlatCAMTool, Gerber):
for tooluid_key, tooluid_value in self.paint_tools.items(): for tooluid_key, tooluid_value in self.paint_tools.items():
if float('%.*f' % (self.decimals, tooluid_value['tooldia'])) == tool_sorted: if float('%.*f' % (self.decimals, tooluid_value['tooldia'])) == tool_sorted:
tool_id += 1 tool_id += 1
id = QtWidgets.QTableWidgetItem('%d' % int(tool_id)) id_item = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
row_no = tool_id - 1 row_no = tool_id - 1
self.tools_table.setItem(row_no, 0, id) # Tool name/id self.tools_table.setItem(row_no, 0, id_item) # Tool name/id
# Make sure that the drill diameter when in MM is with no more than 2 decimals # Make sure that the drill diameter when in MM is with no more than 2 decimals
# There are no drill bits in MM with more than 2 decimals diameter # There are no drill bits in MM with more than 2 decimals diameter
@ -657,7 +675,7 @@ class ToolPaint(FlatCAMTool, Gerber):
tool_type_item = QtWidgets.QComboBox() tool_type_item = QtWidgets.QComboBox()
for item in self.tool_type_item_options: for item in self.tool_type_item_options:
tool_type_item.addItem(item) tool_type_item.addItem(item)
tool_type_item.setStyleSheet('background-color: rgb(255,255,255)') # tool_type_item.setStyleSheet('background-color: rgb(255,255,255)')
idx = tool_type_item.findText(tooluid_value['tool_type']) idx = tool_type_item.findText(tooluid_value['tool_type'])
tool_type_item.setCurrentIndex(idx) tool_type_item.setCurrentIndex(idx)
@ -2213,7 +2231,7 @@ class ToolPaint(FlatCAMTool, Gerber):
log.debug("Could not Paint the polygons. %s" % str(e)) log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit('[ERROR] %s\n%s' % self.app.inform.emit('[ERROR] %s\n%s' %
(_("Could not do Paint All. Try a different combination of parameters. " (_("Could not do Paint All. Try a different combination of parameters. "
"Or a different Method of paint"), str(e))) "Or a different Method of paint"), str(e)))
return return
pol_nr += 1 pol_nr += 1
@ -2373,7 +2391,7 @@ class ToolPaint(FlatCAMTool, Gerber):
log.debug("Could not Paint the polygons. %s" % str(e)) log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit('[ERROR] %s\n%s' % self.app.inform.emit('[ERROR] %s\n%s' %
(_("Could not do Paint All. Try a different combination of parameters. " (_("Could not do Paint All. Try a different combination of parameters. "
"Or a different Method of paint"), str(e))) "Or a different Method of paint"), str(e)))
return return
pol_nr += 1 pol_nr += 1

View File

@ -5,19 +5,29 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtGui, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from copy import copy, deepcopy
from ObjectCollection import * from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection
import time from FlatCAMObj import FlatCAMGeometry, FlatCAMGerber, FlatCAMExcellon
import FlatCAMApp
from copy import deepcopy
# from ObjectCollection import *
import numpy as np
import shapely.affinity as affinity
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
import logging
fcTranslate.apply_language('strings') fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class Panelize(FlatCAMTool): class Panelize(FlatCAMTool):
@ -367,15 +377,13 @@ class Panelize(FlatCAMTool):
# Get source object. # Get source object.
try: try:
obj = self.app.collection.get_by_name(str(name)) panel_obj = self.app.collection.get_by_name(str(name))
except Exception as e: except Exception as e:
log.debug("Panelize.on_panelize() --> %s" % str(e)) log.debug("Panelize.on_panelize() --> %s" % str(e))
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
(_("Could not retrieve object"), name)) (_("Could not retrieve object"), name))
return "Could not retrieve object: %s" % name return "Could not retrieve object: %s" % name
panel_obj = obj
if panel_obj is None: if panel_obj is None:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
(_("Object not found"), panel_obj)) (_("Object not found"), panel_obj))
@ -443,6 +451,18 @@ class Panelize(FlatCAMTool):
rows -= 1 rows -= 1
panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1)) panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1))
if isinstance(panel_obj, FlatCAMExcellon) or isinstance(panel_obj, FlatCAMGeometry):
# make a copy of the panelized Excellon or Geometry tools
copied_tools = dict()
for tt, tt_val in list(panel_obj.tools.items()):
copied_tools[tt] = deepcopy(tt_val)
if isinstance(panel_obj, FlatCAMGerber):
# make a copy of the panelized Gerber apertures
copied_apertures = dict()
for tt, tt_val in list(panel_obj.apertures.items()):
copied_apertures[tt] = deepcopy(tt_val)
def panelize_2(): def panelize_2():
if panel_obj is not None: if panel_obj is not None:
self.app.inform.emit(_("Generating panel ... ")) self.app.inform.emit(_("Generating panel ... "))
@ -452,7 +472,7 @@ class Panelize(FlatCAMTool):
def job_init_excellon(obj_fin, app_obj): def job_init_excellon(obj_fin, app_obj):
currenty = 0.0 currenty = 0.0
self.app.progress.emit(10) self.app.progress.emit(10)
obj_fin.tools = panel_obj.tools.copy() obj_fin.tools = copied_tools
obj_fin.drills = [] obj_fin.drills = []
obj_fin.slots = [] obj_fin.slots = []
obj_fin.solid_geometry = [] obj_fin.solid_geometry = []
@ -472,7 +492,6 @@ class Panelize(FlatCAMTool):
currentx = 0.0 currentx = 0.0
for col in range(columns): for col in range(columns):
element += 1 element += 1
disp_number = 0
old_disp_number = 0 old_disp_number = 0
if panel_obj.drills: if panel_obj.drills:
@ -493,7 +512,7 @@ class Panelize(FlatCAMTool):
drill_nr += 1 drill_nr += 1
disp_number = int(np.interp(drill_nr, [0, geo_len_drills], [0, 100])) disp_number = int(np.interp(drill_nr, [0, geo_len_drills], [0, 100]))
if disp_number > old_disp_number and disp_number <= 100: if old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %s: %d D:%d%%' % self.app.proc_container.update_view_text(' %s: %d D:%d%%' %
(_("Copy"), (_("Copy"),
int(element), int(element),
@ -520,7 +539,7 @@ class Panelize(FlatCAMTool):
slot_nr += 1 slot_nr += 1
disp_number = int(np.interp(slot_nr, [0, geo_len_slots], [0, 100])) disp_number = int(np.interp(slot_nr, [0, geo_len_slots], [0, 100]))
if disp_number > old_disp_number and disp_number <= 100: if old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %s: %d S:%d%%' % self.app.proc_container.update_view_text(' %s: %d S:%d%%' %
(_("Copy"), (_("Copy"),
int(element), int(element),
@ -557,12 +576,12 @@ class Panelize(FlatCAMTool):
# create the initial structure on which to create the panel # create the initial structure on which to create the panel
if isinstance(panel_obj, FlatCAMGeometry): if isinstance(panel_obj, FlatCAMGeometry):
obj_fin.multigeo = panel_obj.multigeo obj_fin.multigeo = panel_obj.multigeo
obj_fin.tools = deepcopy(panel_obj.tools) obj_fin.tools = copied_tools
if panel_obj.multigeo is True: if panel_obj.multigeo is True:
for tool in panel_obj.tools: for tool in panel_obj.tools:
obj_fin.tools[tool]['solid_geometry'][:] = [] obj_fin.tools[tool]['solid_geometry'][:] = []
elif isinstance(panel_obj, FlatCAMGerber): elif isinstance(panel_obj, FlatCAMGerber):
obj_fin.apertures = deepcopy(panel_obj.apertures) obj_fin.apertures = copied_apertures
for ap in obj_fin.apertures: for ap in obj_fin.apertures:
obj_fin.apertures[ap]['geometry'] = list() obj_fin.apertures[ap]['geometry'] = list()
@ -594,7 +613,6 @@ class Panelize(FlatCAMTool):
for col in range(columns): for col in range(columns):
element += 1 element += 1
disp_number = 0
old_disp_number = 0 old_disp_number = 0
if isinstance(panel_obj, FlatCAMGeometry): if isinstance(panel_obj, FlatCAMGeometry):

View File

@ -5,11 +5,11 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from FlatCAMTool import FlatCAMTool from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCSpinner, FCButton, FCTable
from flatcamGUI.GUIElements import RadioSet, FCComboBox, FCSpinner, FCButton, FCTable
from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtCore import pyqtSignal
import re import re
import os import os
from datetime import datetime from datetime import datetime
@ -26,7 +26,7 @@ if '_' not in builtins.__dict__:
class PcbWizard(FlatCAMTool): class PcbWizard(FlatCAMTool):
file_loaded = pyqtSignal(str, str) file_loaded = QtCore.pyqtSignal(str, str)
toolName = _("PcbWizard Import Tool") toolName = _("PcbWizard Import Tool")

View File

@ -6,10 +6,14 @@
# ########################################################## # ##########################################################
from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from FlatCAMObj import * from FlatCAMObj import FlatCAMCNCjob
from shapely.geometry import MultiPolygon, Polygon
from shapely.ops import cascaded_union
from copy import deepcopy
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -18,11 +22,13 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class Properties(FlatCAMTool): class Properties(FlatCAMTool):
toolName = _("Properties") toolName = _("Properties")
calculations_finished = pyqtSignal(float, float, float, float, object) calculations_finished = QtCore.pyqtSignal(float, float, float, float, object)
def __init__(self, app): def __init__(self, app):
FlatCAMTool.__init__(self, app) FlatCAMTool.__init__(self, app)
@ -150,18 +156,18 @@ class Properties(FlatCAMTool):
self.addChild(obj_name, [obj.options['name']]) self.addChild(obj_name, [obj.options['name']])
def job_thread(obj): def job_thread(obj_prop):
proc = self.app.proc_container.new(_("Calculating dimensions ... Please wait.")) proc = self.app.proc_container.new(_("Calculating dimensions ... Please wait."))
length = 0.0 length = 0.0
width = 0.0 width = 0.0
area = 0.0 area = 0.0
geo = obj.solid_geometry geo = obj_prop.solid_geometry
if geo: if geo:
# calculate physical dimensions # calculate physical dimensions
try: try:
xmin, ymin, xmax, ymax = obj.bounds() xmin, ymin, xmax, ymax = obj_prop.bounds()
length = abs(xmax - xmin) length = abs(xmax - xmin)
width = abs(ymax - ymin) width = abs(ymax - ymin)
@ -179,9 +185,9 @@ class Properties(FlatCAMTool):
xmax = [] xmax = []
ymax = [] ymax = []
for tool in obj.tools: for tool_k in obj_prop.tools:
try: try:
x0, y0, x1, y1 = cascaded_union(obj.tools[tool]['solid_geometry']).bounds x0, y0, x1, y1 = cascaded_union(obj_prop.tools[tool_k]['solid_geometry']).bounds
xmin.append(x0) xmin.append(x0)
ymin.append(y0) ymin.append(y0)
xmax.append(x1) xmax.append(x1)
@ -207,25 +213,25 @@ class Properties(FlatCAMTool):
log.debug("Properties.addItems() --> %s" % str(e)) log.debug("Properties.addItems() --> %s" % str(e))
area_chull = 0.0 area_chull = 0.0
if not isinstance(obj, FlatCAMCNCjob): if not isinstance(obj_prop, FlatCAMCNCjob):
# calculate and add convex hull area # calculate and add convex hull area
if geo: if geo:
if isinstance(geo, MultiPolygon): if isinstance(geo, MultiPolygon):
env_obj = geo.convex_hull env_obj = geo.convex_hull
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \ elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon): (isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
env_obj = cascaded_union(obj.solid_geometry) env_obj = cascaded_union(obj_prop.solid_geometry)
env_obj = env_obj.convex_hull env_obj = env_obj.convex_hull
else: else:
env_obj = cascaded_union(obj.solid_geometry) env_obj = cascaded_union(obj_prop.solid_geometry)
env_obj = env_obj.convex_hull env_obj = env_obj.convex_hull
area_chull = env_obj.area area_chull = env_obj.area
else: else:
try: try:
area_chull = [] area_chull = []
for tool in obj.tools: for tool_k in obj_prop.tools:
area_el = cascaded_union(obj.tools[tool]['solid_geometry']).convex_hull area_el = cascaded_union(obj_prop.tools[tool_k]['solid_geometry']).convex_hull
area_chull.append(area_el.area) area_chull.append(area_el.area)
area_chull = max(area_chull) area_chull = max(area_chull)
except Exception as e: except Exception as e:

View File

@ -5,15 +5,18 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from FlatCAMTool import FlatCAMTool from PyQt5 import QtWidgets
from copy import copy, deepcopy
from ObjectCollection import *
import time
from FlatCAMPool import *
from os import getpid
from shapely.ops import nearest_points
from shapely.geometry.base import BaseGeometry
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection
from copy import deepcopy
from FlatCAMPool import *
# from os import getpid
from shapely.ops import nearest_points
from shapely.geometry import MultiPolygon, Polygon
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -22,12 +25,14 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class RulesCheck(FlatCAMTool): class RulesCheck(FlatCAMTool):
toolName = _("Check Rules") toolName = _("Check Rules")
tool_finished = pyqtSignal(list) tool_finished = QtCore.pyqtSignal(list)
def __init__(self, app): def __init__(self, app):
super(RulesCheck, self).__init__(self) super(RulesCheck, self).__init__(self)

View File

@ -6,7 +6,7 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
# from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextCursor from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QVBoxLayout, QWidget from PyQt5.QtWidgets import QVBoxLayout, QWidget

View File

@ -278,7 +278,7 @@ class SolderPaste(FlatCAMTool):
) )
self.pp_combo = FCComboBox() self.pp_combo = FCComboBox()
self.pp_combo.setStyleSheet('background-color: rgb(255,255,255)') # self.pp_combo.setStyleSheet('background-color: rgb(255,255,255)')
self.gcode_form_layout.addRow(pp_label, self.pp_combo) self.gcode_form_layout.addRow(pp_label, self.pp_combo)
# ## Buttons # ## Buttons

View File

@ -5,12 +5,18 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
# from copy import copy, deepcopy from flatcamGUI.GUIElements import FCCheckBox, FCButton
from ObjectCollection import *
import time
from shapely.geometry import Polygon, MultiPolygon, MultiLineString, LineString
from shapely.ops import cascaded_union
import traceback
from copy import deepcopy
import time
import logging
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -19,12 +25,14 @@ fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__: if '_' not in builtins.__dict__:
_ = gettext.gettext _ = gettext.gettext
log = logging.getLogger('base')
class ToolSub(FlatCAMTool): class ToolSub(FlatCAMTool):
job_finished = QtCore.pyqtSignal(bool) job_finished = QtCore.pyqtSignal(bool)
toolName = _("Substract Tool") toolName = _("Subtract Tool")
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
@ -64,8 +72,8 @@ class ToolSub(FlatCAMTool):
self.target_gerber_label = QtWidgets.QLabel('%s:' % _("Target")) self.target_gerber_label = QtWidgets.QLabel('%s:' % _("Target"))
self.target_gerber_label.setToolTip( self.target_gerber_label.setToolTip(
_("Gerber object from which to substract\n" _("Gerber object from which to subtract\n"
"the substractor Gerber object.") "the subtractor Gerber object.")
) )
form_layout.addRow(self.target_gerber_label, self.target_gerber_combo) form_layout.addRow(self.target_gerber_label, self.target_gerber_combo)
@ -76,9 +84,9 @@ class ToolSub(FlatCAMTool):
self.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) self.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sub_gerber_combo.setCurrentIndex(1) self.sub_gerber_combo.setCurrentIndex(1)
self.sub_gerber_label = QtWidgets.QLabel('%s:' % _("Substractor")) self.sub_gerber_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
self.sub_gerber_label.setToolTip( self.sub_gerber_label.setToolTip(
_("Gerber object that will be substracted\n" _("Gerber object that will be subtracted\n"
"from the target Gerber object.") "from the target Gerber object.")
) )
e_lab_1 = QtWidgets.QLabel('') e_lab_1 = QtWidgets.QLabel('')
@ -87,7 +95,7 @@ class ToolSub(FlatCAMTool):
self.intersect_btn = FCButton(_('Substract Gerber')) self.intersect_btn = FCButton(_('Substract Gerber'))
self.intersect_btn.setToolTip( self.intersect_btn.setToolTip(
_("Will remove the area occupied by the substractor\n" _("Will remove the area occupied by the subtractor\n"
"Gerber from the Target Gerber.\n" "Gerber from the Target Gerber.\n"
"Can be used to remove the overlapping silkscreen\n" "Can be used to remove the overlapping silkscreen\n"
"over the soldermask.") "over the soldermask.")
@ -110,8 +118,8 @@ class ToolSub(FlatCAMTool):
self.target_geo_label = QtWidgets.QLabel('%s:' % _("Target")) self.target_geo_label = QtWidgets.QLabel('%s:' % _("Target"))
self.target_geo_label.setToolTip( self.target_geo_label.setToolTip(
_("Geometry object from which to substract\n" _("Geometry object from which to subtract\n"
"the substractor Geometry object.") "the subtractor Geometry object.")
) )
form_geo_layout.addRow(self.target_geo_label, self.target_geo_combo) form_geo_layout.addRow(self.target_geo_label, self.target_geo_combo)
@ -122,9 +130,9 @@ class ToolSub(FlatCAMTool):
self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex())) self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.sub_geo_combo.setCurrentIndex(1) self.sub_geo_combo.setCurrentIndex(1)
self.sub_geo_label = QtWidgets.QLabel('%s:' % _("Substractor")) self.sub_geo_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
self.sub_geo_label.setToolTip( self.sub_geo_label.setToolTip(
_("Geometry object that will be substracted\n" _("Geometry object that will be subtracted\n"
"from the target Geometry object.") "from the target Geometry object.")
) )
e_lab_1 = QtWidgets.QLabel('') e_lab_1 = QtWidgets.QLabel('')
@ -132,12 +140,12 @@ class ToolSub(FlatCAMTool):
form_geo_layout.addRow(self.sub_geo_label, self.sub_geo_combo) form_geo_layout.addRow(self.sub_geo_label, self.sub_geo_combo)
self.close_paths_cb = FCCheckBox(_("Close paths")) self.close_paths_cb = FCCheckBox(_("Close paths"))
self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry substractor object.")) self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry subtractor object."))
self.tools_box.addWidget(self.close_paths_cb) self.tools_box.addWidget(self.close_paths_cb)
self.intersect_geo_btn = FCButton(_('Substract Geometry')) self.intersect_geo_btn = FCButton(_('Subtract Geometry'))
self.intersect_geo_btn.setToolTip( self.intersect_geo_btn.setToolTip(
_("Will remove the area occupied by the substractor\n" _("Will remove the area occupied by the subtractor\n"
"Geometry from the Target Geometry.") "Geometry from the Target Geometry.")
) )
self.tools_box.addWidget(self.intersect_geo_btn) self.tools_box.addWidget(self.intersect_geo_btn)
@ -256,7 +264,7 @@ class ToolSub(FlatCAMTool):
self.sub_grb_obj_name = self.sub_gerber_combo.currentText() self.sub_grb_obj_name = self.sub_gerber_combo.currentText()
if self.sub_grb_obj_name == '': if self.sub_grb_obj_name == '':
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("No Substractor object loaded.")) _("No Subtractor object loaded."))
return return
# Get substractor object. # Get substractor object.
@ -458,7 +466,7 @@ class ToolSub(FlatCAMTool):
self.sub_geo_obj_name = self.sub_geo_combo.currentText() self.sub_geo_obj_name = self.sub_geo_combo.currentText()
if self.sub_geo_obj_name == '': if self.sub_geo_obj_name == '':
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("No Substractor object loaded.")) _("No Subtractor object loaded."))
return return
# Get substractor object. # Get substractor object.
@ -472,7 +480,7 @@ class ToolSub(FlatCAMTool):
if self.sub_geo_obj.multigeo: if self.sub_geo_obj.multigeo:
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Currently, the Substractor geometry cannot be of type Multigeo.")) _("Currently, the Subtractor geometry cannot be of type Multigeo."))
return return
# create the target_options obj # create the target_options obj

View File

@ -5,8 +5,10 @@
# MIT Licence # # MIT Licence #
# ########################################################## # ##########################################################
from PyQt5 import QtWidgets
from FlatCAMTool import FlatCAMTool from FlatCAMTool import FlatCAMTool
from FlatCAMObj import * from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, EvalEntry2
from FlatCAMObj import FlatCAMCNCjob
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
@ -271,7 +273,7 @@ class ToolTransform(FlatCAMTool):
_("Flip the selected object(s) over the X axis.") _("Flip the selected object(s) over the X axis.")
) )
hlay0= QtWidgets.QHBoxLayout() hlay0 = QtWidgets.QHBoxLayout()
self.transform_lay.addLayout(hlay0) self.transform_lay.addLayout(hlay0)
hlay0.addWidget(self.flipx_button) hlay0.addWidget(self.flipx_button)
@ -312,7 +314,7 @@ class ToolTransform(FlatCAMTool):
self.ois_flip = OptionalInputSection(self.flip_ref_cb, [self.flip_ref_entry, self.flip_ref_button], logic=True) self.ois_flip = OptionalInputSection(self.flip_ref_cb, [self.flip_ref_entry, self.flip_ref_button], logic=True)
hlay1= QtWidgets.QHBoxLayout() hlay1 = QtWidgets.QHBoxLayout()
self.transform_lay.addLayout(hlay1) self.transform_lay.addLayout(hlay1)
hlay1.addWidget(self.flip_ref_label) hlay1.addWidget(self.flip_ref_label)

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
share/close_edit_file16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

BIN
share/close_edit_file32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

BIN
share/copy_file16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

BIN
share/copy_file32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

BIN
share/delete_file16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

BIN
share/delete_file32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

BIN
share/deselect_all32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

BIN
share/edit_file16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

BIN
share/edit_file32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

BIN
share/new_file16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

BIN
share/new_file32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

BIN
share/new_file_exc16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

BIN
share/new_file_exc32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

BIN
share/new_file_geo16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

BIN
share/new_file_geo32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

BIN
share/new_file_grb16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

BIN
share/new_file_grb32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

BIN
share/panelize16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

BIN
share/panelize32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

BIN
share/project_save16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

BIN
share/project_save32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B