-clean-up before merge

This commit is contained in:
Marius Stanciu 2019-01-03 21:20:54 +02:00 committed by Marius S
parent 46454c293a
commit 421e9766ea
180 changed files with 0 additions and 21061 deletions

View File

@ -1,33 +0,0 @@
# -*- mode: python -*-
block_cipher = None
a = Analysis(['FlatCAM.py'],
pathex=['/home/jpcaram/flatcam'],
binaries=None,
datas=[('share/*', 'share')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='FlatCAM',
debug=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='FlatCAM')

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
class LoudDict(dict):
"""
A Dictionary with a callback for
item changes.
"""
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.callback = lambda x: None
def __setitem__(self, key, value):
"""
Overridden __setitem__ method. Will emit 'changed(QString)'
if the item was changed, with key as parameter.
"""
if key in self and self.__getitem__(key) == value:
return
dict.__setitem__(self, key, value)
self.callback(key)
def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError("update expected at most 1 arguments, got %d" % len(args))
other = dict(*args, **kwargs)
for key in other:
self[key] = other[key]
def set_change_callback(self, callback):
"""
Assigns a function as callback on item change. The callback
will receive the key of the object that was changed.
:param callback: Function to call on item change.
:type callback: func
:return: None
"""
self.callback = callback

File diff suppressed because it is too large Load Diff

View File

@ -1,979 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
from PyQt4 import QtGui, QtCore, Qt
from GUIElements import *
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, name=None):
super(FlatCAMGUI, self).__init__()
# Divine icon pack by Ipapun @ finicons.com
############
### Menu ###
############
self.menu = self.menuBar()
### File ###
self.menufile = self.menu.addMenu('&File')
# New
self.menufilenew = QtGui.QAction(QtGui.QIcon('share/file16.png'), '&New project', self)
self.menufile.addAction(self.menufilenew)
# Recent
self.recent = self.menufile.addMenu(QtGui.QIcon('share/folder16.png'), "Open recent ...")
# Separator
self.menufile.addSeparator()
# Open gerber ...
self.menufileopengerber = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Gerber ...', self)
self.menufile.addAction(self.menufileopengerber)
# Open Excellon ...
self.menufileopenexcellon = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Excellon ...', self)
self.menufile.addAction(self.menufileopenexcellon)
# Open G-Code ...
self.menufileopengcode = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open G-&Code ...', self)
self.menufile.addAction(self.menufileopengcode)
# Open Project ...
self.menufileopenproject = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Open &Project ...', self)
self.menufile.addAction(self.menufileopenproject)
# Separator
self.menufile.addSeparator()
# Import SVG ...
self.menufileimportsvg = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Import &SVG ...', self)
self.menufile.addAction(self.menufileimportsvg)
# Export SVG ...
self.menufileexportsvg = QtGui.QAction(QtGui.QIcon('share/folder16.png'), 'Export &SVG ...', self)
self.menufile.addAction(self.menufileexportsvg)
# Separator
self.menufile.addSeparator()
# Save Project
self.menufilesaveproject = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), '&Save Project', self)
self.menufile.addAction(self.menufilesaveproject)
# Save Project As ...
self.menufilesaveprojectas = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), 'Save Project &As ...', self)
self.menufile.addAction(self.menufilesaveprojectas)
# Save Project Copy ...
self.menufilesaveprojectcopy = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), 'Save Project C&opy ...', self)
self.menufile.addAction(self.menufilesaveprojectcopy)
# Save Defaults
self.menufilesavedefaults = QtGui.QAction(QtGui.QIcon('share/floppy16.png'), 'Save &Defaults', self)
self.menufile.addAction(self.menufilesavedefaults)
# Separator
self.menufile.addSeparator()
# Quit
self.exit_action = QtGui.QAction(QtGui.QIcon('share/power16.png'), '&Exit', self)
self.menufile.addAction(self.exit_action)
# exitAction.setShortcut('Ctrl+Q')
# exitAction.setStatusTip('Exit application')
#self.exit_action.triggered.connect(QtGui.qApp.quit)
### Edit ###
self.menuedit = self.menu.addMenu('&Edit')
self.menueditnew = self.menuedit.addAction(QtGui.QIcon('share/new_geo16.png'), 'New Geometry')
self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), 'Edit Geometry')
self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), 'Update Geometry')
# Separator
self.menuedit.addSeparator()
self.menueditjoin = self.menuedit.addAction(QtGui.QIcon('share/join16.png'), 'Join Geometry')
self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), 'Delete')
self.menuedit.addSeparator()
### Options ###
self.menuoptions = self.menu.addMenu('&Options')
self.menuoptions_transfer = self.menuoptions.addMenu(QtGui.QIcon('share/transfer.png'), 'Transfer options')
self.menuoptions_transfer_a2p = self.menuoptions_transfer.addAction("Application to Project")
self.menuoptions_transfer_p2a = self.menuoptions_transfer.addAction("Project to Application")
self.menuoptions_transfer_p2o = self.menuoptions_transfer.addAction("Project to Object")
self.menuoptions_transfer_o2p = self.menuoptions_transfer.addAction("Object to Project")
self.menuoptions_transfer_a2o = self.menuoptions_transfer.addAction("Application to Object")
self.menuoptions_transfer_o2a = self.menuoptions_transfer.addAction("Object to Application")
### View ###
self.menuview = self.menu.addMenu('&View')
self.menuviewdisableall = self.menuview.addAction(QtGui.QIcon('share/clear_plot16.png'), 'Disable all plots')
self.menuviewdisableother = self.menuview.addAction(QtGui.QIcon('share/clear_plot16.png'),
'Disable all plots but this one')
self.menuviewenable = self.menuview.addAction(QtGui.QIcon('share/replot16.png'), 'Enable all plots')
### Tool ###
self.menutool = QtGui.QMenu('&Tool')
self.menutoolaction = self.menu.addMenu(self.menutool)
self.menutoolshell = self.menutool.addAction(QtGui.QIcon('share/shell16.png'), '&Command Line')
### Help ###
self.menuhelp = self.menu.addMenu('&Help')
self.menuhelp_about = self.menuhelp.addAction(QtGui.QIcon('share/tv16.png'), 'About FlatCAM')
self.menuhelp_home = self.menuhelp.addAction(QtGui.QIcon('share/home16.png'), 'Home')
self.menuhelp_manual = self.menuhelp.addAction(QtGui.QIcon('share/globe16.png'), 'Manual')
###############
### Toolbar ###
###############
self.toolbarfile = QtGui.QToolBar('File Toolbar')
self.addToolBar(self.toolbarfile)
self.open_gerber_btn = self.toolbarfile.addAction(QtGui.QIcon('share/flatcam_icon32.png'), "Open &Gerber")
self.open_exc_btn = self.toolbarfile.addAction(QtGui.QIcon('share/drill32.png'), "Open &Excellon")
self.open_gcode_btn = self.toolbarfile.addAction(QtGui.QIcon('share/cnc32.png'), "Open Gco&de")
self.save_btn = self.toolbarfile.addAction(QtGui.QIcon('share/floppy32.png'), 'Save Project &As ...')
self.toolbarview= QtGui.QToolBar('View Toolbar')
self.addToolBar(self.toolbarview)
self.zoom_fit_btn = self.toolbarview.addAction(QtGui.QIcon('share/zoom_fit32.png'), "&Zoom Fit")
self.zoom_out_btn = self.toolbarview.addAction(QtGui.QIcon('share/zoom_out32.png'), "&Zoom Out")
self.zoom_in_btn = self.toolbarview.addAction(QtGui.QIcon('share/zoom_in32.png'), "&Zoom In")
# Separator
self.toolbarview.addSeparator()
self.clear_plot_btn = self.toolbarview.addAction(QtGui.QIcon('share/clear_plot32.png'), "&Clear Plot")
self.replot_btn = self.toolbarview.addAction(QtGui.QIcon('share/replot32.png'), "&Replot")
self.toolbareditobj = QtGui.QToolBar('Obj.Editor Toolbar')
self.addToolBar(self.toolbareditobj)
self.newgeo_btn = self.toolbareditobj.addAction(QtGui.QIcon('share/new_geo32.png'), "New Blank Geometry")
self.editgeo_btn = self.toolbareditobj.addAction(QtGui.QIcon('share/edit32.png'), "Edit Geometry")
self.updategeo_btn = self.toolbareditobj.addAction(QtGui.QIcon('share/edit_ok32.png'), "Update Geometry")
self.updategeo_btn.setEnabled(False)
self.toolbaredit = QtGui.QToolBar('Edit Toolbar')
self.addToolBar(self.toolbaredit)
self.delete_btn = self.toolbaredit.addAction(QtGui.QIcon('share/delete32.png'), "&Delete")
self.toolbartools = QtGui.QToolBar('Tools Toolbar')
self.addToolBar(self.toolbartools)
self.shell_btn = self.toolbartools.addAction(QtGui.QIcon('share/shell32.png'), "&Command Line")
self.measure_btn = self.toolbartools.addAction(QtGui.QIcon('share/measure32.png'), "&Measurement Tool")
################
### Splitter ###
################
self.splitter = QtGui.QSplitter()
self.setCentralWidget(self.splitter)
################
### Notebook ###
################
self.notebook = QtGui.QTabWidget()
# self.notebook.setMinimumWidth(250)
### Project ###
project_tab = QtGui.QWidget()
project_tab.setMinimumWidth(250) # Hack
self.project_tab_layout = QtGui.QVBoxLayout(project_tab)
self.project_tab_layout.setContentsMargins(2, 2, 2, 2)
self.notebook.addTab(project_tab, "Project")
### Selected ###
self.selected_tab = QtGui.QWidget()
self.selected_tab_layout = QtGui.QVBoxLayout(self.selected_tab)
self.selected_tab_layout.setContentsMargins(2, 2, 2, 2)
self.selected_scroll_area = VerticalScrollArea()
self.selected_tab_layout.addWidget(self.selected_scroll_area)
self.notebook.addTab(self.selected_tab, "Selected")
### Options ###
self.options_tab = QtGui.QWidget()
self.options_tab.setContentsMargins(0, 0, 0, 0)
self.options_tab_layout = QtGui.QVBoxLayout(self.options_tab)
self.options_tab_layout.setContentsMargins(2, 2, 2, 2)
hlay1 = QtGui.QHBoxLayout()
self.options_tab_layout.addLayout(hlay1)
self.icon = QtGui.QLabel()
self.icon.setPixmap(QtGui.QPixmap('share/gear48.png'))
hlay1.addWidget(self.icon)
self.options_combo = QtGui.QComboBox()
self.options_combo.addItem("APPLICATION DEFAULTS")
self.options_combo.addItem("PROJECT OPTIONS")
hlay1.addWidget(self.options_combo)
hlay1.addStretch()
self.options_scroll_area = VerticalScrollArea()
self.options_tab_layout.addWidget(self.options_scroll_area)
self.notebook.addTab(self.options_tab, "Options")
### Tool ###
self.tool_tab = QtGui.QWidget()
self.tool_tab_layout = QtGui.QVBoxLayout(self.tool_tab)
self.tool_tab_layout.setContentsMargins(2, 2, 2, 2)
self.notebook.addTab(self.tool_tab, "Tool")
self.tool_scroll_area = VerticalScrollArea()
self.tool_tab_layout.addWidget(self.tool_scroll_area)
self.splitter.addWidget(self.notebook)
######################
### Plot and other ###
######################
right_widget = QtGui.QWidget()
# right_widget.setContentsMargins(0, 0, 0, 0)
self.splitter.addWidget(right_widget)
self.right_layout = QtGui.QVBoxLayout()
self.right_layout.setMargin(0)
# self.right_layout.setContentsMargins(0, 0, 0, 0)
right_widget.setLayout(self.right_layout)
################
### Info bar ###
################
infobar = self.statusBar()
#self.info_label = QtGui.QLabel("Welcome to FlatCAM.")
#self.info_label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain)
#infobar.addWidget(self.info_label, stretch=1)
self.fcinfo = FlatCAMInfoBar()
infobar.addWidget(self.fcinfo, stretch=1)
self.position_label = QtGui.QLabel("")
#self.position_label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain)
self.position_label.setMinimumWidth(110)
infobar.addWidget(self.position_label)
self.units_label = QtGui.QLabel("[in]")
# self.units_label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain)
self.units_label.setMargin(2)
infobar.addWidget(self.units_label)
self.progress_bar = QtGui.QProgressBar()
self.progress_bar.setMinimum(0)
self.progress_bar.setMaximum(100)
#infobar.addWidget(self.progress_bar)
self.activity_view = FlatCAMActivityView()
infobar.addWidget(self.activity_view)
#############
### Icons ###
#############
self.app_icon = QtGui.QIcon()
self.app_icon.addFile('share/flatcam_icon16.png', QtCore.QSize(16, 16))
self.app_icon.addFile('share/flatcam_icon24.png', QtCore.QSize(24, 24))
self.app_icon.addFile('share/flatcam_icon32.png', QtCore.QSize(32, 32))
self.app_icon.addFile('share/flatcam_icon48.png', QtCore.QSize(48, 48))
self.app_icon.addFile('share/flatcam_icon128.png', QtCore.QSize(128, 128))
self.app_icon.addFile('share/flatcam_icon256.png', QtCore.QSize(256, 256))
self.setWindowIcon(self.app_icon)
self.setGeometry(100, 100, 1024, 650)
title = 'FlatCAM {}'.format(version)
if name is not None:
title += ' - {}'.format(name)
self.setWindowTitle(title)
self.show()
def closeEvent(self, event):
grect = self.geometry()
self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height())
QtGui.qApp.quit()
class FlatCAMActivityView(QtGui.QWidget):
def __init__(self, parent=None):
super(FlatCAMActivityView, self).__init__(parent=parent)
self.setMinimumWidth(200)
self.icon = QtGui.QLabel(self)
self.icon.setGeometry(0, 0, 12, 12)
self.movie = QtGui.QMovie("share/active.gif")
self.icon.setMovie(self.movie)
#self.movie.start()
layout = QtGui.QHBoxLayout()
layout.setContentsMargins(5, 0, 5, 0)
layout.setAlignment(QtCore.Qt.AlignLeft)
self.setLayout(layout)
layout.addWidget(self.icon)
self.text = QtGui.QLabel(self)
self.text.setText("Idle.")
layout.addWidget(self.text)
def set_idle(self):
self.movie.stop()
self.text.setText("Idle.")
def set_busy(self, msg):
self.movie.start()
self.text.setText(msg)
class FlatCAMInfoBar(QtGui.QWidget):
def __init__(self, parent=None):
super(FlatCAMInfoBar, self).__init__(parent=parent)
self.icon = QtGui.QLabel(self)
self.icon.setGeometry(0, 0, 12, 12)
self.pmap = QtGui.QPixmap('share/graylight12.png')
self.icon.setPixmap(self.pmap)
layout = QtGui.QHBoxLayout()
layout.setContentsMargins(5, 0, 5, 0)
self.setLayout(layout)
layout.addWidget(self.icon)
self.text = QtGui.QLabel(self)
self.text.setText("Hello!")
self.text.setToolTip("Hello!")
layout.addWidget(self.text)
layout.addStretch()
def set_text_(self, text):
self.text.setText(text)
self.text.setToolTip(text)
def set_status(self, text, level="info"):
level = str(level)
self.pmap.fill()
if level == "error":
self.pmap = QtGui.QPixmap('share/redlight12.png')
elif level == "success":
self.pmap = QtGui.QPixmap('share/greenlight12.png')
elif level == "warning":
self.pmap = QtGui.QPixmap('share/yellowlight12.png')
else:
self.pmap = QtGui.QPixmap('share/graylight12.png')
self.icon.setPixmap(self.pmap)
self.set_text_(text)
class OptionsGroupUI(QtGui.QGroupBox):
def __init__(self, title, parent=None):
QtGui.QGroupBox.__init__(self, title, parent=parent)
self.setStyleSheet("""
QGroupBox
{
font-size: 16px;
font-weight: bold;
}
""")
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
class GerberOptionsGroupUI(OptionsGroupUI):
def __init__(self, parent=None):
OptionsGroupUI.__init__(self, "Gerber Options", parent=parent)
## Plot options
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.layout.addWidget(self.plot_options_label)
grid0 = QtGui.QGridLayout()
self.layout.addLayout(grid0)
# Plot CB
self.plot_cb = FCCheckBox(label='Plot')
self.plot_options_label.setToolTip(
"Plot (show) this object."
)
grid0.addWidget(self.plot_cb, 0, 0)
# Solid CB
self.solid_cb = FCCheckBox(label='Solid')
self.solid_cb.setToolTip(
"Solid color polygons."
)
grid0.addWidget(self.solid_cb, 0, 1)
# Multicolored CB
self.multicolored_cb = FCCheckBox(label='Multicolored')
self.multicolored_cb.setToolTip(
"Draw polygons in different colors."
)
grid0.addWidget(self.multicolored_cb, 0, 2)
## Isolation Routing
self.isolation_routing_label = QtGui.QLabel("<b>Isolation Routing:</b>")
self.isolation_routing_label.setToolTip(
"Create a Geometry object with\n"
"toolpaths to cut outside polygons."
)
self.layout.addWidget(self.isolation_routing_label)
grid1 = QtGui.QGridLayout()
self.layout.addLayout(grid1)
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"Diameter of the cutting tool."
)
grid1.addWidget(tdlabel, 0, 0)
self.iso_tool_dia_entry = LengthEntry()
grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
passlabel = QtGui.QLabel('Width (# passes):')
passlabel.setToolTip(
"Width of the isolation gap in\n"
"number (integer) of tool widths."
)
grid1.addWidget(passlabel, 1, 0)
self.iso_width_entry = IntEntry()
grid1.addWidget(self.iso_width_entry, 1, 1)
overlabel = QtGui.QLabel('Pass overlap:')
overlabel.setToolTip(
"How much (fraction of tool width)\n"
"to overlap each pass."
)
grid1.addWidget(overlabel, 2, 0)
self.iso_overlap_entry = FloatEntry()
grid1.addWidget(self.iso_overlap_entry, 2, 1)
self.combine_passes_cb = FCCheckBox(label='Combine Passes')
self.combine_passes_cb.setToolTip(
"Combine all passes into one object"
)
grid1.addWidget(self.combine_passes_cb, 3, 0)
## Board cuttout
self.board_cutout_label = QtGui.QLabel("<b>Board cutout:</b>")
self.board_cutout_label.setToolTip(
"Create toolpaths to cut around\n"
"the PCB and separate it from\n"
"the original board."
)
self.layout.addWidget(self.board_cutout_label)
grid2 = QtGui.QGridLayout()
self.layout.addLayout(grid2)
tdclabel = QtGui.QLabel('Tool dia:')
tdclabel.setToolTip(
"Diameter of the cutting tool."
)
grid2.addWidget(tdclabel, 0, 0)
self.cutout_tooldia_entry = LengthEntry()
grid2.addWidget(self.cutout_tooldia_entry, 0, 1)
marginlabel = QtGui.QLabel('Margin:')
marginlabel.setToolTip(
"Distance from objects at which\n"
"to draw the cutout."
)
grid2.addWidget(marginlabel, 1, 0)
self.cutout_margin_entry = LengthEntry()
grid2.addWidget(self.cutout_margin_entry, 1, 1)
gaplabel = QtGui.QLabel('Gap size:')
gaplabel.setToolTip(
"Size of the gaps in the toolpath\n"
"that will remain to hold the\n"
"board in place."
)
grid2.addWidget(gaplabel, 2, 0)
self.cutout_gap_entry = LengthEntry()
grid2.addWidget(self.cutout_gap_entry, 2, 1)
gapslabel = QtGui.QLabel('Gaps:')
gapslabel.setToolTip(
"Where to place the gaps, Top/Bottom\n"
"Left/Rigt, or on all 4 sides."
)
grid2.addWidget(gapslabel, 3, 0)
self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'},
{'label': '2 (L/R)', 'value': 'lr'},
{'label': '4', 'value': '4'}])
grid2.addWidget(self.gaps_radio, 3, 1)
## Non-copper regions
self.noncopper_label = QtGui.QLabel("<b>Non-copper regions:</b>")
self.noncopper_label.setToolTip(
"Create polygons covering the\n"
"areas without copper on the PCB.\n"
"Equivalent to the inverse of this\n"
"object. Can be used to remove all\n"
"copper from a specified region."
)
self.layout.addWidget(self.noncopper_label)
grid3 = QtGui.QGridLayout()
self.layout.addLayout(grid3)
# Margin
bmlabel = QtGui.QLabel('Boundary Margin:')
bmlabel.setToolTip(
"Specify the edge of the PCB\n"
"by drawing a box around all\n"
"objects with this minimum\n"
"distance."
)
grid3.addWidget(bmlabel, 0, 0)
self.noncopper_margin_entry = LengthEntry()
grid3.addWidget(self.noncopper_margin_entry, 0, 1)
# Rounded corners
self.noncopper_rounded_cb = FCCheckBox(label="Rounded corners")
self.noncopper_rounded_cb.setToolTip(
"Creates a Geometry objects with polygons\n"
"covering the copper-free areas of the PCB."
)
grid3.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2)
## Bounding box
self.boundingbox_label = QtGui.QLabel('<b>Bounding Box:</b>')
self.layout.addWidget(self.boundingbox_label)
grid4 = QtGui.QGridLayout()
self.layout.addLayout(grid4)
bbmargin = QtGui.QLabel('Boundary Margin:')
bbmargin.setToolTip(
"Distance of the edges of the box\n"
"to the nearest polygon."
)
grid4.addWidget(bbmargin, 0, 0)
self.bbmargin_entry = LengthEntry()
grid4.addWidget(self.bbmargin_entry, 0, 1)
self.bbrounded_cb = FCCheckBox(label="Rounded corners")
self.bbrounded_cb.setToolTip(
"If the bounding box is \n"
"to have rounded corners\n"
"their radius is equal to\n"
"the margin."
)
grid4.addWidget(self.bbrounded_cb, 1, 0, 1, 2)
class ExcellonOptionsGroupUI(OptionsGroupUI):
def __init__(self, parent=None):
OptionsGroupUI.__init__(self, "Excellon Options", parent=parent)
## Plot options
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.layout.addWidget(self.plot_options_label)
grid0 = QtGui.QGridLayout()
self.layout.addLayout(grid0)
self.plot_cb = FCCheckBox(label='Plot')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
grid0.addWidget(self.plot_cb, 0, 0)
self.solid_cb = FCCheckBox(label='Solid')
self.solid_cb.setToolTip(
"Solid circles."
)
grid0.addWidget(self.solid_cb, 0, 1)
## Create CNC Job
self.cncjob_label = QtGui.QLabel('<b>Create CNC Job</b>')
self.cncjob_label.setToolTip(
"Create a CNC Job object\n"
"for this drill object."
)
self.layout.addWidget(self.cncjob_label)
grid1 = QtGui.QGridLayout()
self.layout.addLayout(grid1)
cutzlabel = QtGui.QLabel('Cut Z:')
cutzlabel.setToolTip(
"Drill depth (negative)\n"
"below the copper surface."
)
grid1.addWidget(cutzlabel, 0, 0)
self.cutz_entry = LengthEntry()
grid1.addWidget(self.cutz_entry, 0, 1)
travelzlabel = QtGui.QLabel('Travel Z:')
travelzlabel.setToolTip(
"Tool height when travelling\n"
"across the XY plane."
)
grid1.addWidget(travelzlabel, 1, 0)
self.travelz_entry = LengthEntry()
grid1.addWidget(self.travelz_entry, 1, 1)
frlabel = QtGui.QLabel('Feed rate:')
frlabel.setToolTip(
"Tool speed while drilling\n"
"(in units per minute)."
)
grid1.addWidget(frlabel, 2, 0)
self.feedrate_entry = LengthEntry()
grid1.addWidget(self.feedrate_entry, 2, 1)
toolchangezlabel = QtGui.QLabel('Toolchange Z:')
toolchangezlabel.setToolTip(
"Tool Z where user can change drill bit\n"
)
grid1.addWidget(toolchangezlabel, 3, 0)
self.toolchangez_entry = LengthEntry()
grid1.addWidget(self.toolchangez_entry, 3, 1)
spdlabel = QtGui.QLabel('Spindle speed:')
spdlabel.setToolTip(
"Speed of the spindle\n"
"in RPM (optional)"
)
grid1.addWidget(spdlabel, 4, 0)
self.spindlespeed_entry = IntEntry(allow_empty=True)
grid1.addWidget(self.spindlespeed_entry, 4, 1)
#### Milling Holes ####
self.mill_hole_label = QtGui.QLabel('<b>Mill Holes</b>')
self.mill_hole_label.setToolTip(
"Create Geometry for milling holes."
)
self.layout.addWidget(self.mill_hole_label)
grid1 = QtGui.QGridLayout()
self.layout.addLayout(grid1)
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"Diameter of the cutting tool."
)
grid1.addWidget(tdlabel, 0, 0)
self.tooldia_entry = LengthEntry()
grid1.addWidget(self.tooldia_entry, 0, 1)
class GeometryOptionsGroupUI(OptionsGroupUI):
def __init__(self, parent=None):
OptionsGroupUI.__init__(self, "Geometry Options", parent=parent)
## Plot options
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.layout.addWidget(self.plot_options_label)
# Plot CB
self.plot_cb = FCCheckBox(label='Plot')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
self.layout.addWidget(self.plot_cb)
# ------------------------------
## Create CNC Job
# ------------------------------
self.cncjob_label = QtGui.QLabel('<b>Create CNC Job:</b>')
self.cncjob_label.setToolTip(
"Create a CNC Job object\n"
"tracing the contours of this\n"
"Geometry object."
)
self.layout.addWidget(self.cncjob_label)
grid1 = QtGui.QGridLayout()
self.layout.addLayout(grid1)
cutzlabel = QtGui.QLabel('Cut Z:')
cutzlabel.setToolTip(
"Cutting depth (negative)\n"
"below the copper surface."
)
grid1.addWidget(cutzlabel, 0, 0)
self.cutz_entry = LengthEntry()
grid1.addWidget(self.cutz_entry, 0, 1)
# Travel Z
travelzlabel = QtGui.QLabel('Travel Z:')
travelzlabel.setToolTip(
"Height of the tool when\n"
"moving without cutting."
)
grid1.addWidget(travelzlabel, 1, 0)
self.travelz_entry = LengthEntry()
grid1.addWidget(self.travelz_entry, 1, 1)
# Feedrate
frlabel = QtGui.QLabel('Feed Rate:')
frlabel.setToolTip(
"Cutting speed in the XY\n"
"plane in units per minute"
)
grid1.addWidget(frlabel, 2, 0)
self.cncfeedrate_entry = LengthEntry()
grid1.addWidget(self.cncfeedrate_entry, 2, 1)
# Tooldia
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"The diameter of the cutting\n"
"tool (just for display)."
)
grid1.addWidget(tdlabel, 3, 0)
self.cnctooldia_entry = LengthEntry()
grid1.addWidget(self.cnctooldia_entry, 3, 1)
spdlabel = QtGui.QLabel('Spindle speed:')
spdlabel.setToolTip(
"Speed of the spindle\n"
"in RPM (optional)"
)
grid1.addWidget(spdlabel, 4, 0)
self.cncspindlespeed_entry = IntEntry(allow_empty=True)
grid1.addWidget(self.cncspindlespeed_entry, 4, 1)
# ------------------------------
## Paint area
# ------------------------------
self.paint_label = QtGui.QLabel('<b>Paint Area:</b>')
self.paint_label.setToolTip(
"Creates tool paths to cover the\n"
"whole area of a polygon (remove\n"
"all copper). You will be asked\n"
"to click on the desired polygon."
)
self.layout.addWidget(self.paint_label)
grid2 = QtGui.QGridLayout()
self.layout.addLayout(grid2)
# Tool dia
ptdlabel = QtGui.QLabel('Tool dia:')
ptdlabel.setToolTip(
"Diameter of the tool to\n"
"be used in the operation."
)
grid2.addWidget(ptdlabel, 0, 0)
self.painttooldia_entry = LengthEntry()
grid2.addWidget(self.painttooldia_entry, 0, 1)
# Overlap
ovlabel = QtGui.QLabel('Overlap:')
ovlabel.setToolTip(
"How much (fraction) of the tool\n"
"width to overlap each tool pass."
)
grid2.addWidget(ovlabel, 1, 0)
self.paintoverlap_entry = LengthEntry()
grid2.addWidget(self.paintoverlap_entry, 1, 1)
# Margin
marginlabel = QtGui.QLabel('Margin:')
marginlabel.setToolTip(
"Distance by which to avoid\n"
"the edges of the polygon to\n"
"be painted."
)
grid2.addWidget(marginlabel, 2, 0)
self.paintmargin_entry = LengthEntry()
grid2.addWidget(self.paintmargin_entry, 2, 1)
# Method
methodlabel = QtGui.QLabel('Method:')
methodlabel.setToolTip(
"Algorithm to paint the polygon:<BR>"
"<B>Standard</B>: Fixed step inwards.<BR>"
"<B>Seed-based</B>: Outwards from seed."
)
grid2.addWidget(methodlabel, 3, 0)
self.paintmethod_combo = RadioSet([
{"label": "Standard", "value": "standard"},
{"label": "Seed-based", "value": "seed"},
{"label": "Straight lines", "value": "lines"}
], orientation='vertical')
grid2.addWidget(self.paintmethod_combo, 3, 1)
# Connect lines
pathconnectlabel = QtGui.QLabel("Connect:")
pathconnectlabel.setToolTip(
"Draw lines between resulting\n"
"segments to minimize tool lifts."
)
grid2.addWidget(pathconnectlabel, 4, 0)
self.pathconnect_cb = FCCheckBox()
grid2.addWidget(self.pathconnect_cb, 4, 1)
# Paint contour
contourlabel = QtGui.QLabel("Contour:")
contourlabel.setToolTip(
"Cut around the perimeter of the polygon\n"
"to trim rough edges."
)
grid2.addWidget(contourlabel, 5, 0)
self.contour_cb = FCCheckBox()
grid2.addWidget(self.contour_cb, 5, 1)
# Polygon selection
selectlabel = QtGui.QLabel('Selection:')
selectlabel.setToolTip(
"How to select the polygons to paint."
)
grid2.addWidget(selectlabel, 6, 0)
# grid3 = QtGui.QGridLayout()
self.selectmethod_combo = RadioSet([
{"label": "Single", "value": "single"},
{"label": "All", "value": "all"},
# {"label": "Rectangle", "value": "rectangle"}
])
grid2.addWidget(self.selectmethod_combo, 6, 1)
class CNCJobOptionsGroupUI(OptionsGroupUI):
def __init__(self, parent=None):
OptionsGroupUI.__init__(self, "CNC Job Options", parent=None)
## Plot options
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.layout.addWidget(self.plot_options_label)
grid0 = QtGui.QGridLayout()
self.layout.addLayout(grid0)
# Plot CB
# self.plot_cb = QtGui.QCheckBox('Plot')
self.plot_cb = FCCheckBox('Plot')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
grid0.addWidget(self.plot_cb, 0, 0)
# Tool dia for plot
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"Diameter of the tool to be\n"
"rendered in the plot."
)
grid0.addWidget(tdlabel, 1, 0)
self.tooldia_entry = LengthEntry()
grid0.addWidget(self.tooldia_entry, 1, 1)
## Export G-Code
self.export_gcode_label = QtGui.QLabel("<b>Export G-Code:</b>")
self.export_gcode_label.setToolTip(
"Export and save G-Code to\n"
"make this object to a file."
)
self.layout.addWidget(self.export_gcode_label)
# Prepend to G-Code
prependlabel = QtGui.QLabel('Prepend to G-Code:')
prependlabel.setToolTip(
"Type here any G-Code commands you would\n"
"like to add at the beginning of the G-Code file."
)
self.layout.addWidget(prependlabel)
self.prepend_text = FCTextArea()
self.layout.addWidget(self.prepend_text)
# Append text to G-Code
appendlabel = QtGui.QLabel('Append to G-Code:')
appendlabel.setToolTip(
"Type here any G-Code commands you would\n"
"like to append to the generated file.\n"
"I.e.: M2 (End of program)"
)
self.layout.addWidget(appendlabel)
self.append_text = FCTextArea()
self.layout.addWidget(self.append_text)
# Dwell
grid1 = QtGui.QGridLayout()
self.layout.addLayout(grid1)
dwelllabel = QtGui.QLabel('Dwell:')
dwelllabel.setToolTip(
"Pause to allow the spindle to reach its\n"
"speed before cutting."
)
dwelltime = QtGui.QLabel('Duration [sec.]:')
dwelltime.setToolTip(
"Number of second to dwell."
)
self.dwell_cb = FCCheckBox()
self.dwelltime_cb = FCEntry()
grid1.addWidget(dwelllabel, 0, 0)
grid1.addWidget(self.dwell_cb, 0, 1)
grid1.addWidget(dwelltime, 1, 0)
grid1.addWidget(self.dwelltime_cb, 1, 1)
class GlobalOptionsUI(QtGui.QWidget):
"""
This is the app and project options editor.
"""
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent=parent)
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
hlay1 = QtGui.QHBoxLayout()
layout.addLayout(hlay1)
unitslabel = QtGui.QLabel('Units:')
hlay1.addWidget(unitslabel)
self.units_radio = RadioSet([{'label': 'inch', 'value': 'IN'},
{'label': 'mm', 'value': 'MM'}])
hlay1.addWidget(self.units_radio)
####### Gerber #######
# gerberlabel = QtGui.QLabel('<b>Gerber Options</b>')
# layout.addWidget(gerberlabel)
self.gerber_group = GerberOptionsGroupUI()
# self.gerber_group.setFrameStyle(QtGui.QFrame.StyledPanel)
layout.addWidget(self.gerber_group)
####### Excellon #######
# excellonlabel = QtGui.QLabel('<b>Excellon Options</b>')
# layout.addWidget(excellonlabel)
self.excellon_group = ExcellonOptionsGroupUI()
# self.excellon_group.setFrameStyle(QtGui.QFrame.StyledPanel)
layout.addWidget(self.excellon_group)
####### Geometry #######
# geometrylabel = QtGui.QLabel('<b>Geometry Options</b>')
# layout.addWidget(geometrylabel)
self.geometry_group = GeometryOptionsGroupUI()
# self.geometry_group.setStyle(QtGui.QFrame.StyledPanel)
layout.addWidget(self.geometry_group)
####### CNC #######
# cnclabel = QtGui.QLabel('<b>CNC Job Options</b>')
# layout.addWidget(cnclabel)
self.cncjob_group = CNCJobOptionsGroupUI()
# self.cncjob_group.setStyle(QtGui.QFrame.StyledPanel)
layout.addWidget(self.cncjob_group)
# def main():
#
# app = QtGui.QApplication(sys.argv)
# fc = FlatCAMGUI()
# sys.exit(app.exec_())
#
#
# if __name__ == '__main__':
# main()

