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

This commit is contained in:
Camellan 2019-09-26 19:33:52 +04:00
commit eda562e83e
29 changed files with 11587 additions and 10272 deletions

View File

@ -124,7 +124,7 @@ class App(QtCore.QObject):
# ################## Version and VERSION DATE ##############################
# ##########################################################################
version = 8.97
version_date = "2019/09/22"
version_date = "2019/09/27"
beta = True
engine = '3D'
@ -657,6 +657,7 @@ class App(QtCore.QObject):
# Geometry Editor
"geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,
"geometry_editor_milling_type": self.ui.geometry_defaults_form.geometry_editor_group.milling_type_radio,
# CNCJob General
"cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb,
@ -1064,6 +1065,7 @@ class App(QtCore.QObject):
# Geometry Editor
"geometry_editor_sel_limit": 30,
"geometry_editor_milling_type": "cl",
# CNC Job General
"cncjob_plot": True,
@ -1976,6 +1978,7 @@ class App(QtCore.QObject):
self.ui.buttonPreview.clicked.connect(self.handlePreview)
self.ui.buttonFind.clicked.connect(self.handleFindGCode)
self.ui.buttonReplace.clicked.connect(self.handleReplaceGCode)
self.ui.button_copy_all.clicked.connect(self.handleCopyAll)
# portability changed signal
self.ui.general_defaults_form.general_app_group.portability_cb.stateChanged.connect(self.on_portable_checked)
@ -5628,15 +5631,24 @@ class App(QtCore.QObject):
self.report_usage("on_toggle_axis()")
if self.toggle_axis is False:
self.plotcanvas.v_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.plotcanvas.h_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.plotcanvas.redraw()
if self.is_legacy is False:
self.plotcanvas.v_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.plotcanvas.h_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.plotcanvas.redraw()
else:
self.plotcanvas.axes.axhline(color=(0.70, 0.3, 0.3), linewidth=2)
self.plotcanvas.axes.axvline(color=(0.70, 0.3, 0.3), linewidth=2)
self.plotcanvas.canvas.draw()
pass
self.toggle_axis = True
else:
self.plotcanvas.v_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.plotcanvas.h_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.plotcanvas.redraw()
if self.is_legacy is False:
self.plotcanvas.v_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.plotcanvas.h_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.plotcanvas.redraw()
else:
self.plotcanvas.axes.lines[:] = []
self.plotcanvas.canvas.draw()
self.toggle_axis = False
def on_toggle_grid(self):
@ -6539,6 +6551,11 @@ class App(QtCore.QObject):
# Mark end of undo block
cursor.endEditBlock()
def handleCopyAll(self):
text = self.ui.code_editor.toPlainText()
self.clipboard.setText(text)
self.inform.emit(_("Code Editor content copied to clipboard ..."))
def handleRunCode(self):
# trying to run a Tcl command without having the Shell open will create some warnings because the Tcl Shell
# tries to print on a hidden widget, therefore show the dock if hidden
@ -6896,9 +6913,9 @@ class App(QtCore.QObject):
"""
self.report_usage("on_jump_to()")
if self.is_legacy is True:
self.inform.emit(_("Not available with the current Graphic Engine Legacy(2D)."))
return
# if self.is_legacy is True:
# self.inform.emit(_("Not available with the current Graphic Engine Legacy(2D)."))
# return
if not custom_location:
dia_box = Dialog_box(title=_("Jump to ..."),
@ -6919,11 +6936,7 @@ class App(QtCore.QObject):
location = custom_location
if fit_center:
if self.is_legacy is False:
self.plotcanvas.fit_center(loc=location)
else:
pass
# self.plotcanvas.fit_view()
self.plotcanvas.fit_center(loc=location)
cursor = QtGui.QCursor()
@ -6932,13 +6945,17 @@ class App(QtCore.QObject):
jump_loc = self.plotcanvas.translate_coords_2((location[0], location[1]))
cursor.setPos(canvas_origin.x() + jump_loc[0], (canvas_origin.y() + jump_loc[1]))
else:
# the origin finding works but not mapping the location to pixels
# find the canvas origin which is in the top left corner
canvas_origin = self.plotcanvas.native.mapToGlobal(QtCore.QPoint(0, 0))
# determine the coordinates for the lowest left point of the canvas
x0, y0 = canvas_origin.x(), canvas_origin.y() + self.ui.right_layout.geometry().height()
x0, y0 = x0 + self.plotcanvas.axes.transData.transform((0, 0))[0], y0 - \
self.plotcanvas.axes.transData.transform((0, 0))[1]
loc = self.plotcanvas.axes.transData.transform(location)
cursor.setPos(x0 + loc[0]/50, y0 - loc[1]/50)
# transform the given location from data coordinates to display coordinates. THe display coordinates are
# in pixels where the origin 0,0 is in the lowest left point of the display window (in our case is the
# canvas) and the point (width, height) is in the top-right location
loc = self.plotcanvas.axes.transData.transform_point(location)
cursor.setPos(x0 + loc[0], y0 - loc[1])
self.inform.emit('[success] %s' %
_("Done."))
@ -11142,6 +11159,7 @@ class App(QtCore.QObject):
self.kp = self.plotcanvas.graph_event_connect('key_press', self.ui.keyPressEvent)
self.app_cursor = self.plotcanvas.new_cursor()
if self.ui.grid_snap_btn.isChecked():
self.app_cursor.enabled = True
else:

View File

@ -46,3 +46,26 @@ class LoudDict(dict):
"""
self.callback = callback
class FCSignal:
"""
Taken from here: https://blog.abstractfactory.io/dynamic-signals-in-pyqt/
"""
def __init__(self):
self.__subscribers = []
def emit(self, *args, **kwargs):
for subs in self.__subscribers:
subs(*args, **kwargs)
def connect(self, func):
self.__subscribers.append(func)
def disconnect(self, func):
try:
self.__subscribers.remove(func)
except ValueError:
print('Warning: function %s not removed '
'from signal %s' % (func, self))

