Преглед изворни кода

Merge https://bitbucket.org/jpcgt/flatcam/src/master into Menu_clening_icons_adding

# Conflicts:
#	FlatCAMDraw.py
#	FlatCAMGUI.py
Marius Stanciu пре 7 година
родитељ
комит
090fdf2b39
78 измењених фајлова са 497 додато и 514 уклоњено
  1. 1 1
      DblSidedTool.py
  2. 68 53
      FlatCAMApp.py
  3. 39 49
      FlatCAMDraw.py
  4. 55 75
      FlatCAMGUI.py
  5. 48 24
      FlatCAMObj.py
  6. 9 9
      GUIElements.py
  7. 12 9
      ObjectCollection.py
  8. 16 5
      ObjectUI.py
  9. 8 8
      bugs/conf.py
  10. 9 5
      camlib.py
  11. 6 6
      descartes/tests.py
  12. 8 8
      doc/source/conf.py
  13. 2 2
      flatcam
  14. 3 3
      make_win32.py
  15. 10 10
      sandbox/diagnose.py
  16. 3 3
      sandbox/gerber_find.py
  17. 2 2
      svgparse.py
  18. 7 6
      tclCommands/TclCommand.py
  19. 2 3
      tclCommands/TclCommandAddCircle.py
  20. 2 3
      tclCommands/TclCommandAddPolygon.py
  21. 2 3
      tclCommands/TclCommandAddPolyline.py
  22. 2 3
      tclCommands/TclCommandAddRectangle.py
  23. 4 5
      tclCommands/TclCommandAlignDrill.py
  24. 2 3
      tclCommands/TclCommandAlignDrillGrid.py
  25. 2 3
      tclCommands/TclCommandCncjob.py
  26. 3 4
      tclCommands/TclCommandCutout.py
  27. 2 3
      tclCommands/TclCommandDelete.py
  28. 2 3
      tclCommands/TclCommandDrillcncjob.py
  29. 2 3
      tclCommands/TclCommandExportGcode.py
  30. 2 3
      tclCommands/TclCommandExportSVG.py
  31. 3 4
      tclCommands/TclCommandExteriors.py
  32. 2 3
      tclCommands/TclCommandGeoCutout.py
  33. 2 3
      tclCommands/TclCommandGeoUnion.py
  34. 2 3
      tclCommands/TclCommandGetNames.py
  35. 2 3
      tclCommands/TclCommandGetSys.py
  36. 2 3
      tclCommands/TclCommandImportSvg.py
  37. 2 3
      tclCommands/TclCommandInteriors.py
  38. 2 3
      tclCommands/TclCommandIsolate.py
  39. 2 3
      tclCommands/TclCommandJoinExcellon.py
  40. 2 3
      tclCommands/TclCommandJoinGeometry.py
  41. 2 3
      tclCommands/TclCommandMillHoles.py
  42. 4 5
      tclCommands/TclCommandMirror.py
  43. 2 3
      tclCommands/TclCommandNew.py
  44. 2 3
      tclCommands/TclCommandNewGeometry.py
  45. 2 3
      tclCommands/TclCommandOffset.py
  46. 2 3
      tclCommands/TclCommandOpenExcellon.py
  47. 2 3
      tclCommands/TclCommandOpenGCode.py
  48. 4 5
      tclCommands/TclCommandOpenGerber.py
  49. 2 3
      tclCommands/TclCommandOpenProject.py
  50. 2 3
      tclCommands/TclCommandOptions.py
  51. 2 3
      tclCommands/TclCommandPaint.py
  52. 5 8
      tclCommands/TclCommandPanelize.py
  53. 2 3
      tclCommands/TclCommandPlot.py
  54. 2 3
      tclCommands/TclCommandSaveProject.py
  55. 2 3
      tclCommands/TclCommandScale.py
  56. 2 3
      tclCommands/TclCommandSetActive.py
  57. 2 3
      tclCommands/TclCommandSetSys.py
  58. 2 3
      tclCommands/TclCommandSubtractPoly.py
  59. 2 3
      tclCommands/TclCommandSubtractRectangle.py
  60. 2 3
      tclCommands/TclCommandVersion.py
  61. 2 3
      tclCommands/TclCommandWriteGCode.py
  62. 2 2
      tclCommands/__init__.py
  63. 2 2
      termwidget.py
  64. 2 2
      tests/canvas/performance.py
  65. 1 1
      tests/other/destructor_test.py
  66. 3 3
      tests/other/test_fcrts.py
  67. 3 3
      tests/other/test_rt.py
  68. 9 9
      tests/test_excellon_flow.py
  69. 9 9
      tests/test_gerber_flow.py
  70. 21 21
      tests/test_paint.py
  71. 3 3
      tests/test_pathconnect.py
  72. 12 12
      tests/test_polygon_paint.py
  73. 7 7
      tests/test_svg_flow.py
  74. 14 14
      tests/test_tclCommands/__init__.py
  75. 1 1
      tests/test_tclCommands/test_TclCommandCncjob.py
  76. 1 1
      tests/test_tclCommands/test_TclCommandDrillcncjob.py
  77. 2 2
      tests/test_tclCommands/test_TclCommandExportGcode.py
  78. 2 2
      tests/test_tcl_shell.py

+ 1 - 1
DblSidedTool.py

@@ -165,7 +165,7 @@ class DblSidedTool(FlatCAMTool):
         if not isinstance(fcobj, FlatCAMGerber) and \
                 not isinstance(fcobj, FlatCAMExcellon) and \
                 not isinstance(fcobj, FlatCAMGeometry):
-            self.info("ERROR: Only Gerber, Excellon and Geometry objects can be mirrored.")
+            self.app.inform.emit("ERROR: Only Gerber, Excellon and Geometry objects can be mirrored.")
             return
 
         axis = self.mirror_axis.get_value()

+ 68 - 53
FlatCAMApp.py

@@ -8,7 +8,7 @@
 
 import sys
 import traceback
-import urllib
+import urllib.request, urllib.parse, urllib.error
 import getopt
 import random
 import logging
@@ -16,8 +16,8 @@ import simplejson as json
 import re
 import webbrowser
 import os
-import Tkinter
-from PyQt4 import QtCore
+import tkinter
+from PyQt4 import Qt, QtCore, QtGui
 import time  # Just used for debugging. Double check before removing.
 from xml.dom.minidom import parseString as parse_xml_string
 from contextlib import contextmanager
@@ -27,10 +27,10 @@ from contextlib import contextmanager
 ########################################
 import FlatCAMVersion
 from FlatCAMWorker import Worker
-from ObjectCollection import *
-from FlatCAMObj import *
-from PlotCanvas import *
-from FlatCAMGUI import *
+import ObjectCollection
+from FlatCAMObj import FlatCAMCNCjob, FlatCAMExcellon, FlatCAMGerber, FlatCAMGeometry, FlatCAMObj
+from PlotCanvas import PlotCanvas
+from FlatCAMGUI import FlatCAMGUI, GlobalOptionsUI, FlatCAMActivityView, FlatCAMInfoBar
 from FlatCAMCommon import LoudDict
 from FlatCAMShell import FCShell
 from FlatCAMDraw import FlatCAMDraw
@@ -39,6 +39,8 @@ from MeasurementTool import Measurement
 from DblSidedTool import DblSidedTool
 import tclCommands
 
+from camlib import *
+
 
 ########################################
 ##                App                 ##
@@ -54,11 +56,11 @@ class App(QtCore.QObject):
     try:
         cmd_line_options, args = getopt.getopt(sys.argv[1:], "h:", "shellfile=")
     except getopt.GetoptError:
-        print cmd_line_help
+        print(cmd_line_help)
         sys.exit(2)
     for opt, arg in cmd_line_options:
         if opt == '-h':
-            print cmd_line_help
+            print(cmd_line_help)
             sys.exit()
         elif opt == '--shellfile':
             cmd_line_shellfile = arg
@@ -106,16 +108,16 @@ class App(QtCore.QObject):
     # Note: Setting the parameters to unicode does not seem
     #       to have an effect. Then are received as Qstring
     #       anyway.
-    file_opened = QtCore.pyqtSignal(unicode, unicode)  # File type and filename
+    file_opened = QtCore.pyqtSignal(str, str)  # File type and filename
 
     progress = QtCore.pyqtSignal(int)  # Percentage of progress
 
     plots_updated = QtCore.pyqtSignal()
 
-    # Emitted by new_object() and passes the new object as argument.
-    # on_object_created() adds the object to the collection,
+    # Emitted by new_object() and passes the new object as argument and a plot flag
+    # on_object_created() adds the object to the collection, plot the object if plot flag is True
     # and emits new_object_available.
-    object_created = QtCore.pyqtSignal(object)
+    object_created = QtCore.pyqtSignal(object, bool)
 
     # Emitted when a new object has been added to the collection
     # and is ready to be used.
@@ -471,7 +473,7 @@ class App(QtCore.QObject):
         #self.options_write_form()
         self.on_options_combo_change(0)  # Will show the initial form
 
-        self.collection = ObjectCollection()
+        self.collection = ObjectCollection.ObjectCollection()
         self.ui.project_tab_layout.addWidget(self.collection.view)
         #### End of Data ####
 
@@ -619,7 +621,7 @@ class App(QtCore.QObject):
                     cmd_line_shellfile_text = myfile.read()
                     self.shell._sysShell.exec_command(cmd_line_shellfile_text)
             except Exception as ext:
-                print "ERROR: ", ext
+                print(("ERROR: ", ext))
                 sys.exit(2)
 
         # Post-GUI initialization: Experimental attempt
@@ -637,7 +639,7 @@ class App(QtCore.QObject):
             # because tcl  was execudted in old instance of TCL
             pass
         else:
-            self.tcl = Tkinter.Tcl()
+            self.tcl = tkinter.Tcl()
             self.setup_shell()
 
     def defaults_read_form(self):
@@ -700,7 +702,7 @@ class App(QtCore.QObject):
         :return: None
         """
         if not isinstance(self.collection.get_active(), FlatCAMGeometry):
-            self.info("Select a Geometry Object to edit.")
+            self.inform.emit("Select a Geometry Object to edit.")
             return
 
         self.ui.updategeo_btn.setEnabled(True)
@@ -715,7 +717,7 @@ class App(QtCore.QObject):
         """
         geo = self.collection.get_active()
         if not isinstance(geo, FlatCAMGeometry):
-            self.info("Select a Geometry Object to update.")
+            self.inform.emit("Select a Geometry Object to update.")
             return
 
         self.draw.update_fcgeometry(geo)
@@ -856,7 +858,7 @@ class App(QtCore.QObject):
             if result != 'None':
                 self.shell.append_output(result + '\n')
 
-        except Tkinter.TclError, e:
+        except tkinter.TclError as e:
             # This will display more precise answer if something in TCL shell fails
             result = self.tcl.eval("set errorInfo")
             self.log.error("Exec command Exception: %s" % (result + '\n'))
@@ -902,7 +904,7 @@ class App(QtCore.QObject):
             if retval and retfcn(retval):
                 self.shell.append_output(retfcn(retval) + "\n")
 
-        except Exception, e:
+        except Exception as e:
             #self.shell.append_error(''.join(traceback.format_exc()))
             #self.shell.append_error("?\n")
             self.shell.append_error(str(e) + "\n")
@@ -922,14 +924,14 @@ class App(QtCore.QObject):
         if match:
             level = match.group(1)
             msg_ = match.group(2)
-            self.ui.fcinfo.set_status(QtCore.QString(msg_), level=level)
+            self.ui.fcinfo.set_status(str(msg_), level=level)
 
             if toshell:
                 error = level == "error" or level == "warning"
                 self.shell_message(msg, error=error, show=True)
 
         else:
-            self.ui.fcinfo.set_status(QtCore.QString(msg), level="info")
+            self.ui.fcinfo.set_status(str(msg), level="info")
 
             if toshell:
                 self.shell_message(msg)
@@ -980,7 +982,7 @@ class App(QtCore.QObject):
         self.log.debug("   %s" % kind)
         self.log.debug("   %s" % filename)
 
-        record = {'kind': unicode(kind), 'filename': unicode(filename)}
+        record = {'kind': str(kind), 'filename': str(filename)}
         if record in self.recent:
             return
 
@@ -1028,9 +1030,12 @@ class App(QtCore.QObject):
          but before it is attached to the application. The function is
          called with 2 parameters: the new object and the App instance.
         :type initialize: function
+        :param plot: Whether to plot the object or not
+        :type plot: Bool
         :return: None
         :rtype: None
         """
+        self.plot = plot
 
         App.log.debug("new_object()")
 
@@ -1054,6 +1059,10 @@ class App(QtCore.QObject):
                 oname = option[len(kind) + 1:]
                 obj.options[oname] = self.options[option]
 
+        # make sure that the plot option of the new object is reflecting the current status and not the general option
+        # solve issues with the modelview currently used (checkbox on the Project Tab)
+        obj.options['plot'] = self.plot
+
         # Initialize as per user request
         # User must take care to implement initialize
         # in a thread-safe way as is is likely that we
@@ -1072,11 +1081,11 @@ class App(QtCore.QObject):
             t3 = time.time()
             self.log.debug("%f seconds converting units." % (t3 - t2))
 
-        FlatCAMApp.App.log.debug("Moving new object back to main thread.")
+        self.log.debug("Moving new object back to main thread.")
 
         # Move the object to the main thread and let the app know that it is available.
         obj.moveToThread(QtGui.QApplication.instance().thread())
-        self.object_created.emit(obj)
+        self.object_created.emit(obj, self.plot)
 
         return obj
 
@@ -1123,7 +1132,7 @@ class App(QtCore.QObject):
                 layout1.addLayout(layout2)
 
                 logo = QtGui.QLabel()
-                logo.setPixmap(QtGui.QPixmap('share:flatcam_icon256.png'))
+                logo.setPixmap(QtGui.QPixmap('share/flatcam_icon256.png'))
                 layout2.addWidget(logo, stretch=0)
 
                 title = QtGui.QLabel(
@@ -1490,12 +1499,14 @@ class App(QtCore.QObject):
         # Keep this for later
         try:
             name = self.collection.get_active().options["name"]
+            isPlotted = self.collection.get_active().options["plot"]
         except AttributeError:
             self.log.debug("Nothing selected for deletion")
             return
 
-        # Remove plot
-        self.plotcanvas.figure.delaxes(self.collection.get_active().axes)
+        # Remove plot only if the object was plotted otherwise delaxes will fail
+        if isPlotted:
+            self.plotcanvas.figure.delaxes(self.collection.get_active().axes)
         self.plotcanvas.auto_adjust_axes()
 
         # Clear form
@@ -1537,11 +1548,12 @@ class App(QtCore.QObject):
     def on_row_activated(self, index):
         self.ui.notebook.setCurrentWidget(self.ui.selected_tab)
 
-    def on_object_created(self, obj):
+    def on_object_created(self, obj, plot):
         """
         Event callback for object creation.
 
         :param obj: The newly created FlatCAM object.