File diff suppressed because it is too large Load Diff

View File

@ -1,156 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
from FlatCAMGUI import FlatCAMActivityView
from PyQt4 import QtCore
import weakref
# import logging
# log = logging.getLogger('base2')
# #log.setLevel(logging.DEBUG)
# log.setLevel(logging.WARNING)
# #log.setLevel(logging.INFO)
# formatter = logging.Formatter('[%(levelname)s] %(message)s')
# handler = logging.StreamHandler()
# handler.setFormatter(formatter)
# log.addHandler(handler)
class FCProcess(object):
app = None
def __init__(self, descr):
self.callbacks = {
"done": []
}
self.descr = descr
self.status = "Active"
def __del__(self):
self.done()
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
self.app.log.error("Abnormal termination of process!")
self.app.log.error(exc_type)
self.app.log.error(exc_val)
self.app.log.error(exc_tb)
self.done()
def done(self):
for fcn in self.callbacks["done"]:
fcn(self)
def connect(self, callback, event="done"):
if callback not in self.callbacks[event]:
self.callbacks[event].append(callback)
def disconnect(self, callback, event="done"):
try:
self.callbacks[event].remove(callback)
except ValueError:
pass
def set_status(self, status_string):
self.status = status_string
def status_msg(self):
return self.descr
class FCProcessContainer(object):
"""
This is the process container, or controller (as in MVC)
of the Process/Activity tracking.
FCProcessContainer keeps weak references to the FCProcess'es
such that their __del__ method is called when the user
looses track of their reference.
"""
app = None
def __init__(self):
self.procs = []
def add(self, proc):
self.procs.append(weakref.ref(proc))
def new(self, descr):
proc = FCProcess(descr)
proc.connect(self.on_done, event="done")
self.add(proc)
self.on_change(proc)
return proc
def on_change(self, proc):
pass
def on_done(self, proc):
self.remove(proc)
def remove(self, proc):
to_be_removed = []
for pref in self.procs:
if pref() == proc or pref() is None:
to_be_removed.append(pref)
for pref in to_be_removed:
self.procs.remove(pref)
class FCVisibleProcessContainer(QtCore.QObject, FCProcessContainer):
something_changed = QtCore.pyqtSignal()
def __init__(self, view):
assert isinstance(view, FlatCAMActivityView), \
"Expected a FlatCAMActivityView, got %s" % type(view)
FCProcessContainer.__init__(self)
QtCore.QObject.__init__(self)
self.view = view
self.something_changed.connect(self.update_view)
def on_done(self, proc):
self.app.log.debug("FCVisibleProcessContainer.on_done()")
super(FCVisibleProcessContainer, self).on_done(proc)
self.something_changed.emit()
def on_change(self, proc):
self.app.log.debug("FCVisibleProcessContainer.on_change()")
super(FCVisibleProcessContainer, self).on_change(proc)
self.something_changed.emit()
def update_view(self):
if len(self.procs) == 0:
self.view.set_idle()
elif len(self.procs) == 1:
self.view.set_busy(self.procs[0]().status_msg())
else:
self.view.set_busy("%d processes running." % len(self.procs))

