Juan Pablo Caram 9 лет назад
Родитель
Сommit
78a100fa96
8 измененных файлов с 155 добавлено и 31 удалено
  1. 45 16
      FlatCAMApp.py
  2. 5 2
      FlatCAMGUI.py
  3. 4 0
      FlatCAMObj.py
  4. 26 0
      FlatCAMVersion.py
  5. 1 1
      LICENSE
  6. 1 1
      README.md
  7. 0 0
      sandbox/prepare_release.py
  8. 73 11
      tests/test_tcl_shell.py

+ 45 - 16
FlatCAMApp.py

@@ -1,4 +1,5 @@
-import sys, traceback
+import sys
+import traceback
 import urllib
 import urllib
 import getopt
 import getopt
 import random
 import random
@@ -16,6 +17,7 @@ from contextlib import contextmanager
 ########################################
 ########################################
 ##      Imports part of FlatCAM       ##
 ##      Imports part of FlatCAM       ##
 ########################################
 ########################################
+import FlatCAMVersion
 from FlatCAMWorker import Worker
 from FlatCAMWorker import Worker
 from ObjectCollection import *
 from ObjectCollection import *
 from FlatCAMObj import *
 from FlatCAMObj import *
@@ -63,8 +65,10 @@ class App(QtCore.QObject):
     log.addHandler(handler)
     log.addHandler(handler)
 
 
     ## Version
     ## Version
-    version = 8.4
-    version_date = "2015/10"
+    version = 8.5
+    #version_date_str = "2016/7"
+    version_date = (0, 0, 0)
+    version_name = None
 
 
     ## URL for update checks and statistics
     ## URL for update checks and statistics
     version_url = "http://flatcam.org/version"
     version_url = "http://flatcam.org/version"
@@ -115,6 +119,13 @@ class App(QtCore.QObject):
     # in the worker task.
     # in the worker task.
     thread_exception = QtCore.pyqtSignal(object)
     thread_exception = QtCore.pyqtSignal(object)
 
 
+    @property
+    def version_date_str(self):
+        return "{:4d}/{:02d}".format(
+            self.version_date[0],
+            self.version_date[1]
+        )
+
     def __init__(self, user_defaults=True, post_gui=None):
     def __init__(self, user_defaults=True, post_gui=None):
         """
         """
         Starts the application.
         Starts the application.
@@ -123,12 +134,15 @@ class App(QtCore.QObject):
         :rtype: App
         :rtype: App
         """
         """
 
 
+        FlatCAMVersion.setup(self)
+
         App.log.info("FlatCAM Starting...")
         App.log.info("FlatCAM Starting...")
 
 
         ###################
         ###################
         ### OS-specific ###
         ### OS-specific ###
         ###################
         ###################
 
 
+        # Folder for user settings.
         if sys.platform == 'win32':
         if sys.platform == 'win32':
             from win32com.shell import shell, shellcon
             from win32com.shell import shell, shellcon
             App.log.debug("Win32!")
             App.log.debug("Win32!")
@@ -168,8 +182,11 @@ class App(QtCore.QObject):
 
 
         # Application directory. Chdir to it. Otherwise, trying to load
         # Application directory. Chdir to it. Otherwise, trying to load
         # GUI icons will fail as thir path is relative.
         # GUI icons will fail as thir path is relative.
-        # This will fail under cx_freeze ...
-        self.app_home = os.path.dirname(os.path.realpath(__file__))
+        if hasattr(sys, "frozen"):
+            # For cx_freeze and sililar.
+            self.app_home = os.path.dirname(sys.executable)
+        else:
+            self.app_home = os.path.dirname(os.path.realpath(__file__))
         App.log.debug("Application path is " + self.app_home)
         App.log.debug("Application path is " + self.app_home)
         App.log.debug("Started in " + os.getcwd())
         App.log.debug("Started in " + os.getcwd())
         os.chdir(self.app_home)
         os.chdir(self.app_home)
@@ -180,7 +197,7 @@ class App(QtCore.QObject):
 
 
         QtCore.QObject.__init__(self)
         QtCore.QObject.__init__(self)
 
 