View File

@ -5756,9 +5756,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
pass
self.ui.annotation_cb.stateChanged.connect(self.on_annotation_change)
if self.app.is_legacy is False:
# set if to display text annotations
self.ui.annotation_cb.set_value(self.app.defaults["cncjob_annotation"])
# set if to display text annotations
self.ui.annotation_cb.set_value(self.app.defaults["cncjob_annotation"])
# Show/Hide Advanced Options
if self.app.defaults["global_app_level"] == 'b':
@ -5919,20 +5918,23 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
if "toolchange_probe" in ppg.lower():
probe_pp = True
break
except Exception as e:
log.debug("FlatCAMCNCJob.gcode_header() error: --> %s" % str(e))
except KeyError:
# log.debug("FlatCAMCNCJob.gcode_header() error: --> %s" % str(e))
pass
try:
if self.options['ppname_e'] == 'marlin' or self.options['ppname_e'] == 'Repetier':
marlin = True
except Exception as e:
log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e))
except KeyError:
# log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e))
pass
try:
if "toolchange_probe" in self.options['ppname_e'].lower():
probe_pp = True
except Exception as e:
log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e))
except KeyError:
# log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e))
pass
if marlin is True:
gcode = ';Marlin(Repetier) G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s\n' % \
@ -6244,7 +6246,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
# self.plot(kind=kind)
self.annotation.redraw()
else:
self.inform.emit(_("Not available with the current Graphic Engine Legacy(2D)."))
kind = self.ui.cncplot_method_combo.get_value()
self.plot(kind=kind)
def convert_units(self, units):
log.debug("FlatCAMObj.FlatCAMECNCjob.convert_units()")

View File