View File

@ -1,33 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
import termwidget
class FCShell(termwidget.TermWidget):
def __init__(self, sysShell, *args):
termwidget.TermWidget.__init__(self, *args)
self._sysShell = sysShell
def is_command_complete(self, text):
def skipQuotes(text):
quote = text[0]
text = text[1:]
endIndex = str(text).index(quote)
return text[endIndex:]
while text:
if text[0] in ('"', "'"):
try:
text = skipQuotes(text)
except ValueError:
return False
text = text[1:]
return True
def child_exec_command(self, text):
self._sysShell.exec_command(text)

View File

@ -1,80 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
from PyQt4 import QtGui
class FlatCAMTool(QtGui.QWidget):
toolName = "FlatCAM Generic Tool"
def __init__(self, app, parent=None):
"""
:param app: The application this tool will run in.
:type app: App
:param parent: Qt Parent
:return: FlatCAMTool
"""
QtGui.QWidget.__init__(self, parent)
# self.setSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Maximum)
self.layout = QtGui.QVBoxLayout()
self.setLayout(self.layout)
self.app = app
self.menuAction = None
def install(self, icon=None, separator=None, **kwargs):
before = None
# 'pos' is the menu where the Action has to be installed
# if no 'pos' kwarg is provided then by default our Action will be installed in the menutool
# as it previously was
if 'pos' in kwargs:
pos = kwargs['pos']
else:
pos = self.app.ui.menutool
# 'before' is the Action in the menu stated by 'pos' kwarg, before which we want our Action to be installed
# if 'before' kwarg is not provided, by default our Action will be added in the last place.
if 'before' in kwargs:
before = (kwargs['before'])
# create the new Action
self.menuAction = QtGui.QAction(self)
# if provided, add an icon to this Action
if icon is not None:
self.menuAction.setIcon(icon)
# set the text name of the Action, which will be displayed in the menu
self.menuAction.setText(self.toolName)
# add a ToolTip to the new Action
# self.menuAction.setToolTip(self.toolTip) # currently not available
# insert the action in the position specified by 'before' and 'pos' kwargs
pos.insertAction(before, self.menuAction)
# if separator parameter is True add a Separator after the newly created Action
if separator is True:
pos.addSeparator()
self.menuAction.triggered.connect(self.run)
def run(self):
# Remove anything else in the GUI
self.app.ui.tool_scroll_area.takeWidget()
# Put ourself in the GUI
self.app.ui.tool_scroll_area.setWidget(self)
# Switch notebook to tool page
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
self.show()

View File

@ -1,34 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
#################################################
# FlatCAM - Version settings #
#################################################
import logging
version = {
"number": 8.5,
"date": (2016, 7, 1), # Year, Month, Day
"name": None,
"release": False,
}
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,66 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
from PyQt4 import QtCore
class Worker(QtCore.QObject):
"""
Implements a queue of tasks to be carried out in order
in a single independent thread.
"""
# avoid multiple tests for debug availability
pydevd_failed = False
def __init__(self, app, name=None):
super(Worker, self).__init__()
self.app = app
self.name = name
def allow_debug(self):
"""
allow debuging/breakpoints in this threads
should work from PyCharm and PyDev
:return:
"""
if not self.pydevd_failed:
try:
import pydevd
pydevd.settrace(suspend=False, trace_only_current_thread=True)
except ImportError:
self.pydevd_failed=True
def run(self):
self.app.log.debug("Worker Started!")
self.allow_debug()
# Tasks are queued in the event listener.
self.app.worker_task.connect(self.do_worker_task)
def do_worker_task(self, task):
self.app.log.debug("Running task: %s" % str(task))
self.allow_debug()
if ('worker_name' in task and task['worker_name'] == self.name) or \
('worker_name' not in task and self.name is None):
try:
task['fcn'](*task['params'])
except Exception as e:
self.app.thread_exception.emit(e)
raise e
return
self.app.log.debug("Task ignored.")

View File

@ -1,407 +0,0 @@
from PyQt4 import QtGui, QtCore
from copy import copy
#import FlatCAMApp
import re
import logging
log = logging.getLogger('base')
class RadioSet(QtGui.QWidget):
def __init__(self, choices, orientation='horizontal', parent=None):
"""
The choices are specified as a list of dictionaries containing:
* 'label': Shown in the UI
* 'value': The value returned is selected
:param choices: List of choices. See description.
:param orientation: 'horizontal' (default) of 'vertical'.
:param parent: Qt parent widget.
:type choices: list
"""
super(RadioSet, self).__init__(parent)
self.choices = copy(choices)
if orientation == 'horizontal':
layout = QtGui.QHBoxLayout()
else:
layout = QtGui.QVBoxLayout()
group = QtGui.QButtonGroup(self)
for choice in self.choices:
choice['radio'] = QtGui.QRadioButton(choice['label'])
group.addButton(choice['radio'])
layout.addWidget(choice['radio'], stretch=0)
choice['radio'].toggled.connect(self.on_toggle)
layout.addStretch()
self.setLayout(layout)
self.group_toggle_fn = lambda: None
def on_toggle(self):
log.debug("Radio toggled")
radio = self.sender()
if radio.isChecked():
self.group_toggle_fn()
return
def get_value(self):
for choice in self.choices:
if choice['radio'].isChecked():
return choice['value']
log.error("No button was toggled in RadioSet.")
return None
def set_value(self, val):
for choice in self.choices:
if choice['value'] == val:
choice['radio'].setChecked(True)
return
log.error("Value given is not part of this RadioSet: %s" % str(val))
class LengthEntry(QtGui.QLineEdit):
def __init__(self, output_units='IN', parent=None):
super(LengthEntry, self).__init__(parent)
self.output_units = output_units
self.format_re = re.compile(r"^([^\s]+)(?:\s([a-zA-Z]+))?$")
# Unit conversion table OUTPUT-INPUT
self.scales = {
'IN': {'IN': 1.0,
'MM': 1/25.4},
'MM': {'IN': 25.4,
'MM': 1.0}
}
self.readyToEdit = True
def mousePressEvent(self, e, Parent=None):
# required to deselect on 2nd click
super(LengthEntry, self).mousePressEvent(e)
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
# required to remove cursor on focusOut
super(LengthEntry, self).focusOutEvent(e)
self.deselect()
self.readyToEdit = True
def returnPressed(self, *args, **kwargs):
val = self.get_value()
if val is not None:
self.set_text(str(val))
else:
log.warning("Could not interpret entry: %s" % self.get_text())
def get_value(self):
raw = str(self.text()).strip(' ')
# match = self.format_re.search(raw)
try:
units = raw[-2:]
units = self.scales[self.output_units][units.upper()]
value = raw[:-2]
return float(eval(value))*units
except IndexError:
value = raw
return float(eval(value))
except KeyError:
value = raw
return float(eval(value))
except:
log.warning("Could not parse value in entry: %s" % str(raw))
return None
def set_value(self, val):
self.setText(str(val))
class FloatEntry(QtGui.QLineEdit):
def __init__(self, parent=None):
super(FloatEntry, self).__init__(parent)
self.readyToEdit = True
def mousePressEvent(self, e, Parent=None):
# required to deselect on 2nd click
super(FloatEntry, self).mousePressEvent(e)
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
# required to remove cursor on focusOut
super(FloatEntry, self).focusOutEvent(e)
self.deselect()
self.readyToEdit = True
def returnPressed(self, *args, **kwargs):
val = self.get_value()
if val is not None:
self.set_text(str(val))
else:
log.warning("Could not interpret entry: %s" % self.text())
def get_value(self):
raw = str(self.text()).strip(' ')
try:
evaled = eval(raw)
except:
log.error("Could not evaluate: %s" % str(raw))
return None
return float(evaled)
def set_value(self, val):
self.setText("%.6f" % val)
class IntEntry(QtGui.QLineEdit):
def __init__(self, parent=None, allow_empty=False, empty_val=None):
super(IntEntry, self).__init__(parent)
self.allow_empty = allow_empty
self.empty_val = empty_val
self.readyToEdit = True
def mousePressEvent(self, e, Parent=None):
# required to deselect on 2nd click
super(IntEntry, self).mousePressEvent(e)
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
# required to remove cursor on focusOut
super(IntEntry, self).focusOutEvent(e)
self.deselect()
self.readyToEdit = True
def get_value(self):
if self.allow_empty:
if str(self.text()) == "":
return self.empty_val
return int(self.text())
def set_value(self, val):
if val == self.empty_val and self.allow_empty:
self.setText("")
return
self.setText(str(val))
class FCEntry(QtGui.QLineEdit):
def __init__(self, parent=None):
super(FCEntry, self).__init__(parent)
self.readyToEdit = True
def mousePressEvent(self, e, Parent=None):
# required to deselect on 2nd click
super(FCEntry, self).mousePressEvent(e)
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
# required to remove cursor on focusOut
super(FCEntry, self).focusOutEvent(e)
self.deselect()
self.readyToEdit = True
def get_value(self):
return str(self.text())
def set_value(self, val):
self.setText(str(val))
class EvalEntry(QtGui.QLineEdit):
def __init__(self, parent=None):
super(EvalEntry, self).__init__(parent)
self.readyToEdit = True
def mousePressEvent(self, e, Parent=None):
# required to deselect on 2nd click
super(EvalEntry, self).mousePressEvent(e)
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
# required to remove cursor on focusOut
super(EvalEntry, self).focusOutEvent(e)
self.deselect()
self.readyToEdit = True
def returnPressed(self, *args, **kwargs):
val = self.get_value()
if val is not None:
self.setText(str(val))
else:
log.warning("Could not interpret entry: %s" % self.get_text())
def get_value(self):
raw = str(self.text()).strip(' ')
try:
return eval(raw)
except:
log.error("Could not evaluate: %s" % str(raw))
return None
def set_value(self, val):
self.setText(str(val))
class FCCheckBox(QtGui.QCheckBox):
def __init__(self, label='', parent=None):
super(FCCheckBox, self).__init__(str(label), parent)
def get_value(self):
return self.isChecked()
def set_value(self, val):
self.setChecked(val)
def toggle(self):
self.set_value(not self.get_value())
class FCTextArea(QtGui.QPlainTextEdit):
def __init__(self, parent=None):
super(FCTextArea, self).__init__(parent)
def set_value(self, val):
self.setPlainText(val)
def get_value(self):
return str(self.toPlainText())
class FCInputDialog(QtGui.QInputDialog):
def __init__(self, parent=None, ok=False, val=None):
super(FCInputDialog, self).__init__(parent)
self.allow_empty = ok
self.empty_val = val
self.readyToEdit = True
def mousePressEvent(self, e, Parent=None):
# required to deselect on 2nd click
super(FCInputDialog, self).mousePressEvent(e)
if self.readyToEdit:
self.selectAll()
self.readyToEdit = False
def focusOutEvent(self, e):
# required to remove cursor on focusOut
super(FCInputDialog, self).focusOutEvent(e)
self.deselect()
self.readyToEdit = True
def get_value(self, title=None, message=None, min=None, max=None, decimals=None):
if title is None:
title = "FlatCAM action"
if message is None:
message = "Please enter the value: "
if min is None:
min = 0.0
if max is None:
max = 100.0
if decimals is None:
decimals = 1
self.val,self.ok = self.getDouble(self, title, message, min=min,
max=max, decimals=decimals)
return [self.val,self.ok]
def set_value(self, val):
pass
class FCButton(QtGui.QPushButton):
def __init__(self, parent=None):
super(FCButton, self).__init__(parent)
def get_value(self):
return self.isChecked()
def set_value(self, val):
self.setText(str(val))
class VerticalScrollArea(QtGui.QScrollArea):
"""
This widget extends QtGui.QScrollArea to make a vertical-only
scroll area that also expands horizontally to accomodate
its contents.
"""
def __init__(self, parent=None):
QtGui.QScrollArea.__init__(self, parent=parent)
self.setWidgetResizable(True)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
def eventFilter(self, source, event):
"""
The event filter gets automatically installed when setWidget()
is called.
:param source:
:param event:
:return:
"""
if event.type() == QtCore.QEvent.Resize and source == self.widget():
# log.debug("VerticalScrollArea: Widget resized:")
# log.debug(" minimumSizeHint().width() = %d" % self.widget().minimumSizeHint().width())
# log.debug(" verticalScrollBar().width() = %d" % self.verticalScrollBar().width())
self.setMinimumWidth(self.widget().sizeHint().width() +
self.verticalScrollBar().sizeHint().width())
# if self.verticalScrollBar().isVisible():
# log.debug(" Scroll bar visible")
# self.setMinimumWidth(self.widget().minimumSizeHint().width() +
# self.verticalScrollBar().width())
# else:
# log.debug(" Scroll bar hidden")
# self.setMinimumWidth(self.widget().minimumSizeHint().width())
return QtGui.QWidget.eventFilter(self, source, event)
class OptionalInputSection:
def __init__(self, cb, optinputs):
"""
Associates the a checkbox with a set of inputs.
:param cb: Checkbox that enables the optional inputs.
:param optinputs: List of widgets that are optional.
:return:
"""
assert isinstance(cb, FCCheckBox), \
"Expected an FCCheckBox, got %s" % type(cb)
self.cb = cb
self.optinputs = optinputs
self.on_cb_change()
self.cb.stateChanged.connect(self.on_cb_change)
def on_cb_change(self):
if self.cb.checkState():
for widget in self.optinputs:
widget.setEnabled(True)
else:
for widget in self.optinputs:
widget.setEnabled(False)

