2019-05-30 18:05:12 +00:00
|
|
|
# ########################################################## ##
|
2019-03-10 13:22:16 +00:00
|
|
|
# FlatCAM: 2D Post-processing for Manufacturing #
|
|
|
|
# http://flatcam.org #
|
|
|
|
# File Author: Marius Adrian Stanciu (c) #
|
|
|
|
# Date: 3/10/2019 #
|
|
|
|
# MIT Licence #
|
2019-05-30 18:05:12 +00:00
|
|
|
# ########################################################## ##
|
2019-03-10 13:22:16 +00:00
|
|
|
|
2019-01-03 19:25:08 +00:00
|
|
|
from FlatCAMTool import FlatCAMTool
|
|
|
|
|
2019-03-11 10:23:26 +00:00
|
|
|
from flatcamGUI.GUIElements import RadioSet, FCEntry
|
2019-01-03 19:25:08 +00:00
|
|
|
from PyQt5 import QtGui, QtCore, QtWidgets
|
|
|
|
|
2019-03-07 23:32:18 +00:00
|
|
|
import gettext
|
|
|
|
import FlatCAMTranslation as fcTranslate
|
2019-03-10 15:12:58 +00:00
|
|
|
|
2019-03-13 23:09:06 +00:00
|
|
|
fcTranslate.apply_language('strings')
|
2019-03-10 15:12:58 +00:00
|
|
|
import builtins
|
|
|
|
if '_' not in builtins.__dict__:
|
|
|
|
_ = gettext.gettext
|
2019-01-03 19:25:08 +00:00
|
|
|
|
2019-03-08 12:10:23 +00:00
|
|
|
|
2019-01-03 19:25:08 +00:00
|
|
|
class Film(FlatCAMTool):
|
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
toolName = _("Film PCB")
|
2019-01-03 19:25:08 +00:00
|
|
|
|
|
|
|
def __init__(self, app):
|
|
|
|
FlatCAMTool.__init__(self, app)
|
|
|
|
|
|
|
|
# Title
|
2019-02-18 01:45:34 +00:00
|
|
|
title_label = QtWidgets.QLabel("%s" % self.toolName)
|
|
|
|
title_label.setStyleSheet("""
|
|
|
|
QLabel
|
|
|
|
{
|
|
|
|
font-size: 16px;
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
|
|
|
""")
|
2019-01-03 19:25:08 +00:00
|
|
|
self.layout.addWidget(title_label)
|
|
|
|
|
|
|
|
# Form Layout
|
|
|
|
tf_form_layout = QtWidgets.QFormLayout()
|
|
|
|
self.layout.addLayout(tf_form_layout)
|
|
|
|
|
|
|
|
# Type of object for which to create the film
|
|
|
|
self.tf_type_obj_combo = QtWidgets.QComboBox()
|
|
|
|
self.tf_type_obj_combo.addItem("Gerber")
|
|
|
|
self.tf_type_obj_combo.addItem("Excellon")
|
|
|
|
self.tf_type_obj_combo.addItem("Geometry")
|
|
|
|
|
|
|
|
# we get rid of item1 ("Excellon") as it is not suitable for creating film
|
|
|
|
self.tf_type_obj_combo.view().setRowHidden(1, True)
|
|
|
|
self.tf_type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
|
|
|
|
self.tf_type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
|
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
self.tf_type_obj_combo_label = QtWidgets.QLabel(_("Object Type:"))
|
2019-01-03 19:25:08 +00:00
|
|
|
self.tf_type_obj_combo_label.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("Specify the type of object for which to create the film.\n"
|
2019-01-03 19:25:08 +00:00
|
|
|
"The object can be of type: Gerber or Geometry.\n"
|
|
|
|
"The selection here decide the type of objects that will be\n"
|
2019-03-07 15:37:38 +00:00
|
|
|
"in the Film Object combobox.")
|
2019-01-03 19:25:08 +00:00
|
|
|
)
|
|
|
|
tf_form_layout.addRow(self.tf_type_obj_combo_label, self.tf_type_obj_combo)
|
|
|
|
|
|
|
|
# List of objects for which we can create the film
|
|
|
|
self.tf_object_combo = QtWidgets.QComboBox()
|
|
|
|
self.tf_object_combo.setModel(self.app.collection)
|
|
|
|
self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
2019-02-04 18:49:37 +00:00
|
|
|
self.tf_object_combo.setCurrentIndex(1)
|
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
self.tf_object_label = QtWidgets.QLabel(_("Film Object:"))
|
2019-01-03 19:25:08 +00:00
|
|
|
self.tf_object_label.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("Object for which to create the film.")
|
2019-01-03 19:25:08 +00:00
|
|
|
)
|
|
|
|
tf_form_layout.addRow(self.tf_object_label, self.tf_object_combo)
|
|
|
|
|
|
|
|
# Type of Box Object to be used as an envelope for film creation
|
|
|
|
# Within this we can create negative
|
|
|
|
self.tf_type_box_combo = QtWidgets.QComboBox()
|
|
|
|
self.tf_type_box_combo.addItem("Gerber")
|
|
|
|
self.tf_type_box_combo.addItem("Excellon")
|
|
|
|
self.tf_type_box_combo.addItem("Geometry")
|
|
|
|
|
|
|
|
# we get rid of item1 ("Excellon") as it is not suitable for box when creating film
|
|
|
|
self.tf_type_box_combo.view().setRowHidden(1, True)
|
|
|
|
self.tf_type_box_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
|
|
|
|
self.tf_type_box_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
|
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
self.tf_type_box_combo_label = QtWidgets.QLabel(_("Box Type:"))
|
2019-01-03 19:25:08 +00:00
|
|
|
self.tf_type_box_combo_label.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("Specify the type of object to be used as an container for\n"
|
2019-01-03 19:25:08 +00:00
|
|
|
"film creation. It can be: Gerber or Geometry type."
|
|
|
|
"The selection here decide the type of objects that will be\n"
|
2019-03-07 15:37:38 +00:00
|
|
|
"in the Box Object combobox.")
|
2019-01-03 19:25:08 +00:00
|
|
|
)
|
|
|
|
tf_form_layout.addRow(self.tf_type_box_combo_label, self.tf_type_box_combo)
|
|
|
|
|
|
|
|
# Box
|
|
|
|
self.tf_box_combo = QtWidgets.QComboBox()
|
|
|
|
self.tf_box_combo.setModel(self.app.collection)
|
|
|
|
self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
2019-02-04 18:49:37 +00:00
|
|
|
self.tf_box_combo.setCurrentIndex(1)
|
2019-01-03 19:25:08 +00:00
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
self.tf_box_combo_label = QtWidgets.QLabel(_("Box Object:"))
|
2019-01-03 19:25:08 +00:00
|
|
|
self.tf_box_combo_label.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("The actual object that is used a container for the\n "
|
2019-01-03 19:25:08 +00:00
|
|
|
"selected object for which we create the film.\n"
|
|
|
|
"Usually it is the PCB outline but it can be also the\n"
|
|
|
|
"same object for which the film is created.")
|
2019-03-07 15:37:38 +00:00
|
|
|
)
|
2019-01-03 19:25:08 +00:00
|
|
|
tf_form_layout.addRow(self.tf_box_combo_label, self.tf_box_combo)
|
|
|
|
|
|
|
|
# Film Type
|
|
|
|
self.film_type = RadioSet([{'label': 'Positive', 'value': 'pos'},
|
|
|
|
{'label': 'Negative', 'value': 'neg'}])
|
2019-03-10 12:34:13 +00:00
|
|
|
self.film_type_label = QtWidgets.QLabel(_("Film Type:"))
|
2019-01-03 19:25:08 +00:00
|
|
|
self.film_type_label.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("Generate a Positive black film or a Negative film.\n"
|
2019-01-03 19:25:08 +00:00
|
|
|
"Positive means that it will print the features\n"
|
|
|
|
"with black on a white canvas.\n"
|
|
|
|
"Negative means that it will print the features\n"
|
|
|
|
"with white on a black canvas.\n"
|
2019-03-07 15:37:38 +00:00
|
|
|
"The Film format is SVG.")
|
2019-01-03 19:25:08 +00:00
|
|
|
)
|
|
|
|
tf_form_layout.addRow(self.film_type_label, self.film_type)
|
|
|
|
|
|
|
|
# Boundary for negative film generation
|
|
|
|
|
2019-02-02 21:56:08 +00:00
|
|
|
self.boundary_entry = FCEntry()
|
2019-03-10 12:34:13 +00:00
|
|
|
self.boundary_label = QtWidgets.QLabel(_("Border:"))
|
2019-01-03 19:25:08 +00:00
|
|
|
self.boundary_label.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("Specify a border around the object.\n"
|
2019-01-03 19:25:08 +00:00
|
|
|
"Only for negative film.\n"
|
|
|
|
"It helps if we use as a Box Object the same \n"
|
|
|
|
"object as in Film Object. It will create a thick\n"
|
|
|
|
"black bar around the actual print allowing for a\n"
|
|
|
|
"better delimitation of the outline features which are of\n"
|
|
|
|
"white color like the rest and which may confound with the\n"
|
2019-03-07 15:37:38 +00:00
|
|
|
"surroundings if not for this border.")
|
2019-01-03 19:25:08 +00:00
|
|
|
)
|
|
|
|
tf_form_layout.addRow(self.boundary_label, self.boundary_entry)
|
|
|
|
|
2019-02-04 18:49:37 +00:00
|
|
|
self.film_scale_entry = FCEntry()
|
2019-03-10 12:34:13 +00:00
|
|
|
self.film_scale_label = QtWidgets.QLabel(_("Scale Stroke:"))
|
2019-02-04 18:49:37 +00:00
|
|
|
self.film_scale_label.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("Scale the line stroke thickness of each feature in the SVG file.\n"
|
2019-02-04 18:49:37 +00:00
|
|
|
"It means that the line that envelope each SVG feature will be thicker or thinner,\n"
|
2019-03-07 15:37:38 +00:00
|
|
|
"therefore the fine features may be more affected by this parameter.")
|
2019-02-04 18:49:37 +00:00
|
|
|
)
|
|
|
|
tf_form_layout.addRow(self.film_scale_label, self.film_scale_entry)
|
|
|
|
|
2019-01-03 19:25:08 +00:00
|
|
|
# Buttons
|
|
|
|
hlay = QtWidgets.QHBoxLayout()
|
|
|
|
self.layout.addLayout(hlay)
|
|
|
|
hlay.addStretch()
|
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
self.film_object_button = QtWidgets.QPushButton(_("Save Film"))
|
2019-01-03 19:25:08 +00:00
|
|
|
self.film_object_button.setToolTip(
|
2019-03-10 12:34:13 +00:00
|
|
|
_("Create a Film for the selected object, within\n"
|
2019-01-03 19:25:08 +00:00
|
|
|
"the specified box. Does not create a new \n "
|
|
|
|
"FlatCAM object, but directly save it in SVG format\n"
|
2019-03-07 15:37:38 +00:00
|
|
|
"which can be opened with Inkscape.")
|
2019-01-03 19:25:08 +00:00
|
|
|
)
|
|
|
|
hlay.addWidget(self.film_object_button)
|
|
|
|
|
|
|
|
self.layout.addStretch()
|
|
|
|
|
2019-05-30 18:05:12 +00:00
|
|
|
# ## Signals
|
2019-01-03 19:25:08 +00:00
|
|
|
self.film_object_button.clicked.connect(self.on_film_creation)
|
|
|
|
self.tf_type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
|
|
|
|
self.tf_type_box_combo.currentIndexChanged.connect(self.on_type_box_index_changed)
|
|
|
|
|
|
|
|
def on_type_obj_index_changed(self, index):
|
|
|
|
obj_type = self.tf_type_obj_combo.currentIndex()
|
|
|
|
self.tf_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
|
|
|
|
self.tf_object_combo.setCurrentIndex(0)
|
|
|
|
|
|
|
|
def on_type_box_index_changed(self, index):
|
|
|
|
obj_type = self.tf_type_box_combo.currentIndex()
|
|
|
|
self.tf_box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
|
|
|
|
self.tf_box_combo.setCurrentIndex(0)
|
|
|
|
|
2019-03-10 15:12:58 +00:00
|
|
|
def run(self, toggle=True):
|
2019-02-03 21:08:09 +00:00
|
|
|
self.app.report_usage("ToolFilm()")
|
|
|
|
|
2019-03-11 20:58:27 +00:00
|
|
|
if toggle:
|
|
|
|
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
|
|
|
|
if self.app.ui.splitter.sizes()[0] == 0:
|
|
|
|
self.app.ui.splitter.setSizes([1, 1])
|
|
|
|
else:
|
2019-03-11 21:04:38 +00:00
|
|
|
try:
|
|
|
|
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
|
|
|
|
self.app.ui.splitter.setSizes([0, 1])
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2019-03-13 23:09:06 +00:00
|
|
|
else:
|
|
|
|
if self.app.ui.splitter.sizes()[0] == 0:
|
|
|
|
self.app.ui.splitter.setSizes([1, 1])
|
2019-03-11 20:58:27 +00:00
|
|
|
|
|
|
|
FlatCAMTool.run(self)
|
2019-03-10 15:12:58 +00:00
|
|
|
|
2019-02-27 15:32:52 +00:00
|
|
|
self.set_tool_ui()
|
2019-02-12 02:00:11 +00:00
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
self.app.ui.notebook.setTabText(2, _("Film Tool"))
|
2019-01-03 19:25:08 +00:00
|
|
|
|
2019-01-27 01:32:09 +00:00
|
|
|
def install(self, icon=None, separator=None, **kwargs):
|
|
|
|
FlatCAMTool.install(self, icon, separator, shortcut='ALT+L', **kwargs)
|
|
|
|
|
2019-02-02 16:26:01 +00:00
|
|
|
def set_tool_ui(self):
|
|
|
|
self.reset_fields()
|
|
|
|
|
2019-02-04 14:29:12 +00:00
|
|
|
f_type = self.app.defaults["tools_film_type"] if self.app.defaults["tools_film_type"] else 'neg'
|
|
|
|
self.film_type.set_value(str(f_type))
|
2019-02-04 18:49:37 +00:00
|
|
|
|
2019-03-11 18:00:07 +00:00
|
|
|
b_entry = self.app.defaults["tools_film_boundary"] if self.app.defaults["tools_film_boundary"] else 0.0
|
2019-02-04 14:29:12 +00:00
|
|
|
self.boundary_entry.set_value(float(b_entry))
|
2019-02-02 16:26:01 +00:00
|
|
|
|
2019-02-04 18:49:37 +00:00
|
|
|
scale_stroke_width = self.app.defaults["tools_film_scale"] if self.app.defaults["tools_film_scale"] else 0.0
|
|
|
|
self.film_scale_entry.set_value(int(scale_stroke_width))
|
|
|
|
|
2019-01-03 19:25:08 +00:00
|
|
|
def on_film_creation(self):
|
|
|
|
try:
|
|
|
|
name = self.tf_object_combo.currentText()
|
|
|
|
except:
|
2019-03-10 12:34:13 +00:00
|
|
|
self.app.inform.emit(_("[ERROR_NOTCL] No FlatCAM object selected. Load an object for Film and retry."))
|
2019-01-03 19:25:08 +00:00
|
|
|
return
|
2019-02-04 18:49:37 +00:00
|
|
|
|
2019-01-03 19:25:08 +00:00
|
|
|
try:
|
|
|
|
boxname = self.tf_box_combo.currentText()
|
|
|
|
except:
|
2019-03-10 12:34:13 +00:00
|
|
|
self.app.inform.emit(_("[ERROR_NOTCL] No FlatCAM object selected. Load an object for Box and retry."))
|
2019-01-03 19:25:08 +00:00
|
|
|
return
|
|
|
|
|
2019-02-02 21:56:08 +00:00
|
|
|
try:
|
|
|
|
border = float(self.boundary_entry.get_value())
|
|
|
|
except ValueError:
|
|
|
|
# try to convert comma to decimal point. if it's still not working error message and return
|
|
|
|
try:
|
|
|
|
border = float(self.boundary_entry.get_value().replace(',', '.'))
|
|
|
|
except ValueError:
|
2019-03-28 22:26:00 +00:00
|
|
|
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
2019-03-07 15:37:38 +00:00
|
|
|
"use a number."))
|
2019-02-02 21:56:08 +00:00
|
|
|
return
|
|
|
|
|
2019-02-04 18:49:37 +00:00
|
|
|
try:
|
|
|
|
scale_stroke_width = int(self.film_scale_entry.get_value())
|
|
|
|
except ValueError:
|
2019-03-28 22:26:00 +00:00
|
|
|
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
2019-03-07 15:37:38 +00:00
|
|
|
"use a number."))
|
2019-02-04 18:49:37 +00:00
|
|
|
return
|
|
|
|
|
2019-01-03 19:25:08 +00:00
|
|
|
if border is None:
|
|
|
|
border = 0
|
|
|
|
|
2019-03-10 12:34:13 +00:00
|
|
|
self.app.inform.emit(_("Generating Film ..."))
|
2019-01-03 19:25:08 +00:00
|
|
|
|
|
|
|
if self.film_type.get_value() == "pos":
|
|
|
|
try:
|
2019-03-11 18:00:07 +00:00
|
|
|
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
|
2019-03-10 12:34:13 +00:00
|
|
|
caption=_("Export SVG positive"),
|
2019-02-03 21:08:09 +00:00
|
|
|
directory=self.app.get_last_save_folder() + '/' + name,
|
|
|
|
filter="*.svg")
|
2019-01-03 19:25:08 +00:00
|
|
|
except TypeError:
|
2019-03-11 18:00:07 +00:00
|
|
|
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export SVG positive"))
|
2019-01-03 19:25:08 +00:00
|
|
|
|
|
|
|
filename = str(filename)
|
|
|
|
|
|
|
|
if str(filename) == "":
|
2019-03-28 22:26:00 +00:00
|
|
|
self.app.inform.emit(_("[WARNING_NOTCL] Export SVG positive cancelled."))
|
2019-01-03 19:25:08 +00:00
|
|
|
return
|
|
|
|
else:
|
2019-02-04 18:49:37 +00:00
|
|
|
self.app.export_svg_black(name, boxname, filename, scale_factor=scale_stroke_width)
|
2019-01-03 19:25:08 +00:00
|
|
|
else:
|
|
|
|
try:
|
2019-03-11 18:00:07 +00:00
|
|
|
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
|
2019-03-10 12:34:13 +00:00
|
|
|
caption=_("Export SVG negative"),
|
2019-02-03 21:08:09 +00:00
|
|
|
directory=self.app.get_last_save_folder() + '/' + name,
|
|
|
|
filter="*.svg")
|
2019-01-03 19:25:08 +00:00
|
|
|
except TypeError:
|
2019-03-11 18:00:07 +00:00
|
|
|
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export SVG negative"))
|
2019-01-03 19:25:08 +00:00
|
|
|
|
|
|
|
filename = str(filename)
|
|
|
|
|
|
|
|
if str(filename) == "":
|
2019-03-28 22:26:00 +00:00
|
|
|
self.app.inform.emit(_("[WARNING_NOTCL] Export SVG negative cancelled."))
|
2019-01-03 19:25:08 +00:00
|
|
|
return
|
|
|
|
else:
|
2019-02-04 18:49:37 +00:00
|
|
|
self.app.export_svg_negative(name, boxname, filename, border, scale_factor=scale_stroke_width)
|
2019-01-03 19:25:08 +00:00
|
|
|
|
|
|
|
def reset_fields(self):
|
|
|
|
self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
|
|
|
self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|