From a9b4ff767b9adf19c716c6a8c6e3e6ed0de93a43 Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Sun, 3 Jul 2016 15:59:25 -0400 Subject: [PATCH 1/5] Some cleanup to shell tests. --- FlatCAMApp.py | 13 ++++--- FlatCAMObj.py | 4 +++ tests/test_tcl_shell.py | 80 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 62757efe..f069424a 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -789,28 +789,31 @@ class App(QtCore.QObject): 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. :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) try: - self.shell.open_proccessing() + self.shell.open_proccessing() # Disables input box. result = self.tcl.eval(str(text)) if result != 'None': self.shell.append_output(result + '\n') + 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") self.log.error("Exec command Exception: %s" % (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: raise e + finally: self.shell.close_proccessing() pass diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 8860bcb9..0b1152d7 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -25,6 +25,7 @@ class FlatCAMObj(QtCore.QObject): def __init__(self, name): """ + Constructor. :param name: Name of the object given by the user. :return: FlatCAMObj @@ -57,6 +58,9 @@ class FlatCAMObj(QtCore.QObject): ``self.options`` is only updated, not overwritten. This ensures that options set by the app do not vanish when reading the objects from a project file. + + :param d: Dictionary with attributes to set. + :return: None """ for attr in self.ser_attrs: diff --git a/tests/test_tcl_shell.py b/tests/test_tcl_shell.py index 510a4911..59ac3340 100644 --- a/tests/test_tcl_shell.py +++ b/tests/test_tcl_shell.py @@ -43,6 +43,7 @@ class TclShellTest(unittest.TestCase): cls.setup = True cls.app = QtGui.QApplication(sys.argv) + # Create App, keep app defaults (do not load # user-defined defaults). cls.fc = App(user_defaults=False) @@ -54,6 +55,7 @@ class TclShellTest(unittest.TestCase): @classmethod def tearDownClass(cls): + cls.fc.tcl = None cls.app.closeAllWindows() del cls.fc @@ -61,46 +63,106 @@ class TclShellTest(unittest.TestCase): pass 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('new') + # IN self.fc.exec_command_test('set_sys units IN') 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") + # MM self.fc.exec_command_test('set_sys units MM') 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") def test_gerber_flow(self): + """ + Typical workflow from Gerber to GCode. - # open gerber files top, bottom and cutout + :return: None + """ - self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_top_filename, self.gerber_top_name)) + 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) self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber), "Expected FlatCAMGerber, instead, %s is %s" % (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) self.assertTrue(isinstance(gerber_bottom_obj, FlatCAMGerber), "Expected FlatCAMGerber, instead, %s is %s" % (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) self.assertTrue(isinstance(gerber_cutout_obj, FlatCAMGerber), "Expected FlatCAMGerber, instead, %s is %s" % (self.gerber_cutout_name, type(gerber_cutout_obj))) # 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') self.assertTrue(isinstance(obj, FlatCAMGeometry), "Expected FlatCAMGeometry, instead, %s is %s" % From f5c7fdab7150e0be48bcf8354ea3cb0749944530 Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Mon, 4 Jul 2016 16:25:59 -0400 Subject: [PATCH 2/5] Some cleanup to shell tests. --- FlatCAMApp.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index f069424a..c9e6daaf 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -63,8 +63,8 @@ class App(QtCore.QObject): log.addHandler(handler) ## Version - version = 8.4 - version_date = "2015/10" + version = 8.5 + version_date = "2016/7" ## URL for update checks and statistics version_url = "http://flatcam.org/version" @@ -129,6 +129,7 @@ class App(QtCore.QObject): ### OS-specific ### ################### + # Folder for user settings. if sys.platform == 'win32': from win32com.shell import shell, shellcon App.log.debug("Win32!") @@ -168,8 +169,11 @@ class App(QtCore.QObject): # Application directory. Chdir to it. Otherwise, trying to load # 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("Started in " + os.getcwd()) os.chdir(self.app_home) From 38d69570094deddcf467b9c058d115193da86f5b Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Fri, 15 Jul 2016 22:23:48 -0400 Subject: [PATCH 3/5] Added SVG-to-GCode flow test. --- FlatCAMApp.py | 38 ++++++++++++++++++++++++++++++-------- FlatCAMGUI.py | 7 +++++-- FlatCAMVersion.py | 22 ++++++++++++++++++++++ sandbox/prepare_release.py | 0 4 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 FlatCAMVersion.py create mode 100644 sandbox/prepare_release.py diff --git a/FlatCAMApp.py b/FlatCAMApp.py index c9e6daaf..3e93c6a8 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1,4 +1,5 @@ -import sys, traceback +import sys +import traceback import urllib import getopt import random @@ -16,6 +17,7 @@ from contextlib import contextmanager ######################################## ## Imports part of FlatCAM ## ######################################## +import FlatCAMVersion from FlatCAMWorker import Worker from ObjectCollection import * from FlatCAMObj import * @@ -64,7 +66,9 @@ class App(QtCore.QObject): ## Version version = 8.5 - version_date = "2016/7" + #version_date_str = "2016/7" + version_date = (0, 0, 0) + version_name = None ## URL for update checks and statistics version_url = "http://flatcam.org/version" @@ -115,6 +119,13 @@ class App(QtCore.QObject): # in the worker task. 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): """ Starts the application. @@ -123,6 +134,8 @@ class App(QtCore.QObject): :rtype: App """ + FlatCAMVersion.setup(self) + App.log.info("FlatCAM Starting...") ################### @@ -184,7 +197,7 @@ class App(QtCore.QObject): QtCore.QObject.__init__(self) - self.ui = FlatCAMGUI(self.version) + self.ui = FlatCAMGUI(self.version, name=self.version_name) self.connect(self.ui, QtCore.SIGNAL("geomUpdate(int, int, int, int)"), self.save_geometry) @@ -549,7 +562,11 @@ class App(QtCore.QObject): self.shell.setWindowIcon(self.ui.app_icon) self.shell.setWindowTitle("FlatCAM Shell") 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.init_tcl() @@ -583,7 +600,7 @@ class App(QtCore.QObject): App.log.debug("END of constructor. Releasing control.") def init_tcl(self): - if hasattr(self,'tcl'): + if hasattr(self, 'tcl'): # self.tcl = None # 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, @@ -1063,7 +1080,8 @@ class App(QtCore.QObject): self.report_usage("on_about") 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): def __init__(self, parent=None): @@ -1085,12 +1103,16 @@ class App(QtCore.QObject): title = QtGui.QLabel( "FlatCAM
" - "Version %s (%s)
" + "Version {} ({})
" "
" "2D Computer-Aided Printed Circuit Board
" "Manufacturing.
" "
" - "(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) diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 9f22faa5..908152e6 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -7,7 +7,7 @@ class FlatCAMGUI(QtGui.QMainWindow): # Emitted when persistent window geometry needs to be retained geom_update = QtCore.pyqtSignal(int, int, int, int, name='geomUpdate') - def __init__(self, version): + def __init__(self, version, name=None): super(FlatCAMGUI, self).__init__() # Divine icon pack by Ipapun @ finicons.com @@ -248,7 +248,10 @@ class FlatCAMGUI(QtGui.QMainWindow): self.setWindowIcon(self.app_icon) 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() def closeEvent(self, event): diff --git a/FlatCAMVersion.py b/FlatCAMVersion.py new file mode 100644 index 00000000..f5935cfc --- /dev/null +++ b/FlatCAMVersion.py @@ -0,0 +1,22 @@ +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"] diff --git a/sandbox/prepare_release.py b/sandbox/prepare_release.py new file mode 100644 index 00000000..e69de29b From 85188ce3cbff148921b35807385903d3ac792e81 Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Fri, 15 Jul 2016 22:26:19 -0400 Subject: [PATCH 4/5] Separate file for version settings. --- FlatCAMVersion.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FlatCAMVersion.py b/FlatCAMVersion.py index f5935cfc..07e00a7d 100644 --- a/FlatCAMVersion.py +++ b/FlatCAMVersion.py @@ -1,3 +1,7 @@ +################################################# +# FlatCAM - Version settings # +################################################# + import logging version = { From 468e50194c77697d9c7249713f4106b26f15665f Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Fri, 15 Jul 2016 23:11:50 -0400 Subject: [PATCH 5/5] Updated dates on license and readme. --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 60d2ed54..ba87cb83 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ 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: diff --git a/README.md b/README.md index 5b156d13..3be39a38 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ FlatCAM: 2D Computer-Aided PCB Manufacturing ============================================ -(c) 2014-2015 Juan Pablo Caram +(c) 2014-2016 Juan Pablo Caram 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