+        :param plot: If to plot the new object, bool
         :return: None
         """
         t0 = time.time()  # DEBUG
@@ -1552,7 +1564,9 @@ class App(QtCore.QObject):
 
         self.inform.emit("Object (%s) created: %s" % (obj.kind, obj.options['name']))
         self.new_object_available.emit(obj)
-        obj.plot()
+        if plot:
+            obj.plot()
+
         self.on_zoom_fit(None)
         t1 = time.time()  # DEBUG
         self.log.debug("%f seconds adding object and plotting." % (t1 - t0))
@@ -1634,10 +1648,11 @@ class App(QtCore.QObject):
         try:
             App.log.debug('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % (
                 event.button, event.x, event.y, event.xdata, event.ydata))
+            modifiers = QtGui.QApplication.keyboardModifiers()
+            if modifiers == QtCore.Qt.ControlModifier:
+                self.clipboard.setText(self.defaults["point_clipboard_format"] % (event.xdata, event.ydata))
 
-            self.clipboard.setText(self.defaults["point_clipboard_format"] % (event.xdata, event.ydata))
-
-        except Exception, e:
+        except Exception as e:
             App.log.debug("Outside plot?")
             App.log.debug(str(e))
 
@@ -1707,7 +1722,7 @@ class App(QtCore.QObject):
         # The Qt methods above will return a QString which can cause problems later.
         # So far json.dump() will fail to serialize it.
         # TODO: Improve the serialization methods and remove this fix.
-        filename = unicode(filename)
+        filename = str(filename)
 
         if filename == "":
             self.inform.emit("Open cancelled.")
@@ -1734,7 +1749,7 @@ class App(QtCore.QObject):
         # The Qt methods above will return a QString which can cause problems later.
         # So far json.dump() will fail to serialize it.
         # TODO: Improve the serialization methods and remove this fix.
-        filename = unicode(filename)
+        filename = str(filename)
 
         if filename == "":
             self.inform.emit("Open cancelled.")
@@ -1761,7 +1776,7 @@ class App(QtCore.QObject):
         # The Qt methods above will return a QString which can cause problems later.
         # So far json.dump() will fail to serialize it.
         # TODO: Improve the serialization methods and remove this fix.
-        filename = unicode(filename)
+        filename = str(filename)
 
         if filename == "":
             self.inform.emit("Open cancelled.")
@@ -1788,7 +1803,7 @@ class App(QtCore.QObject):
         # The Qt methods above will return a QString which can cause problems later.
         # So far json.dump() will fail to serialize it.
         # TODO: Improve the serialization methods and remove this fix.
-        filename = unicode(filename)
+        filename = str(filename)
 
         if filename == "":
             self.inform.emit("Open cancelled.")
@@ -1838,7 +1853,7 @@ class App(QtCore.QObject):
         except TypeError:
             filename = QtGui.QFileDialog.getSaveFileName(caption="Export SVG")
 
-        filename = unicode(filename)
+        filename = str(filename)
 
         if filename == "":
             self.inform.emit("Export SVG cancelled.")
@@ -1861,7 +1876,7 @@ class App(QtCore.QObject):
         except TypeError:
             filename = QtGui.QFileDialog.getOpenFileName(caption="Import SVG")
 
-        filename = unicode(filename)
+        filename = str(filename)
 
         if filename == "":
             self.inform.emit("Open cancelled.")
@@ -1904,7 +1919,7 @@ class App(QtCore.QObject):
         except TypeError:
             filename = QtGui.QFileDialog.getSaveFileName(caption="Save Project As ...")
 
-        filename = unicode(filename)
+        filename = str(filename)
 
         try:
             f = open(filename, 'r')
@@ -2038,7 +2053,7 @@ class App(QtCore.QObject):
                 app_obj.progress.emit(0)
                 raise IOError('Failed to open file: ' + filename)
 
-            except ParseError, e:
+            except ParseError as e:
                 app_obj.inform.emit("[error] Failed to parse file: " + filename + ". " + e[0])
                 app_obj.progress.emit(0)
                 self.log.error(str(e))
@@ -2339,7 +2354,7 @@ class App(QtCore.QObject):
         self.worker_task.emit({'fcn': worker_task, 'params': [self]})
 
     def register_folder(self, filename):
-        self.defaults["last_folder"] = os.path.split(unicode(filename))[0]
+        self.defaults["last_folder"] = os.path.split(str(filename))[0]
 
     def set_progress_bar(self, percentage, text=""):
         self.ui.progress_bar.setValue(int(percentage))
@@ -3591,7 +3606,7 @@ class App(QtCore.QObject):
             output = ''
             import collections
             od = collections.OrderedDict(sorted(commands.items()))
-            for cmd_, val in od.iteritems():
+            for cmd_, val in list(od.items()):
                 #print cmd, '\n', ''.join(['~']*len(cmd))
                 output += cmd_ + ' \n' + ''.join(['~'] * len(cmd_)) + '\n'
 
@@ -3645,7 +3660,7 @@ class App(QtCore.QObject):
 
             try:
                 obj.follow(**kwa)
-            except Exception, e:
+            except Exception as e:
                 return "ERROR: %s" % str(e)
 
         # def get_sys(param):
@@ -4139,11 +4154,11 @@ class App(QtCore.QObject):
 
         # TODO: Move this to constructor
         icons = {
-            "gerber": "share:flatcam_icon16.png",
-            "excellon": "share:drill16.png",
-            "cncjob": "share:cnc16.png",
-            "project": "share:project16.png",
-            "svg": "share:geometry16.png"
+            "gerber": "share/flatcam_icon16.png",
+            "excellon": "share/drill16.png",
+            "cncjob": "share/cnc16.png",
+            "project": "share/project16.png",
+            "svg": "share/geometry16.png"
         }
 
         openers = {
@@ -4232,12 +4247,12 @@ class App(QtCore.QObject):
             "?s=" + str(self.defaults['serial']) + \
             "&v=" + str(self.version) + \
             "&os=" + str(self.os) + \
-            "&" + urllib.urlencode(self.defaults["stats"])
+            "&" + urllib.parse.urlencode(self.defaults["stats"])
         App.log.debug("Checking for updates @ %s" % full_url)
 
         ### Get the data
         try:
-            f = urllib.urlopen(full_url)
+            f = urllib.request.urlopen(full_url)
         except:
             # App.log.warning("Failed checking for latest version. Could not connect.")
             self.log.warning("Failed checking for latest version. Could not connect.")
@@ -4246,7 +4261,7 @@ class App(QtCore.QObject):
 
         try:
             data = json.load(f)
-        except Exception, e:
+        except Exception as e:
             App.log.error("Could not parse information about latest version.")
             self.inform.emit("[error] Could not parse information about latest version.")
             App.log.debug("json.load(): %s" % str(e))
@@ -4264,7 +4279,7 @@ class App(QtCore.QObject):
         App.log.debug("Newer version available.")
         self.message.emit(
             "Newer Version Available",
-            QtCore.QString("There is a newer version of FlatCAM " +
+            str("There is a newer version of FlatCAM " +
                            "available for download:<br><br>" +
                            "<B>" + data["name"] + "</b><br>" +
                            data["message"].replace("\n", "<br>")),

+ 39 - 49
FlatCAMDraw.py

@@ -694,31 +694,23 @@ class FlatCAMDraw(QtCore.QObject):
         self.drawing_toolbar.setDisabled(disabled)
         self.app.ui.addToolBar(self.drawing_toolbar)
 
-        self.select_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:pointer32.png'), "Select 'Esc'")
-        # Separator
-        self.drawing_toolbar.addSeparator()
-        self.add_circle_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:circle32.png'), 'Add Circle')
-        self.add_arc_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:arc32.png'), 'Add Arc')
-        self.add_rectangle_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:rectangle32.png'), 'Add Rectangle')
-        self.add_polygon_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:polygon32.png'), 'Add Polygon')
-        self.add_path_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:path32.png'), 'Add Path')
-
-        # Separator
-        self.drawing_toolbar.addSeparator()
-        self.union_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:union32.png'), 'Polygon Union')
-        self.intersection_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:intersection32.png'), 'Polygon Intersection')
-        self.subtract_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:subtract32.png'), 'Polygon Subtraction')
-        self.cutpath_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:cutpath32.png'), 'Cut Path')
-
-        # Separator
-        self.drawing_toolbar.addSeparator()
-        self.move_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:move32.png'), "Move Objects 'm'")
-        self.copy_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:copy32.png'), "Copy Objects 'c'")
-        self.delete_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share:deleteshape32.png'), "Delete Shape '-'")
+        self.select_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), "Select 'Esc'")
+        self.add_circle_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/circle32.png'), 'Add Circle')
+        self.add_arc_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/arc32.png'), 'Add Arc')
+        self.add_rectangle_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/rectangle32.png'), 'Add Rectangle')
+        self.add_polygon_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), 'Add Polygon')
+        self.add_path_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/path32.png'), 'Add Path')
+        self.union_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/union32.png'), 'Polygon Union')
+        self.intersection_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/intersection32.png'), 'Polygon Intersection')
+        self.subtract_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/subtract32.png'), 'Polygon Subtraction')
+        self.cutpath_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/cutpath32.png'), 'Cut Path')
+        self.move_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/move32.png'), "Move Objects 'm'")
+        self.copy_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/copy32.png'), "Copy Objects 'c'")
+        self.delete_btn = self.drawing_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'), "Delete Shape '-'")
 
         ### Snap Toolbar ###
-        self.snap_toolbar = QtGui.QToolBar("Grid Toolbar")
-        self.grid_snap_btn = self.snap_toolbar.addAction(QtGui.QIcon('share:grid32.png'), 'Snap to grid')
+        self.snap_toolbar = QtGui.QToolBar()
+        self.grid_snap_btn = self.snap_toolbar.addAction(QtGui.QIcon('share/grid32.png'), 'Snap to grid')
         self.grid_gap_x_entry = QtGui.QLineEdit()
         self.grid_gap_x_entry.setMaximumWidth(70)
         self.grid_gap_x_entry.setToolTip("Grid X distance")
@@ -728,7 +720,7 @@ class FlatCAMDraw(QtCore.QObject):
         self.grid_gap_y_entry.setToolTip("Grid Y distante")
         self.snap_toolbar.addWidget(self.grid_gap_y_entry)
 
-        self.corner_snap_btn = self.snap_toolbar.addAction(QtGui.QIcon('share:corner32.png'), 'Snap to corner')
+        self.corner_snap_btn = self.snap_toolbar.addAction(QtGui.QIcon('share/corner32.png'), 'Snap to corner')
         self.snap_max_dist_entry = QtGui.QLineEdit()
         self.snap_max_dist_entry.setMaximumWidth(70)
         self.snap_max_dist_entry.setToolTip("Max. magnet distance")
@@ -740,23 +732,21 @@ class FlatCAMDraw(QtCore.QObject):
         ### Application menu ###
         self.menu = QtGui.QMenu("Drawing")
         self.app.ui.menu.insertMenu(self.app.ui.menutoolaction, self.menu)
-        # self.select_menuitem = self.menu.addAction(QtGui.QIcon('share:pointer16.png'), "Select 'Esc'")
-        # self.add_circle_menuitem = self.menu.addAction(QtGui.QIcon('share:circle16.png'), 'Add Circle')
-        # self.add_arc_menuitem = self.menu.addAction(QtGui.QIcon('share:arc16.png'), 'Add Arc')
-        # self.add_rectangle_menuitem = self.menu.addAction(QtGui.QIcon('share:rectangle16.png'), 'Add Rectangle')
-        # self.add_polygon_menuitem = self.menu.addAction(QtGui.QIcon('share:polygon16.png'), 'Add Polygon')
-        # self.add_path_menuitem = self.menu.addAction(QtGui.QIcon('share:path16.png'), 'Add Path')
-        self.union_menuitem = self.menu.addAction(QtGui.QIcon('share:union16.png'), 'Polygon Union')
-        self.intersection_menuitem = self.menu.addAction(QtGui.QIcon('share:intersection16.png'), 'Polygon Intersection')
-        # self.subtract_menuitem = self.menu.addAction(QtGui.QIcon('share:subtract16.png'), 'Polygon Subtraction')
-        self.cutpath_menuitem = self.menu.addAction(QtGui.QIcon('share:cutpath16.png'), 'Cut Path')
-        # Add Separator
-        self.menu.addSeparator()
-        # self.move_menuitem = self.menu.addAction(QtGui.QIcon('share:move16.png'), "Move Objects 'm'")
-        # self.copy_menuitem = self.menu.addAction(QtGui.QIcon('share:copy16.png'), "Copy Objects 'c'")
-        self.delete_menuitem = self.menu.addAction(QtGui.QIcon('share:deleteshape16.png'), "Delete Shape '-'")
-        self.buffer_menuitem = self.menu.addAction(QtGui.QIcon('share:buffer16.png'), "Buffer selection 'b'")
-        self.paint_menuitem = self.menu.addAction(QtGui.QIcon('share:paint16.png'), "Paint selection")
+        # self.select_menuitem = self.menu.addAction(QtGui.QIcon('share/pointer16.png'), "Select 'Esc'")
+        # self.add_circle_menuitem = self.menu.addAction(QtGui.QIcon('share/circle16.png'), 'Add Circle')
+        # self.add_arc_menuitem = self.menu.addAction(QtGui.QIcon('share/arc16.png'), 'Add Arc')
+        # self.add_rectangle_menuitem = self.menu.addAction(QtGui.QIcon('share/rectangle16.png'), 'Add Rectangle')
+        # self.add_polygon_menuitem = self.menu.addAction(QtGui.QIcon('share/polygon16.png'), 'Add Polygon')
+        # self.add_path_menuitem = self.menu.addAction(QtGui.QIcon('share/path16.png'), 'Add Path')
+        self.union_menuitem = self.menu.addAction(QtGui.QIcon('share/union16.png'), 'Polygon Union')
+        self.intersection_menuitem = self.menu.addAction(QtGui.QIcon('share/intersection16.png'), 'Polygon Intersection')
+        # self.subtract_menuitem = self.menu.addAction(QtGui.QIcon('share/subtract16.png'), 'Polygon Subtraction')
+        self.cutpath_menuitem = self.menu.addAction(QtGui.QIcon('share/cutpath16.png'), 'Cut Path')
+        # self.move_menuitem = self.menu.addAction(QtGui.QIcon('share/move16.png'), "Move Objects 'm'")
+        # self.copy_menuitem = self.menu.addAction(QtGui.QIcon('share/copy16.png'), "Copy Objects 'c'")
+        self.delete_menuitem = self.menu.addAction(QtGui.QIcon('share/deleteshape16.png'), "Delete Shape '-'")
+        self.buffer_menuitem = self.menu.addAction(QtGui.QIcon('share/buffer16.png'), "Buffer selection 'b'")
+        self.paint_menuitem = self.menu.addAction(QtGui.QIcon('share/paint16.png'), "Paint selection")
         self.menu.addSeparator()
 
         self.paint_menuitem.triggered.connect(self.on_paint_tool)
@@ -994,7 +984,7 @@ class FlatCAMDraw(QtCore.QObject):
                         self.tools[t]["button"].setChecked(False)
 
                 self.active_tool = self.tools[tool]["constructor"](self)
-                self.app.info(self.active_tool.start_msg)
+                self.app.inform.emit(self.active_tool.start_msg)
             else:
                 self.app.log.debug("%s is NOT checked." % tool)
                 for t in self.tools:
@@ -1013,7 +1003,7 @@ class FlatCAMDraw(QtCore.QObject):
         if self.active_tool is not None and event.button is 1:
             # Dispatch event to active_tool
             msg = self.active_tool.click(self.snap(event.xdata, event.ydata))
-            self.app.info(msg)
+            self.app.inform.emit(msg)
 
             # If it is a shape generating tool
             if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
@@ -1125,14 +1115,14 @@ class FlatCAMDraw(QtCore.QObject):
                 self.active_tool.make()
                 if self.active_tool.complete:
                     self.on_shape_complete()
-                    self.app.info("Done.")
+                    self.app.inform.emit("Done.")
             return
 
         ### Abort the current action
         if event.key == 'escape':
             # TODO: ...?
             #self.on_tool_select("select")
-            self.app.info("Cancelled.")
+            self.app.inform.emit("Cancelled.")
 
             self.delete_utility_geometry()
 
@@ -1152,14 +1142,14 @@ class FlatCAMDraw(QtCore.QObject):
             self.move_btn.setChecked(True)
             self.on_tool_select('move')
             self.active_tool.set_origin(self.snap(event.xdata, event.ydata))
-            self.app.info("Click on target point.")
+            self.app.inform.emit("Click on target point.")
 
         ### Copy
         if event.key == 'c':
             self.copy_btn.setChecked(True)
             self.on_tool_select('copy')
             self.active_tool.set_origin(self.snap(event.xdata, event.ydata))
-            self.app.info("Click on target point.")
+            self.app.inform.emit("Click on target point.")
 
         ### Snap
         if event.key == 'g':
@@ -1176,7 +1166,7 @@ class FlatCAMDraw(QtCore.QObject):
         if self.active_tool is not None:
             response = self.active_tool.on_key(event.key)
         if response is not None:
-            self.app.info(response)
+            self.app.inform.emit(response)
 
     def on_canvas_key_release(self, event):
         self.key = None
@@ -1484,7 +1474,7 @@ class FlatCAMDraw(QtCore.QObject):
 
         for param in [tooldia, overlap, margin]:
             if not isinstance(param, float):
-                param_name = [k for k, v in locals().iteritems() if v is param][0]
+                param_name = [k for k, v in list(locals().items()) if v is param][0]
                 self.app.inform.emit("[warning] Invalid value for {}".format())
 
         # Todo: Check for valid method.

+ 55 - 75
FlatCAMGUI.py

@@ -28,83 +28,79 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.menufile = self.menu.addMenu('&File')
 
         # New
-        self.menufilenew = QtGui.QAction(QtGui.QIcon('share:file16.png'), '&New project', self)
+        self.menufilenew = QtGui.QAction(QtGui.QIcon('share/file16.png'), '&New project', self)
         self.menufile.addAction(self.menufilenew)
 
         # Recent
-        self.recent = self.menufile.addMenu(QtGui.QIcon('share:folder16.png'), "Open recent ...")
+        self.recent = self.menufile.addMenu(QtGui.QIcon('share/folder16.png'), "Open recent ...")
 
         # Separator
         self.menufile.addSeparator()
 
         # Open gerber ...
-        self.menufileopengerber = QtGui.QAction(QtGui.QIcon('share:folder16.png'), 'Open &Gerber ...', self)
+        self.menufileopengerber = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Gerber ...', self)
         self.menufile.addAction(self.menufileopengerber)
 
         # Open Excellon ...
-        self.menufileopenexcellon = QtGui.QAction(QtGui.QIcon('share:folder16.png'), 'Open &Excellon ...', self)
+        self.menufileopenexcellon = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Excellon ...', self)
         self.menufile.addAction(self.menufileopenexcellon)
 
         # Open G-Code ...
-        self.menufileopengcode = QtGui.QAction(QtGui.QIcon('share:folder16.png'), 'Open G-&Code ...', self)
+        self.menufileopengcode = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open G-&Code ...', self)
         self.menufile.addAction(self.menufileopengcode)
 
         # Open Project ...
-        self.menufileopenproject = QtGui.QAction(QtGui.QIcon('share:folder16.png'), 'Open &Project ...', self)
+        self.menufileopenproject = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Project ...', self)
         self.menufile.addAction(self.menufileopenproject)
 
         # Separator
         self.menufile.addSeparator()
 
         # Import SVG ...
-        self.menufileimportsvg = QtGui.QAction(QtGui.QIcon('share:folder16.png'), 'Import &SVG ...', self)
+        self.menufileimportsvg = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Import &SVG ...', self)
         self.menufile.addAction(self.menufileimportsvg)
 
         # Export SVG ...
-        self.menufileexportsvg = QtGui.QAction(QtGui.QIcon('share:folder16.png'), 'Export &SVG ...', self)
+        self.menufileexportsvg = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Export &SVG ...', self)
         self.menufile.addAction(self.menufileexportsvg)
 
         # Separator
         self.menufile.addSeparator()
 
         # Save Project
-        self.menufilesaveproject = QtGui.QAction(QtGui.QIcon('share:floppy16.png'), '&Save Project', self)
+        self.menufilesaveproject = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), '&Save Project', self)
         self.menufile.addAction(self.menufilesaveproject)
 
         # Save Project As ...
-        self.menufilesaveprojectas = QtGui.QAction(QtGui.QIcon('share:floppy16.png'), 'Save Project &As ...', self)
+        self.menufilesaveprojectas = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), 'Save Project &As ...', self)
         self.menufile.addAction(self.menufilesaveprojectas)
 
         # Save Project Copy ...
-        self.menufilesaveprojectcopy = QtGui.QAction(QtGui.QIcon('share:floppy16.png'), 'Save Project C&opy ...', self)
+        self.menufilesaveprojectcopy = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), 'Save Project C&opy ...', self)
         self.menufile.addAction(self.menufilesaveprojectcopy)
 
         # Save Defaults
-        self.menufilesavedefaults = QtGui.QAction(QtGui.QIcon('share:floppy16.png'), 'Save &Defaults', self)
+        self.menufilesavedefaults = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), 'Save &Defaults', self)
         self.menufile.addAction(self.menufilesavedefaults)
 
         # Separator
         self.menufile.addSeparator()
 
         # Quit
-        self.exit_action = QtGui.QAction(QtGui.QIcon('share:power16.png'), '&Exit', self)
-        self.menufile.addAction(self.exit_action)
+        self.exit_action = QtGui.QAction(QtGui.QIcon('share/power16.png'), '&Exit', self)
         # exitAction.setShortcut('Ctrl+Q')
         # exitAction.setStatusTip('Exit application')
         #self.exit_action.triggered.connect(QtGui.qApp.quit)
 
-
         ### Edit ###
         self.menuedit = self.menu.addMenu('&Edit')
-        self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share:new_geo16.png'), 'New Geometry')
-        self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share:edit16.png'), 'Edit Geometry')
-        self.menueditok = self.menuedit.addAction(QtGui.QIcon('share:edit_ok16.png'), 'Update Geometry')
-
-        # Separator
-        self.menuedit.addSeparator()
-
-        self.menueditjoin = self.menuedit.addAction(QtGui.QIcon('share:join16.png'), 'Join Geometry')
-        self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share:trash16.png'), 'Delete')
+        self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Geometry')
+        self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Geometry')
+        self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), 'Update Geometry')
+        #self.menueditok.
+        #self.menueditcancel = self.menuedit.addAction(QtGui.QIcon('share/cancel_edit16.png'), "Cancel Edit")
+        self.menueditjoin = self.menuedit.addAction(QtGui.QIcon('share/join16.png'), 'Join Geometry')
+        self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), 'Delete')
 
         ### Options ###
         self.menuoptions = self.menu.addMenu('&Options')
@@ -118,58 +114,42 @@ class FlatCAMGUI(QtGui.QMainWindow):
 
         ### View ###
         self.menuview = self.menu.addMenu('&View')
-        self.menuviewdisableall = self.menuview.addAction(QtGui.QIcon('share:clear_plot16.png'), 'Disable all plots')
-        self.menuviewdisableother = self.menuview.addAction(QtGui.QIcon('share:clear_plot16.png'),
+        self.menuviewdisableall = self.menuview.addAction(QtGui.QIcon('share/clear_plot16.png'), 'Disable all plots')
+        self.menuviewdisableother = self.menuview.addAction(QtGui.QIcon('share/clear_plot16.png'),
                                                             'Disable all plots but this one')
-        self.menuviewenable = self.menuview.addAction(QtGui.QIcon('share:replot16.png'), 'Enable all plots')
+        self.menuviewenable = self.menuview.addAction(QtGui.QIcon('share/replot16.png'), 'Enable all plots')
 
         ### Tool ###
 
         self.menutool = QtGui.QMenu('&Tool')
         self.menutoolaction = self.menu.addMenu(self.menutool)
-        self.menutoolshell = self.menutool.addAction(QtGui.QIcon('share:shell16.png'), '&Command Line')
+        self.menutoolshell = self.menutool.addAction(QtGui.QIcon('share/shell16.png'), '&Command Line')
 
         ### Help ###
         self.menuhelp = self.menu.addMenu('&Help')
-        self.menuhelp_about = self.menuhelp.addAction(QtGui.QIcon('share:tv16.png'), 'About FlatCAM')
-        self.menuhelp_home = self.menuhelp.addAction(QtGui.QIcon('share:home16.png'), 'Home')
-        self.menuhelp_manual = self.menuhelp.addAction(QtGui.QIcon('share:globe16.png'), 'Manual')
+        self.menuhelp_about = self.menuhelp.addAction(QtGui.QIcon('share/tv16.png'), 'About FlatCAM')
+        self.menuhelp_home = self.menuhelp.addAction(QtGui.QIcon('share/home16.png'), 'Home')
+        self.menuhelp_manual = self.menuhelp.addAction(QtGui.QIcon('share/globe16.png'), 'Manual')
 
         ###############
         ### Toolbar ###
         ###############
-        self.toolbarfile = QtGui.QToolBar('File Toolbar')
-        self.addToolBar(self.toolbarfile)
-        self.open_gerber_btn = self.toolbarfile.addAction(QtGui.QIcon('share:flatcam_icon32.png'), "Open &Gerber")
-        self.open_exc_btn = self.toolbarfile.addAction(QtGui.QIcon('share:drill32.png'), "Open &Excellon")
-        self.open_gcode_btn = self.toolbarfile.addAction(QtGui.QIcon('share:cnc32.png'), "Open Gco&de")
-        self.save_btn = self.toolbarfile.addAction(QtGui.QIcon('share:floppy32.png'), 'Save Project &As ...')
-
-        self.toolbarview= QtGui.QToolBar('View Toolbar')
-        self.addToolBar(self.toolbarview)
-        self.zoom_fit_btn = self.toolbarview.addAction(QtGui.QIcon('share:zoom_fit32.png'), "&Zoom Fit")
-        self.zoom_out_btn = self.toolbarview.addAction(QtGui.QIcon('share:zoom_out32.png'), "&Zoom Out")
-        self.zoom_in_btn = self.toolbarview.addAction(QtGui.QIcon('share:zoom_in32.png'), "&Zoom In")
-        # Separator
-        self.toolbarview.addSeparator()
-        self.clear_plot_btn = self.toolbarview.addAction(QtGui.QIcon('share:clear_plot32.png'), "&Clear Plot")
-        self.replot_btn = self.toolbarview.addAction(QtGui.QIcon('share:replot32.png'), "&Replot")
-
-        self.toolbareditobj = QtGui.QToolBar('Obj.Editor Toolbar')
-        self.addToolBar(self.toolbareditobj)
-        self.newgeo_btn = self.toolbareditobj.addAction(QtGui.QIcon('share:new_geo32.png'), "New Blank Geometry")
-        self.editgeo_btn = self.toolbareditobj.addAction(QtGui.QIcon('share:edit32.png'), "Edit Geometry")
-        self.updategeo_btn = self.toolbareditobj.addAction(QtGui.QIcon('share:edit_ok32.png'), "Update Geometry")
-        self.updategeo_btn.setEnabled(False)
 
-        self.toolbaredit = QtGui.QToolBar('Edit Toolbar')
-        self.addToolBar(self.toolbaredit)
-        self.delete_btn = self.toolbaredit.addAction(QtGui.QIcon('share:delete32.png'), "&Delete")
-
-        self.toolbartools = QtGui.QToolBar('Tools Toolbar')
-        self.addToolBar(self.toolbartools)
-        self.shell_btn = self.toolbartools.addAction(QtGui.QIcon('share:shell32.png'), "&Command Line")
-        self.measure_btn = self.toolbartools.addAction(QtGui.QIcon('share:measure32.png'), "&Measurement Tool")
+        self.toolbar = QtGui.QToolBar()
+        self.addToolBar(self.toolbar)
+
+        self.zoom_fit_btn = self.toolbar.addAction(QtGui.QIcon('share/zoom_fit32.png'), "&Zoom Fit")
+        self.zoom_out_btn = self.toolbar.addAction(QtGui.QIcon('share/zoom_out32.png'), "&Zoom Out")
+        self.zoom_in_btn = self.toolbar.addAction(QtGui.QIcon('share/zoom_in32.png'), "&Zoom In")
+        self.clear_plot_btn = self.toolbar.addAction(QtGui.QIcon('share/clear_plot32.png'), "&Clear Plot")
+        self.replot_btn = self.toolbar.addAction(QtGui.QIcon('share/replot32.png'), "&Replot")
+        self.newgeo_btn = self.toolbar.addAction(QtGui.QIcon('share/new_geo32.png'), "New Blank Geometry")
+        self.editgeo_btn = self.toolbar.addAction(QtGui.QIcon('share/edit32.png'), "Edit Geometry")
+        self.updategeo_btn = self.toolbar.addAction(QtGui.QIcon('share/edit_ok32.png'), "Update Geometry")
+        self.updategeo_btn.setEnabled(False)
+        #self.canceledit_btn = self.toolbar.addAction(QtGui.QIcon('share/cancel_edit32.png'), "Cancel Edit")
+        self.delete_btn = self.toolbar.addAction(QtGui.QIcon('share/delete32.png'), "&Delete")
+        self.shell_btn = self.toolbar.addAction(QtGui.QIcon('share/shell32.png'), "&Command Line")
 
         ################
         ### Splitter ###
@@ -209,7 +189,7 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.options_tab_layout.addLayout(hlay1)
 
         self.icon = QtGui.QLabel()
-        self.icon.setPixmap(QtGui.QPixmap('share:gear48.png'))
+        self.icon.setPixmap(QtGui.QPixmap('share/gear48.png'))
         hlay1.addWidget(self.icon)
 
         self.options_combo = QtGui.QComboBox()
@@ -277,12 +257,12 @@ class FlatCAMGUI(QtGui.QMainWindow):
         ### Icons ###
         #############
         self.app_icon = QtGui.QIcon()
-        self.app_icon.addFile('share:flatcam_icon16.png', QtCore.QSize(16, 16))
-        self.app_icon.addFile('share:flatcam_icon24.png', QtCore.QSize(24, 24))
-        self.app_icon.addFile('share:flatcam_icon32.png', QtCore.QSize(32, 32))
-        self.app_icon.addFile('share:flatcam_icon48.png', QtCore.QSize(48, 48))
-        self.app_icon.addFile('share:flatcam_icon128.png', QtCore.QSize(128, 128))
-        self.app_icon.addFile('share:flatcam_icon256.png', QtCore.QSize(256, 256))
+        self.app_icon.addFile('share/flatcam_icon16.png', QtCore.QSize(16, 16))
+        self.app_icon.addFile('share/flatcam_icon24.png', QtCore.QSize(24, 24))
+        self.app_icon.addFile('share/flatcam_icon32.png', QtCore.QSize(32, 32))
+        self.app_icon.addFile('share/flatcam_icon48.png', QtCore.QSize(48, 48))
+        self.app_icon.addFile('share/flatcam_icon128.png', QtCore.QSize(128, 128))
+        self.app_icon.addFile('share/flatcam_icon256.png', QtCore.QSize(256, 256))
         self.setWindowIcon(self.app_icon)
 
         self.setGeometry(100, 100, 1024, 650)
@@ -308,7 +288,7 @@ class FlatCAMActivityView(QtGui.QWidget):
 
         self.icon = QtGui.QLabel(self)
         self.icon.setGeometry(0, 0, 12, 12)
-        self.movie = QtGui.QMovie("share:active.gif")
+        self.movie = QtGui.QMovie("share/active.gif")
         self.icon.setMovie(self.movie)
         #self.movie.start()
 
@@ -339,7 +319,7 @@ class FlatCAMInfoBar(QtGui.QWidget):
 
         self.icon = QtGui.QLabel(self)
         self.icon.setGeometry(0, 0, 12, 12)
-        self.pmap = QtGui.QPixmap('share:graylight12.png')
+        self.pmap = QtGui.QPixmap('share/graylight12.png')
         self.icon.setPixmap(self.pmap)
 
         layout = QtGui.QHBoxLayout()
@@ -364,13 +344,13 @@ class FlatCAMInfoBar(QtGui.QWidget):
         level = str(level)
         self.pmap.fill()
         if level == "error":
-            self.pmap = QtGui.QPixmap('share:redlight12.png')
+            self.pmap = QtGui.QPixmap('share/redlight12.png')
         elif level == "success":
-            self.pmap = QtGui.QPixmap('share:greenlight12.png')
+            self.pmap = QtGui.QPixmap('share/greenlight12.png')
         elif level == "warning":
-            self.pmap = QtGui.QPixmap('share:yellowlight12.png')
+            self.pmap = QtGui.QPixmap('share/yellowlight12.png')
         else:
-            self.pmap = QtGui.QPixmap('share:graylight12.png')
+            self.pmap = QtGui.QPixmap('share/graylight12.png')
 
         self.icon.setPixmap(self.pmap)
         self.set_text_(text)

+ 48 - 24
FlatCAMObj.py

@@ -6,7 +6,7 @@
 # MIT Licence                                              #
 ############################################################
 
-from cStringIO import StringIO
+from io import StringIO
 from PyQt4 import QtCore
 from copy import copy
 from ObjectUI import *
@@ -101,7 +101,7 @@ class FlatCAMObj(QtCore.QObject):
         old_name = copy(self.options["name"])
         new_name = self.ui.name_entry.get_value()
         self.options["name"] = self.ui.name_entry.get_value()
-        self.app.info("Name changed from %s to %s" % (old_name, new_name))
+        self.app.inform.emit("Name changed from %s to %s" % (old_name, new_name))
 
     def on_offset_button_click(self):
         self.app.report_usage("obj_on_offset_button")
@@ -221,7 +221,7 @@ class FlatCAMObj(QtCore.QObject):
         try:
             self.form_fields[option].set_value(self.options[option])
         except KeyError:
-            self.app.log.warn("Tried to set an option or field that does not exist: %s" % option)
+            self.app.log.warning("Tried to set an option or field that does not exist: %s" % option)
 
     def read_form_item(self, option):
         """