@ -605,30 +605,33 @@ class ObjectCollection(QtCore.QAbstractItemModel):
def delete_all(self):
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_all()")
try:
self.app.all_objects_list.clear()
self.app.plotcanvas.redraw()
self.app.geo_editor.clear()
self.app.all_objects_list.clear()
self.app.exc_editor.clear()
self.app.geo_editor.clear()
self.app.dblsidedtool.reset_fields()
self.app.exc_editor.clear()
self.app.panelize_tool.reset_fields()
self.app.dblsidedtool.reset_fields()
self.app.cutout_tool.reset_fields()
self.app.panelize_tool.reset_fields()
self.app.film_tool.reset_fields()
self.app.cutout_tool.reset_fields()
self.beginResetModel()
self.app.film_tool.reset_fields()
self.checked_indexes = []
self.beginResetModel()
for group in self.root_item.child_items:
group.remove_children()
self.checked_indexes = []
for group in self.root_item.child_items:
group.remove_children()
self.endResetModel()
self.endResetModel()
self.app.plotcanvas.redraw()
except Exception as e:
log.debug("ObjectCollection.delete_all() --> %s" % str(e))
def get_active(self):
"""

View File

@ -9,6 +9,25 @@ CAD program, and create G-Code for Isolation routing.
=================================================
25.09.2019
- added a Copy All button in the Code Editor, clicking this button will copy all text in the editor to the clipboard
- added a 'Milling Type' radio button in Geometry Editor Preferences to contorl the type of geometry will be generated in the Geo Editor (for conventional milling or for the climb milling)
- added the functionality to allow climb/conventional milling selection for the geometry created in the Geometry Editor
- now any Geometry that is edited in Geometry editor will have coordinates ordered such that the resulting Gcode will allow the selected milling type in the 'Milling Type' radio button in Geometry Editor Preferences (which depends also of the spindle direction)
- some strings update
- French Google-translation at 100%
- German Google-translation update to 100%
- updated the other languages and the .POT file
25.09.2019
- French translation at 33%
- fixed the 'Jump To' function to work in legacy graphic engine
- in legacy graphic engine fixed the mouse cursor shape when grid snapping is ON, such that it fits with the shape from the OpenGL graphic engine
- in legacy graphic engine fixed the axis toggle
- French Google-translation at 48%
24.09.2019
- fixed the fullscreen method to show the application window in fullscreen wherever the mouse pointer it is therefore on the screen we are working on; before it was showing always on the primary screen
@ -16,6 +35,9 @@ CAD program, and create G-Code for Isolation routing.
- in legacy graphic engine, fixed issue where immediately after changing the mouse cursor snapping the mouse cursor shape was not updated
- in legacy graphic engine, fixed issue where while zooming the mouse cursor shape was not updated
- in legacy graphic engine, fixed issue where immediately after panning finished the mouse cursor shape was not updated
- unfortunately the fix for issue where while zooming the mouse cursor shape was not updated braked something in way that Matplotlib work with PyQt5, therefore I removed it
- fixed a bug in legacy graphic engine: when doing the self.app.collection.delete_all() in new_project an app crash occurred
- implemented the Annotation change in CNCJob Selected Tab for the legacy graphic engine
23.09.2019

View File

@ -17,10 +17,11 @@ from camlib import *
from FlatCAMTool import FlatCAMTool
from flatcamGUI.ObjectUI import LengthEntry, RadioSet
from shapely.geometry import LineString, LinearRing, MultiLineString
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
# from shapely.geometry import mapping
from shapely.ops import cascaded_union, unary_union
import shapely.affinity as affinity
from shapely.geometry.polygon import orient
from numpy import arctan2, Inf, array, sqrt, sign, dot
from numpy.linalg import norm as numpy_norm
@ -3562,19 +3563,33 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.select_tool("select")
if self.app.defaults['geometry_spindledir'] == 'CW':
if self.app.defaults['geometry_editor_milling_type'] == 'cl':
milling_type = 1 # CCW motion = climb milling (spindle is rotating CW)
else:
milling_type = -1 # CW motion = conventional milling (spindle is rotating CW)
else:
if self.app.defaults['geometry_editor_milling_type'] == 'cl':
milling_type = -1 # CCW motion = climb milling (spindle is rotating CCW)
else:
milling_type = 1 # CW motion = conventional milling (spindle is rotating CCW)
# Link shapes into editor.
if multigeo_tool:
self.multigeo_tool = multigeo_tool
geo_to_edit = fcgeometry.flatten(geometry=fcgeometry.tools[self.multigeo_tool]['solid_geometry'])
self.app.inform.emit('[WARNING_NOTCL] %s: %s %s: %s' %
(_("Editing MultiGeo Geometry, tool"),
str(self.multigeo_tool),
_("with diameter"),
str(fcgeometry.tools[self.multigeo_tool]['tooldia'])
)
)
geo_to_edit = self.flatten(geometry=fcgeometry.tools[self.multigeo_tool]['solid_geometry'],
orient_val=milling_type)
self.app.inform.emit(
'[WARNING_NOTCL] %s: %s %s: %s' % (
_("Editing MultiGeo Geometry, tool"),
str(self.multigeo_tool),
_("with diameter"),
str(fcgeometry.tools[self.multigeo_tool]['tooldia'])
)
)
else:
geo_to_edit = fcgeometry.flatten()
geo_to_edit = self.flatten(geometry=fcgeometry.solid_geometry,
orient_val=milling_type)
for shape in geo_to_edit:
if shape is not None: # TODO: Make flatten never create a None
@ -3672,7 +3687,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.defaults["global_point_clipboard_format"] % (self.pos[0], self.pos[1]))
return
# Selection with left mouse button
if self.active_tool is not None and event.button == 1:
@ -4055,8 +4069,37 @@ class FlatCAMGeoEditor(QtCore.QObject):
def on_shape_complete(self):
self.app.log.debug("on_shape_complete()")
geom = self.active_tool.geometry.geo
if self.app.defaults['geometry_editor_milling_type'] == 'cl':
# reverse the geometry coordinates direction to allow creation of Gcode for climb milling
try:
pl = []
for p in geom:
if p is not None:
if isinstance(p, Polygon):
pl.append(Polygon(p.exterior.coords[::-1], p.interiors))
elif isinstance(p, LinearRing):
pl.append(Polygon(p.coords[::-1]))
# elif isinstance(p, LineString):
# pl.append(LineString(p.coords[::-1]))
geom = MultiPolygon(pl)
except TypeError:
if isinstance(geom, Polygon) and geom is not None:
geom = Polygon(geom.exterior.coords[::-1], geom.interiors)
elif isinstance(geom, LinearRing) and geom is not None:
geom = Polygon(geom.coords[::-1])
elif isinstance(geom, LineString) and geom is not None:
geom = LineString(geom.coords[::-1])
else:
log.debug("FlatCAMGeoEditor.on_shape_complete() Error --> Unexpected Geometry %s" %
type(geom))
except Exception as e:
log.debug("FlatCAMGeoEditor.on_shape_complete() Error --> %s" % str(e))
return 'fail'
# Add shape
self.add_shape(self.active_tool.geometry)
self.add_shape(DrawToolShape(geom))
# Remove any utility shapes
self.delete_utility_geometry()
@ -4641,6 +4684,47 @@ class FlatCAMGeoEditor(QtCore.QObject):
'[success] %s' % _("Paint done."))
self.replot()
def flatten(self, geometry, orient_val=1, reset=True, pathonly=False):
"""
Creates a list of non-iterable linear geometry objects.
Polygons are expanded into its exterior and interiors if specified.
Results are placed in self.flat_geometry
:param geometry: Shapely type or list or list of list of such.
:param orient_val: will orient the exterior coordinates CW if 1 and CCW for else (whatever else means ...)
https://shapely.readthedocs.io/en/stable/manual.html#polygons
:param reset: Clears the contents of self.flat_geometry.
:param pathonly: Expands polygons into linear elements.
"""
if reset:
self.flat_geo = []
# ## If iterable, expand recursively.
try:
for geo in geometry:
if geo is not None:
self.flatten(geometry=geo,
orient_val=orient_val,
reset=False,
pathonly=pathonly)
# ## Not iterable, do the actual indexing and add.
except TypeError:
if type(geometry) == Polygon:
geometry = orient(geometry, orient_val)
if pathonly and type(geometry) == Polygon:
self.flat_geo.append(geometry.exterior)
self.flatten(geometry=geometry.interiors,
reset=False,
pathonly=True)
else:
self.flat_geo.append(geometry)
return self.flat_geo
def distance(pt1, pt2):
return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)

View File

@ -1844,6 +1844,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.buttonReplace.setToolTip(_("Will replace the string from the Find box with the one in the Replace box."))
self.buttonReplace.setMinimumWidth(100)
self.entryReplace = FCEntry()
self.entryReplace.setToolTip(_("String to replace the one in the Find box throughout the text."))
@ -1851,6 +1852,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.sel_all_cb.setToolTip(_("When checked it will replace all instances in the 'Find' box\n"
"with the text in the 'Replace' box.."))
self.button_copy_all = QtWidgets.QPushButton(_('Copy All'))
self.button_copy_all.setToolTip(_("Will copy all the text in the Code Editor to the clipboard."))
self.button_copy_all.setMinimumWidth(100)
self.buttonOpen = QtWidgets.QPushButton(_('Open Code'))
self.buttonOpen.setToolTip(_("Will open a text file in the editor."))
@ -1870,6 +1876,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
cnc_tab_lay_1.addWidget(self.buttonReplace)
cnc_tab_lay_1.addWidget(self.entryReplace)
cnc_tab_lay_1.addWidget(self.sel_all_cb)
cnc_tab_lay_1.addWidget(self.button_copy_all)
self.cncjob_tab_layout.addLayout(cnc_tab_lay_1, 1, 0, 1, 5)
cnc_tab_lay_3 = QtWidgets.QHBoxLayout()
@ -2347,15 +2354,24 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
# Toggle axis
if key == QtCore.Qt.Key_G:
if self.app.toggle_axis is False:
self.app.plotcanvas.v_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.app.plotcanvas.h_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.app.plotcanvas.redraw()
if self.app.is_legacy is False:
self.app.plotcanvas.v_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.app.plotcanvas.h_line.set_data(color=(0.70, 0.3, 0.3, 1.0))
self.app.plotcanvas.redraw()
else:
self.app.plotcanvas.axes.axhline(color=(0.70, 0.3, 0.3), linewidth=2)
self.app.plotcanvas.axes.axvline(color=(0.70, 0.3, 0.3), linewidth=2)
self.app.plotcanvas.canvas.draw()
self.app.toggle_axis = True
else:
self.app.plotcanvas.v_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.app.plotcanvas.h_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.app.plotcanvas.redraw()
if self.app.is_legacy is False:
self.app.plotcanvas.v_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.app.plotcanvas.h_line.set_data(color=(0.0, 0.0, 0.0, 0.0))
self.app.plotcanvas.redraw()
else:
self.app.plotcanvas.axes.lines[:] = []
self.app.plotcanvas.canvas.draw()
self.app.toggle_axis = False
# Open Preferences Window

View File

@ -10,14 +10,6 @@
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import pyqtSignal
# Prevent conflict with Qt5 and above.
from matplotlib import use as mpl_use
mpl_use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.widgets import Cursor
# needed for legacy mode
# Used for solid polygons in Matplotlib
from descartes.patch import PolygonPatch
@ -25,14 +17,21 @@ from descartes.patch import PolygonPatch
from shapely.geometry import Polygon, LineString, LinearRing, Point, MultiPolygon, MultiLineString
import FlatCAMApp
from copy import deepcopy
import logging
import traceback
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
# Prevent conflict with Qt5 and above.
from matplotlib import use as mpl_use
mpl_use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
# from matplotlib.widgets import Cursor
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
@ -78,7 +77,7 @@ class CanvasCache(QtCore.QObject):
self.axes.set_xticks([])
self.axes.set_yticks([])
self.canvas = FigureCanvasAgg(self.figure)
self.canvas = FigureCanvas(self.figure)
self.cache = None
@ -115,33 +114,6 @@ class CanvasCache(QtCore.QObject):
# log.debug("A new object is available. Should plot it!")
class FigureCanvas(FigureCanvasQTAgg):
"""
Reimplemented this so I can emit a signal when the idle drawing is finished and display the mouse shape
"""
idle_drawing_finished = pyqtSignal()
def __init__(self, figure):
super().__init__(figure=figure)
def _draw_idle(self):
if self.height() < 0 or self.width() < 0:
self._draw_pending = False
if not self._draw_pending:
return
try:
self.draw()
except Exception:
# Uncaught exceptions are fatal for PyQt5, so catch them instead.
traceback.print_exc()
finally:
self._draw_pending = False
# I reimplemented this class only to launch this signal
self.idle_drawing_finished.emit()
class PlotCanvasLegacy(QtCore.QObject):
"""
Class handling the plotting area in the application.
@ -151,6 +123,7 @@ class PlotCanvasLegacy(QtCore.QObject):
# Request for new bitmap to display. The parameter
# is a list with [xmin, xmax, ymin, ymax, zoom(optional)]
update_screen_request = QtCore.pyqtSignal(list)
double_click = QtCore.pyqtSignal(object)
def __init__(self, container, app):
@ -188,6 +161,7 @@ class PlotCanvasLegacy(QtCore.QObject):
# The canvas is the top level container (FigureCanvasQTAgg)
self.canvas = FigureCanvas(self.figure)
self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
self.canvas.setFocus()
self.native = self.canvas
@ -203,15 +177,17 @@ class PlotCanvasLegacy(QtCore.QObject):
# Update every time the canvas is re-drawn.
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
# ################### NOT IMPLEMENTED YET - EXPERIMENTAL #######################
# ## Bitmap Cache
self.cache = CanvasCache(self, self.app)
self.cache_thread = QtCore.QThread()
self.cache.moveToThread(self.cache_thread)
# super(PlotCanvas, self).connect(self.cache_thread, QtCore.SIGNAL("started()"), self.cache.run)
self.cache_thread.started.connect(self.cache.run)
self.cache_thread.start()
self.cache.new_screen.connect(self.on_new_screen)
# self.cache = CanvasCache(self, self.app)
# self.cache_thread = QtCore.QThread()
# self.cache.moveToThread(self.cache_thread)
# # super(PlotCanvas, self).connect(self.cache_thread, QtCore.SIGNAL("started()"), self.cache.run)
# self.cache_thread.started.connect(self.cache.run)
#
# self.cache_thread.start()
# self.cache.new_screen.connect(self.on_new_screen)
# ##############################################################################
# Events
self.mp = self.graph_event_connect('button_press_event', self.on_mouse_press)
@ -226,11 +202,11 @@ class PlotCanvasLegacy(QtCore.QObject):
# self.graph_event_connect('key_release_event', self.on_key_up)
self.odr = self.graph_event_connect('draw_event', self.on_draw)
self.mouse = [0, 0]
self.key = None
self.pan_axes = []
self.panning = False
self.mouse = [0, 0]
# signal is the mouse is dragging
self.is_dragging = False
@ -238,9 +214,6 @@ class PlotCanvasLegacy(QtCore.QObject):
# signal if there is a doubleclick
self.is_dblclk = False
# pay attention, this signal should be connected only after the self.canvas and self.mouse is declared
self.canvas.idle_drawing_finished.connect(lambda: self.draw_cursor(x_pos=self.mouse[0], y_pos=self.mouse[1]))
def graph_event_connect(self, event_name, callback):
"""
Attach an event handler to the canvas through the Matplotlib interface.
@ -294,6 +267,31 @@ class PlotCanvasLegacy(QtCore.QObject):
print(str(e))
return c
def draw_cursor(self, x_pos, y_pos):
"""
Draw a cursor at the mouse grid snapped position
:param x_pos: mouse x position
:param y_pos: mouse y position
:return:
"""
# there is no point in drawing mouse cursor when panning as it jumps in a confusing way
if self.app.app_cursor.enabled is True and self.panning is False:
try:
x, y = self.app.geo_editor.snap(x_pos, y_pos)
# Pointer (snapped)
elements = self.axes.plot(x, y, 'k+', ms=33, mew=1, animated=True)
for el in elements:
self.axes.draw_artist(el)
except Exception as e:
# this happen at app initialization since self.app.geo_editor does not exist yet
# I could reshuffle the object instantiating order but what's the point? I could crash something else
# and that's pythonic, too
pass
self.canvas.blit(self.axes.bbox)
def clear_cursor(self, state):
if state is True:
@ -445,6 +443,26 @@ class PlotCanvasLegacy(QtCore.QObject):
def fit_view(self):
self.auto_adjust_axes()
def fit_center(self, loc, rect=None):
x = loc[0]
y = loc[1]
xmin, xmax = self.axes.get_xlim()
ymin, ymax = self.axes.get_ylim()
half_width = (xmax - xmin) / 2
half_height = (ymax - ymin) / 2
# Adjust axes
for ax in self.figure.get_axes():
ax.set_xlim((x - half_width , x + half_width))
ax.set_ylim((y - half_height, y + half_height))
# Re-draw
self.canvas.draw()
# #### Temporary place-holder for cached update #####
self.update_screen_request.emit([0, 0, 0, 0, 0])
def zoom(self, factor, center=None):
"""
Zooms the plot by factor around a given
@ -484,14 +502,13 @@ class PlotCanvasLegacy(QtCore.QObject):
for ax in self.figure.get_axes():
ax.set_xlim((xmin, xmax))
ax.set_ylim((ymin, ymax))
# Async re-draw
self.canvas.draw_idle()
# #### Temporary place-holder for cached update #####
self.update_screen_request.emit([0, 0, 0, 0, 0])
def pan(self, x, y):
def pan(self, x, y, idle=True):
xmin, xmax = self.axes.get_xlim()
ymin, ymax = self.axes.get_ylim()
width = xmax - xmin
@ -503,7 +520,10 @@ class PlotCanvasLegacy(QtCore.QObject):
ax.set_ylim((ymin + y * height, ymax + y * height))
# Re-draw
self.canvas.draw_idle()
if idle:
self.canvas.draw_idle()
else:
self.canvas.draw()
# #### Temporary place-holder for cached update #####
self.update_screen_request.emit([0, 0, 0, 0, 0])
@ -516,8 +536,8 @@ class PlotCanvasLegacy(QtCore.QObject):
:return: Axes attached to the figure.
:rtype: Axes
"""
return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)
new_ax = self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)
return new_ax
def remove_current_axes(self):
"""
@ -653,31 +673,6 @@ class PlotCanvasLegacy(QtCore.QObject):
# self.canvas.blit(self.axes.bbox)
def draw_cursor(self, x_pos, y_pos):
"""
Draw a cursor at the mouse grid snapped position
:param x_pos: mouse x position
:param y_pos: mouse y position
:return:
"""
# there is no point in drawing mouse cursor when panning as it jumps in a confusing way
if self.app.app_cursor.enabled is True and self.panning is False:
try:
x, y = self.app.geo_editor.snap(x_pos, y_pos)
# Pointer (snapped)
elements = self.axes.plot(x, y, 'k+', ms=40, mew=2, animated=True)
for el in elements:
self.axes.draw_artist(el)
except Exception as e:
# this happen at app initialization since self.app.geo_editor does not exist yet
# I could reshuffle the object instantiating order but what's the point? I could crash something else
# and that's pythonic, too
pass
self.canvas.blit(self.axes.bbox)
def translate_coords(self, position):
"""
This does not do much. It's just for code compatibility
@ -750,66 +745,6 @@ class FakeCursor(QtCore.QObject):
pass
class MplCursor(Cursor):
"""
Unfortunately this gets attached to the current axes and if a new axes is added
it will not be showed until that axes is deleted.
Not the kind of behavior needed here so I don't use it anymore.
"""
def __init__(self, axes, color='red', linewidth=1):
super().__init__(ax=axes, useblit=True, color=color, linewidth=linewidth)
self._enabled = True
self.axes = axes
self.color = color
self.linewidth = linewidth
self.x = None
self.y = None
@property
def enabled(self):
return True if self._enabled else False
@enabled.setter
def enabled(self, value):
self._enabled = value
self.visible = self._enabled
self.canvas.draw()
def onmove(self, event):
pass
def set_data(self, event, pos):
"""Internal event handler to draw the cursor when the mouse moves."""
self.x = pos[0]
self.y = pos[1]
if self.ignore(event):
return
if not self.canvas.widgetlock.available(self):
return
if event.inaxes != self.ax:
self.linev.set_visible(False)
self.lineh.set_visible(False)
if self.needclear:
self.canvas.draw()
self.needclear = False
return
self.needclear = True
if not self.visible:
return
self.linev.set_xdata((self.x, self.x))
self.lineh.set_ydata((self.y, self.y))
self.linev.set_visible(self.visible and self.vertOn)
self.lineh.set_visible(self.visible and self.horizOn)
self._update()
class ShapeCollectionLegacy:
"""
This will create the axes for each collection of shapes and will also
@ -930,7 +865,10 @@ class ShapeCollectionLegacy:
self.shape_id = 0
self.axes.cla()
self.app.plotcanvas.auto_adjust_axes()
try:
self.app.plotcanvas.auto_adjust_axes()
except Exception as e:
log.debug("ShapeCollectionLegacy.clear() --> %s" % str(e))
if update is True:
self.redraw()
@ -1013,12 +951,17 @@ class ShapeCollectionLegacy:
self.axes.plot(x, y, linespec, color=linecolor)
else:
path_num += 1
if isinstance(local_shapes[element]['shape'], Polygon):
self.axes.annotate(str(path_num), xy=local_shapes[element]['shape'].exterior.coords[0],
xycoords='data', fontsize=20)
else:
self.axes.annotate(str(path_num), xy=local_shapes[element]['shape'].coords[0],
xycoords='data', fontsize=20)
if self.obj.ui.annotation_cb.get_value():
if isinstance(local_shapes[element]['shape'], Polygon):
self.axes.annotate(
str(path_num),
xy=local_shapes[element]['shape'].exterior.coords[0],
xycoords='data', fontsize=20)
else:
self.axes.annotate(
str(path_num),
xy=local_shapes[element]['shape'].coords[0],
xycoords='data', fontsize=20)
patch = PolygonPatch(local_shapes[element]['shape'],
facecolor=local_shapes[element]['face_color'],
@ -1039,14 +982,18 @@ class ShapeCollectionLegacy:
log.debug("ShapeCollectionLegacy.redraw() --> %s" % str(e))
else:
if isinstance(local_shapes[element]['shape'], Polygon):
x, y = local_shapes[element]['shape'].exterior.xy
self.axes.plot(x, y, local_shapes[element]['color'], linestyle='-')
for ints in local_shapes[element]['shape'].interiors:
x, y = ints.coords.xy
ext_shape = local_shapes[element]['shape'].exterior
if ext_shape is not None:
x, y = ext_shape.xy
self.axes.plot(x, y, local_shapes[element]['color'], linestyle='-')
for ints in local_shapes[element]['shape'].interiors:
if ints is not None:
x, y = ints.coords.xy
self.axes.plot(x, y, local_shapes[element]['color'], linestyle='-')
else:
x, y = local_shapes[element]['shape'].coords.xy
self.axes.plot(x, y, local_shapes[element]['color'], linestyle='-')
if local_shapes[element]['shape'] is not None:
x, y = local_shapes[element]['shape'].coords.xy
self.axes.plot(x, y, local_shapes[element]['color'], linestyle='-')
self.app.plotcanvas.auto_adjust_axes()
@ -1108,3 +1055,62 @@ class ShapeCollectionLegacy:
if self._visible is False:
self.redraw()
self._visible = value
# class MplCursor(Cursor):
# """
# Unfortunately this gets attached to the current axes and if a new axes is added
# it will not be showed until that axes is deleted.
# Not the kind of behavior needed here so I don't use it anymore.
# """
# def __init__(self, axes, color='red', linewidth=1):
#
# super().__init__(ax=axes, useblit=True, color=color, linewidth=linewidth)
# self._enabled = True
#
# self.axes = axes
# self.color = color
# self.linewidth = linewidth
#
# self.x = None
# self.y = None
#
# @property
# def enabled(self):
# return True if self._enabled else False
#
# @enabled.setter
# def enabled(self, value):
# self._enabled = value
# self.visible = self._enabled
# self.canvas.draw()
#
# def onmove(self, event):
# pass
#
# def set_data(self, event, pos):
# """Internal event handler to draw the cursor when the mouse moves."""
# self.x = pos[0]
# self.y = pos[1]
#
# if self.ignore(event):
# return
# if not self.canvas.widgetlock.available(self):
# return
# if event.inaxes != self.ax:
# self.linev.set_visible(False)
# self.lineh.set_visible(False)
#
# if self.needclear:
# self.canvas.draw()
# self.needclear = False
# return
# self.needclear = True
# if not self.visible:
# return
# self.linev.set_xdata((self.x, self.x))
#
# self.lineh.set_ydata((self.y, self.y))
# self.linev.set_visible(self.visible and self.vertOn)
# self.lineh.set_visible(self.visible and self.horizOn)
#
# self._update()

View File

@ -2954,6 +2954,18 @@ class GeometryEditorPrefGroupUI(OptionsGroupUI):
grid0.addWidget(self.sel_limit_label, 0, 0)
grid0.addWidget(self.sel_limit_entry, 0, 1)
# Milling Type
milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
milling_type_label.setToolTip(
_("Milling type:\n"
"- climb / best for precision milling and to reduce tool usage\n"
"- conventional / useful when there is no backlash compensation")
)
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
{'label': _('Conv.'), 'value': 'cv'}])
grid0.addWidget(milling_type_label, 1, 0)
grid0.addWidget(self.milling_type_radio, 1, 1)
self.layout.addStretch()