View File

@ -1,9 +0,0 @@
The MIT License (MIT)
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,9 +0,0 @@
recursive-include share *.png
recursive-include share *.svg
include flatcam.desktop
include LICENSE
include README.md
include *.py
include MANIFEST.in

View File

@ -1,362 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
from FlatCAMObj import *
import inspect # TODO: Remove
import FlatCAMApp
from PyQt4 import Qt, QtGui, QtCore
class KeySensitiveListView(QtGui.QListView):
"""
QtGui.QListView extended to emit a signal on key press.
"""
keyPressed = QtCore.pyqtSignal(int)
def keyPressEvent(self, event):
super(KeySensitiveListView, self).keyPressEvent(event)
self.keyPressed.emit(event.key())
#class ObjectCollection(QtCore.QAbstractListModel):
class ObjectCollection():
"""
Object storage and management.
"""
classdict = {
"gerber": FlatCAMGerber,
"excellon": FlatCAMExcellon,
"cncjob": FlatCAMCNCjob,
"geometry": FlatCAMGeometry
}
icon_files = {
"gerber": "share:flatcam_icon16.png",
"excellon": "share:drill16.png",
"cncjob": "share:cnc16.png",
"geometry": "share:geometry16.png"
}
def __init__(self, parent=None):
#QtCore.QAbstractListModel.__init__(self, parent=parent)
### Icons for the list view
self.icons = {}
for kind in ObjectCollection.icon_files:
self.icons[kind] = QtGui.QPixmap(ObjectCollection.icon_files[kind])
### Data ###
self.object_list = []
self.checked_indexes = []
# Names of objects that are expected to become available.
# For example, when the creation of a new object will run
# in the background and will complete some time in the
# future. This is a way to reserve the name and to let other
# tasks know that they have to wait until available.
self.promises = set()
### View
#self.view = QtGui.QListView()
self.view = KeySensitiveListView()
self.view.setSelectionMode(Qt.QAbstractItemView.ExtendedSelection)
self.model = QtGui.QStandardItemModel(self.view)
self.view.setModel(self.model)
self.model.itemChanged.connect(self.on_item_changed)
self.click_modifier = None
## GUI Events
self.view.selectionModel().selectionChanged.connect(self.on_list_selection_change)
self.view.activated.connect(self.on_item_activated)
self.view.keyPressed.connect(self.on_key)
self.view.clicked.connect(self.on_mouse_down)
def promise(self, obj_name):
FlatCAMApp.App.log.debug("Object %s has been promised." % obj_name)
self.promises.add(obj_name)
def has_promises(self):
return len(self.promises) > 0
def on_key(self, key):
# Delete
if key == QtCore.Qt.Key_Delete:
# Delete via the application to
# ensure cleanup of the GUI
self.get_active().app.on_delete()
return
if key == QtCore.Qt.Key_Space:
self.get_active().ui.plot_cb.toggle()
return
def print_list(self):
for obj in self.object_list:
print(obj)
def on_mouse_down(self, event):
FlatCAMApp.App.log.debug("Mouse button pressed on list")
#self.print_list()
def rowCount(self, parent=QtCore.QModelIndex(), *args, **kwargs):
return len(self.object_list)
def columnCount(self, *args, **kwargs):
return 1
def data(self, index, role=Qt.Qt.DisplayRole):
if not index.isValid() or not 0 <= index.row() < self.rowCount():
return QtCore.QVariant()
row = index.row()
if role == Qt.Qt.DisplayRole:
return self.object_list[row].options["name"]
if role == Qt.Qt.DecorationRole:
return self.icons[self.object_list[row].kind]
# if role == Qt.Qt.CheckStateRole:
# if row in self.checked_indexes:
# return Qt.Qt.Checked
# else:
# return Qt.Qt.Unchecked
def append(self, obj, active=False):
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.append()")
name = obj.options["name"]
# Check promises and clear if exists
if name in self.promises:
self.promises.remove(name)
FlatCAMApp.App.log.debug("Promised object %s became available." % name)
FlatCAMApp.App.log.debug("%d promised objects remaining." % len(self.promises))
# Prevent same name
while name in self.get_names():
## Create a new name
# Ends with number?
FlatCAMApp.App.log.debug("new_object(): Object name (%s) exists, changing." % name)
match = re.search(r'(.*[^\d])?(\d+)$', name)
if match: # Yes: Increment the number!
base = match.group(1) or ''
num = int(match.group(2))
name = base + str(num + 1)
else: # No: add a number!
name += "_1"
obj.options["name"] = name
obj.set_ui(obj.ui_type())
# Required before appending (Qt MVC)
#self.beginInsertRows(QtCore.QModelIndex(), len(self.object_list), len(self.object_list))
# Simply append to the python list
self.object_list.append(obj)
# Create the model item to insert into the QListView
icon = QtGui.QIcon(self.icons[obj.kind])#self.icons["gerber"])
item = QtGui.QStandardItem(icon, str(name))
# Item is not editable, so that double click
# does not allow cell value modification.
item.setEditable(False)
# The item is checkable, to add the checkbox.
item.setCheckable(True)
if obj.options["plot"] is True:
item.setCheckState(2) #Qt.Checked)
else:
item.setCheckState(0) #Qt.Unchecked)
self.model.appendRow(item)
obj.option_changed.connect(self.on_object_option_changed)
# Required after appending (Qt MVC)
#self.endInsertRows()
def on_object_option_changed(self, obj, key):
if key == "plot":
self.model.blockSignals(True)
name = obj.options["name"]
state = 0 #Qt.Unchecked
for index in range(self.model.rowCount()):
item = self.model.item(index)
if self.object_list[item.row()].options["name"] == name:
if obj.options["plot"] == True:
state = 2 #Qt.Checked
item.setCheckState(state)
obj.ui.plot_cb.set_value(state)
break
self.model.blockSignals(False)
def get_names(self):
"""
Gets a list of the names of all objects in the collection.
:return: List of names.
:rtype: list
"""
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.get_names()")
return [x.options['name'] for x in self.object_list]
def get_bounds(self):
"""
Finds coordinates bounding all objects in the collection.
:return: [xmin, ymin, xmax, ymax]
:rtype: list
"""
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_bounds()")
# TODO: Move the operation out of here.
xmin = Inf
ymin = Inf
xmax = -Inf
ymax = -Inf
for obj in self.object_list:
try:
gxmin, gymin, gxmax, gymax = obj.bounds()
xmin = min([xmin, gxmin])
ymin = min([ymin, gymin])
xmax = max([xmax, gxmax])
ymax = max([ymax, gymax])
except:
FlatCAMApp.App.log.warning("DEV WARNING: Tried to get bounds of empty geometry.")
return [xmin, ymin, xmax, ymax]
def get_by_name(self, name):
"""
Fetches the FlatCAMObj with the given `name`.
:param name: The name of the object.
:type name: str
:return: The requested object or None if no such object.
:rtype: FlatCAMObj or None
"""
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_by_name()")
for obj in self.object_list:
if obj.options['name'] == name:
return obj
return None
def delete_active(self):
selections = self.view.selectedIndexes()
if len(selections) == 0:
return
row = selections[0].row()
#self.beginRemoveRows(QtCore.QModelIndex(), row, row)
self.object_list.pop(row)
self.model.removeRow(row)
#self.endRemoveRows()
def get_active(self):
"""
Returns the active object or None
:return: FlatCAMObj or None
"""
selections = self.view.selectedIndexes()
if len(selections) == 0:
return None
row = selections[0].row()
return self.object_list[row]
def get_selected(self):
"""
Returns list of objects selected in the view.
:return: List of objects
"""
return [self.object_list[sel.row()] for sel in self.view.selectedIndexes()]
def set_active(self, name):
"""
Selects object by name from the project list. This triggers the
list_selection_changed event and call on_list_selection_changed.
:param name: Name of the FlatCAM Object
:return: None
"""
iobj = self.model.createIndex(self.get_names().index(name), 0) # Column 0
self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Select)
def set_inactive(self, name):
"""
Unselect object by name from the project list. This triggers the
list_selection_changed event and call on_list_selection_changed.
:param name: Name of the FlatCAM Object
:return: None
"""
iobj = self.model.createIndex(self.get_names().index(name), 0) # Column 0
self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel.Deselect)
def set_all_inactive(self):
"""
Unselect all objects from the project list. This triggers the
list_selection_changed event and call on_list_selection_changed.
:return: None
"""
for name in self.get_names():
self.set_inactive(name)
def on_list_selection_change(self, current, previous):
FlatCAMApp.App.log.debug("on_list_selection_change()")
FlatCAMApp.App.log.debug("Current: %s, Previous %s" % (str(current), str(previous)))
try:
selection_index = current.indexes()[0].row()
except IndexError:
FlatCAMApp.App.log.debug("on_list_selection_change(): Index Error (Nothing selected?)")
return
self.object_list[selection_index].build_ui()
def on_item_changed(self, item):
FlatCAMApp.App.log.debug("on_item_changed(): " + str(item.row()) + " " + self.object_list[item.row()].options["name"])
if item.checkState() == QtCore.Qt.Checked:
self.object_list[item.row()].options["plot"] = True #(item.checkState() == QtCore.Qt.Checked)
else:
self.object_list[item.row()].options["plot"] = False #(item.checkState() == QtCore.Qt.Checked)
self.object_list[item.row()].plot()
return
def on_item_activated(self, index):
"""
Double-click or Enter on item.
:param index: Index of the item in the list.
:return: None
"""
self.object_list[index.row()].build_ui()
def delete_all(self):
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_all()")
# self.beginResetModel()
self.model.removeRows(0, self.model.rowCount())
self.object_list = []
self.checked_indexes = []
# self.endResetModel()
def get_list(self):
return self.object_list

View File