@@ -473,7 +473,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             # Propagate options
             follow_obj.options["cnctooldia"] = self.options["isotooldia"]
             follow_obj.solid_geometry = self.solid_geometry
-            app_obj.info("Follow geometry created: %s" % follow_obj.options["name"])
+            app_obj.inform.emit("Follow geometry created: %s" % follow_obj.options["name"])
 
         # TODO: Do something if this is None. Offer changing name?
         self.app.new_object("geometry", follow_name, follow_init)
@@ -519,7 +519,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                 elif type(geom) is Polygon:
                     geom = Polygon(geom.exterior.coords[::-1], geom.interiors)
                 else:
-                    raise "Unexpected Geometry"
+                    raise str("Unexpected Geometry")
             return geom
 
         if combine:
@@ -534,7 +534,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                     offset = (2 * i + 1) / 2.0 * dia - i * overlap * dia
                     geom = generate_envelope (offset, i == 0)
                     geo_obj.solid_geometry.append(geom)
-                app_obj.info("Isolation geometry created: %s" % geo_obj.options["name"])
+                app_obj.inform.emit("Isolation geometry created: %s" % geo_obj.options["name"])
 
             # TODO: Do something if this is None. Offer changing name?
             self.app.new_object("geometry", iso_name, iso_init)
@@ -553,7 +553,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                     # Propagate options
                     geo_obj.options["cnctooldia"] = self.options["isotooldia"]
                     geo_obj.solid_geometry = generate_envelope (offset, i == 0)