View File

@ -88,7 +88,7 @@ def apply_patches():
def _get_tick_frac_labels(self):
"""Get the major ticks, minor ticks, and major labels"""
minor_num = 4 # number of minor ticks per major division
if (self.axis.scale_type == 'linear'):
if self.axis.scale_type == 'linear':
domain = self.axis.domain
if domain[1] < domain[0]:
flip = True

View File

@ -177,7 +177,7 @@ class ToolMove(FlatCAMTool):
self.replot_signal.emit(obj_list)
except Exception as e:
proc.done()
self.app.inform.emit('[ERROR_NOTCL] %s --> %s' % (_('ToolMove.on_left_click()'), str(e)))
self.app.inform.emit('[ERROR_NOTCL] %s --> %s' % ('ToolMove.on_left_click()', str(e)))
return "fail"
proc.done()
@ -194,8 +194,8 @@ class ToolMove(FlatCAMTool):
except TypeError as e:
log.debug("ToolMove.on_left_click() --> %s" % str(e))
self.app.inform.emit('[ERROR_NOTCL] %s' %
_('ToolMove.on_left_click() --> Error when mouse left click.'))
self.app.inform.emit('[ERROR_NOTCL] ToolMove.on_left_click() --> %s' %
_('Error when mouse left click.'))
return
self.clicked_move = 1