@ -1,875 +0,0 @@
import sys
from PyQt4 import QtGui, QtCore
#from GUIElements import *
from GUIElements import FCEntry, FloatEntry, EvalEntry, FCCheckBox, \
LengthEntry, FCTextArea, IntEntry, RadioSet, OptionalInputSection
class ObjectUI(QtGui.QWidget):
"""
Base class for the UI of FlatCAM objects. Deriving classes should
put UI elements in ObjectUI.custom_box (QtGui.QLayout).
"""
def __init__(self, icon_file='share/flatcam_icon32.png', title='FlatCAM Object', parent=None):
QtGui.QWidget.__init__(self, parent=parent)
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
## Page Title box (spacing between children)
self.title_box = QtGui.QHBoxLayout()
layout.addLayout(self.title_box)
## Page Title icon
pixmap = QtGui.QPixmap(icon_file)
self.icon = QtGui.QLabel()
self.icon.setPixmap(pixmap)
self.title_box.addWidget(self.icon, stretch=0)
## Title label
self.title_label = QtGui.QLabel("<font size=5><b>" + title + "</b></font>")
self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.title_box.addWidget(self.title_label, stretch=1)
## Object name
self.name_box = QtGui.QHBoxLayout()
layout.addLayout(self.name_box)
name_label = QtGui.QLabel("Name:")
self.name_box.addWidget(name_label)
self.name_entry = FCEntry()
self.name_box.addWidget(self.name_entry)
## Box box for custom widgets
# This gets populated in offspring implementations.
self.custom_box = QtGui.QVBoxLayout()
layout.addLayout(self.custom_box)
###########################
## Common to all objects ##
###########################
#### Scale ####
self.scale_label = QtGui.QLabel('<b>Scale:</b>')
self.scale_label.setToolTip(
"Change the size of the object."
)
layout.addWidget(self.scale_label)
self.scale_grid = QtGui.QGridLayout()
layout.addLayout(self.scale_grid)
# Factor
faclabel = QtGui.QLabel('Factor:')
faclabel.setToolTip(
"Factor by which to multiply\n"
"geometric features of this object."
)
self.scale_grid.addWidget(faclabel, 0, 0)
self.scale_entry = FloatEntry()
self.scale_entry.set_value(1.0)
self.scale_grid.addWidget(self.scale_entry, 0, 1)
# GO Button
self.scale_button = QtGui.QPushButton('Scale')
self.scale_button.setToolTip(
"Perform scaling operation."
)
layout.addWidget(self.scale_button)
#### Offset ####
self.offset_label = QtGui.QLabel('<b>Offset:</b>')
self.offset_label.setToolTip(
"Change the position of this object."
)
layout.addWidget(self.offset_label)
self.offset_grid = QtGui.QGridLayout()
layout.addLayout(self.offset_grid)
self.offset_vectorlabel = QtGui.QLabel('Vector:')
self.offset_vectorlabel.setToolTip(
"Amount by which to move the object\n"
"in the x and y axes in (x, y) format."
)
self.offset_grid.addWidget(self.offset_vectorlabel, 0, 0)
self.offsetvector_entry = EvalEntry()
self.offsetvector_entry.setText("(0.0, 0.0)")
self.offset_grid.addWidget(self.offsetvector_entry, 0, 1)
self.offset_button = QtGui.QPushButton('Offset')
self.offset_button.setToolTip(
"Perform the offset operation."
)
layout.addWidget(self.offset_button)
self.auto_offset_button = QtGui.QPushButton('Offset auto')
self.auto_offset_button.setToolTip(
"Align the object with the x and y axes."
)
layout.addWidget(self.auto_offset_button)
#### Mirror ####
self.mirror_label = QtGui.QLabel('<b>Mirror:</b>')
self.mirror_label.setToolTip(
"Flip the object along an axis."
)
layout.addWidget(self.mirror_label)
self.mirror_axis_grid = QtGui.QGridLayout()
layout.addLayout(self.mirror_axis_grid)
axislabel = QtGui.QLabel('Axis:')
axislabel.setToolTip(
"Mirror axis parallel to the x or y axis."
)
self.mirror_axis_grid.addWidget(axislabel, 0, 0)
self.mirror_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
{'label': 'Y', 'value': 'Y'}])
self.mirror_axis_radio.set_value('Y')
self.mirror_axis_grid.addWidget(self.mirror_axis_radio, 0, 1)
self.mirror_auto_center_cb = FCCheckBox(label='Center axis automatically')
self.mirror_auto_center_cb.setToolTip(
"Place the mirror axis on the middle of the object."
)
self.mirror_auto_center_cb.set_value(True)
layout.addWidget(self.mirror_auto_center_cb)
self.mirror_button = QtGui.QPushButton('Mirror')
self.mirror_button.setToolTip(
"Perform the mirror operation."
)
layout.addWidget(self.mirror_button)
layout.addStretch()
class CNCObjectUI(ObjectUI):
"""
User interface for CNCJob objects.
"""
def __init__(self, parent=None):
"""
Creates the user interface for CNCJob objects. GUI elements should
be placed in ``self.custom_box`` to preserve the layout.
"""
ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png', parent=parent)
# Scale and offset are not available for CNCJob objects.
# Hiding from the GUI.
for i in range(0, self.scale_grid.count()):
self.scale_grid.itemAt(i).widget().hide()
self.scale_label.hide()
self.scale_button.hide()
for i in range(0, self.offset_grid.count()):
self.offset_grid.itemAt(i).widget().hide()
self.offset_label.hide()
self.offset_button.hide()
self.auto_offset_button.hide()
self.mirror_label.hide()
for i in range(0, self.mirror_axis_grid.count()):
self.mirror_axis_grid.itemAt(i).widget().hide()
self.mirror_auto_center_cb.hide()
self.mirror_button.hide()
## Plot options
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.custom_box.addWidget(self.plot_options_label)
grid0 = QtGui.QGridLayout()
self.custom_box.addLayout(grid0)
# Plot CB
# self.plot_cb = QtGui.QCheckBox('Plot')
self.plot_cb = FCCheckBox('Plot')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
grid0.addWidget(self.plot_cb, 0, 0)
# Tool dia for plot
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"Diameter of the tool to be\n"
"rendered in the plot."
)
grid0.addWidget(tdlabel, 1, 0)
self.tooldia_entry = LengthEntry()
grid0.addWidget(self.tooldia_entry, 1, 1)
# Update plot button
self.updateplot_button = QtGui.QPushButton('Update Plot')
self.updateplot_button.setToolTip(
"Update the plot."
)
self.custom_box.addWidget(self.updateplot_button)
##################
## Export G-Code
##################
self.export_gcode_label = QtGui.QLabel("<b>Export G-Code:</b>")
self.export_gcode_label.setToolTip(
"Export and save G-Code to\n"
"make this object to a file."
)
self.custom_box.addWidget(self.export_gcode_label)
# Prepend text to Gerber
prependlabel = QtGui.QLabel('Prepend to G-Code:')
prependlabel.setToolTip(
"Type here any G-Code commands you would\n"
"like to add to the beginning of the generated file."
)
self.custom_box.addWidget(prependlabel)
self.prepend_text = FCTextArea()
self.custom_box.addWidget(self.prepend_text)
# Append text to Gerber
appendlabel = QtGui.QLabel('Append to G-Code:')
appendlabel.setToolTip(
"Type here any G-Code commands you would\n"
"like to append to the generated file.\n"
"I.e.: M2 (End of program)"
)
self.custom_box.addWidget(appendlabel)
self.append_text = FCTextArea()
self.custom_box.addWidget(self.append_text)
processorlabel = QtGui.QLabel('Postprocessing-Script:')
processorlabel.setToolTip(
"Enter a Postprocessing Script here.\n"
"It gets applied to the G-Code after it\n"
"is generated."
)
self.custom_box.addWidget(processorlabel)
self.process_script = FCTextArea()
self.custom_box.addWidget(self.process_script)
# Dwell
grid1 = QtGui.QGridLayout()
self.custom_box.addLayout(grid1)
dwelllabel = QtGui.QLabel('Dwell:')
dwelllabel.setToolTip(
"Pause to allow the spindle to reach its\n"
"speed before cutting."
)
dwelltime = QtGui.QLabel('Duration [sec.]:')
dwelltime.setToolTip(
"Number of second to dwell."
)
self.dwell_cb = FCCheckBox()
self.dwelltime_entry = FCEntry()
grid1.addWidget(dwelllabel, 0, 0)
grid1.addWidget(self.dwell_cb, 0, 1)
grid1.addWidget(dwelltime, 1, 0)
grid1.addWidget(self.dwelltime_entry, 1, 1)
# GO Button
self.export_gcode_button = QtGui.QPushButton('Export G-Code')
self.export_gcode_button.setToolTip(
"Opens dialog to save G-Code\n"
"file."
)
self.custom_box.addWidget(self.export_gcode_button)
class GeometryObjectUI(ObjectUI):
"""
User interface for Geometry objects.
"""
def __init__(self, parent=None):
super(GeometryObjectUI, self).__init__(title='Geometry Object', icon_file='share/geometry32.png', parent=parent)
## Plot options
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.custom_box.addWidget(self.plot_options_label)
# Plot CB
self.plot_cb = FCCheckBox(label='Plot')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
self.custom_box.addWidget(self.plot_cb)
#-----------------------------------
# Create CNC Job
#-----------------------------------
self.cncjob_label = QtGui.QLabel('<b>Create CNC Job:</b>')
self.cncjob_label.setToolTip(
"Create a CNC Job object\n"
"tracing the contours of this\n"
"Geometry object."
)
self.custom_box.addWidget(self.cncjob_label)
grid1 = QtGui.QGridLayout()
self.custom_box.addLayout(grid1)
cutzlabel = QtGui.QLabel('Cut Z:')
cutzlabel.setToolTip(
"Cutting depth (negative)\n"
"below the copper surface."
)
grid1.addWidget(cutzlabel, 0, 0)
self.cutz_entry = LengthEntry()
grid1.addWidget(self.cutz_entry, 0, 1)
# Travel Z
travelzlabel = QtGui.QLabel('Travel Z:')
travelzlabel.setToolTip(
"Height of the tool when\n"
"moving without cutting."
)
grid1.addWidget(travelzlabel, 1, 0)
self.travelz_entry = LengthEntry()
grid1.addWidget(self.travelz_entry, 1, 1)
# Feedrate
frlabel = QtGui.QLabel('Feed Rate:')
frlabel.setToolTip(
"Cutting speed in the XY\n"
"plane in units per minute"
)
grid1.addWidget(frlabel, 2, 0)
self.cncfeedrate_entry = LengthEntry()
grid1.addWidget(self.cncfeedrate_entry, 2, 1)
# Tooldia
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"The diameter of the cutting\n"
"tool (just for display)."
)
grid1.addWidget(tdlabel, 3, 0)
self.cnctooldia_entry = LengthEntry()
grid1.addWidget(self.cnctooldia_entry, 3, 1)
# Spindlespeed
spdlabel = QtGui.QLabel('Spindle speed:')
spdlabel.setToolTip(
"Speed of the spindle\n"
"in RPM (optional)"
)
grid1.addWidget(spdlabel, 4, 0)
self.cncspindlespeed_entry = IntEntry(allow_empty=True)
grid1.addWidget(self.cncspindlespeed_entry, 4, 1)
# Multi-pass
mpasslabel = QtGui.QLabel('Multi-Depth:')
mpasslabel.setToolTip(
"Use multiple passes to limit\n"
"the cut depth in each pass. Will\n"
"cut multiple times until Cut Z is\n"
"reached."
)
grid1.addWidget(mpasslabel, 5, 0)
self.mpass_cb = FCCheckBox()
grid1.addWidget(self.mpass_cb, 5, 1)
maxdepthlabel = QtGui.QLabel('Depth/pass:')
maxdepthlabel.setToolTip(
"Depth of each pass (positive)."
)
grid1.addWidget(maxdepthlabel, 6, 0)
self.maxdepth_entry = LengthEntry()
grid1.addWidget(self.maxdepth_entry, 6, 1)
self.ois_mpass = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
# Button
self.generate_cnc_button = QtGui.QPushButton('Generate')
self.generate_cnc_button.setToolTip(
"Generate the CNC Job object."
)
self.custom_box.addWidget(self.generate_cnc_button)
#------------------------------
# Paint area
#------------------------------
self.paint_label = QtGui.QLabel('<b>Paint Area:</b>')
self.paint_label.setToolTip(
"Creates tool paths to cover the\n"
"whole area of a polygon (remove\n"
"all copper). You will be asked\n"
"to click on the desired polygon."
)
self.custom_box.addWidget(self.paint_label)
grid2 = QtGui.QGridLayout()
self.custom_box.addLayout(grid2)
# Tool dia
ptdlabel = QtGui.QLabel('Tool dia:')
ptdlabel.setToolTip(
"Diameter of the tool to\n"
"be used in the operation."
)
grid2.addWidget(ptdlabel, 0, 0)
self.painttooldia_entry = LengthEntry()
grid2.addWidget(self.painttooldia_entry, 0, 1)
# Overlap
ovlabel = QtGui.QLabel('Overlap:')
ovlabel.setToolTip(
"How much (fraction) of the tool\n"
"width to overlap each tool pass."
)
grid2.addWidget(ovlabel, 1, 0)
self.paintoverlap_entry = LengthEntry()
grid2.addWidget(self.paintoverlap_entry, 1, 1)
# Margin
marginlabel = QtGui.QLabel('Margin:')
marginlabel.setToolTip(
"Distance by which to avoid\n"
"the edges of the polygon to\n"
"be painted."
)
grid2.addWidget(marginlabel, 2, 0)
self.paintmargin_entry = LengthEntry()
grid2.addWidget(self.paintmargin_entry, 2, 1)
# Method
methodlabel = QtGui.QLabel('Method:')
methodlabel.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
methodlabel.setToolTip(
"Algorithm to paint the polygon:<BR>"
"<B>Standard</B>: Fixed step inwards.<BR>"
"<B>Seed-based</B>: Outwards from seed."
)
grid2.addWidget(methodlabel, 3, 0)
self.paintmethod_combo = RadioSet([
{"label": "Standard", "value": "standard"},
{"label": "Seed-based", "value": "seed"},
{"label": "Straight lines", "value": "lines"}
], orientation='vertical')
grid2.addWidget(self.paintmethod_combo, 3, 1)
# Connect lines
pathconnectlabel = QtGui.QLabel("Connect:")
pathconnectlabel.setToolTip(
"Draw lines between resulting\n"
"segments to minimize tool lifts."
)
grid2.addWidget(pathconnectlabel, 4, 0)
self.pathconnect_cb = FCCheckBox()
grid2.addWidget(self.pathconnect_cb, 4, 1)
contourlabel = QtGui.QLabel("Contour:")
contourlabel.setToolTip(
"Cut around the perimeter of the polygon\n"
"to trim rough edges."
)
grid2.addWidget(contourlabel, 5, 0)
self.paintcontour_cb = FCCheckBox()
grid2.addWidget(self.paintcontour_cb, 5, 1)
# Polygon selection
selectlabel = QtGui.QLabel('Selection:')
selectlabel.setToolTip(
"How to select the polygons to paint."
)
grid2.addWidget(selectlabel, 6, 0)
#grid3 = QtGui.QGridLayout()
self.selectmethod_combo = RadioSet([
{"label": "Single", "value": "single"},
{"label": "All", "value": "all"},
#{"label": "Rectangle", "value": "rectangle"}
])
grid2.addWidget(self.selectmethod_combo, 6, 1)
# GO Button
self.generate_paint_button = QtGui.QPushButton('Generate')
self.generate_paint_button.setToolTip(
"After clicking here, click inside\n"
"the polygon you wish to be painted.\n"
"A new Geometry object with the tool\n"
"paths will be created."
)
self.custom_box.addWidget(self.generate_paint_button)
class ExcellonObjectUI(ObjectUI):
"""
User interface for Excellon objects.
"""
def __init__(self, parent=None):
ObjectUI.__init__(self, title='Excellon Object',
icon_file='share/drill32.png',
parent=parent)
#### Plot options ####
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.custom_box.addWidget(self.plot_options_label)
grid0 = QtGui.QGridLayout()
self.custom_box.addLayout(grid0)
self.plot_cb = FCCheckBox(label='Plot')
self.plot_cb.setToolTip(
"Plot (show) this object."
)
grid0.addWidget(self.plot_cb, 0, 0)
self.solid_cb = FCCheckBox(label='Solid')
self.solid_cb.setToolTip(
"Solid circles."
)
grid0.addWidget(self.solid_cb, 0, 1)
#### Tools ####
self.tools_table_label = QtGui.QLabel('<b>Tools</b>')
self.tools_table_label.setToolTip(
"Tools in this Excellon object."
)
self.custom_box.addWidget(self.tools_table_label)
self.tools_table = QtGui.QTableWidget()
self.tools_table.setFixedHeight(100)
self.custom_box.addWidget(self.tools_table)
#### Create CNC Job ####
self.cncjob_label = QtGui.QLabel('<b>Create CNC Job</b>')
self.cncjob_label.setToolTip(
"Create a CNC Job object\n"
"for this drill object."
)
self.custom_box.addWidget(self.cncjob_label)
grid1 = QtGui.QGridLayout()
self.custom_box.addLayout(grid1)
cutzlabel = QtGui.QLabel('Cut Z:')
cutzlabel.setToolTip(
"Drill depth (negative)\n"
"below the copper surface."
)
grid1.addWidget(cutzlabel, 0, 0)
self.cutz_entry = LengthEntry()
grid1.addWidget(self.cutz_entry, 0, 1)
travelzlabel = QtGui.QLabel('Travel Z:')
travelzlabel.setToolTip(
"Tool height when travelling\n"
"across the XY plane."
)
grid1.addWidget(travelzlabel, 1, 0)
self.travelz_entry = LengthEntry()
grid1.addWidget(self.travelz_entry, 1, 1)
frlabel = QtGui.QLabel('Feed rate:')
frlabel.setToolTip(
"Tool speed while drilling\n"
"(in units per minute)."
)
grid1.addWidget(frlabel, 2, 0)
self.feedrate_entry = LengthEntry()
grid1.addWidget(self.feedrate_entry, 2, 1)
# Tool change:
toolchlabel = QtGui.QLabel("Tool change:")
toolchlabel.setToolTip(
"Include tool-change sequence\n"
"in G-Code (Pause for tool change)."
)
self.toolchange_cb = FCCheckBox()
grid1.addWidget(toolchlabel, 3, 0)
grid1.addWidget(self.toolchange_cb, 3, 1)
# Tool change Z:
toolchzlabel = QtGui.QLabel("Tool change Z:")
toolchzlabel.setToolTip(
"Z-axis position (height) for\n"
"tool change."
)
grid1.addWidget(toolchzlabel, 4, 0)
self.toolchangez_entry = LengthEntry()
grid1.addWidget(self.toolchangez_entry, 4, 1)
self.ois_tcz = OptionalInputSection(self.toolchange_cb, [self.toolchangez_entry])
# Spindlespeed
spdlabel = QtGui.QLabel('Spindle speed:')
spdlabel.setToolTip(
"Speed of the spindle\n"
"in RPM (optional)"
)
grid1.addWidget(spdlabel, 5, 0)
self.spindlespeed_entry = IntEntry(allow_empty=True)
grid1.addWidget(self.spindlespeed_entry, 5, 1)
choose_tools_label = QtGui.QLabel(
"Select from the tools section above\n"
"the tools you want to include."
)
self.custom_box.addWidget(choose_tools_label)
self.generate_cnc_button = QtGui.QPushButton('Generate')
self.generate_cnc_button.setToolTip(
"Generate the CNC Job."
)
self.custom_box.addWidget(self.generate_cnc_button)
#### Milling Holes ####
self.mill_hole_label = QtGui.QLabel('<b>Mill Holes</b>')
self.mill_hole_label.setToolTip(
"Create Geometry for milling holes."
)
self.custom_box.addWidget(self.mill_hole_label)
grid1 = QtGui.QGridLayout()
self.custom_box.addLayout(grid1)
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"Diameter of the cutting tool."
)
grid1.addWidget(tdlabel, 0, 0)
self.tooldia_entry = LengthEntry()
grid1.addWidget(self.tooldia_entry, 0, 1)
choose_tools_label2 = QtGui.QLabel(
"Select from the tools section above\n"
"the tools you want to include."
)
self.custom_box.addWidget(choose_tools_label2)
self.generate_milling_button = QtGui.QPushButton('Generate Geometry')
self.generate_milling_button.setToolTip(
"Create the Geometry Object\n"
"for milling toolpaths."
)
self.custom_box.addWidget(self.generate_milling_button)
class GerberObjectUI(ObjectUI):
"""
User interface for Gerber objects.
"""
def __init__(self, parent=None):
ObjectUI.__init__(self, title='Gerber Object', parent=parent)
## Plot options
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
self.custom_box.addWidget(self.plot_options_label)
grid0 = QtGui.QGridLayout()
self.custom_box.addLayout(grid0)
# Plot CB
self.plot_cb = FCCheckBox(label='Plot')
self.plot_options_label.setToolTip(
"Plot (show) this object."
)
grid0.addWidget(self.plot_cb, 0, 0)
# Solid CB
self.solid_cb = FCCheckBox(label='Solid')
self.solid_cb.setToolTip(
"Solid color polygons."
)
grid0.addWidget(self.solid_cb, 0, 1)
# Multicolored CB
self.multicolored_cb = FCCheckBox(label='Multicolored')
self.multicolored_cb.setToolTip(
"Draw polygons in different colors."
)
grid0.addWidget(self.multicolored_cb, 0, 2)
## Isolation Routing
self.isolation_routing_label = QtGui.QLabel("<b>Isolation Routing:</b>")
self.isolation_routing_label.setToolTip(
"Create a Geometry object with\n"
"toolpaths to cut outside polygons."
)
self.custom_box.addWidget(self.isolation_routing_label)
grid1 = QtGui.QGridLayout()
self.custom_box.addLayout(grid1)
tdlabel = QtGui.QLabel('Tool dia:')
tdlabel.setToolTip(
"Diameter of the cutting tool."
)
grid1.addWidget(tdlabel, 0, 0)
self.iso_tool_dia_entry = LengthEntry()
grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
passlabel = QtGui.QLabel('Width (# passes):')
passlabel.setToolTip(
"Width of the isolation gap in\n"
"number (integer) of tool widths."
)
grid1.addWidget(passlabel, 1, 0)
self.iso_width_entry = IntEntry()
grid1.addWidget(self.iso_width_entry, 1, 1)
overlabel = QtGui.QLabel('Pass overlap:')
overlabel.setToolTip(
"How much (fraction of tool width)\n"
"to overlap each pass."
)
grid1.addWidget(overlabel, 2, 0)
self.iso_overlap_entry = FloatEntry()
grid1.addWidget(self.iso_overlap_entry, 2, 1)
# combine all passes CB
self.combine_passes_cb = FCCheckBox(label='Combine Passes')
self.combine_passes_cb.setToolTip(
"Combine all passes into one object"
)
grid1.addWidget(self.combine_passes_cb, 3, 0)
self.generate_iso_button = QtGui.QPushButton('Generate Geometry')
self.generate_iso_button.setToolTip(
"Create the Geometry Object\n"
"for isolation routing."
)
self.custom_box.addWidget(self.generate_iso_button)
## Board cuttout
self.board_cutout_label = QtGui.QLabel("<b>Board cutout:</b>")
self.board_cutout_label.setToolTip(
"Create toolpaths to cut around\n"
"the PCB and separate it from\n"
"the original board."
)
self.custom_box.addWidget(self.board_cutout_label)
grid2 = QtGui.QGridLayout()
self.custom_box.addLayout(grid2)
tdclabel = QtGui.QLabel('Tool dia:')
tdclabel.setToolTip(
"Diameter of the cutting tool."
)
grid2.addWidget(tdclabel, 0, 0)
self.cutout_tooldia_entry = LengthEntry()
grid2.addWidget(self.cutout_tooldia_entry, 0, 1)
marginlabel = QtGui.QLabel('Margin:')
marginlabel.setToolTip(
"Distance from objects at which\n"
"to draw the cutout."
)
grid2.addWidget(marginlabel, 1, 0)
self.cutout_margin_entry = LengthEntry()
grid2.addWidget(self.cutout_margin_entry, 1, 1)
gaplabel = QtGui.QLabel('Gap size:')
gaplabel.setToolTip(
"Size of the gaps in the toolpath\n"
"that will remain to hold the\n"
"board in place."
)
grid2.addWidget(gaplabel, 2, 0)
self.cutout_gap_entry = LengthEntry()
grid2.addWidget(self.cutout_gap_entry, 2, 1)
gapslabel = QtGui.QLabel('Gaps:')
gapslabel.setToolTip(
"Where to place the gaps, Top/Bottom\n"
"Left/Rigt, or on all 4 sides."
)
grid2.addWidget(gapslabel, 3, 0)
self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'},
{'label': '2 (L/R)', 'value': 'lr'},
{'label': '4', 'value': '4'}])
grid2.addWidget(self.gaps_radio, 3, 1)
self.generate_cutout_button = QtGui.QPushButton('Generate Geometry')
self.generate_cutout_button.setToolTip(
"Generate the geometry for\n"
"the board cutout."
)
self.custom_box.addWidget(self.generate_cutout_button)
## Non-copper regions
self.noncopper_label = QtGui.QLabel("<b>Non-copper regions:</b>")
self.noncopper_label.setToolTip(
"Create polygons covering the\n"
"areas without copper on the PCB.\n"
"Equivalent to the inverse of this\n"
"object. Can be used to remove all\n"
"copper from a specified region."
)
self.custom_box.addWidget(self.noncopper_label)
grid3 = QtGui.QGridLayout()
self.custom_box.addLayout(grid3)
# Margin
bmlabel = QtGui.QLabel('Boundary Margin:')
bmlabel.setToolTip(
"Specify the edge of the PCB\n"
"by drawing a box around all\n"
"objects with this minimum\n"
"distance."
)
grid3.addWidget(bmlabel, 0, 0)
self.noncopper_margin_entry = LengthEntry()
grid3.addWidget(self.noncopper_margin_entry, 0, 1)
# Rounded corners
self.noncopper_rounded_cb = FCCheckBox(label="Rounded corners")
self.noncopper_rounded_cb.setToolTip(
"Creates a Geometry objects with polygons\n"
"covering the copper-free areas of the PCB."
)
grid3.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2)
self.generate_noncopper_button = QtGui.QPushButton('Generate Geometry')
self.custom_box.addWidget(self.generate_noncopper_button)
## Bounding box
self.boundingbox_label = QtGui.QLabel('<b>Bounding Box:</b>')
self.custom_box.addWidget(self.boundingbox_label)
grid4 = QtGui.QGridLayout()
self.custom_box.addLayout(grid4)
bbmargin = QtGui.QLabel('Boundary Margin:')
bbmargin.setToolTip(
"Distance of the edges of the box\n"
"to the nearest polygon."
)
grid4.addWidget(bbmargin, 0, 0)
self.bbmargin_entry = LengthEntry()
grid4.addWidget(self.bbmargin_entry, 0, 1)
self.bbrounded_cb = FCCheckBox(label="Rounded corners")
self.bbrounded_cb.setToolTip(
"If the bounding box is \n"
"to have rounded corners\n"
"their radius is equal to\n"
"the margin."
)
grid4.addWidget(self.bbrounded_cb, 1, 0, 1, 2)
self.generate_bb_button = QtGui.QPushButton('Generate Geometry')
self.generate_bb_button.setToolTip(
"Generate the Geometry object."
)
self.custom_box.addWidget(self.generate_bb_button)
# def main():
#
# app = QtGui.QApplication(sys.argv)
# fc = GerberObjectUI()
# sys.exit(app.exec_())
#
#
# if __name__ == '__main__':
# main()