-                    app_obj.info("Isolation geometry created: %s" % geo_obj.options["name"])
+                    app_obj.inform.emit("Isolation geometry created: %s" % geo_obj.options["name"])
 
                 # TODO: Do something if this is None. Offer changing name?
                 self.app.new_object("geometry", iso_name, iso_init)
@@ -728,20 +728,20 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                     exc_final.drills.append({"point": point, "tool": drill['tool']})
                 toolsrework=dict()
                 max_numeric_tool=0
-                for toolname in exc.tools.iterkeys():
+                for toolname in list(exc.tools.copy().keys()):
                     numeric_tool=int(toolname)
                     if numeric_tool>max_numeric_tool:
                         max_numeric_tool=numeric_tool
                     toolsrework[exc.tools[toolname]['C']]=toolname
 
                 #exc_final as last because names from final tools will be used
-                for toolname in exc_final.tools.iterkeys():
+                for toolname in list(exc_final.tools.copy().keys()):
                     numeric_tool=int(toolname)
                     if numeric_tool>max_numeric_tool:
                         max_numeric_tool=numeric_tool
                     toolsrework[exc_final.tools[toolname]['C']]=toolname
 
-                for toolvalues in toolsrework.iterkeys():
+                for toolvalues in list(toolsrework.copy().keys()):
                     if toolsrework[toolvalues] in exc_final.tools:
                         if exc_final.tools[toolsrework[toolvalues]]!={"C": toolvalues}:
                             exc_final.tools[str(max_numeric_tool+1)]={"C": toolvalues}
@@ -756,18 +756,29 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
         # Populate tool list
         n = len(self.tools)
-        self.ui.tools_table.setColumnCount(2)
-        self.ui.tools_table.setHorizontalHeaderLabels(['#', 'Diameter'])
+        self.ui.tools_table.setColumnCount(3)
+        self.ui.tools_table.setHorizontalHeaderLabels(['#', 'Diameter', 'Count'])
         self.ui.tools_table.setRowCount(n)
         self.ui.tools_table.setSortingEnabled(False)
+
         i = 0
         for tool in self.tools:
+
+            drill_cnt = 0   # variable to store the nr of drills per tool
+            # Find no of drills for the current tool
+            for drill in self.drills:
+                if drill.get('tool') == tool:
+                    drill_cnt += 1
+
             id = QtGui.QTableWidgetItem(tool)
             id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
             self.ui.tools_table.setItem(i, 0, id)  # Tool name/id
             dia = QtGui.QTableWidgetItem(str(self.tools[tool]['C']))
             dia.setFlags(QtCore.Qt.ItemIsEnabled)
+            drill_count = QtGui.QTableWidgetItem('%d' % drill_cnt)
+            drill_count.setFlags(QtCore.Qt.ItemIsEnabled)
             self.ui.tools_table.setItem(i, 1, dia)  # Diameter
+            self.ui.tools_table.setItem(i, 2, drill_count)  # Number of drills per tool
             i += 1
         
         # sort the tool diameter column
@@ -777,7 +788,11 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         
         self.ui.tools_table.resizeColumnsToContents()
         self.ui.tools_table.resizeRowsToContents()
-        self.ui.tools_table.horizontalHeader().setStretchLastSection(True)
+        horizontal_header = self.ui.tools_table.horizontalHeader()
+        horizontal_header.setResizeMode(0, QtGui.QHeaderView.ResizeToContents)
+        horizontal_header.setResizeMode(1, QtGui.QHeaderView.Stretch)
+        horizontal_header.setResizeMode(2, QtGui.QHeaderView.ResizeToContents)
+        # horizontal_header.setStretchLastSection(True)
         self.ui.tools_table.verticalHeader().hide()
         self.ui.tools_table.setSortingEnabled(True)
 
@@ -846,7 +861,14 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             tooldia = self.options["tooldia"]
 
         # Sort tools by diameter. items() -> [('name', diameter), ...]
-        sorted_tools = sorted(self.tools.items(), key=lambda tl: tl[1])
+        # sorted_tools = sorted(list(self.tools.items()), key=lambda tl: tl[1])
+
+        # Python3 no longer allows direct comparison between dicts so we need to sort tools differently
+        sort = []
+        for k, v in self.tools.items():
+            sort.append((k, v.get('C')))
+        sorted_tools = sorted(sort, key=lambda t1: t1[1])
+        log.debug("Tools are sorted: %s" % str(sorted_tools))
 
         if tools == "all":
             tools = [i[0] for i in sorted_tools]  # List if ordered tool names.
@@ -1044,6 +1066,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
             "tooldia": self.ui.tooldia_entry,
             "append": self.ui.append_text,
             "prepend": self.ui.prepend_text,
+            "postprocess": self.ui.process_script,
             "dwell": self.ui.dwell_cb,
             "dwelltime": self.ui.dwelltime_entry
         })
@@ -1066,15 +1089,16 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         self.read_form()
 
         try:
-            filename = unicode(QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ...",
+            filename = str(QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ...",
                                                          directory=self.app.defaults["last_folder"]))
         except TypeError:
