Merge branch '8p5'

This commit is contained in:
Juan Pablo Caram 2016-07-23 15:09:37 -04:00
commit 78a100fa96
8 changed files with 153 additions and 29 deletions

View File

@ -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 *
@ -63,8 +65,10 @@ class App(QtCore.QObject):
log.addHandler(handler)
## 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
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,12 +134,15 @@ class App(QtCore.QObject):
:rtype: App
"""
FlatCAMVersion.setup(self)
App.log.info("FlatCAM Starting...")
###################
### OS-specific ###
###################
# Folder for user settings.
if sys.platform == 'win32':
from win32com.shell import shell, shellcon
App.log.debug("Win32!")
@ -168,8 +182,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)
@ -180,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)
@ -545,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()
@ -579,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,
@ -789,28 +810,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
@ -1056,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):
@ -1078,12 +1103,16 @@ class App(QtCore.QObject):
title = QtGui.QLabel(
"<font size=8><B>FlatCAM</B></font><BR>"
"Version %s (%s)<BR>"
"Version {} ({})<BR>"
"<BR>"
"2D Computer-Aided Printed Circuit Board<BR>"
"Manufacturing.<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)

View File

@ -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):

View File

@ -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:

26
FlatCAMVersion.py Normal file
View File

@ -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"]

View File

@ -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:

View File

@ -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.
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.

View File

View File

@ -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" %