Merge branch '8p5'
This commit is contained in:
commit
78a100fa96
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"]
|
2
LICENSE
2
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:
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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" %
|
||||
|
|
Loading…
Reference in New Issue