View File

@ -1,526 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://caram.cl/software/flatcam #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
from PyQt4 import QtGui, QtCore
# Prevent conflict with Qt5 and above.
from matplotlib import use as mpl_use
mpl_use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_agg import FigureCanvasAgg
import FlatCAMApp
import logging
log = logging.getLogger('base')
class CanvasCache(QtCore.QObject):
"""
Case story #1:
1) No objects in the project.
2) Object is created (new_object() emits object_created(obj)).
on_object_created() adds (i) object to collection and emits
(ii) new_object_available() then calls (iii) object.plot()
3) object.plot() creates axes if necessary on
app.collection.figure. Then plots on it.
4) Plots on a cache-size canvas (in background).
5) Plot completes. Bitmap is generated.
6) Visible canvas is painted.
"""
# Signals:
# A bitmap is ready to be displayed.
new_screen = QtCore.pyqtSignal()
def __init__(self, plotcanvas, app, dpi=50):
super(CanvasCache, self).__init__()
self.app = app
self.plotcanvas = plotcanvas
self.dpi = dpi
self.figure = Figure(dpi=dpi)
self.axes = self.figure.add_axes([0.0, 0.0, 1.0, 1.0], alpha=1.0)
self.axes.set_frame_on(False)
self.axes.set_xticks([])
self.axes.set_yticks([])
self.canvas = FigureCanvasAgg(self.figure)
self.cache = None
def run(self):
log.debug("CanvasCache Thread Started!")
self.plotcanvas.update_screen_request.connect(self.on_update_req)
self.app.new_object_available.connect(self.on_new_object_available)
def on_update_req(self, extents):
"""
Event handler for an updated display request.
:param extents: [xmin, xmax, ymin, ymax, zoom(optional)]
"""
log.debug("Canvas update requested: %s" % str(extents))
# Note: This information below might be out of date. Establish
# a protocol regarding when to change the canvas in the main
# thread and when to check these values here in the background,
# or pass this data in the signal (safer).
log.debug("Size: %s [px]" % str(self.plotcanvas.get_axes_pixelsize()))
log.debug("Density: %s [units/px]" % str(self.plotcanvas.get_density()))
# Move the requested screen portion to the main thread
# and inform about the update:
self.new_screen.emit()
# Continue to update the cache.
def on_new_object_available(self):
log.debug("A new object is available. Should plot it!")
class PlotCanvas(QtCore.QObject):
"""
Class handling the plotting area in the application.
"""
# Signals:
# Request for new bitmap to display. The parameter
# is a list with [xmin, xmax, ymin, ymax, zoom(optional)]
update_screen_request = QtCore.pyqtSignal(list)
def __init__(self, container, app):
"""
The constructor configures the Matplotlib figure that
will contain all plots, creates the base axes and connects
events to the plotting area.
:param container: The parent container in which to draw plots.
:rtype: PlotCanvas
"""
super(PlotCanvas, self).__init__()
self.app = app
# Options
self.x_margin = 15 # pixels
self.y_margin = 25 # Pixels
# Parent container
self.container = container
# Plots go onto a single matplotlib.figure
self.figure = Figure(dpi=50) # TODO: dpi needed?
self.figure.patch.set_visible(False)
# These axes show the ticks and grid. No plotting done here.
# New axes must have a label, otherwise mpl returns an existing one.
self.axes = self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label="base", alpha=0.0)
self.axes.set_aspect(1)
self.axes.grid(True)
self.axes.axhline(color='Black')
self.axes.axvline(color='Black')
# The canvas is the top level container (FigureCanvasQTAgg)
self.canvas = FigureCanvas(self.figure)
# self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
# self.canvas.setFocus()
#self.canvas.set_hexpand(1)
#self.canvas.set_vexpand(1)
#self.canvas.set_can_focus(True) # For key press
# Attach to parent
#self.container.attach(self.canvas, 0, 0, 600, 400) # TODO: Height and width are num. columns??
self.container.addWidget(self.canvas) # Qt
# Copy a bitmap of the canvas for quick animation.
# Update every time the canvas is re-drawn.
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
### Bitmap Cache
self.cache = CanvasCache(self, self.app)
self.cache_thread = QtCore.QThread()
self.cache.moveToThread(self.cache_thread)
super(PlotCanvas, self).connect(self.cache_thread, QtCore.SIGNAL("started()"), self.cache.run)
# self.connect()
self.cache_thread.start()
self.cache.new_screen.connect(self.on_new_screen)
# Events
self.canvas.mpl_connect('button_press_event', self.on_mouse_press)
self.canvas.mpl_connect('button_release_event', self.on_mouse_release)
self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
#self.canvas.connect('configure-event', self.auto_adjust_axes)
self.canvas.mpl_connect('resize_event', self.auto_adjust_axes)
#self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
#self.canvas.connect("scroll-event", self.on_scroll)
self.canvas.mpl_connect('scroll_event', self.on_scroll)
self.canvas.mpl_connect('key_press_event', self.on_key_down)
self.canvas.mpl_connect('key_release_event', self.on_key_up)
self.canvas.mpl_connect('draw_event', self.on_draw)
self.mouse = [0, 0]
self.key = None
self.pan_axes = []
self.panning = False
def on_new_screen(self):
log.debug("Cache updated the screen!")
def on_key_down(self, event):
"""
:param event:
:return:
"""
FlatCAMApp.App.log.debug('on_key_down(): ' + str(event.key))
self.key = event.key
def on_key_up(self, event):
"""
:param event:
:return:
"""
self.key = None
def mpl_connect(self, event_name, callback):
"""
Attach an event handler to the canvas through the Matplotlib interface.
:param event_name: Name of the event
:type event_name: str
:param callback: Function to call
:type callback: func
:return: Connection id
:rtype: int
"""
return self.canvas.mpl_connect(event_name, callback)
def mpl_disconnect(self, cid):
"""
Disconnect callback with the give id.
:param cid: Callback id.
:return: None
"""
self.canvas.mpl_disconnect(cid)
def connect(self, event_name, callback):
"""
Attach an event handler to the canvas through the native Qt interface.
:param event_name: Name of the event
:type event_name: str
:param callback: Function to call
:type callback: function
:return: Nothing
"""
self.canvas.connect(event_name, callback)
def clear(self):
"""
Clears axes and figure.
:return: None
"""
# Clear
self.axes.cla()
try:
self.figure.clf()
except KeyError:
FlatCAMApp.App.log.warning("KeyError in MPL figure.clf()")
# Re-build
self.figure.add_axes(self.axes)
self.axes.set_aspect(1)
self.axes.grid(True)
# Re-draw
self.canvas.draw_idle()
def adjust_axes(self, xmin, ymin, xmax, ymax):
"""
Adjusts all axes while maintaining the use of the whole canvas
and an aspect ratio to 1:1 between x and y axes. The parameters are an original
request that will be modified to fit these restrictions.
:param xmin: Requested minimum value for the X axis.
:type xmin: float
:param ymin: Requested minimum value for the Y axis.
:type ymin: float
:param xmax: Requested maximum value for the X axis.
:type xmax: float
:param ymax: Requested maximum value for the Y axis.
:type ymax: float
:return: None
"""
# FlatCAMApp.App.log.debug("PC.adjust_axes()")
width = xmax - xmin
height = ymax - ymin
try:
r = width / height
except ZeroDivisionError:
FlatCAMApp.App.log.error("Height is %f" % height)
return
canvas_w, canvas_h = self.canvas.get_width_height()
canvas_r = float(canvas_w) / canvas_h
x_ratio = float(self.x_margin) / canvas_w
y_ratio = float(self.y_margin) / canvas_h
if r > canvas_r:
ycenter = (ymin + ymax) / 2.0
newheight = height * r / canvas_r
ymin = ycenter - newheight / 2.0
ymax = ycenter + newheight / 2.0
else:
xcenter = (xmax + xmin) / 2.0
newwidth = width * canvas_r / r
xmin = xcenter - newwidth / 2.0
xmax = xcenter + newwidth / 2.0
# Adjust axes
for ax in self.figure.get_axes():
if ax._label != 'base':
ax.set_frame_on(False) # No frame
ax.set_xticks([]) # No tick
ax.set_yticks([]) # No ticks
ax.patch.set_visible(False) # No background
ax.set_aspect(1)
ax.set_xlim((xmin, xmax))
ax.set_ylim((ymin, ymax))
ax.set_position([x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])
# Sync re-draw to proper paint on form resize
self.canvas.draw()
##### Temporary place-holder for cached update #####
self.update_screen_request.emit([0, 0, 0, 0, 0])
def auto_adjust_axes(self, *args):
"""
Calls ``adjust_axes()`` using the extents of the base axes.
:rtype : None
:return: None
"""
xmin, xmax = self.axes.get_xlim()
ymin, ymax = self.axes.get_ylim()
self.adjust_axes(xmin, ymin, xmax, ymax)
def zoom(self, factor, center=None):
"""
Zooms the plot by factor around a given
center point. Takes care of re-drawing.
:param factor: Number by which to scale the plot.
:type factor: float
:param center: Coordinates [x, y] of the point around which to scale the plot.
:type center: list
:return: None
"""
xmin, xmax = self.axes.get_xlim()
ymin, ymax = self.axes.get_ylim()
width = xmax - xmin
height = ymax - ymin
if center is None or center == [None, None]:
center = [(xmin + xmax) / 2.0, (ymin + ymax) / 2.0]
# For keeping the point at the pointer location
relx = (xmax - center[0]) / width
rely = (ymax - center[1]) / height
new_width = width / factor
new_height = height / factor
xmin = center[0] - new_width * (1 - relx)
xmax = center[0] + new_width * relx
ymin = center[1] - new_height * (1 - rely)
ymax = center[1] + new_height * rely
# Adjust axes
for ax in self.figure.get_axes():
ax.set_xlim((xmin, xmax))
ax.set_ylim((ymin, ymax))
# Async re-draw
self.canvas.draw_idle()
##### Temporary place-holder for cached update #####
self.update_screen_request.emit([0, 0, 0, 0, 0])
def pan(self, x, y):
xmin, xmax = self.axes.get_xlim()
ymin, ymax = self.axes.get_ylim()
width = xmax - xmin
height = ymax - ymin
# Adjust axes
for ax in self.figure.get_axes():
ax.set_xlim((xmin + x * width, xmax + x * width))
ax.set_ylim((ymin + y * height, ymax + y * height))
# Re-draw
self.canvas.draw_idle()
##### Temporary place-holder for cached update #####
self.update_screen_request.emit([0, 0, 0, 0, 0])
def new_axes(self, name):
"""
Creates and returns an Axes object attached to this object's Figure.
:param name: Unique label for the axes.
:return: Axes attached to the figure.
:rtype: Axes
"""
return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)
def on_scroll(self, event):
"""
Scroll event handler.
:param event: Event object containing the event information.
:return: None
"""
# So it can receive key presses
# self.canvas.grab_focus()
self.canvas.setFocus()
# Event info
# z, direction = event.get_scroll_direction()
if self.key is None:
if event.button == 'up':
self.zoom(1.5, self.mouse)
else:
self.zoom(1 / 1.5, self.mouse)
return
if self.key == 'shift':
if event.button == 'up':
self.pan(0.3, 0)
else:
self.pan(-0.3, 0)
return
if self.key == 'control':
if event.button == 'up':
self.pan(0, 0.3)
else:
self.pan(0, -0.3)
return
def on_mouse_press(self, event):
# Check for middle mouse button press
if event.button == self.app.mouse_pan_button:
# Prepare axes for pan (using 'matplotlib' pan function)
self.pan_axes = []
for a in self.figure.get_axes():
if (event.x is not None and event.y is not None and a.in_axes(event) and
a.get_navigate() and a.can_pan()):
a.start_pan(event.x, event.y, 1)
self.pan_axes.append(a)
# Set pan view flag
if len(self.pan_axes) > 0: self.panning = True;
def on_mouse_release(self, event):
# Check for middle mouse button release to complete pan procedure
if event.button == self.app.mouse_pan_button:
for a in self.pan_axes:
a.end_pan()
# Clear pan flag
self.panning = False
def on_mouse_move(self, event):
"""
Mouse movement event hadler. Stores the coordinates. Updates view on pan.
:param event: Contains information about the event.
:return: None
"""
self.mouse = [event.xdata, event.ydata]
# Update pan view on mouse move
if self.panning is True:
for a in self.pan_axes:
a.drag_pan(1, event.key, event.x, event.y)
# Async re-draw (redraws only on thread idle state, uses timer on backend)
self.canvas.draw_idle()
##### Temporary place-holder for cached update #####
self.update_screen_request.emit([0, 0, 0, 0, 0])
def on_draw(self, renderer):
# Store background on canvas redraw
self.background = self.canvas.copy_from_bbox(self.axes.bbox)
def get_axes_pixelsize(self):
"""
Axes size in pixels.
:return: Pixel width and height
:rtype: tuple
"""
bbox = self.axes.get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
width, height = bbox.width, bbox.height
width *= self.figure.dpi
height *= self.figure.dpi
return width, height
def get_density(self):
"""
Returns unit length per pixel on horizontal
and vertical axes.
:return: X and Y density
:rtype: tuple
"""
xpx, ypx = self.get_axes_pixelsize()
xmin, xmax = self.axes.get_xlim()
ymin, ymax = self.axes.get_ylim()
width = xmax - xmin
height = ymax - ymin
return width / xpx, height / ypx