-            filename = unicode(QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ..."))
+            filename = str(QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ..."))
 
         preamble = str(self.ui.prepend_text.get_value())
         postamble = str(self.ui.append_text.get_value())
+        processor = str(self.ui.process_script.get_value())
 
-        self.export_gcode(filename, preamble=preamble, postamble=postamble)
+        self.export_gcode(filename, preamble=preamble, postamble=postamble, processor=processor)
 
     def dwell_generator(self, lines):
         """
@@ -1110,7 +1134,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
         raise StopIteration
 
-    def export_gcode(self, filename, preamble='', postamble=''):
+    def export_gcode(self, filename, preamble='', postamble='', processor=''):
 
         lines = StringIO(self.gcode)
 
@@ -1279,11 +1303,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             return
 
         if self.options["selectmethod"] == "single":
-            self.app.info("Click inside the desired polygon.")
+            self.app.inform.emit("Click inside the desired polygon.")
 
             # To be called after clicking on the plot.
             def doit(event):
-                self.app.info("Painting polygon...")
+                self.app.inform.emit("Painting polygon...")
                 self.app.plotcanvas.mpl_disconnect(subscription)
                 point = [event.xdata, event.ydata]
                 self.paint_poly_single_click(point, tooldia, overlap,
@@ -1351,9 +1375,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             geo_obj.options["cnctooldia"] = tooldia
 
             # Experimental...
-            print "Indexing...",
+            print("Indexing...")
             geo_obj.make_index()
-            print "Done"
+            print("Done")
 
             self.app.inform.emit("Done.")
 
@@ -1437,9 +1461,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             geo_obj.options["cnctooldia"] = tooldia
 
             # Experimental...
-            print "Indexing...",
+            print("Indexing...")
             geo_obj.make_index()
-            print "Done"
+            print("Done")
 
             self.app.inform.emit("Done.")
 

+ 9 - 9
GUIElements.py

@@ -81,7 +81,7 @@ class LengthEntry(QtGui.QLineEdit):
     def returnPressed(self, *args, **kwargs):
         val = self.get_value()
         if val is not None:
-            self.set_text(QtCore.QString(str(val)))
+            self.set_text(str(val))
         else:
             log.warning("Could not interpret entry: %s" % self.get_text())
 
@@ -105,7 +105,7 @@ class LengthEntry(QtGui.QLineEdit):
             return None
 
     def set_value(self, val):
-        self.setText(QtCore.QString(str(val)))
+        self.setText(str(val))
 
 
 class FloatEntry(QtGui.QLineEdit):
@@ -115,7 +115,7 @@ class FloatEntry(QtGui.QLineEdit):
     def returnPressed(self, *args, **kwargs):
         val = self.get_value()
         if val is not None:
-            self.set_text(QtCore.QString(str(val)))
+            self.set_text(str(val))
         else:
             log.warning("Could not interpret entry: %s" % self.text())
 
@@ -151,10 +151,10 @@ class IntEntry(QtGui.QLineEdit):
     def set_value(self, val):
 
         if val == self.empty_val and self.allow_empty:
-            self.setText(QtCore.QString(""))
+            self.setText("")
             return
 
-        self.setText(QtCore.QString(str(val)))
+        self.setText(str(val))
 
 
 class FCEntry(QtGui.QLineEdit):
@@ -165,7 +165,7 @@ class FCEntry(QtGui.QLineEdit):
         return str(self.text())
 
     def set_value(self, val):
-        self.setText(QtCore.QString(str(val)))
+        self.setText(str(val))
 
 
 class EvalEntry(QtGui.QLineEdit):
@@ -175,7 +175,7 @@ class EvalEntry(QtGui.QLineEdit):
     def returnPressed(self, *args, **kwargs):
         val = self.get_value()
         if val is not None:
-            self.setText(QtCore.QString(str(val)))
+            self.setText(str(val))
         else:
             log.warning("Could not interpret entry: %s" % self.get_text())
 
@@ -188,12 +188,12 @@ class EvalEntry(QtGui.QLineEdit):
             return None
 
     def set_value(self, val):
-        self.setText(QtCore.QString(str(val)))
+        self.setText(str(val))
 
 
 class FCCheckBox(QtGui.QCheckBox):
     def __init__(self, label='', parent=None):
-        super(FCCheckBox, self).__init__(QtCore.QString(label), parent)
+        super(FCCheckBox, self).__init__(str(label), parent)
 
     def get_value(self):
         return self.isChecked()

+ 12 - 9
ObjectCollection.py

@@ -6,7 +6,6 @@
 # MIT Licence                                              #
 ############################################################
 
-#from PyQt4.QtCore import QModelIndex
 from FlatCAMObj import *
 import inspect  # TODO: Remove
 import FlatCAMApp
@@ -47,6 +46,7 @@ class ObjectCollection():
 
     def __init__(self, parent=None):
         #QtCore.QAbstractListModel.__init__(self, parent=parent)
+
         ### Icons for the list view
         self.icons = {}
         for kind in ObjectCollection.icon_files:
@@ -70,7 +70,6 @@ class ObjectCollection():
         self.model = QtGui.QStandardItemModel(self.view)
         self.view.setModel(self.model)
         self.model.itemChanged.connect(self.on_item_changed)
-        #self.view.setModel(self)
 
         self.click_modifier = None
 
@@ -102,7 +101,7 @@ class ObjectCollection():
 
     def print_list(self):
         for obj in self.object_list:
-            print obj
+            print(obj)
 
     def on_mouse_down(self, event):
         FlatCAMApp.App.log.debug("Mouse button pressed on list")
@@ -163,12 +162,16 @@ class ObjectCollection():
 
         # Create the model item to insert into the QListView
         icon = QtGui.QIcon(self.icons[obj.kind])#self.icons["gerber"])
-        item = QtGui.QStandardItem(icon, name)
+        item = QtGui.QStandardItem(icon, str(name))
+        # Item is not editable, so that double click
+        # does not allow cell value modification.
+        item.setEditable(False)
+        # The item is checkable, to add the checkbox.
         item.setCheckable(True)
-        if obj.options["plot"] == True:
-            item.setCheckState(2)#Qt.Checked)
+        if obj.options["plot"] is True:
+            item.setCheckState(2)   #Qt.Checked)
         else:
-            item.setCheckState(0) #Qt.Unchecked)
+            item.setCheckState(0)   #Qt.Unchecked)
 
         self.model.appendRow(item)
 
@@ -289,7 +292,7 @@ class ObjectCollection():
         :param name: Name of the FlatCAM Object
         :return: None
         """
-        iobj = self.createIndex(self.get_names().index(name), 0)  # Column 0
+        iobj = self.model.createIndex(self.get_names().index(name), 0)  # Column 0
         self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Select)
 
     def set_inactive(self, name):
@@ -300,7 +303,7 @@ class ObjectCollection():
         :param name: Name of the FlatCAM Object
         :return: None
         """
-        iobj = self.createIndex(self.get_names().index(name), 0)  # Column 0
+        iobj = self.model.createIndex(self.get_names().index(name), 0)  # Column 0
         self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Deselect)
 
     def set_all_inactive(self):

+ 16 - 5
ObjectUI.py

@@ -11,7 +11,7 @@ class ObjectUI(QtGui.QWidget):
     put UI elements in ObjectUI.custom_box (QtGui.QLayout).
     """
 
-    def __init__(self, icon_file='share:flatcam_icon32.png', title='FlatCAM Object', parent=None):
+    def __init__(self, icon_file='share/flatcam_icon32.png', title='FlatCAM Object', parent=None):
         QtGui.QWidget.__init__(self, parent=parent)
 
         layout = QtGui.QVBoxLayout()
@@ -117,7 +117,7 @@ class CNCObjectUI(ObjectUI):
         be placed in ``self.custom_box`` to preserve the layout.
         """
 
-        ObjectUI.__init__(self, title='CNC Job Object', icon_file='share:cnc32.png', parent=parent)
+        ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png', parent=parent)
 
         # Scale and offset are not available for CNCJob objects.
         # Hiding from the GUI.
@@ -196,6 +196,17 @@ class CNCObjectUI(ObjectUI):
         self.append_text = FCTextArea()
         self.custom_box.addWidget(self.append_text)
 
+        processorlabel = QtGui.QLabel('Postprocessing-Script:')
+        processorlabel.setToolTip(
+            "Enter a Postprocessing Script here.\n"
+            "It gets applied to the G-Code after it\n"
+            "is generated."
+        )
+        self.custom_box.addWidget(processorlabel)
+        self.process_script = FCTextArea()
+        self.custom_box.addWidget(self.process_script)
+
+
         # Dwell
         grid1 = QtGui.QGridLayout()
         self.custom_box.addLayout(grid1)
@@ -231,7 +242,7 @@ class GeometryObjectUI(ObjectUI):
     """
 
     def __init__(self, parent=None):
-        super(GeometryObjectUI, self).__init__(title='Geometry Object', icon_file='share:geometry32.png', parent=parent)
+        super(GeometryObjectUI, self).__init__(title='Geometry Object', icon_file='share/geometry32.png', parent=parent)
 
         ## Plot options
         self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
@@ -450,7 +461,7 @@ class ExcellonObjectUI(ObjectUI):
 
     def __init__(self, parent=None):
         ObjectUI.__init__(self, title='Excellon Object',
-                          icon_file='share:drill32.png',
+                          icon_file='share/drill32.png',
                           parent=parent)
 
         #### Plot options ####
@@ -814,4 +825,4 @@ class GerberObjectUI(ObjectUI):
 #
 #
 # if __name__ == '__main__':
-#     main()
+#     main()

+ 8 - 8
bugs/conf.py

@@ -43,8 +43,8 @@ source_suffix = '.rst'
 master_doc = 'index'
 
 # General information about the project.
-project = u'FlatCAM Bugs'
-copyright = u'2014, Juan Pablo Caram'
+project = 'FlatCAM Bugs'
+copyright = '2014, Juan Pablo Caram'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -196,8 +196,8 @@ latex_elements = {
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-  ('index', 'FlatCAMBugs.tex', u'FlatCAM Bugs Documentation',
-   u'Juan Pablo Caram', 'manual'),
+  ('index', 'FlatCAMBugs.tex', 'FlatCAM Bugs Documentation',
+   'Juan Pablo Caram', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -226,8 +226,8 @@ latex_documents = [
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('index', 'flatcambugs', u'FlatCAM Bugs Documentation',
-     [u'Juan Pablo Caram'], 1)
+    ('index', 'flatcambugs', 'FlatCAM Bugs Documentation',
+     ['Juan Pablo Caram'], 1)
 ]
 
 # If true, show URL addresses after external links.
@@ -240,8 +240,8 @@ man_pages = [
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-  ('index', 'FlatCAMBugs', u'FlatCAM Bugs Documentation',
-   u'Juan Pablo Caram', 'FlatCAMBugs', 'One line description of project.',
+  ('index', 'FlatCAMBugs', 'FlatCAM Bugs Documentation',
+   'Juan Pablo Caram', 'FlatCAMBugs', 'One line description of project.',
    'Miscellaneous'),
 ]
 

+ 9 - 5
camlib.py

@@ -9,7 +9,7 @@
 #from scipy import optimize
 #import traceback
 
-from cStringIO import StringIO
+from io import StringIO
 from numpy import arctan2, Inf, array, sqrt, pi, ceil, sin, cos, dot, float32, \
     transpose
 from numpy.linalg import solve, norm
@@ -2360,7 +2360,7 @@ class Gerber (Geometry):
             else:
                 self.solid_geometry = self.solid_geometry.difference(new_poly)
 
-        except Exception, err:
+        except Exception as err:
             ex_type, ex, tb = sys.exc_info()
             traceback.print_tb(tb)
             #print traceback.format_exc()
@@ -2972,14 +2972,18 @@ class CNCjob(Geometry):
         # Tools
 
         # Sort tools by diameter. items() -> [('name', diameter), ...]
-        sorted_tools = sorted(exobj.tools.items(), key=lambda tl: tl[1])
+        #sorted_tools = sorted(list(exobj.tools.items()), key=lambda tl: tl[1])
+        sort = []
+        for k, v in exobj.tools.items():
+            sort.append((k, v.get('C')))
+        sorted_tools = sorted(sort, key=lambda t1: t1[1])
 
         if tools == "all":
             tools = [i[0] for i in sorted_tools]   # List if ordered tool names.
             log.debug("Tools 'all' and sorted are: %s" % str(tools))
         else:
             selected_tools = [x.strip() for x in tools.split(",")]
-            selected_tools = filter(lambda tl: tl in selected_tools, selected_tools)
+            selected_tools = [tl for tl in selected_tools if tl in selected_tools]
 
             # Create a sorted list of selected tools from the sorted_tools list
             tools = [i for i, j in sorted_tools for k in selected_tools if i == k]
@@ -4103,7 +4107,7 @@ class FlatCAMRTree(object):
         :param pt:
         :return:
         """
-        return self.rti.nearest(pt, objects=True).next()
+        return next(self.rti.nearest(pt, objects=True))
 
 
 class FlatCAMRTreeStorage(FlatCAMRTree):

+ 6 - 6
descartes/tests.py

@@ -8,10 +8,10 @@ class PolygonTestCase(unittest.TestCase):
                 MultiPoint([(-5, 0), (5, 0)]).buffer(3.0))
     def test_patch(self):
         patch = PolygonPatch(self.polygon)
-        self.failUnlessEqual(str(type(patch)), 
+        self.assertEqual(str(type(patch)), 
             "<class 'matplotlib.patches.PathPatch'>")
         path = patch.get_path()
-        self.failUnless(len(path.vertices) == len(path.codes) == 198)
+        self.assertTrue(len(path.vertices) == len(path.codes) == 198)
 
 class JSONPolygonTestCase(unittest.TestCase):
     polygon = Point(0, 0).buffer(10.0).difference(
@@ -19,10 +19,10 @@ class JSONPolygonTestCase(unittest.TestCase):
     def test_patch(self):
         geo = self.polygon.__geo_interface__
         patch = PolygonPatch(geo)
-        self.failUnlessEqual(str(type(patch)), 
+        self.assertEqual(str(type(patch)), 
             "<class 'matplotlib.patches.PathPatch'>")
         path = patch.get_path()
-        self.failUnless(len(path.vertices) == len(path.codes) == 198)
+        self.assertTrue(len(path.vertices) == len(path.codes) == 198)
 
 class GeoInterfacePolygonTestCase(unittest.TestCase):
     class GeoThing:
@@ -32,7 +32,7 @@ class GeoInterfacePolygonTestCase(unittest.TestCase):
                 MultiPoint([(-5, 0), (5, 0)]).buffer(3.0)).__geo_interface__
     def test_patch(self):
         patch = PolygonPatch(self.thing)
-        self.failUnlessEqual(str(type(patch)), 
+        self.assertEqual(str(type(patch)), 
             "<class 'matplotlib.patches.PathPatch'>")
         path = patch.get_path()
-        self.failUnless(len(path.vertices) == len(path.codes) == 198)
+        self.assertTrue(len(path.vertices) == len(path.codes) == 198)

+ 8 - 8
doc/source/conf.py

@@ -45,8 +45,8 @@ source_suffix = '.rst'
 master_doc = 'index'
 
 # General information about the project.
-project = u'Cirkuix'
-copyright = u'2014, Juan Pablo Caram'
+project = 'Cirkuix'
+copyright = '2014, Juan Pablo Caram'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -203,8 +203,8 @@ latex_elements = {
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-  ('index', 'Cirkuix.tex', u'Cirkuix Documentation',
-   u'Juan Pablo Caram', 'manual'),
+  ('index', 'Cirkuix.tex', 'Cirkuix Documentation',
+   'Juan Pablo Caram', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -233,8 +233,8 @@ latex_documents = [
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('index', 'cirkuix', u'Cirkuix Documentation',
-     [u'Juan Pablo Caram'], 1)
+    ('index', 'cirkuix', 'Cirkuix Documentation',
+     ['Juan Pablo Caram'], 1)
 ]
 
 # If true, show URL addresses after external links.
@@ -247,8 +247,8 @@ man_pages = [
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-  ('index', 'Cirkuix', u'Cirkuix Documentation',
-   u'Juan Pablo Caram', 'Cirkuix', 'One line description of project.',
+  ('index', 'Cirkuix', 'Cirkuix Documentation',
+   'Juan Pablo Caram', 'Cirkuix', 'One line description of project.',
    'Miscellaneous'),
 ]
 

+ 2 - 2
flatcam

@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 ############################################################
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
@@ -30,6 +30,6 @@ debug_trace()
 # NOTE: Never talk to the GUI from threads! This is why I commented the above.
 
 app = QtGui.QApplication(sys.argv)
-QtCore.QDir.setSearchPaths("share", QtCore.QStringList(("share", "share/flatcam", "/usr/share/flatcam")));
+QtCore.QDir.setSearchPaths("share", str(("share", "share/flatcam", "/usr/share/flatcam")));
 fc = App()
 sys.exit(app.exec_())

+ 3 - 3
make_win32.py

@@ -43,16 +43,16 @@ if sys.platform == "win32":
 buildOptions = dict(
     compressed=False,
     include_files=include_files,
-    icon='share:flatcam_icon48.ico',
+    icon='share/flatcam_icon48.ico',
     # excludes=['PyQt4', 'tk', 'tcl']
     excludes=['scipy.lib.lapack.flapack.pyd',
               'scipy.lib.blas.fblas.pyd',
               'QtOpenGL4.dll']
 )
 
-print "INCLUDE_FILES", include_files
+print(("INCLUDE_FILES", include_files))
 
-execfile('clean.py')
+exec(compile(open('clean.py').read(), 'clean.py', 'exec'))
 
 setup(
     name="FlatCAM",

+ 10 - 10
sandbox/diagnose.py

@@ -1,34 +1,34 @@
 #import sys
 import platform
 
-print "Platform", platform.system(), platform.release()
-print "Distro", platform.dist()
-print "Python", platform.python_version()
+print(("Platform", platform.system(), platform.release()))
+print(("Distro", platform.dist()))
+print(("Python", platform.python_version()))
 
 
 import rtree
 
-print "rtree", rtree.__version__
+print(("rtree", rtree.__version__))
 
 
 import shapely
 import shapely.geos
 
-print "shapely", shapely.__version__
-print "GEOS library", shapely.geos.geos_version
+print(("shapely", shapely.__version__))
+print(("GEOS library", shapely.geos.geos_version))
 
 
 from PyQt4 import Qt
 
-print "Qt", Qt.qVersion()
+print(("Qt", Qt.qVersion()))
 
 
 import numpy
 
-print "Numpy", numpy.__version__
+print(("Numpy", numpy.__version__))
 
 
 import matplotlib
 
-print "MatPlotLib", matplotlib.__version__
-print "MPL Numpy", matplotlib.__version__numpy__
+print(("MatPlotLib", matplotlib.__version__))
+print(("MPL Numpy", matplotlib.__version__numpy__))

+ 3 - 3
sandbox/gerber_find.py

@@ -19,10 +19,10 @@ def gerber_find(filename, coords, frac_digits=5, tol=0.1):
                     current_y = parse_gerber_number(match.group(3), frac_digits)
 
                 if distance(coords, (current_x, current_y)) <= tol:
-                    print line_num, ":", line.strip('\n\r')
+                    print((line_num, ":", line.strip('\n\r')))
         except Exception as e:
-            print str(e)
-            print line_num, ":", line.strip('\n\r')
+            print((str(e)))
+            print((line_num, ":", line.strip('\n\r')))
 
 
 if __name__ == "__main__":

+ 2 - 2
svgparse.py

@@ -521,6 +521,6 @@ if __name__ == "__main__":
     tree = ET.parse('tests/svg/drawing.svg')
     root = tree.getroot()
     ns = re.search(r'\{(.*)\}', root.tag).group(1)
-    print ns
+    print(ns)
     for geo in getsvggeo(root):
-        print geo
+        print(geo)

+ 7 - 6
tclCommands/TclCommand.py

@@ -5,6 +5,7 @@ import abc
 import collections
 from PyQt4 import QtCore
 from contextlib import contextmanager
+from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMObj
 
 
 class TclCommand(object):
@@ -97,7 +98,7 @@ class TclCommand(object):
 
             command_string = []
 
-            for arg_key, arg_type in self.help['args'].items():
+            for arg_key, arg_type in list(self.help['args'].items()):
                 command_string.append(get_decorated_argument(arg_key, arg_type, True))
 
             return "> " + alias_name + " " + " ".join(command_string)
@@ -147,7 +148,7 @@ class TclCommand(object):
         for alias in self.aliases:
             help_string.append(get_decorated_command(alias))
 
-        for key, value in self.help['args'].items():
+        for key, value in list(self.help['args'].items()):
             help_string.append(get_decorated_argument(key, value))
 
         # timeout is unique for signaled commands (this is not best oop practice, but much easier for now)
@@ -206,13 +207,13 @@ class TclCommand(object):
 
         # check arguments
         idx = 0
-        arg_names_items = self.arg_names.items()
+        arg_names_items = list(self.arg_names.items())
         for argument in arguments:
             if len(self.arg_names) > idx:
                 key, arg_type = arg_names_items[idx]
                 try:
                     named_args[key] = arg_type(argument)
-                except Exception, e:
+                except Exception as e:
                     self.raise_tcl_error("Cannot cast named argument '%s' to type %s  with exception '%s'."
                                          % (key, arg_type, str(e)))
             else:
@@ -228,7 +229,7 @@ class TclCommand(object):
                     named_args[key] = self.option_types[key](options[key])
                 else:
                     named_args[key] = int(options[key])
-            except Exception, e:
+            except Exception as e:
                 self.raise_tcl_error("Cannot cast argument '-%s' to type '%s' with exception '%s'."
                                      % (key, self.option_types[key], str(e)))
 
@@ -292,7 +293,7 @@ class TclCommandSignaled(TclCommand):
     """
         !!! I left it here only  for demonstration !!!
         Go to TclCommandCncjob and  into class definition put
-            class TclCommandCncjob(TclCommand.TclCommandSignaled):
+            class TclCommandCncjob(TclCommandSignaled):
         also change
             obj.generatecncjob(use_thread = False, **args)
         to

+ 2 - 3
tclCommands/TclCommandAddCircle.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandAddCircle(TclCommand.TclCommand):
+class TclCommandAddCircle(TclCommand):
     """
     Tcl shell command to creates a circle in the given Geometry object.
 

+ 2 - 3
tclCommands/TclCommandAddPolygon.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandAddPolygon(TclCommand.TclCommandSignaled):
+class TclCommandAddPolygon(TclCommandSignaled):
     """
     Tcl shell command to create a polygon in the given Geometry object
     """

+ 2 - 3
tclCommands/TclCommandAddPolyline.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandAddPolyline(TclCommand.TclCommandSignaled):
+class TclCommandAddPolyline(TclCommandSignaled):
     """
     Tcl shell command to create a polyline in the given Geometry object
     """

+ 2 - 3
tclCommands/TclCommandAddRectangle.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandAddRectangle(TclCommand.TclCommandSignaled):
+class TclCommandAddRectangle(TclCommandSignaled):
     """
     Tcl shell command to add a rectange to the given Geometry object.
     """

+ 4 - 5
tclCommands/TclCommandAlignDrill.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandAlignDrill(TclCommand.TclCommandSignaled):
+class TclCommandAlignDrill(TclCommandSignaled):
     """
     Tcl shell command to create excellon with drills for aligment.
     """
@@ -180,7 +179,7 @@ class TclCommandAlignDrill(TclCommand.TclCommandSignaled):
                                    name + "_aligndrill",
                                    alligndrill_init_me)
 
-            except Exception, e:
+            except Exception as e:
                 return "Operation failed: %s" % str(e)
 
         else:
@@ -195,7 +194,7 @@ class TclCommandAlignDrill(TclCommand.TclCommandSignaled):
                 px = dist
                 py = dist
                 obj.app.new_object("excellon", name + "_alligndrill", alligndrill_init_me)
-            except Exception, e:
+            except Exception as e:
                 return "Operation failed: %s" % str(e)
 
         return 'Ok'

+ 2 - 3
tclCommands/TclCommandAlignDrillGrid.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandAlignDrillGrid(TclCommand.TclCommandSignaled):
+class TclCommandAlignDrillGrid(TclCommandSignaled):
     """
     Tcl shell command to create an Excellon object
     with drills for aligment grid.

+ 2 - 3
tclCommands/TclCommandCncjob.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandCncjob(TclCommand.TclCommandSignaled):
+class TclCommandCncjob(TclCommandSignaled):
     """
     Tcl shell command to Generates a CNC Job from a Geometry Object.
 

+ 3 - 4
tclCommands/TclCommandCutout.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandCutout(TclCommand.TclCommand):
+class TclCommandCutout(TclCommand):
     """
     Tcl shell command to create a board cutout geometry.
 
@@ -95,5 +94,5 @@ class TclCommandCutout(TclCommand.TclCommand):
 
         try:
             obj.app.new_object("geometry", name + "_cutout", geo_init_me)
-        except Exception, e:
+        except Exception as e:
             return "Operation failed: %s" % str(e)

+ 2 - 3
tclCommands/TclCommandDelete.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandDelete(TclCommand.TclCommand):
+class TclCommandDelete(TclCommand):
     """
     Tcl shell command to delete an object.
 

+ 2 - 3
tclCommands/TclCommandDrillcncjob.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandDrillcncjob(TclCommand.TclCommandSignaled):
+class TclCommandDrillcncjob(TclCommandSignaled):
     """
     Tcl shell command to Generates a Drill CNC Job from a Excellon Object.
     """

+ 2 - 3
tclCommands/TclCommandExportGcode.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandExportGcode(TclCommand.TclCommandSignaled):
+class TclCommandExportGcode(TclCommandSignaled):
     """
     Tcl shell command to export gcode as tcl output for "set X [export_gcode ...]"
 

+ 2 - 3
tclCommands/TclCommandExportSVG.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandExportSVG(TclCommand.TclCommand):
+class TclCommandExportSVG(TclCommand):
     """
     Tcl shell command to export a Geometry Object as an SVG File.
 

+ 3 - 4
tclCommands/TclCommandExteriors.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandExteriors(TclCommand.TclCommandSignaled):
+class TclCommandExteriors(TclCommandSignaled):
     """
     Tcl shell command to get exteriors of polygons
     """
@@ -54,7 +53,7 @@ class TclCommandExteriors(TclCommand.TclCommandSignaled):
         if obj is None:
             self.raise_tcl_error("Object not found: %s" % name)
 
-        if not isinstance(obj, Geometry):
+        if not isinstance(obj, FlatCAMGeometry):
             self.raise_tcl_error('Expected Geometry, got %s %s.' % (name, type(obj)))
 
         def geo_init(geo_obj, app_obj):

+ 2 - 3
tclCommands/TclCommandGeoCutout.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandGeoCutout(TclCommand.TclCommandSignaled):
+class TclCommandGeoCutout(TclCommandSignaled):
     """
     Tcl shell command to cut holding gaps from geometry.
     """

+ 2 - 3
tclCommands/TclCommandGeoUnion.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandGeoUnion(TclCommand.TclCommand):
+class TclCommandGeoUnion(TclCommand):
     """
     Tcl shell command to run a union (addition) operation on the
     components of a geometry object.

+ 2 - 3
tclCommands/TclCommandGetNames.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandGetNames(TclCommand.TclCommand):
+class TclCommandGetNames(TclCommand):
     """
     Tcl shell command to set an object as active in the GUI.
 

+ 2 - 3
tclCommands/TclCommandGetSys.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandGetSys(TclCommand.TclCommand):
+class TclCommandGetSys(TclCommand):
     """
     Tcl shell command to get the value of a system variable
 

+ 2 - 3
tclCommands/TclCommandImportSvg.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandImportSvg(TclCommand.TclCommandSignaled):
+class TclCommandImportSvg(TclCommandSignaled):
     """
     Tcl shell command to import an SVG file as a Geometry Object.
     """

+ 2 - 3
tclCommands/TclCommandInteriors.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandInteriors(TclCommand.TclCommandSignaled):
+class TclCommandInteriors(TclCommandSignaled):
     """
     Tcl shell command to get interiors of polygons
     """

+ 2 - 3
tclCommands/TclCommandIsolate.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandIsolate(TclCommand.TclCommandSignaled):
+class TclCommandIsolate(TclCommandSignaled):
     """
     Tcl shell command to Creates isolation routing geometry for the given Gerber.
 

+ 2 - 3
tclCommands/TclCommandJoinExcellon.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandJoinExcellon(TclCommand.TclCommand):
+class TclCommandJoinExcellon(TclCommand):
     """
     Tcl shell command to merge Excellon objects.
 

+ 2 - 3
tclCommands/TclCommandJoinGeometry.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandJoinGeometry(TclCommand.TclCommand):
+class TclCommandJoinGeometry(TclCommand):
     """
     Tcl shell command to merge Excellon objects.
 

+ 2 - 3
tclCommands/TclCommandMillHoles.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandMillHoles(TclCommand.TclCommandSignaled):
+class TclCommandMillHoles(TclCommandSignaled):
     """
     Tcl shell command to Create Geometry Object for milling holes from Excellon.
 

+ 4 - 5
tclCommands/TclCommandMirror.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandMirror(TclCommand.TclCommandSignaled):
+class TclCommandMirror(TclCommandSignaled):
     """
     Tcl shell command to mirror an object.
     """
@@ -90,7 +89,7 @@ class TclCommandMirror(TclCommand.TclCommandSignaled):
                 obj.mirror(axis, [px, py])
                 obj.plot()
 
-            except Exception, e:
+            except Exception as e:
                 return "Operation failed: %s" % str(e)
 
         else:
@@ -104,5 +103,5 @@ class TclCommandMirror(TclCommand.TclCommandSignaled):
             try:
                 obj.mirror(axis, [dist, dist])
                 obj.plot()
-            except Exception, e:
+            except Exception as e:
                 return "Operation failed: %s" % str(e)

+ 2 - 3
tclCommands/TclCommandNew.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandNew(TclCommand.TclCommand):
+class TclCommandNew(TclCommand):
     """
     Tcl shell command to starts a new project. Clears objects from memory
     """

+ 2 - 3
tclCommands/TclCommandNewGeometry.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandNewGeometry(TclCommand.TclCommandSignaled):
+class TclCommandNewGeometry(TclCommandSignaled):
     """
     Tcl shell command to subtract polygon from the given Geometry object.
     """

+ 2 - 3
tclCommands/TclCommandOffset.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandOffset(TclCommand.TclCommand):
+class TclCommandOffset(TclCommand):
     """
     Tcl shell command to change the position of the object.
 

+ 2 - 3
tclCommands/TclCommandOpenExcellon.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandOpenExcellon(TclCommand.TclCommandSignaled):
+class TclCommandOpenExcellon(TclCommandSignaled):
     """
     Tcl shell command to open an Excellon file.
     """

+ 2 - 3
tclCommands/TclCommandOpenGCode.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandOpenGCode(TclCommand.TclCommandSignaled):
+class TclCommandOpenGCode(TclCommandSignaled):
     """
     Tcl shell command to open a G-Code file.
     """

+ 4 - 5
tclCommands/TclCommandOpenGerber.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandOpenGerber(TclCommand.TclCommandSignaled):
+class TclCommandOpenGerber(TclCommandSignaled):
     """
     Tcl shell command to opens a Gerber file
     """
@@ -48,7 +47,7 @@ class TclCommandOpenGerber(TclCommand.TclCommandSignaled):
         # How the object should be initialized
         def obj_init(gerber_obj, app_obj):
 
-            if not isinstance(gerber_obj, Geometry):
+            if not isinstance(gerber_obj, FlatCAMGerber):
                 self.raise_tcl_error('Expected FlatCAMGerber, got %s %s.' % (outname, type(gerber_obj)))
 
             # Opening the file happens here
@@ -61,7 +60,7 @@ class TclCommandOpenGerber(TclCommand.TclCommandSignaled):
                 app_obj.progress.emit(0)
                 self.raise_tcl_error('Failed to open file: %s' % filename)
 
-            except ParseError, e:
+            except ParseError as e:
                 app_obj.inform.emit("[error] Failed to parse file: %s, %s " % (filename, str(e)))
                 app_obj.progress.emit(0)
                 self.log.error(str(e))

+ 2 - 3
tclCommands/TclCommandOpenProject.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandOpenProject(TclCommand.TclCommandSignaled):
+class TclCommandOpenProject(TclCommandSignaled):
     """
     Tcl shell command to open a FlatCAM project.
     """

+ 2 - 3
tclCommands/TclCommandOptions.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandOptions(TclCommand.TclCommandSignaled):
+class TclCommandOptions(TclCommandSignaled):
     """
     Tcl shell command to open an Excellon file.
     """

+ 2 - 3
tclCommands/TclCommandPaint.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandPaint(TclCommand.TclCommandSignaled):
+class TclCommandPaint(TclCommandSignaled):
     """
     Paint the interior of polygons
     """

+ 5 - 8
tclCommands/TclCommandPanelize.py

@@ -1,10 +1,8 @@
-from ObjectCollection import *
 from copy import copy,deepcopy
+from tclCommands.TclCommand import *
 
-import TclCommand
 
-
-class TclCommandPanelize(TclCommand.TclCommand):
+class TclCommandPanelize(TclCommand):
     """
     Tcl shell command to pannelize an object.
 
@@ -13,7 +11,7 @@ class TclCommandPanelize(TclCommand.TclCommand):
     """
 
     # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon)
-    aliases = ['panelize']
+    aliases = ['panelize', 'pan', 'panel']
 
     # Dictionary of types from Tcl command, needs to be ordered
     arg_names = collections.OrderedDict([
@@ -130,10 +128,9 @@ class TclCommandPanelize(TclCommand.TclCommand):
                 for col in range(args['columns']):
                     local_outname = outname + ".tmp." + str(col) + "." + str(row)
                     if isinstance(obj, FlatCAMExcellon):
-                        self.app.new_object("excellon", local_outname, initialize_local_excellon)
+                        self.app.new_object("excellon", local_outname, initialize_local_excellon, plot=False)
                     else:
-                        self.app.new_object("geometry", local_outname, initialize_local)
-
+                        self.app.new_object("geometry", local_outname, initialize_local, plot=False)
                     currentx += lenghtx
                 currenty += lenghty
 

+ 2 - 3
tclCommands/TclCommandPlot.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandPlot(TclCommand.TclCommand):
+class TclCommandPlot(TclCommand):
     """
     Tcl shell command to update the plot on the user interface.
 

+ 2 - 3
tclCommands/TclCommandSaveProject.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandSaveProject(TclCommand.TclCommandSignaled):
+class TclCommandSaveProject(TclCommandSignaled):
     """
     Tcl shell command to save the FlatCAM project to file.
     """

+ 2 - 3
tclCommands/TclCommandScale.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandScale(TclCommand.TclCommand):
+class TclCommandScale(TclCommand):
     """
     Tcl shell command to resizes the object by a factor.
 

+ 2 - 3
tclCommands/TclCommandSetActive.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandSetActive(TclCommand.TclCommand):
+class TclCommandSetActive(TclCommand):
     """
     Tcl shell command to set an object as active in the GUI.
 

+ 2 - 3
tclCommands/TclCommandSetSys.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandSetSys(TclCommand.TclCommand):
+class TclCommandSetSys(TclCommand):
     """
     Tcl shell command to set the value of a system variable
 

+ 2 - 3
tclCommands/TclCommandSubtractPoly.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandSubtractPoly(TclCommand.TclCommandSignaled):
+class TclCommandSubtractPoly(TclCommandSignaled):
     """
     Tcl shell command to create a new empty Geometry object.
     """

+ 2 - 3
tclCommands/TclCommandSubtractRectangle.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandSubtractRectangle(TclCommand.TclCommandSignaled):
+class TclCommandSubtractRectangle(TclCommandSignaled):
     """
     Tcl shell command to subtract a rectange from the given Geometry object.
     """

+ 2 - 3
tclCommands/TclCommandVersion.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandVersion(TclCommand.TclCommand):
+class TclCommandVersion(TclCommand):
     """
     Tcl shell command to check the program version.
 

+ 2 - 3
tclCommands/TclCommandWriteGCode.py

@@ -1,8 +1,7 @@
-from ObjectCollection import *
-import TclCommand
+from tclCommands.TclCommand import *
 
 
-class TclCommandWriteGCode(TclCommand.TclCommandSignaled):
+class TclCommandWriteGCode(TclCommandSignaled):
     """
     Tcl shell command to save the G-code of a CNC Job object to file.
     """

+ 2 - 2
tclCommands/__init__.py

@@ -72,9 +72,9 @@ def register_all_commands(app, commands):
     :return: None
     """
 
-    tcl_modules = {k: v for k, v in sys.modules.items() if k.startswith('tclCommands.TclCommand')}
+    tcl_modules = {k: v for k, v in list(sys.modules.items()) if k.startswith('tclCommands.TclCommand')}
 
-    for key, mod in tcl_modules.items():
+    for key, mod in list(tcl_modules.items()):
         if key != 'tclCommands.TclCommand':
             class_name = key.split('.')[1]
             class_type = getattr(mod, class_name)

+ 2 - 2
termwidget.py

@@ -3,7 +3,7 @@ Terminal emulator widget.
 Shows intput and output text. Allows to enter commands. Supports history.
 """
 
-import cgi
+import html
 from PyQt4.QtCore import pyqtSignal, Qt
 from PyQt4.QtGui import QColor, QKeySequence, QLineEdit, QPalette, \
                         QSizePolicy, QTextCursor, QTextEdit, \
@@ -151,7 +151,7 @@ class TermWidget(QWidget):
         """
         assert style in ('in', 'out', 'err')
 
-        text = cgi.escape(text)
+        text = html.escape(text)
         text = text.replace('\n', '<br/>')
 
         if style == 'in':

+ 2 - 2
tests/canvas/performance.py

@@ -1,9 +1,9 @@
-from __future__ import division
+
 import matplotlib
 matplotlib.use('Agg')
 import matplotlib.pyplot as plt
 import numpy as np
-import cStringIO
+import io
 from matplotlib.backends.backend_agg import FigureCanvasAgg
 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
 from matplotlib.figure import Figure

+ 1 - 1
tests/other/destructor_test.py

@@ -8,7 +8,7 @@ class MyObj():
         pass
 
     def __del__(self):
-        print "##### Destroyed ######"
+        print("##### Destroyed ######")
 
 
 def parse():

+ 3 - 3
tests/other/test_fcrts.py

@@ -26,12 +26,12 @@ for geo in geoms:
 current_pt = (0, 0)
 pt, geo = s.nearest(current_pt)
 while geo is not None:
-    print pt, geo
-    print "OBJECTS BEFORE:", s.objects
+    print((pt, geo))
+    print(("OBJECTS BEFORE:", s.objects))
 
     #geo.coords = list(geo.coords[::-1])
     s.remove(geo)
 
-    print "OBJECTS AFTER:", s.objects
+    print(("OBJECTS AFTER:", s.objects))
     current_pt = geo.coords[-1]
     pt, geo = s.nearest(current_pt)

+ 3 - 3
tests/other/test_rt.py

@@ -13,12 +13,12 @@ rt = rtindex.Index(properties=p)
 
 # If interleaved is True, the coordinates must be in
 # the form [xmin, ymin, ..., kmin, xmax, ymax, ..., kmax].
-print rt.interleaved
+print((rt.interleaved))
 
 [rt.add(0, pt2rect(pt)) for pt in pts]
-print [r.bbox for r in list(rt.nearest((0, 0), 10, True))]
+print([r.bbox for r in list(rt.nearest((0, 0), 10, True))])
 
 for pt in pts:
     rt.delete(0, pt2rect(pt))
-    print pt2rect(pt), [r.bbox for r in list(rt.nearest((0, 0), 10, True))]
+    print((pt2rect(pt), [r.bbox for r in list(rt.nearest((0, 0), 10, True))]))
 

+ 9 - 9
tests/test_excellon_flow.py

@@ -36,18 +36,18 @@ class ExcellonFlowTestCase(unittest.TestCase):
     def test_flow(self):
         # Names of available objects.
         names = self.fc.collection.get_names()
-        print names
+        print(names)
 
         #--------------------------------------
         # Total of 1 objects.
         #--------------------------------------
-        self.assertEquals(len(names), 1,
+        self.assertEqual(len(names), 1,
                           "Expected 1 object, found %d" % len(names))
 
         #--------------------------------------
         # Object's name matches the file name.
         #--------------------------------------
-        self.assertEquals(names[0], self.filename,
+        self.assertEqual(names[0], self.filename,
                           "Expected name == %s, got %s" % (self.filename, names[0]))
 
         #---------------------------------------
@@ -65,14 +65,14 @@ class ExcellonFlowTestCase(unittest.TestCase):
         # TODO: Open GUI with double-click on object.
         # Opens the Object's GUI, populates it.
         excellon_obj.build_ui()
-        for option, value in excellon_obj.options.iteritems():
+        for option, value in list(excellon_obj.options.items()):
             try:
                 form_field = excellon_obj.form_fields[option]
             except KeyError:
-                print ("**********************************************************\n"
+                print(("**********************************************************\n"
                        "* WARNING: Option '{}' has no form field\n"
                        "**********************************************************"
-                       "".format(option))
+                       "".format(option)))
                 continue
             self.assertEqual(value, form_field.get_value(),
                              "Option '{}' == {} but form has {}".format(
@@ -87,7 +87,7 @@ class ExcellonFlowTestCase(unittest.TestCase):
         form_field = excellon_obj.form_fields['feedrate']
         value = form_field.get_value()
         form_field.set_value(value * 1.1)  # Increase by 10%
-        print "'feedrate' == {}".format(value)
+        print(("'feedrate' == {}".format(value)))
 
         #--------------------------------------------------
         # Create GCode using all tools.
@@ -119,7 +119,7 @@ class ExcellonFlowTestCase(unittest.TestCase):
         self.assertEqual(value, form_value,
                          "Form value for '{}' == {} was not read into options"
                          "which has {}".format('feedrate', form_value, value))
-        print "'feedrate' == {}".format(value)
+        print(("'feedrate' == {}".format(value)))
 
         #---------------------------------------------
         # Check that only 1 object has been created.
@@ -160,4 +160,4 @@ class ExcellonFlowTestCase(unittest.TestCase):
         self.assertTrue(os.path.isfile(output_filename))
         os.remove(output_filename)
 
-        print names
+        print(names)

+ 9 - 9
tests/test_gerber_flow.py

@@ -36,18 +36,18 @@ class GerberFlowTestCase(unittest.TestCase):
     def test_flow(self):
         # Names of available objects.
         names = self.fc.collection.get_names()
-        print names
+        print(names)
 
         #--------------------------------------
         # Total of 1 objects.
         #--------------------------------------
-        self.assertEquals(len(names), 1,
+        self.assertEqual(len(names), 1,
                           "Expected 1 object, found %d" % len(names))
 
         #--------------------------------------
         # Object's name matches the file name.
         #--------------------------------------
-        self.assertEquals(names[0], self.filename,
+        self.assertEqual(names[0], self.filename,
                           "Expected name == %s, got %s" % (self.filename, names[0]))
 
         #---------------------------------------
@@ -65,14 +65,14 @@ class GerberFlowTestCase(unittest.TestCase):
         # TODO: Open GUI with double-click on object.
         # Opens the Object's GUI, populates it.
         gerber_obj.build_ui()
-        for option, value in gerber_obj.options.iteritems():
+        for option, value in list(gerber_obj.options.items()):
             try:
                 form_field = gerber_obj.form_fields[option]
             except KeyError:
-                print ("**********************************************************\n"
+                print(("**********************************************************\n"
                        "* WARNING: Option '{}' has no form field\n"
                        "**********************************************************"
-                       "".format(option))
+                       "".format(option)))
                 continue
             self.assertEqual(value, form_field.get_value(),
                              "Option '{}' == {} but form has {}".format(
@@ -87,7 +87,7 @@ class GerberFlowTestCase(unittest.TestCase):
         form_field = gerber_obj.form_fields['isotooldia']
         value = form_field.get_value()
         form_field.set_value(value * 1.1)  # Increase by 10%
-        print "'isotooldia' == {}".format(value)
+        print(("'isotooldia' == {}".format(value)))
 
         #--------------------------------------------------
         # Create isolation routing using default values
@@ -110,7 +110,7 @@ class GerberFlowTestCase(unittest.TestCase):
         self.assertEqual(value, form_value,
                          "Form value for '{}' == {} was not read into options"
                          "which has {}".format('isotooldia', form_value, value))
-        print "'isotooldia' == {}".format(value)
+        print(("'isotooldia' == {}".format(value)))
 
         #---------------------------------------------
         # Check that only 1 object has been created.
@@ -187,4 +187,4 @@ class GerberFlowTestCase(unittest.TestCase):
         self.assertTrue(os.path.isfile(output_filename))
         os.remove(output_filename)
 
-        print names
+        print(names)

+ 21 - 21
tests/test_paint.py

@@ -75,66 +75,66 @@ class PaintConnectTest(PaintTestCase):
         self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
 
     def test_jump(self):
-        print "Test: WALK Expected"
+        print("Test: WALK Expected")
         paths = [
             LineString([[0.5, 2], [2, 4.5]]),
             LineString([[2, 0.5], [4.5, 2]])
         ]
         for p in paths:
-            print p
+            print(p)
 
         tooldia = 1.0
 
-        print "--"
+        print("--")
         result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
 
         result = list(result.get_objects())
         for r in result:
-            print r
+            print(r)
 
         self.assertEqual(len(result), 1)
 
         # self.plot_summary_A(paths, tooldia, result, "WALK expected.")
 
     def test_no_jump1(self):
-        print "Test: FLY Expected"
+        print("Test: FLY Expected")
         paths = [
             LineString([[0, 2], [2, 5]]),
             LineString([[2, 0], [5, 2]])
         ]
         for p in paths:
-            print p
+            print(p)
 
         tooldia = 1.0
 
-        print "--"
+        print("--")
         result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
 
         result = list(result.get_objects())
         for r in result:
-            print r
+            print(r)
 
         self.assertEqual(len(result), len(paths))
 
         # self.plot_summary_A(paths, tooldia, result, "FLY Expected")
 
     def test_no_jump2(self):
-        print "Test: FLY Expected"
+        print("Test: FLY Expected")
         paths = [
             LineString([[0.5, 2], [2, 4.5]]),
             LineString([[2, 0.5], [4.5, 2]])
         ]
         for p in paths:
-            print p
+            print(p)
 
         tooldia = 1.1
 
-        print "--"
+        print("--")
         result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
 
         result = list(result.get_objects())
         for r in result:
-            print r
+            print(r)
 
         self.assertEqual(len(result), len(paths))
 
@@ -153,22 +153,22 @@ class PaintConnectTest2(PaintTestCase):
         )
 
     def test_no_jump3(self):
-        print "TEST: No jump expected"
+        print("TEST: No jump expected")
         paths = [
             LineString([[0.5, 1], [1.5, 3]]),
             LineString([[4, 1], [4, 4]])
         ]
         for p in paths:
-            print p
+            print(p)
 
         tooldia = 1.0
 
-        print "--"
+        print("--")
         result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
 
         result = list(result.get_objects())
         for r in result:
-            print r
+            print(r)
 
         self.assertEqual(len(result), len(paths))
 
@@ -182,26 +182,26 @@ class PaintConnectTest3(PaintTestCase):
 
     def setUp(self):
         self.boundary = Polygon([[0, 0], [0, 5], [5, 5], [5, 0]])
-        print "TEST w/ LinearRings"
+        print("TEST w/ LinearRings")
 
     def test_jump2(self):
-        print "Test: WALK Expected"
+        print("Test: WALK Expected")
         paths = [
             LineString([[0.5, 2], [2, 4.5]]),
             LineString([[2, 0.5], [4.5, 2]]),
             self.boundary.buffer(-0.5).exterior
         ]
         for p in paths:
-            print p
+            print(p)
 
         tooldia = 1.0
 
-        print "--"
+        print("--")
         result = Geometry.paint_connect(mkstorage(deepcopy(paths)), self.boundary, tooldia)
 
         result = list(result.get_objects())
         for r in result:
-            print r
+            print(r)
 
         self.assertEqual(len(result), 1)
 

+ 3 - 3
tests/test_pathconnect.py

@@ -20,7 +20,7 @@ def mkstorage(paths):
 class PathConnectTest1(unittest.TestCase):
 
     def setUp(self):
-        print "PathConnectTest1.setUp()"
+        print("PathConnectTest1.setUp()")
         pass
 
     def test_simple_connect(self):
@@ -68,8 +68,8 @@ class PathConnectTest1(unittest.TestCase):
                                                          [2 + offset_x, 1 + offset_y]])))
 
     def test_ring_interfere_connect(self):
-        print
-        print "TEST STARTING ..."
+        print()
+        print("TEST STARTING ...")
 
         paths = [
             LineString([[0, 0], [1, 1]]),

+ 12 - 12
tests/test_polygon_paint.py

@@ -30,9 +30,9 @@ class PolyPaintTestCase(unittest.TestCase):
 
     def test_poly_paint_svg_all(self):
 
-        print "*********************************"
-        print "*         svg_all               *"
-        print "*********************************"
+        print("*********************************")
+        print("*         svg_all               *")
+        print("*********************************")
 
         # Clear workspace
         self.fc.on_file_new()
@@ -69,9 +69,9 @@ class PolyPaintTestCase(unittest.TestCase):
 
     def test_poly_paint_svg_click(self):
 
-        print "*********************************"
-        print "*         svg_click             *"
-        print "*********************************"
+        print("*********************************")
+        print("*         svg_click             *")
+        print("*********************************")
 
         # Clear workspace
         self.fc.on_file_new()
@@ -109,9 +109,9 @@ class PolyPaintTestCase(unittest.TestCase):
 
     def test_poly_paint_noncopper_all(self):
 
-        print "*********************************"
-        print "*         noncopper_all         *"
-        print "*********************************"
+        print("*********************************")
+        print("*         noncopper_all         *")
+        print("*********************************")
 
         # Clear workspace
         self.fc.on_file_new()
@@ -165,9 +165,9 @@ class PolyPaintTestCase(unittest.TestCase):
 
     def test_poly_paint_noncopper_click(self):
 
-        print "*********************************"
-        print "*         noncopper_click       *"
-        print "*********************************"
+        print("*********************************")
+        print("*         noncopper_click       *")
+        print("*********************************")
 
         # Clear workspace
         self.fc.on_file_new()

+ 7 - 7
tests/test_svg_flow.py

@@ -29,18 +29,18 @@ class SVGFlowTestCase(unittest.TestCase):
         self.fc.import_svg('tests/svg/' + self.filename)
 
         names = self.fc.collection.get_names()
-        print names
+        print(names)
 
         #--------------------------------------
         # Total of 1 objects.
         #--------------------------------------
-        self.assertEquals(len(names), 1,
+        self.assertEqual(len(names), 1,
                           "Expected 1 object, found %d" % len(names))
 
         #--------------------------------------
         # Object's name matches the file name.
         #--------------------------------------
-        self.assertEquals(names[0], self.filename,
+        self.assertEqual(names[0], self.filename,
                           "Expected name == %s, got %s" % (self.filename, names[0]))
 
         #---------------------------------------
@@ -58,14 +58,14 @@ class SVGFlowTestCase(unittest.TestCase):
         # TODO: Open GUI with double-click on object.
         # Opens the Object's GUI, populates it.
         geo_obj.build_ui()
-        for option, value in geo_obj.options.iteritems():
+        for option, value in list(geo_obj.options.items()):
             try:
                 form_field = geo_obj.form_fields[option]
             except KeyError:
-                print ("**********************************************************\n"
+                print(("**********************************************************\n"
                        "* WARNING: Option '{}' has no form field\n"
                        "**********************************************************"
-                       "".format(option))
+                       "".format(option)))
                 continue
             self.assertEqual(value, form_field.get_value(),
                              "Option '{}' == {} but form has {}".format(
@@ -126,4 +126,4 @@ class SVGFlowTestCase(unittest.TestCase):
         self.assertTrue(os.path.isfile(output_filename))
         os.remove(output_filename)
 
-        print names
+        print(names)

+ 14 - 14
tests/test_tclCommands/__init__.py

@@ -2,17 +2,17 @@ import pkgutil
 import sys
 
 # allowed command tests (please append them alphabetically ordered)
-from test_TclCommandAddPolygon import *
-from test_TclCommandAddPolyline import *
-from test_TclCommandCncjob import *
-from test_TclCommandDrillcncjob import *
-from test_TclCommandExportGcode import *
-from test_TclCommandExteriors import *
-from test_TclCommandImportSvg import *
-from test_TclCommandInteriors import *
-from test_TclCommandIsolate import *
-from test_TclCommandNew import *
-from test_TclCommandNewGeometry  import *
-from test_TclCommandOpenExcellon import *
-from test_TclCommandOpenGerber import *
-from test_TclCommandPaintPolygon import *
+from .test_TclCommandAddPolygon import *
+from .test_TclCommandAddPolyline import *
+from .test_TclCommandCncjob import *
+from .test_TclCommandDrillcncjob import *
+from .test_TclCommandExportGcode import *
+from .test_TclCommandExteriors import *
+from .test_TclCommandImportSvg import *
+from .test_TclCommandInteriors import *
+from .test_TclCommandIsolate import *
+from .test_TclCommandNew import *
+from .test_TclCommandNewGeometry  import *
+from .test_TclCommandOpenExcellon import *
+from .test_TclCommandOpenGerber import *
+from .test_TclCommandPaintPolygon import *

+ 1 - 1
tests/test_tclCommands/test_TclCommandCncjob.py

@@ -1,5 +1,5 @@
 from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMObj
-from test_TclCommandIsolate import *
+from .test_TclCommandIsolate import *
 
 def test_cncjob(self):
     """

+ 1 - 1
tests/test_tclCommands/test_TclCommandDrillcncjob.py

@@ -1,5 +1,5 @@
 from FlatCAMObj import FlatCAMObj
-from test_TclCommandOpenExcellon import *
+from .test_TclCommandOpenExcellon import *
 
 
 def test_drillcncjob(self):

+ 2 - 2
tests/test_tclCommands/test_TclCommandExportGcode.py

@@ -1,8 +1,8 @@
 import os
 import tempfile
 
-from test_TclCommandCncjob import *
-from test_TclCommandDrillcncjob import *
+from .test_TclCommandCncjob import *
+from .test_TclCommandDrillcncjob import *
 
 
 def test_export_gcodecncjob(self):

+ 2 - 2
tests/test_tcl_shell.py

@@ -82,7 +82,7 @@ class TclShellTest(unittest.TestCase):
         # Units must be IN
         #----------------------------------------
         units = self.fc.exec_command_test('get_sys units')
-        self.assertEquals(units, "IN")
+        self.assertEqual(units, "IN")
 
         # MM
         self.fc.exec_command_test('set_sys units MM')
@@ -92,7 +92,7 @@ class TclShellTest(unittest.TestCase):
         # Units must be MM
         #----------------------------------------
         units = self.fc.exec_command_test('get_sys units')
-        self.assertEquals(units, "MM")
+        self.assertEqual(units, "MM")
 
     def test_gerber_flow(self):
         """