View File

@ -1065,7 +1065,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
# init values for the next usage
self.reset_usage()
self.app.report_usage(_("on_paint_button_click"))
self.app.report_usage("on_paint_button_click")
try:
self.overlap = float(self.ncc_overlap_entry.get_value())

View File

@ -917,7 +917,7 @@ class ToolPaint(FlatCAMTool, Gerber):
# init values for the next usage
self.reset_usage()
self.app.report_usage(_("on_paint_button_click"))
self.app.report_usage("on_paint_button_click")
# self.app.call_source = 'paint'
# #####################################################
@ -1490,7 +1490,7 @@ class ToolPaint(FlatCAMTool, Gerber):
except Exception as e:
proc.done()
self.app.inform.emit('[ERROR_NOTCL] %s --> %s' %
(_('PaintTool.paint_poly()'),
('PaintTool.paint_poly()',
str(e)))
return
proc.done()

View File

@ -1355,7 +1355,7 @@ class SolderPaste(FlatCAMTool):
except Exception as e:
log.debug('ToolSolderPaste.on_view_gcode() -->%s' % str(e))
self.app.inform.emit('[ERROR] %s --> %s' %
(_('ToolSolderPaste.on_view_gcode()'), str(e)))
('ToolSolderPaste.on_view_gcode()', str(e)))
return
self.app.ui.code_editor.moveCursor(QtGui.QTextCursor.Start)

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

File diff suppressed because it is too large Load Diff