View File

@ -1,8 +0,0 @@
FlatCAM: 2D Computer-Aided PCB Manufacturing
============================================
(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
CAD program, and create G-Code for Isolation routing.

View File

@ -1,198 +0,0 @@
from PyQt4 import QtGui
from GUIElements import RadioSet, EvalEntry, LengthEntry
from FlatCAMTool import FlatCAMTool
#from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon
from FlatCAMObj import *
from shapely.geometry import Point
from shapely import affinity
class DblSidedTool(FlatCAMTool):
toolName = "Double-Sided PCB Tool"
def __init__(self, app):
FlatCAMTool.__init__(self, app)
## Title
title_label = QtGui.QLabel("<font size=4><b>%s</b></font>" % self.toolName)
self.layout.addWidget(title_label)
## Form Layout
form_layout = QtGui.QFormLayout()
self.layout.addLayout(form_layout)
## Layer to mirror
self.object_combo = QtGui.QComboBox()
self.object_combo.setModel(self.app.collection.model)
self.botlay_label = QtGui.QLabel("Bottom Layer:")
self.botlay_label.setToolTip(
"Layer to be mirrorer."
)
# form_layout.addRow("Bottom Layer:", self.object_combo)
form_layout.addRow(self.botlay_label, self.object_combo)
## Axis
self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'},
{'label': 'Y', 'value': 'Y'}])
self.mirax_label = QtGui.QLabel("Mirror Axis:")
self.mirax_label.setToolTip(
"Mirror vertically (X) or horizontally (Y)."
)
# form_layout.addRow("Mirror Axis:", self.mirror_axis)
form_layout.addRow(self.mirax_label, self.mirror_axis)
## Axis Location
self.axis_location = RadioSet([{'label': 'Point', 'value': 'point'},
{'label': 'Box', 'value': 'box'}])
self.axloc_label = QtGui.QLabel("Axis Location:")
self.axloc_label.setToolTip(
"The axis should pass through a <b>point</b> or cut "
"a specified <b>box</b> (in a Geometry object) in "
"the middle."
)
# form_layout.addRow("Axis Location:", self.axis_location)
form_layout.addRow(self.axloc_label, self.axis_location)
## Point/Box
self.point_box_container = QtGui.QVBoxLayout()
self.pb_label = QtGui.QLabel("Point/Box:")
self.pb_label.setToolTip(
"Specify the point (x, y) through which the mirror axis "
"passes or the Geometry object containing a rectangle "
"that the mirror axis cuts in half."
)
# form_layout.addRow("Point/Box:", self.point_box_container)
form_layout.addRow(self.pb_label, self.point_box_container)
self.point = EvalEntry()
self.point_box_container.addWidget(self.point)
self.box_combo = QtGui.QComboBox()
self.box_combo.setModel(self.app.collection.model)
self.point_box_container.addWidget(self.box_combo)
self.box_combo.hide()
## Alignment holes
self.alignment_holes = EvalEntry()
self.ah_label = QtGui.QLabel("Alignment Holes:")
self.ah_label.setToolTip(
"Alignment holes (x1, y1), (x2, y2), ... "
"on one side of the mirror axis."
)
form_layout.addRow(self.ah_label, self.alignment_holes)
## Drill diameter for alignment holes
self.drill_dia = LengthEntry()
self.dd_label = QtGui.QLabel("Drill diam.:")
self.dd_label.setToolTip(
"Diameter of the drill for the "
"alignment holes."
)
form_layout.addRow(self.dd_label, self.drill_dia)
## Buttons
hlay = QtGui.QHBoxLayout()
self.layout.addLayout(hlay)
hlay.addStretch()
self.create_alignment_hole_button = QtGui.QPushButton("Create Alignment Drill")
self.create_alignment_hole_button.setToolTip(
"Creates an Excellon Object containing the "
"specified alignment holes and their mirror "
"images."
)
self.mirror_object_button = QtGui.QPushButton("Mirror Object")
self.mirror_object_button.setToolTip(
"Mirrors (flips) the specified object around "
"the specified axis. Does not create a new "
"object, but modifies it."
)
hlay.addWidget(self.create_alignment_hole_button)
hlay.addWidget(self.mirror_object_button)
self.layout.addStretch()
## Signals
self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
self.mirror_object_button.clicked.connect(self.on_mirror)
self.axis_location.group_toggle_fn = self.on_toggle_pointbox
## Initialize form
self.mirror_axis.set_value('X')
self.axis_location.set_value('point')
def on_create_alignment_holes(self):
axis = self.mirror_axis.get_value()
mode = self.axis_location.get_value()
if mode == "point":
px, py = self.point.get_value()
else:
selection_index = self.box_combo.currentIndex()
bb_obj = self.app.collection.object_list[selection_index] # TODO: Direct access??
xmin, ymin, xmax, ymax = bb_obj.bounds()
px = 0.5 * (xmin + xmax)
py = 0.5 * (ymin + ymax)
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
dia = self.drill_dia.get_value()
tools = {"1": {"C": dia}}
# holes = self.alignment_holes.get_value()
holes = eval('[{}]'.format(self.alignment_holes.text()))
drills = []
for hole in holes:
point = Point(hole)
point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
drills.append({"point": point, "tool": "1"})
drills.append({"point": point_mirror, "tool": "1"})
def obj_init(obj_inst, app_inst):
obj_inst.tools = tools
obj_inst.drills = drills
obj_inst.create_geometry()
self.app.new_object("excellon", "Alignment Drills", obj_init)
def on_mirror(self):
selection_index = self.object_combo.currentIndex()
fcobj = self.app.collection.object_list[selection_index]
# For now, lets limit to Gerbers and Excellons.
# assert isinstance(gerb, FlatCAMGerber)
if not isinstance(fcobj, FlatCAMGerber) and \
not isinstance(fcobj, FlatCAMExcellon) and \
not isinstance(fcobj, FlatCAMGeometry):
self.app.inform.emit("ERROR: Only Gerber, Excellon and Geometry objects can be mirrored.")
return
axis = self.mirror_axis.get_value()
mode = self.axis_location.get_value()
if mode == "point":
px, py = self.point.get_value()
else:
selection_index = self.box_combo.currentIndex()
bb_obj = self.app.collection.object_list[selection_index] # TODO: Direct access??
xmin, ymin, xmax, ymax = bb_obj.bounds()
px = 0.5 * (xmin + xmax)
py = 0.5 * (ymin + ymax)
# Ensure that the selected object will display when it is mirrored.
# If an object's plot setting is False it will still be available in
# the combo box. If the plot is not enforced to True then the user
# gets no feedback of the operation.
fcobj.options["plot"] = True;
fcobj.mirror(axis, [px, py])
fcobj.plot()
def on_toggle_pointbox(self):
if self.axis_location.get_value() == "point":
self.point.show()
self.box_combo.hide()
else:
self.point.hide()
self.box_combo.show()

View File

@ -1,74 +0,0 @@
from PyQt4 import QtGui
from FlatCAMTool import FlatCAMTool
from copy import copy
from math import sqrt
class Measurement(FlatCAMTool):
toolName = "Measurement Tool"
def __init__(self, app):
FlatCAMTool.__init__(self, app)
# self.setContentsMargins(0, 0, 0, 0)
self.layout.setMargin(0)
self.layout.setContentsMargins(0, 0, 3, 0)
self.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Maximum)
self.point1 = None
self.point2 = None
self.label = QtGui.QLabel("Click on a reference point ...")
self.label.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Plain)
self.label.setMargin(3)
self.layout.addWidget(self.label)
# self.layout.setMargin(0)
self.setVisible(False)
self.click_subscription = None
self.move_subscription = None
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, **kwargs)
self.app.ui.right_layout.addWidget(self)
self.app.plotcanvas.mpl_connect('key_press_event', self.on_key_press)
def run(self):
self.toggle()
def on_click(self, event):
if self.point1 is None:
self.point1 = (event.xdata, event.ydata)
else:
self.point2 = copy(self.point1)
self.point1 = (event.xdata, event.ydata)
self.on_move(event)
def on_key_press(self, event):
if event.key == 'r':
self.toggle()
def toggle(self):
if self.isVisible():
self.setVisible(False)
self.app.plotcanvas.mpl_disconnect(self.move_subscription)
self.app.plotcanvas.mpl_disconnect(self.click_subscription)
else:
self.setVisible(True)
self.move_subscription = self.app.plotcanvas.mpl_connect('motion_notify_event', self.on_move)
self.click_subscription = self.app.plotcanvas.mpl_connect('button_press_event', self.on_click)
def on_move(self, event):
if self.point1 is None:
self.label.setText("Click on a reference point...")
else:
try:
dx = event.xdata - self.point1[0]
dy = event.ydata - self.point1[1]
d = sqrt(dx**2 + dy**2)
self.label.setText("D = %.4f D(x) = %.4f D(y) = %.4f" % (d, dx, dy))
except TypeError:
pass
if self.update is not None:
self.update()