-        self.ui = FlatCAMGUI(self.version)
+        self.ui = FlatCAMGUI(self.version, name=self.version_name)
         self.connect(self.ui,
         self.connect(self.ui,
                      QtCore.SIGNAL("geomUpdate(int, int, int, int)"),
                      QtCore.SIGNAL("geomUpdate(int, int, int, int)"),
                      self.save_geometry)
                      self.save_geometry)
@@ -545,7 +562,11 @@ class App(QtCore.QObject):
         self.shell.setWindowIcon(self.ui.app_icon)
         self.shell.setWindowIcon(self.ui.app_icon)
         self.shell.setWindowTitle("FlatCAM Shell")
         self.shell.setWindowTitle("FlatCAM Shell")
         self.shell.resize(*self.defaults["shell_shape"])
         self.shell.resize(*self.defaults["shell_shape"])
-        self.shell.append_output("FlatCAM %s\n(c) 2014-2015 Juan Pablo Caram\n\n" % self.version)
+        self.shell.append_output("FlatCAM {}".format(self.version))
+        if self.version_name:
+            self.shell.append_output(" - {}".format(self.version_name))
+        self.shell.append_output("\n(c) 2014-{} Juan Pablo Caram\n\n".format(
+            self.version_date[0]))
         self.shell.append_output("Type help to get started.\n\n")
         self.shell.append_output("Type help to get started.\n\n")
 
 
         self.init_tcl()
         self.init_tcl()
@@ -579,7 +600,7 @@ class App(QtCore.QObject):
         App.log.debug("END of constructor. Releasing control.")
         App.log.debug("END of constructor. Releasing control.")
 
 
     def init_tcl(self):
     def init_tcl(self):
-        if hasattr(self,'tcl'):
+        if hasattr(self, 'tcl'):
             # self.tcl = None
             # self.tcl = None
             # TODO  we need  to clean  non default variables and procedures here
             # TODO  we need  to clean  non default variables and procedures here
             # new object cannot be used here as it  will not remember values created for next passes,
             # new object cannot be used here as it  will not remember values created for next passes,
@@ -789,28 +810,31 @@ class App(QtCore.QObject):
 
 
     def exec_command_test(self, text, reraise=True):
     def exec_command_test(self, text, reraise=True):
         """
         """
+        Same as exec_command(...) with additional control over  exceptions.
         Handles input from the shell. See FlatCAMApp.setup_shell for shell commands.
         Handles input from the shell. See FlatCAMApp.setup_shell for shell commands.
 
 
         :param text: Input command
         :param text: Input command
-        :param reraise: raise exception and not hide it, used mainly in unittests
-        :return: output if there was any
+        :param reraise: Re-raise TclError exceptions in Python (mostly for unitttests).
+        :return: Output from the command
         """
         """
 
 
         text = str(text)
         text = str(text)
 
 
         try:
         try:
-            self.shell.open_proccessing()
+            self.shell.open_proccessing()  # Disables input box.
             result = self.tcl.eval(str(text))
             result = self.tcl.eval(str(text))
             if result != 'None':
             if result != 'None':
                 self.shell.append_output(result + '\n')
                 self.shell.append_output(result + '\n')
+
         except Tkinter.TclError, e:
         except Tkinter.TclError, e:
-            #this will display more precise answer if something in  TCL shell fail
+            # This will display more precise answer if something in TCL shell fails
             result = self.tcl.eval("set errorInfo")
             result = self.tcl.eval("set errorInfo")
             self.log.error("Exec command Exception: %s" % (result + '\n'))
             self.log.error("Exec command Exception: %s" % (result + '\n'))
             self.shell.append_error('ERROR: ' + result + '\n')
             self.shell.append_error('ERROR: ' + result + '\n')
-            #show error in console and just return or in test raise exception
+            # Show error in console and just return or in test raise exception
             if reraise:
             if reraise:
                 raise e
                 raise e
+
         finally:
         finally:
             self.shell.close_proccessing()
             self.shell.close_proccessing()
             pass
             pass
@@ -1056,7 +1080,8 @@ class App(QtCore.QObject):
         self.report_usage("on_about")
         self.report_usage("on_about")
 
 
         version = self.version
         version = self.version
-        version_date = self.version_date
+        version_date_str = self.version_date_str
+        version_year = self.version_date[0]
 
 
         class AboutDialog(QtGui.QDialog):
         class AboutDialog(QtGui.QDialog):
             def __init__(self, parent=None):
             def __init__(self, parent=None):
@@ -1078,12 +1103,16 @@ class App(QtCore.QObject):
 
 
                 title = QtGui.QLabel(
                 title = QtGui.QLabel(
                     "<font size=8><B>FlatCAM</B></font><BR>"
                     "<font size=8><B>FlatCAM</B></font><BR>"
-                    "Version %s (%s)<BR>"
+                    "Version {} ({})<BR>"
                     "<BR>"
                     "<BR>"
                     "2D Computer-Aided Printed Circuit Board<BR>"
                     "2D Computer-Aided Printed Circuit Board<BR>"
                     "Manufacturing.<BR>"
                     "Manufacturing.<BR>"
                     "<BR>"
                     "<BR>"
-                    "(c) 2014-2015 Juan Pablo Caram" % (version, version_date)
+                    "(c) 2014-{} Juan Pablo Caram".format(
+                        version,
+                        version_date_str,
+                        version_year
+                    )
                 )
                 )
                 layout2.addWidget(title, stretch=1)
                 layout2.addWidget(title, stretch=1)
 
 

+ 5 - 2
FlatCAMGUI.py

@@ -7,7 +7,7 @@ class FlatCAMGUI(QtGui.QMainWindow):
     # Emitted when persistent window geometry needs to be retained
     # Emitted when persistent window geometry needs to be retained
     geom_update = QtCore.pyqtSignal(int, int, int, int, name='geomUpdate')
     geom_update = QtCore.pyqtSignal(int, int, int, int, name='geomUpdate')
 
 
-    def __init__(self, version):
+    def __init__(self, version, name=None):
         super(FlatCAMGUI, self).__init__()
         super(FlatCAMGUI, self).__init__()
 
 
         # Divine icon pack by Ipapun @ finicons.com
         # Divine icon pack by Ipapun @ finicons.com
@@ -248,7 +248,10 @@ class FlatCAMGUI(QtGui.QMainWindow):
         self.setWindowIcon(self.app_icon)
         self.setWindowIcon(self.app_icon)
 
 
         self.setGeometry(100, 100, 1024, 650)
         self.setGeometry(100, 100, 1024, 650)
-        self.setWindowTitle('FlatCAM %s - Development Version' % version)
+        title = 'FlatCAM {}'.format(version)
+        if name is not None:
+            title += ' - {}'.format(name)
+        self.setWindowTitle(title)
         self.show()
         self.show()
 
 
     def closeEvent(self, event):
     def closeEvent(self, event):

+ 4 - 0
FlatCAMObj.py

@@ -25,6 +25,7 @@ class FlatCAMObj(QtCore.QObject):
 
 
     def __init__(self, name):
     def __init__(self, name):
         """
         """
+        Constructor.
 
 
         :param name: Name of the object given by the user.
         :param name: Name of the object given by the user.
         :return: FlatCAMObj
         :return: FlatCAMObj
@@ -57,6 +58,9 @@ class FlatCAMObj(QtCore.QObject):
         ``self.options`` is only updated, not overwritten. This ensures that
         ``self.options`` is only updated, not overwritten. This ensures that
         options set by the app do not vanish when reading the objects
         options set by the app do not vanish when reading the objects
         from a project file.
         from a project file.
+
+        :param d: Dictionary with attributes to set.
+        :return: None
         """
         """
 
 
         for attr in self.ser_attrs:
         for attr in self.ser_attrs:

+ 26 - 0
FlatCAMVersion.py