View File

@ -1,319 +0,0 @@
from PyQt4 import QtGui, QtCore
from PyQt4 import Qt
from GUIElements import FCEntry, FCButton
from FlatCAMTool import FlatCAMTool
from camlib import *
class ToolTransform(FlatCAMTool):
toolName = "Object Transformation"
rotateName = "Rotate Transformation"
skewName = "Skew/Shear Transformation"
flipName = "Flip Transformation"
def __init__(self, app):
FlatCAMTool.__init__(self, app)
self.transform_lay = QtGui.QVBoxLayout()
self.layout.addLayout(self.transform_lay)
## Title
title_label = QtGui.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
self.transform_lay.addWidget(title_label)
self.empty_label = QtGui.QLabel("")
self.empty_label.setFixedWidth(80)
self.empty_label1 = QtGui.QLabel("")
self.empty_label1.setFixedWidth(80)
self.empty_label2 = QtGui.QLabel("")
self.empty_label2.setFixedWidth(80)
self.transform_lay.addWidget(self.empty_label)
## Rotate Title
rotate_title_label = QtGui.QLabel("<font size=3><b>%s</b></font>" % self.rotateName)
self.transform_lay.addWidget(rotate_title_label)
## Form Layout
form_layout = QtGui.QFormLayout()
self.transform_lay.addLayout(form_layout)
self.rotate_entry = FCEntry()
self.rotate_entry.setFixedWidth(70)
self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.rotate_label = QtGui.QLabel("Angle Rotation:")
self.rotate_label.setToolTip(
"Angle for Rotation action, in degrees.\n"
"Float number between -360 and 359.\n"
"Positive numbers for CW motion.\n"
"Negative numbers for CCW motion."
)
self.rotate_label.setFixedWidth(80)
self.rotate_button = FCButton()
self.rotate_button.set_value("Rotate")
self.rotate_button.setToolTip(
"Rotate the selected object(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected objects.\n"
)
self.rotate_button.setFixedWidth(70)
form_layout.addRow(self.rotate_label, self.rotate_entry)
form_layout.addRow(self.empty_label, self.rotate_button)
self.transform_lay.addWidget(self.empty_label1)
## Skew Title
skew_title_label = QtGui.QLabel("<font size=3><b>%s</b></font>" % self.skewName)
self.transform_lay.addWidget(skew_title_label)
## Form Layout
form1_layout = QtGui.QFormLayout()
self.transform_lay.addLayout(form1_layout)
self.skewx_entry = FCEntry()
self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.skewx_entry.setFixedWidth(70)
self.skewx_label = QtGui.QLabel("Angle SkewX:")
self.skewx_label.setToolTip(
"Angle for Skew action, in degrees.\n"
"Float number between -360 and 359."
)
self.skewx_label.setFixedWidth(80)
self.skewx_button = FCButton()
self.skewx_button.set_value("Skew_X")
self.skewx_button.setToolTip(
"Skew/shear the selected object(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected objects.\n")
self.skewx_button.setFixedWidth(70)
self.skewy_entry = FCEntry()
self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.skewy_entry.setFixedWidth(70)
self.skewy_label = QtGui.QLabel("Angle SkewY:")
self.skewy_label.setToolTip(
"Angle for Skew action, in degrees.\n"
"Float number between -360 and 359."
)
self.skewy_label.setFixedWidth(80)
self.skewy_button = FCButton()
self.skewy_button.set_value("Skew_Y")
self.skewy_button.setToolTip(
"Skew/shear the selected object(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected objects.\n")
self.skewy_button.setFixedWidth(70)
form1_layout.addRow(self.skewx_label, self.skewx_entry)
form1_layout.addRow(self.empty_label, self.skewx_button)
form1_layout.addRow(self.skewy_label, self.skewy_entry)
form1_layout.addRow(self.empty_label, self.skewy_button)
self.transform_lay.addWidget(self.empty_label2)
## Flip Title
flip_title_label = QtGui.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
self.transform_lay.addWidget(flip_title_label)
## Form Layout
form2_layout = QtGui.QFormLayout()
self.transform_lay.addLayout(form2_layout)
self.flipx_button = FCButton()
self.flipx_button.set_value("Flip_X")
self.flipx_button.setToolTip(
"Flip the selected object(s) over the X axis.\n"
"Does not create a new object.\n "
)
self.flipx_button.setFixedWidth(70)
self.flipy_button = FCButton()
self.flipy_button.set_value("Flip_Y")
self.flipy_button.setToolTip(
"Flip the selected object(s) over the X axis.\n"
"Does not create a new object.\n "
)
self.flipy_button.setFixedWidth(70)
form2_layout.setSpacing(16)
form2_layout.addRow(self.flipx_button, self.flipy_button)
self.transform_lay.addStretch()
## Signals
self.rotate_button.clicked.connect(self.on_rotate)
self.skewx_button.clicked.connect(self.on_skewx)
self.skewy_button.clicked.connect(self.on_skewy)
self.flipx_button.clicked.connect(self.on_flipx)
self.flipy_button.clicked.connect(self.on_flipy)
self.rotate_entry.returnPressed.connect(self.on_rotate)
self.skewx_entry.returnPressed.connect(self.on_skewx)
self.skewy_entry.returnPressed.connect(self.on_skewy)
## Initialize form
self.rotate_entry.set_value('0')
self.skewx_entry.set_value('0')
self.skewy_entry.set_value('0')
def on_rotate(self):
value = float(self.rotate_entry.get_value())
self.on_rotate_action(value)
return
def on_flipx(self):
self.on_flip("Y")
return
def on_flipy(self):
self.on_flip("X")
return
def on_skewx(self):
value = float(self.skewx_entry.get_value())
self.on_skew("X", value)
return
def on_skewy(self):
value = float(self.skewy_entry.get_value())
self.on_skew("Y", value)
return
def on_rotate_action(self, num):
obj_list = self.app.collection.get_selected()
xminlist = []
yminlist = []
xmaxlist = []
ymaxlist = []
if not obj_list:
self.app.inform.emit("WARNING: No object selected.")
msg = "Please Select an object to rotate!"
warningbox = QtGui.QMessageBox()
warningbox.setText(msg)
warningbox.setWindowTitle("Warning ...")
warningbox.setWindowIcon(QtGui.QIcon('share/warning.png'))
warningbox.setStandardButtons(QtGui.QMessageBox.Ok)
warningbox.setDefaultButton(QtGui.QMessageBox.Ok)
warningbox.exec_()
else:
try:
# first get a bounding box to fit all
for obj in obj_list:
xmin, ymin, xmax, ymax = obj.bounds()
xminlist.append(xmin)
yminlist.append(ymin)
xmaxlist.append(xmax)
ymaxlist.append(ymax)
# get the minimum x,y and maximum x,y for all objects selected
xminimal = min(xminlist)
yminimal = min(yminlist)
xmaximal = max(xmaxlist)
ymaximal = max(ymaxlist)
for sel_obj in obj_list:
px = 0.5 * (xminimal + xmaximal)
py = 0.5 * (yminimal + ymaximal)
sel_obj.rotate(-num, point=(px, py))
sel_obj.plot()
self.app.inform.emit('Object was rotated ...')
except Exception as e:
self.app.inform.emit("[ERROR] Due of %s, rotation movement was not executed." % str(e))
raise
def on_flip(self, axis):
obj_list = self.app.collection.get_selected()
xminlist = []
yminlist = []
xmaxlist = []
ymaxlist = []
if not obj_list:
self.app.inform.emit("WARNING: No object selected.")
msg = "Please Select an object to flip!"
warningbox = QtGui.QMessageBox()
warningbox.setText(msg)
warningbox.setWindowTitle("Warning ...")
warningbox.setWindowIcon(QtGui.QIcon('share/warning.png'))
warningbox.setStandardButtons(QtGui.QMessageBox.Ok)
warningbox.setDefaultButton(QtGui.QMessageBox.Ok)
warningbox.exec_()
return
else:
try:
# first get a bounding box to fit all
for obj in obj_list:
xmin, ymin, xmax, ymax = obj.bounds()
xminlist.append(xmin)
yminlist.append(ymin)
xmaxlist.append(xmax)
ymaxlist.append(ymax)
# get the minimum x,y and maximum x,y for all objects selected
xminimal = min(xminlist)
yminimal = min(yminlist)
xmaximal = max(xmaxlist)
ymaximal = max(ymaxlist)
px = 0.5 * (xminimal + xmaximal)
py = 0.5 * (yminimal + ymaximal)
# execute mirroring
for obj in obj_list:
if axis is 'X':
obj.mirror('X', [px, py])
obj.plot()
self.app.inform.emit('Flipped on the Y axis ...')
elif axis is 'Y':
obj.mirror('Y', [px, py])
obj.plot()
self.app.inform.emit('Flipped on the X axis ...')
except Exception as e:
self.app.inform.emit("[ERROR] Due of %s, Flip action was not executed.")
raise
def on_skew(self, axis, num):
obj_list = self.app.collection.get_selected()
xminlist = []
yminlist = []
if not obj_list:
self.app.inform.emit("WARNING: No object selected.")
msg = "Please Select an object to skew/shear!"
warningbox = QtGui.QMessageBox()
warningbox.setText(msg)
warningbox.setWindowTitle("Warning ...")
warningbox.setWindowIcon(QtGui.QIcon('share/warning.png'))
warningbox.setStandardButtons(QtGui.QMessageBox.Ok)
warningbox.setDefaultButton(QtGui.QMessageBox.Ok)
warningbox.exec_()
else:
try:
# first get a bounding box to fit all
for obj in obj_list:
xmin, ymin, xmax, ymax = obj.bounds()
xminlist.append(xmin)
yminlist.append(ymin)
# get the minimum x,y and maximum x,y for all objects selected
xminimal = min(xminlist)
yminimal = min(yminlist)
for obj in obj_list:
if axis is 'X':
obj.skew(num, 0, point=(xminimal, yminimal))
elif axis is 'Y':
obj.skew(0, num, point=(xminimal, yminimal))
obj.plot()
self.app.inform.emit('Object was skewed on %s axis ...' % str(axis))
except Exception as e:
self.app.inform.emit("[ERROR] Due of %s, Skew action was not executed." % str(e))
raise
# end of file

4423
camlib.py

File diff suppressed because it is too large Load Diff

35
flatcam
View File

@ -1,35 +0,0 @@
#!/usr/bin/env python3
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
############################################################
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
from FlatCAMApp import App
def debug_trace():
"""
Set a tracepoint in the Python debugger that works with Qt
:return: None
"""
from PyQt4.QtCore import pyqtRemoveInputHook
#from pdb import set_trace
pyqtRemoveInputHook()
#set_trace()
debug_trace()
# All X11 calling should be thread safe otherwise we have strange issues
# QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
# NOTE: Never talk to the GUI from threads! This is why I commented the above.
app = QtGui.QApplication(sys.argv)
QtCore.QDir.setSearchPaths("share", str(("share", "share/flatcam", "/usr/share/flatcam")));
fc = App()
sys.exit(app.exec_())

View File

@ -1,8 +0,0 @@
[Desktop Entry]
Name=FlatCAM
Comment=2D Computer-Aided PCB Manufacturing
Exec=flatcam
Icon=flatcam
Keywords=cam;pcb;gerber
Terminal=false
Type=Application

View File

@ -1,70 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
# #
# Creates a portlable copy of FlatCAM, including Python #
# itself and all dependencies. #
# #
# This is not an aid to install FlatCAM from source on #
# Windows platforms. It is only useful when FlatCAM is up #
# and running and ready to be packaged. #
############################################################
# Files not needed: Qt, tk.dll, tcl.dll, tk/, tcl/, vtk/,
# scipy.lib.lapack.flapack.pyd, scipy.lib.blas.fblas.pyd,
# numpy.core._dotblas.pyd, scipy.sparse.sparsetools._bsr.pyd,
# scipy.sparse.sparsetools._csr.pyd, scipy.sparse.sparsetools._csc.pyd,
# scipy.sparse.sparsetools._coo.pyd
import os, site, sys
from cx_Freeze import setup, Executable
# this is done to solve the tkinter not being found (Python3)
# still the DLL's need to be copied to the lib folder but the script can't do it
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')
## Get the site-package folder, not everybody will install
## Python into C:\PythonXX
site_dir = site.getsitepackages()[1]
include_files = []
include_files.append((os.path.join(site_dir, "shapely"), "shapely"))
include_files.append((os.path.join(site_dir, "svg"), "svg"))
include_files.append((os.path.join(site_dir, "svg/path"), "svg"))
include_files.append((os.path.join(site_dir, "matplotlib"), "matplotlib"))
include_files.append(("share", "share"))
include_files.append((os.path.join(site_dir, "rtree"), "rtree"))
include_files.append(("README.md", "README.md"))
include_files.append(("LICENSE", "LICENSE"))
base = None
## Lets not open the console while running the app
if sys.platform == "win32":
base = "Win32GUI"
buildOptions = dict(
include_files=include_files,
# excludes=['PyQt4', 'tk', 'tcl']
excludes=['scipy.lib.lapack.flapack.pyd',
'scipy.lib.blas.fblas.pyd',
'QtOpenGL4.dll']
)
print(("INCLUDE_FILES", include_files))
exec(compile(open('clean.py').read(), 'clean.py', 'exec'))
setup(
name="FlatCAM",
author="Juan Pablo Caram",
version="8.5",
description="FlatCAM: 2D Computer Aided PCB Manufacturing",
options=dict(build_exe=buildOptions),
executables=[Executable("FlatCAM.py", icon='share/flatcam_icon48.ico', base=base)]
)

View File

@ -1,10 +0,0 @@
# This file contains python only requirements to be installed with pip
# Python pacakges that cannot be installed with pip (e.g. PyQT4) are not included.
# Usage: pip install -r requirements.txt
numpy>=1.8
matplotlib>=1.3.1
shapely>=1.3
simplejson
rtree
scipy
svg.path

View File

@ -1,75 +0,0 @@
############################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Damian Wrobel <dwrobel@ertelnet.rybnik.pl> #
# Date: 05/23/2017 #
# MIT Licence #
# A setuptools based setup module #
############################################################
from setuptools import setup
import glob
setup(
name='FlatCAM',
version='8.5',
description='2D Computer-Aided PCB Manufacturing',
long_description=('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.'),
url='http://flatcam.org/',
author='Juan Pablo Caram',
license='MIT',
packages=[
'descartes',
'tclCommands'
],
py_modules=[
"camlib",
"DblSidedTool",
"FlatCAMApp",
"FlatCAMCommon",
"FlatCAMDraw",
"FlatCAMGUI",
"FlatCAMObj",
"FlatCAMProcess",
"FlatCAMShell",
"FlatCAMTool",
"FlatCAMVersion",
"FlatCAMWorker",
"GUIElements",
"MeasurementTool",
"ObjectCollection",
"ObjectUI",
"PlotCanvas",
"svgparse",
"termwidget"
],
install_requires=[
'simplejson',
'numpy>=1.8',
'scipy',
'matplotlib>=1.3.1',
'shapely>=1.3'
'rtree',
'svg.path'
],
include_package_data=True,
data_files=[
('share/flatcam', glob.glob('share/*'))
],
scripts=['flatcam']
)

View File

@ -1,20 +0,0 @@
#!/bin/sh -e
# "-e" exists on first error.
apt-get install libpng-dev \
libfreetype6 \
libfreetype6-dev \
python-dev \
python-simplejson \
python-qt4 \
python-numpy \
python-scipy \
python-matplotlib \
libgeos-dev \
python-shapely \
python-pip \
libspatialindex-dev
easy_install -U distribute
pip install --upgrade matplotlib Shapely
pip install rtree
pip install svg.path

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

Some files were not shown because too many files have changed in this diff Show More