@@ -0,0 +1,26 @@
+#################################################
+#  FlatCAM - Version settings                   #
+#################################################
+
+import logging
+
+version = {
+    "number": 8.5,
+    "date": (2016, 7, 1),  # Year, Month, Day
+    "name": None,
+    "release": True,
+}
+
+
+def setup(app):
+    app.version = version["number"]
+    app.version_date = version["date"]
+    if version["release"]:
+        app.log.setLevel(logging.WARNING)
+    else:
+        app.log.setLevel(logging.DEBUG)
+
+    if version["name"] is None and version["release"] == False:
+        app.version_name = "Development Version"
+    else:
+        app.version_name = version["name"]

+ 1 - 1
LICENSE

@@ -1,6 +1,6 @@
 The MIT License (MIT)
 The MIT License (MIT)
 
 
-Copyright (c) 2014 Juan Pablo Caram
+Copyright (c) 2014-2016 Juan Pablo Caram
 
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 
 

+ 1 - 1
README.md

@@ -5,4 +5,4 @@ FlatCAM: 2D Computer-Aided PCB Manufacturing
 
 
 FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router.
 FlatCAM is a program for preparing CNC jobs for making PCBs on a CNC router.
 Among other things, it can take a Gerber file generated by your favorite PCB
 Among other things, it can take a Gerber file generated by your favorite PCB
-CAD program, and create G-Code for Isolation routing.
+CAD program, and create G-Code for Isolation routing.

+ 0 - 0
sandbox/prepare_release.py


+ 73 - 11
tests/test_tcl_shell.py

@@ -43,6 +43,7 @@ class TclShellTest(unittest.TestCase):
 
 
         cls.setup = True
         cls.setup = True
         cls.app = QtGui.QApplication(sys.argv)
         cls.app = QtGui.QApplication(sys.argv)
+
         # Create App, keep app defaults (do not load
         # Create App, keep app defaults (do not load
         # user-defined defaults).
         # user-defined defaults).
         cls.fc = App(user_defaults=False)
         cls.fc = App(user_defaults=False)
@@ -54,6 +55,7 @@ class TclShellTest(unittest.TestCase):
 
 
     @classmethod
     @classmethod
     def tearDownClass(cls):
     def tearDownClass(cls):
+
         cls.fc.tcl = None
         cls.fc.tcl = None
         cls.app.closeAllWindows()
         cls.app.closeAllWindows()
         del cls.fc
         del cls.fc
@@ -61,46 +63,106 @@ class TclShellTest(unittest.TestCase):
         pass
         pass
 
 
     def test_set_get_units(self):
     def test_set_get_units(self):
+        """
+        Tests setting and getting units via the ``set_sys`` command,
+        and persistance after ``new`` command.
+
+        :return: None
+        """
 
 
+        # MM
         self.fc.exec_command_test('set_sys units MM')
         self.fc.exec_command_test('set_sys units MM')
         self.fc.exec_command_test('new')
         self.fc.exec_command_test('new')
 
 
+        # IN
         self.fc.exec_command_test('set_sys units IN')
         self.fc.exec_command_test('set_sys units IN')
         self.fc.exec_command_test('new')
         self.fc.exec_command_test('new')
-        units=self.fc.exec_command_test('get_sys units')
+
+        #----------------------------------------
+        # Units must be IN
+        #----------------------------------------
+        units = self.fc.exec_command_test('get_sys units')
         self.assertEquals(units, "IN")
         self.assertEquals(units, "IN")
 
 
+        # MM
         self.fc.exec_command_test('set_sys units MM')
         self.fc.exec_command_test('set_sys units MM')
         self.fc.exec_command_test('new')
         self.fc.exec_command_test('new')
-        units=self.fc.exec_command_test('get_sys units')
+
+        #----------------------------------------
+        # Units must be MM
+        #----------------------------------------
+        units = self.fc.exec_command_test('get_sys units')
         self.assertEquals(units, "MM")
         self.assertEquals(units, "MM")
 
 
     def test_gerber_flow(self):
     def test_gerber_flow(self):
-
-        # open  gerber files top, bottom and cutout
-
-        self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_top_filename, self.gerber_top_name))
+        """
+        Typical workflow from Gerber to GCode.
+
+        :return: None
+        """
+
+        gbr_cmd = 'open_gerber {path}/{filename} -outname {outname}'
+
+        #-----------------------------------------
+        # Open top layer and check for object type
+        #-----------------------------------------
+        cmd = gbr_cmd.format(
+            path=self.gerber_files,
+            filename=self.copper_top_filename,
+            outname=self.gerber_top_name)
+        self.fc.exec_command_test(cmd)
         gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name)
         gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name)
         self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber),
         self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber),
                         "Expected FlatCAMGerber, instead, %s is %s" %
                         "Expected FlatCAMGerber, instead, %s is %s" %
                         (self.gerber_top_name, type(gerber_top_obj)))
                         (self.gerber_top_name, type(gerber_top_obj)))
 
 
-        self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_bottom_filename, self.gerber_bottom_name))
+        #--------------------------------------------
+        # Open bottom layer and check for object type
+        #--------------------------------------------
+        cmd = gbr_cmd.format(
+            path=self.gerber_files,
+            filename=self.copper_bottom_filename,
+            outname=self.gerber_bottom_name)
+        self.fc.exec_command_test(cmd)
         gerber_bottom_obj = self.fc.collection.get_by_name(self.gerber_bottom_name)
         gerber_bottom_obj = self.fc.collection.get_by_name(self.gerber_bottom_name)
         self.assertTrue(isinstance(gerber_bottom_obj, FlatCAMGerber),
         self.assertTrue(isinstance(gerber_bottom_obj, FlatCAMGerber),
                         "Expected FlatCAMGerber, instead, %s is %s" %
                         "Expected FlatCAMGerber, instead, %s is %s" %
                         (self.gerber_bottom_name, type(gerber_bottom_obj)))
                         (self.gerber_bottom_name, type(gerber_bottom_obj)))
 
 
-        self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.cutout_filename, self.gerber_cutout_name))
+        #--------------------------------------------
+        # Open cutout layer and check for object type
+        #--------------------------------------------
+        cmd = gbr_cmd.format(
+            path=self.gerber_files,
+            filename=self.cutout_filename,
+            outname=self.gerber_cutout_name
+        )
+        self.fc.exec_command_test(cmd)
         gerber_cutout_obj = self.fc.collection.get_by_name(self.gerber_cutout_name)
         gerber_cutout_obj = self.fc.collection.get_by_name(self.gerber_cutout_name)
         self.assertTrue(isinstance(gerber_cutout_obj, FlatCAMGerber),
         self.assertTrue(isinstance(gerber_cutout_obj, FlatCAMGerber),
                         "Expected FlatCAMGerber, instead, %s is %s" %
                         "Expected FlatCAMGerber, instead, %s is %s" %
                         (self.gerber_cutout_name, type(gerber_cutout_obj)))
                         (self.gerber_cutout_name, type(gerber_cutout_obj)))
 
 
         # exteriors delete and join geometries for top layer
         # exteriors delete and join geometries for top layer
-        self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_cutout_name, self.engraver_diameter))
-        self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_iso', self.gerber_cutout_name + '_iso_exterior'))
-        self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_iso'))
+        cmd = 'isolate {objname} -dia {dia}'.format(
+            objname=self.gerber_cutout_name,
+            dia=self.engraver_diameter)
+        self.fc.exec_command_test(cmd)
+
+        cmd = 'exteriors {objname} -outname {outname}'.format(
+            objname=self.gerber_cutout_name + '_iso',
+            outname=self.gerber_cutout_name + '_iso_exterior')
+        self.fc.exec_command_test(cmd)
+
+        cmd = 'delete {objname}'.format(
+            objname=self.gerber_cutout_name + '_iso')
+        self.fc.exec_command_test(cmd)
+
+        # TODO: Check deleteb object is gone.
+
+        #--------------------------------------------
+        # Exteriors of cutout layer, check type
+        #--------------------------------------------
         obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior')
         obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior')
         self.assertTrue(isinstance(obj, FlatCAMGeometry),
         self.assertTrue(isinstance(obj, FlatCAMGeometry),
                         "Expected FlatCAMGeometry, instead, %s is %s" %
                         "Expected FlatCAMGeometry, instead, %s is %s" %