Convertion to Qt. Major refactoring.
This commit is contained in:
parent
74a1331a7a
commit
16734f5d1a
24
FlatCAM.py
24
FlatCAM.py
|
@ -1,13 +1,15 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
import sys
|
||||
from PyQt4 import QtGui
|
||||
from FlatCAMApp import App
|
||||
|
||||
from gi.repository import Gtk
|
||||
from FlatCAMApp import *
|
||||
def debug_trace():
|
||||
'''Set a tracepoint in the Python debugger that works with Qt'''
|
||||
from PyQt4.QtCore import pyqtRemoveInputHook
|
||||
#from pdb import set_trace
|
||||
pyqtRemoveInputHook()
|
||||
#set_trace()
|
||||
|
||||
app = App()
|
||||
Gtk.main()
|
||||
debug_trace()
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
fc = App()
|
||||
sys.exit(app.exec_())
|
2994
FlatCAMApp.py
2994
FlatCAMApp.py
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
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
|
|
@ -0,0 +1,688 @@
|
|||
from PyQt4 import QtGui, QtCore, Qt
|
||||
from GUIElements import *
|
||||
|
||||
|
||||
class FlatCAMGUI(QtGui.QMainWindow):
|
||||
|
||||
def __init__(self):
|
||||
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', self)
|
||||
self.menufile.addAction(self.menufilenew)
|
||||
# Open recent
|
||||
|
||||
# Recent
|
||||
self.recent = self.menufile.addMenu(QtGui.QIcon('share/folder16.png'), "Open recent ...")
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# Quit
|
||||
exit_action = QtGui.QAction(QtGui.QIcon('share/power16.png'), '&Exit', self)
|
||||
# exitAction.setShortcut('Ctrl+Q')
|
||||
# exitAction.setStatusTip('Exit application')
|
||||
exit_action.triggered.connect(QtGui.qApp.quit)
|
||||
|
||||
self.menufile.addAction(exit_action)
|
||||
|
||||
### Edit ###
|
||||
self.menuedit = self.menu.addMenu('&Edit')
|
||||
self.menueditdelete = self.menuedit.addAction(QtGui.QIcon('share/trash16.png'), 'Delete')
|
||||
|
||||
### Options ###
|
||||
self.menuoptions = self.menu.addMenu('&Options')
|
||||
self.menuoptions_transfer = self.menuoptions.addMenu('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 = self.menu.addMenu('&Tool')
|
||||
|
||||
### Help ###
|
||||
self.menuhelp = self.menu.addMenu('&Help')
|
||||
self.menuhelp_about = self.menuhelp.addAction(QtGui.QIcon('share/tv16.png'), 'About FlatCAM')
|
||||
self.menuhelp_manual = self.menuhelp.addAction(QtGui.QIcon('share/globe16.png'), 'Manual')
|
||||
|
||||
###############
|
||||
### Toolbar ###
|
||||
###############
|
||||
self.toolbar = QtGui.QToolBar()
|
||||
self.addToolBar(self.toolbar)
|
||||
|
||||
self.zoom_fit_btn = self.toolbar.addAction(QtGui.QIcon('share/zoom_fit32.png'), "&Zoom Fit")
|
||||
self.zoom_out_btn = self.toolbar.addAction(QtGui.QIcon('share/zoom_out32.png'), "&Zoom Out")
|
||||
self.zoom_in_btn = self.toolbar.addAction(QtGui.QIcon('share/zoom_in32.png'), "&Zoom In")
|
||||
self.clear_plot_btn = self.toolbar.addAction(QtGui.QIcon('share/clear_plot32.png'), "&Clear Plot")
|
||||
self.replot_btn = self.toolbar.addAction(QtGui.QIcon('share/replot32.png'), "&Replot")
|
||||
self.delete_btn = self.toolbar.addAction(QtGui.QIcon('share/delete32.png'), "&Delete")
|
||||
|
||||
################
|
||||
### Splitter ###
|
||||
################
|
||||
self.splitter = QtGui.QSplitter()
|
||||
self.setCentralWidget(self.splitter)
|
||||
|
||||
################
|
||||
### Notebook ###
|
||||
################
|
||||
self.notebook = QtGui.QTabWidget()
|
||||
# self.notebook.setMinimumWidth(250)
|
||||
|
||||
### Projet ###
|
||||
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.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)
|
||||
|
||||
#############
|
||||
### Icons ###
|
||||
#############
|
||||
app_icon = QtGui.QIcon()
|
||||
app_icon.addFile('share/flatcam_icon16.png', QtCore.QSize(16, 16))
|
||||
app_icon.addFile('share/flatcam_icon24.png', QtCore.QSize(24, 24))
|
||||
app_icon.addFile('share/flatcam_icon32.png', QtCore.QSize(32, 32))
|
||||
app_icon.addFile('share/flatcam_icon48.png', QtCore.QSize(48, 48))
|
||||
app_icon.addFile('share/flatcam_icon128.png', QtCore.QSize(128, 128))
|
||||
app_icon.addFile('share/flatcam_icon256.png', QtCore.QSize(256, 256))
|
||||
self.setWindowIcon(app_icon)
|
||||
|
||||
self.setGeometry(100, 100, 750, 500)
|
||||
self.setWindowTitle('FlatCAM - 0.5')
|
||||
self.show()
|
||||
|
||||
|
||||
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)
|
||||
|
||||
## 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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
## 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)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
# 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.layout.addWidget(appendlabel)
|
||||
|
||||
self.append_text = FCTextArea()
|
||||
self.layout.addWidget(self.append_text)
|
||||
|
||||
|
||||
class GlobalOptionsUI(QtGui.QWidget):
|
||||
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()
|
522
FlatCAMObj.py
522
FlatCAMObj.py
|
@ -1,66 +1,15 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GLib
|
||||
from gi.repository import GObject
|
||||
|
||||
import inspect # TODO: Remove
|
||||
|
||||
import FlatCAMApp
|
||||
from camlib import *
|
||||
from PyQt4 import QtCore
|
||||
from ObjectUI import *
|
||||
|
||||
|
||||
class LoudDict(dict):
|
||||
"""
|
||||
A Dictionary with a callback for
|
||||
item changes.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LoudDict, self).__init__(*args, **kwargs)
|
||||
self.callback = lambda x: None
|
||||
self.silence = False
|
||||
|
||||
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
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""
|
||||
Overridden __setitem__ method. Will call self.callback
|
||||
if the item was changed and self.silence is False.
|
||||
"""
|
||||
super(LoudDict, self).__setitem__(key, value)
|
||||
try:
|
||||
if self.__getitem__(key) == value:
|
||||
return
|
||||
except KeyError:
|
||||
pass
|
||||
if self.silence:
|
||||
return
|
||||
self.callback(key)
|
||||
import FlatCAMApp
|
||||
import inspect # TODO: For debugging only.
|
||||
from camlib import *
|
||||
from FlatCAMCommon import LoudDict
|
||||
|
||||
|
||||
########################################
|
||||
## FlatCAMObj ##
|
||||
########################################
|
||||
class FlatCAMObj(GObject.GObject, object):
|
||||
class FlatCAMObj(QtCore.QObject):
|
||||
"""
|
||||
Base type of objects handled in FlatCAM. These become interactive
|
||||
in the GUI, can be plotted, and their options can be modified
|
||||
|
@ -71,7 +20,7 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
# The app should set this value.
|
||||
app = None
|
||||
|
||||
def __init__(self, name, ui):
|
||||
def __init__(self, name):
|
||||
"""
|
||||
|
||||
:param name: Name of the object given by the user.
|
||||
|
@ -79,53 +28,60 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
:type ui: ObjectUI
|
||||
:return: FlatCAMObj
|
||||
"""
|
||||
GObject.GObject.__init__(self)
|
||||
QtCore.QObject.__init__(self)
|
||||
|
||||
# View
|
||||
self.ui = ui
|
||||
self.ui = None
|
||||
|
||||
self.options = LoudDict(name=name)
|
||||
self.options.set_change_callback(self.on_options_change)
|
||||
|
||||
self.form_fields = {"name": self.ui.name_entry}
|
||||
self.radios = {} # Name value pairs for radio sets
|
||||
self.radios_inv = {} # Inverse of self.radios
|
||||
self.form_fields = {}
|
||||
|
||||
self.axes = None # Matplotlib axes
|
||||
self.kind = None # Override with proper name
|
||||
|
||||
self.muted_ui = False
|
||||
|
||||
self.ui.name_entry.connect('activate', self.on_name_activate)
|
||||
self.ui.offset_button.connect('clicked', self.on_offset_button_click)
|
||||
self.ui.offset_button.connect('activate', self.on_offset_button_click)
|
||||
self.ui.scale_button.connect('clicked', self.on_scale_button_click)
|
||||
self.ui.scale_button.connect('activate', self.on_scale_button_click)
|
||||
# assert isinstance(self.ui, ObjectUI)
|
||||
# self.ui.name_entry.returnPressed.connect(self.on_name_activate)
|
||||
# self.ui.offset_button.clicked.connect(self.on_offset_button_click)
|
||||
# self.ui.scale_button.clicked.connect(self.on_scale_button_click)
|
||||
|
||||
def on_options_change(self, key):
|
||||
self.emit(QtCore.SIGNAL("optionChanged"), key)
|
||||
|
||||
def set_ui(self, ui):
|
||||
self.ui = ui
|
||||
|
||||
self.form_fields = {"name": self.ui.name_entry}
|
||||
|
||||
assert isinstance(self.ui, ObjectUI)
|
||||
self.ui.name_entry.returnPressed.connect(self.on_name_activate)
|
||||
self.ui.offset_button.clicked.connect(self.on_offset_button_click)
|
||||
self.ui.scale_button.clicked.connect(self.on_scale_button_click)
|
||||
|
||||
def __str__(self):
|
||||
return "<FlatCAMObj({:12s}): {:20s}>".format(self.kind, self.options["name"])
|
||||
|
||||
def on_name_activate(self, *args):
|
||||
def on_name_activate(self):
|
||||
old_name = copy(self.options["name"])
|
||||
new_name = self.ui.name_entry.get_text()
|
||||
self.options["name"] = self.ui.name_entry.get_text()
|
||||
new_name = self.ui.name_entry.get_value()
|
||||
self.options["name"] = self.ui.name_entry.get_value()
|
||||
self.app.info("Name changed from %s to %s" % (old_name, new_name))
|
||||
|
||||
def on_offset_button_click(self, *args):
|
||||
def on_offset_button_click(self):
|
||||
self.read_form()
|
||||
vect = self.ui.offsetvector_entry.get_value()
|
||||
self.offset(vect)
|
||||
self.plot()
|
||||
|
||||
def on_scale_button_click(self, *args):
|
||||
def on_scale_button_click(self):
|
||||
self.read_form()
|
||||
factor = self.ui.scale_entry.get_value()
|
||||
self.scale(factor)
|
||||
self.plot()
|
||||
|
||||
def on_options_change(self, key):
|
||||
self.form_fields[key].set_value(self.options[key])
|
||||
return
|
||||
|
||||
def setup_axes(self, figure):
|
||||
"""
|
||||
1) Creates axes if they don't exist. 2) Clears axes. 3) Attaches
|
||||
|
@ -189,21 +145,24 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
self.muted_ui = True
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> FlatCAMObj.build_ui()")
|
||||
|
||||
# Where the UI for this object is drawn
|
||||
# box_selected = self.app.builder.get_object("box_selected")
|
||||
box_selected = self.app.builder.get_object("vp_selected")
|
||||
|
||||
# Remove anything else in the box
|
||||
box_children = box_selected.get_children()
|
||||
for child in box_children:
|
||||
box_selected.remove(child)
|
||||
# box_children = self.app.ui.notebook.selected_contents.get_children()
|
||||
# for child in box_children:
|
||||
# self.app.ui.notebook.selected_contents.remove(child)
|
||||
# while self.app.ui.selected_layout.count():
|
||||
# self.app.ui.selected_layout.takeAt(0)
|
||||
|
||||
# Put in the UI
|
||||
# box_selected.pack_start(sw, True, True, 0)
|
||||
box_selected.add(self.ui)
|
||||
# self.app.ui.notebook.selected_contents.add(self.ui)
|
||||
# self.app.ui.selected_layout.addWidget(self.ui)
|
||||
try:
|
||||
self.app.ui.selected_scroll_area.takeWidget()
|
||||
except:
|
||||
self.app.log.debug("Nothing to remove")
|
||||
self.app.ui.selected_scroll_area.setWidget(self.ui)
|
||||
self.to_form()
|
||||
GLib.idle_add(box_selected.show_all)
|
||||
GLib.idle_add(self.ui.show_all)
|
||||
|
||||
self.muted_ui = False
|
||||
|
||||
def set_form_item(self, option):
|
||||
|
@ -243,6 +202,7 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
:return: Whether to continue plotting or not depending on the "plot" option.
|
||||
:rtype: bool
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> FlatCAMObj.plot()")
|
||||
|
||||
# Axes must exist and be attached to canvas.
|
||||
if self.axes is None or self.axes not in self.app.plotcanvas.figure.axes:
|
||||
|
@ -254,7 +214,7 @@ class FlatCAMObj(GObject.GObject, object):
|
|||
return False
|
||||
|
||||
# Clear axes or we will plot on top of them.
|
||||
self.axes.cla()
|
||||
self.axes.cla() # TODO: Thread safe?
|
||||
# GLib.idle_add(self.axes.cla)
|
||||
return True
|
||||
|
||||
|
@ -284,29 +244,14 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
Represents Gerber code.
|
||||
"""
|
||||
|
||||
ui_type = GerberObjectUI
|
||||
|
||||
def __init__(self, name):
|
||||
Gerber.__init__(self)
|
||||
FlatCAMObj.__init__(self, name, GerberObjectUI())
|
||||
FlatCAMObj.__init__(self, name)
|
||||
|
||||
self.kind = "gerber"
|
||||
|
||||
self.form_fields.update({
|
||||
"plot": self.ui.plot_cb,
|
||||
"multicolored": self.ui.multicolored_cb,
|
||||
"solid": self.ui.solid_cb,
|
||||
"isotooldia": self.ui.iso_tool_dia_entry,
|
||||
"isopasses": self.ui.iso_width_entry,
|
||||
"isooverlap": self.ui.iso_overlap_entry,
|
||||
"cutouttooldia": self.ui.cutout_tooldia_entry,
|
||||
"cutoutmargin": self.ui.cutout_margin_entry,
|
||||
"cutoutgapsize": self.ui.cutout_gap_entry,
|
||||
"gaps": self.ui.gaps_radio,
|
||||
"noncoppermargin": self.ui.noncopper_margin_entry,
|
||||
"noncopperrounded": self.ui.noncopper_rounded_cb,
|
||||
"bboxmargin": self.ui.bbmargin_entry,
|
||||
"bboxrounded": self.ui.bbrounded_cb
|
||||
})
|
||||
|
||||
# The 'name' is already in self.options from FlatCAMObj
|
||||
# Automatically updates the UI
|
||||
self.options.update({
|
||||
|
@ -331,21 +276,45 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
# assert isinstance(self.ui, GerberObjectUI)
|
||||
# self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
# self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
|
||||
# self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
|
||||
# self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click)
|
||||
# self.ui.generate_cutout_button.clicked.connect(self.on_generatecutout_button_click)
|
||||
# self.ui.generate_bb_button.clicked.connect(self.on_generatebb_button_click)
|
||||
# self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
|
||||
|
||||
def set_ui(self, ui):
|
||||
FlatCAMObj.set_ui(self, ui)
|
||||
|
||||
FlatCAMApp.App.log.debug("FlatCAMGerber.set_ui()")
|
||||
|
||||
self.form_fields.update({
|
||||
"plot": self.ui.plot_cb,
|
||||
"multicolored": self.ui.multicolored_cb,
|
||||
"solid": self.ui.solid_cb,
|
||||
"isotooldia": self.ui.iso_tool_dia_entry,
|
||||
"isopasses": self.ui.iso_width_entry,
|
||||
"isooverlap": self.ui.iso_overlap_entry,
|
||||
"cutouttooldia": self.ui.cutout_tooldia_entry,
|
||||
"cutoutmargin": self.ui.cutout_margin_entry,
|
||||
"cutoutgapsize": self.ui.cutout_gap_entry,
|
||||
"gaps": self.ui.gaps_radio,
|
||||
"noncoppermargin": self.ui.noncopper_margin_entry,
|
||||
"noncopperrounded": self.ui.noncopper_rounded_cb,
|
||||
"bboxmargin": self.ui.bbmargin_entry,
|
||||
"bboxrounded": self.ui.bbrounded_cb
|
||||
})
|
||||
|
||||
assert isinstance(self.ui, GerberObjectUI)
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
self.ui.solid_cb.connect('clicked', self.on_solid_cb_click)
|
||||
self.ui.solid_cb.connect('activate', self.on_solid_cb_click)
|
||||
self.ui.multicolored_cb.connect('clicked', self.on_multicolored_cb_click)
|
||||
self.ui.multicolored_cb.connect('activate', self.on_multicolored_cb_click)
|
||||
self.ui.generate_iso_button.connect('clicked', self.on_iso_button_click)
|
||||
self.ui.generate_iso_button.connect('activate', self.on_iso_button_click)
|
||||
self.ui.generate_cutout_button.connect('clicked', self.on_generatecutout_button_click)
|
||||
self.ui.generate_cutout_button.connect('activate', self.on_generatecutout_button_click)
|
||||
self.ui.generate_bb_button.connect('clicked', self.on_generatebb_button_click)
|
||||
self.ui.generate_bb_button.connect('activate', self.on_generatebb_button_click)
|
||||
self.ui.generate_noncopper_button.connect('clicked', self.on_generatenoncopper_button_click)
|
||||
self.ui.generate_noncopper_button.connect('activate', self.on_generatenoncopper_button_click)
|
||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
|
||||
self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click)
|
||||
self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click)
|
||||
self.ui.generate_cutout_button.clicked.connect(self.on_generatecutout_button_click)
|
||||
self.ui.generate_bb_button.clicked.connect(self.on_generatebb_button_click)
|
||||
self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
|
||||
|
||||
def on_generatenoncopper_button_click(self, *args):
|
||||
self.read_form()
|
||||
|
@ -478,17 +447,13 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
|
||||
def plot(self):
|
||||
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> FlatCAMGerber.plot()")
|
||||
|
||||
# Does all the required setup and returns False
|
||||
# if the 'ptint' option is set to False.
|
||||
if not FlatCAMObj.plot(self):
|
||||
return
|
||||
|
||||
# if self.options["mergepolys"]:
|
||||
# geometry = self.solid_geometry
|
||||
# else:
|
||||
# geometry = self.buffered_paths + \
|
||||
# [poly['polygon'] for poly in self.regions] + \
|
||||
# self.flash_geometry
|
||||
geometry = self.solid_geometry
|
||||
|
||||
# Make sure geometry is iterable.
|
||||
|
@ -523,8 +488,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
x, y = ints.coords.xy
|
||||
self.axes.plot(x, y, linespec)
|
||||
|
||||
# self.app.plotcanvas.auto_adjust_axes()
|
||||
GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
||||
self.app.plotcanvas.auto_adjust_axes()
|
||||
#GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
||||
#self.emit(QtCore.SIGNAL("plotChanged"), self)
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
|
@ -538,29 +504,21 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
Represents Excellon/Drill code.
|
||||
"""
|
||||
|
||||
ui_type = ExcellonObjectUI
|
||||
|
||||
def __init__(self, name):
|
||||
Excellon.__init__(self)
|
||||
FlatCAMObj.__init__(self, name, ExcellonObjectUI())
|
||||
FlatCAMObj.__init__(self, name)
|
||||
|
||||
self.kind = "excellon"
|
||||
|
||||
self.form_fields.update({
|
||||
"name": self.ui.name_entry,
|
||||
"plot": self.ui.plot_cb,
|
||||
"solid": self.ui.solid_cb,
|
||||
"drillz": self.ui.cutz_entry,
|
||||
"travelz": self.ui.travelz_entry,
|
||||
"feedrate": self.ui.feedrate_entry,
|
||||
"toolselection": self.ui.tools_entry
|
||||
})
|
||||
|
||||
self.options.update({
|
||||
"plot": True,
|
||||
"solid": False,
|
||||
"drillz": -0.1,
|
||||
"travelz": 0.1,
|
||||
"feedrate": 5.0,
|
||||
"toolselection": ""
|
||||
# "toolselection": ""
|
||||
})
|
||||
|
||||
# TODO: Document this.
|
||||
|
@ -571,49 +529,100 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
def build_ui(self):
|
||||
FlatCAMObj.build_ui(self)
|
||||
|
||||
# Populate tool list
|
||||
n = len(self.tools)
|
||||
self.ui.tools_table.setColumnCount(2)
|
||||
self.ui.tools_table.setHorizontalHeaderLabels(['#', 'Diameter'])
|
||||
self.ui.tools_table.setRowCount(n)
|
||||
self.ui.tools_table.setSortingEnabled(False)
|
||||
i = 0
|
||||
for tool in self.tools:
|
||||
id = QtGui.QTableWidgetItem(tool)
|
||||
id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.ui.tools_table.setItem(i, 0, id) # Tool name/id
|
||||
dia = QtGui.QTableWidgetItem(str(self.tools[tool]['C']))
|
||||
dia.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
self.ui.tools_table.setItem(i, 1, dia) # Diameter
|
||||
i += 1
|
||||
self.ui.tools_table.resizeColumnsToContents()
|
||||
self.ui.tools_table.resizeRowsToContents()
|
||||
self.ui.tools_table.horizontalHeader().setStretchLastSection(True)
|
||||
self.ui.tools_table.verticalHeader().hide()
|
||||
self.ui.tools_table.setSortingEnabled(True)
|
||||
|
||||
def set_ui(self, ui):
|
||||
FlatCAMObj.set_ui(self, ui)
|
||||
|
||||
FlatCAMApp.App.log.debug("FlatCAMExcellon.set_ui()")
|
||||
|
||||
self.form_fields.update({
|
||||
"plot": self.ui.plot_cb,
|
||||
"solid": self.ui.solid_cb,
|
||||
"drillz": self.ui.cutz_entry,
|
||||
"travelz": self.ui.travelz_entry,
|
||||
"feedrate": self.ui.feedrate_entry,
|
||||
# "toolselection": self.ui.tools_entry
|
||||
})
|
||||
|
||||
assert isinstance(self.ui, ExcellonObjectUI)
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
self.ui.solid_cb.connect('clicked', self.on_solid_cb_click)
|
||||
self.ui.solid_cb.connect('activate', self.on_solid_cb_click)
|
||||
self.ui.choose_tools_button.connect('clicked', lambda args: self.show_tool_chooser())
|
||||
self.ui.choose_tools_button.connect('activate', lambda args: self.show_tool_chooser())
|
||||
self.ui.generate_cnc_button.connect('clicked', self.on_create_cncjob_button_click)
|
||||
self.ui.generate_cnc_button.connect('activate', self.on_create_cncjob_button_click)
|
||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click)
|
||||
# self.ui.choose_tools_button.clicked.connect(self.show_tool_chooser)
|
||||
self.ui.generate_cnc_button.clicked.connect(self.on_create_cncjob_button_click)
|
||||
|
||||
def on_create_cncjob_button_click(self, *args):
|
||||
self.read_form()
|
||||
|
||||
# Get the tools from the list
|
||||
tools = [str(x.text()) for x in self.ui.tools_table.selectedItems()]
|
||||
|
||||
if len(tools) == 0:
|
||||
self.app.inform.emit("Please select one or more tools from the list and try again.")
|
||||
return
|
||||
|
||||
job_name = self.options["name"] + "_cnc"
|
||||
|
||||
# Object initialization function for app.new_object()
|
||||
def job_init(job_obj, app_obj):
|
||||
assert isinstance(job_obj, FlatCAMCNCjob)
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
|
||||
app_obj.progress.emit(20)
|
||||
job_obj.z_cut = self.options["drillz"]
|
||||
job_obj.z_move = self.options["travelz"]
|
||||
job_obj.feedrate = self.options["feedrate"]
|
||||
# There could be more than one drill size...
|
||||
# job_obj.tooldia = # TODO: duplicate variable!
|
||||
# job_obj.options["tooldia"] =
|
||||
job_obj.generate_from_excellon_by_tool(self, self.options["toolselection"])
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
|
||||
tools_csv = ','.join(tools)
|
||||
# job_obj.generate_from_excellon_by_tool(self, self.options["toolselection"])
|
||||
job_obj.generate_from_excellon_by_tool(self, tools_csv)
|
||||
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
|
||||
app_obj.progress.emit(50)
|
||||
job_obj.gcode_parse()
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating New Geometry..."))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating New Geometry..."))
|
||||
app_obj.progress.emit(60)
|
||||
job_obj.create_geometry()
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.8, "Plotting..."))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.8, "Plotting..."))
|
||||
app_obj.progress.emit(80)
|
||||
|
||||
# To be run in separate thread
|
||||
def job_thread(app_obj):
|
||||
app_obj.new_object("cncjob", job_name, job_init)
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
|
||||
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
|
||||
app_obj.progress.emit(100)
|
||||
# GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, ""))
|
||||
|
||||
# Send to worker
|
||||
self.app.worker.add_task(job_thread, [self.app])
|
||||
# self.app.worker.add_task(job_thread, [self.app])
|
||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
|
@ -663,32 +672,34 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
x, y = ints.coords.xy
|
||||
self.axes.plot(x, y, 'g-')
|
||||
|
||||
#self.app.plotcanvas.auto_adjust_axes()
|
||||
GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
||||
self.app.plotcanvas.auto_adjust_axes()
|
||||
# GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
||||
# self.emit(QtCore.SIGNAL("plotChanged"), self)
|
||||
|
||||
def show_tool_chooser(self):
|
||||
win = Gtk.Window()
|
||||
box = Gtk.Box(spacing=2)
|
||||
box.set_orientation(Gtk.Orientation(1))
|
||||
win.add(box)
|
||||
for tool in self.tools:
|
||||
self.tool_cbs[tool] = Gtk.CheckButton(label=tool + ": " + str(self.tools[tool]))
|
||||
box.pack_start(self.tool_cbs[tool], False, False, 1)
|
||||
button = Gtk.Button(label="Accept")
|
||||
box.pack_start(button, False, False, 1)
|
||||
win.show_all()
|
||||
|
||||
def on_accept(widget):
|
||||
win.destroy()
|
||||
tool_list = []
|
||||
for toolx in self.tool_cbs:
|
||||
if self.tool_cbs[toolx].get_active():
|
||||
tool_list.append(toolx)
|
||||
self.options["toolselection"] = ", ".join(tool_list)
|
||||
self.to_form()
|
||||
|
||||
button.connect("activate", on_accept)
|
||||
button.connect("clicked", on_accept)
|
||||
# win = Gtk.Window()
|
||||
# box = Gtk.Box(spacing=2)
|
||||
# box.set_orientation(Gtk.Orientation(1))
|
||||
# win.add(box)
|
||||
# for tool in self.tools:
|
||||
# self.tool_cbs[tool] = Gtk.CheckButton(label=tool + ": " + str(self.tools[tool]))
|
||||
# box.pack_start(self.tool_cbs[tool], False, False, 1)
|
||||
# button = Gtk.Button(label="Accept")
|
||||
# box.pack_start(button, False, False, 1)
|
||||
# win.show_all()
|
||||
#
|
||||
# def on_accept(widget):
|
||||
# win.destroy()
|
||||
# tool_list = []
|
||||
# for toolx in self.tool_cbs:
|
||||
# if self.tool_cbs[toolx].get_active():
|
||||
# tool_list.append(toolx)
|
||||
# self.options["toolselection"] = ", ".join(tool_list)
|
||||
# self.to_form()
|
||||
#
|
||||
# button.connect("activate", on_accept)
|
||||
# button.connect("clicked", on_accept)
|
||||
return
|
||||
|
||||
|
||||
class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
||||
|
@ -696,23 +707,21 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
Represents G-Code.
|
||||
"""
|
||||
|
||||
ui_type = CNCObjectUI
|
||||
|
||||
def __init__(self, name, units="in", kind="generic", z_move=0.1,
|
||||
feedrate=3.0, z_cut=-0.002, tooldia=0.0):
|
||||
FlatCAMApp.App.log.debug("Creating CNCJob object...")
|
||||
CNCjob.__init__(self, units=units, kind=kind, z_move=z_move,
|
||||
feedrate=feedrate, z_cut=z_cut, tooldia=tooldia)
|
||||
FlatCAMObj.__init__(self, name, CNCObjectUI())
|
||||
FlatCAMObj.__init__(self, name)
|
||||
|
||||
self.kind = "cncjob"
|
||||
|
||||
self.options.update({
|
||||
"plot": True,
|
||||
"tooldia": 0.4 / 25.4 # 0.4mm in inches
|
||||
})
|
||||
|
||||
self.form_fields.update({
|
||||
"name": self.ui.name_entry,
|
||||
"plot": self.ui.plot_cb,
|
||||
"tooldia": self.ui.tooldia_entry
|
||||
"tooldia": 0.4 / 25.4, # 0.4mm in inches
|
||||
"append": ""
|
||||
})
|
||||
|
||||
# Attributes to be included in serialization
|
||||
|
@ -720,25 +729,47 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
self.ui.updateplot_button.connect('clicked', self.on_updateplot_button_click)
|
||||
self.ui.updateplot_button.connect('activate', self.on_updateplot_button_click)
|
||||
self.ui.export_gcode_button.connect('clicked', self.on_exportgcode_button_click)
|
||||
self.ui.export_gcode_button.connect('activate', self.on_exportgcode_button_click)
|
||||
def set_ui(self, ui):
|
||||
FlatCAMObj.set_ui(self, ui)
|
||||
|
||||
FlatCAMApp.App.log.debug("FlatCAMCNCJob.set_ui()")
|
||||
|
||||
assert isinstance(self.ui, CNCObjectUI)
|
||||
|
||||
self.form_fields.update({
|
||||
"plot": self.ui.plot_cb,
|
||||
"tooldia": self.ui.tooldia_entry,
|
||||
"append": self.ui.append_text
|
||||
})
|
||||
|
||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
self.ui.updateplot_button.clicked.connect(self.on_updateplot_button_click)
|
||||
self.ui.export_gcode_button.clicked.connect(self.on_exportgcode_button_click)
|
||||
|
||||
def on_updateplot_button_click(self, *args):
|
||||
"""
|
||||
Callback for the "Updata Plot" button. Reads the form for updates
|
||||
and plots the object.
|
||||
"""
|
||||
self.read_form()
|
||||
self.plot()
|
||||
|
||||
def on_exportgcode_button_click(self, *args):
|
||||
def on_success(app_obj, filename):
|
||||
f = open(filename, 'w')
|
||||
f.write(self.gcode)
|
||||
f.close()
|
||||
app_obj.info("Saved to: " + filename)
|
||||
|
||||
self.app.file_chooser_save_action(on_success)
|
||||
try:
|
||||
filename = QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ...",
|
||||
directory=self.app.last_folder)
|
||||
except TypeError:
|
||||
filename = QtGui.QFileDialog.getSaveFileName(caption="Export G-Code ...")
|
||||
|
||||
postamble = str(self.ui.append_text.get_value())
|
||||
|
||||
f = open(filename, 'w')
|
||||
f.write(self.gcode + "\n" + postamble)
|
||||
f.close()
|
||||
|
||||
self.app.file_opened.emit("cncjob", filename)
|
||||
self.app.inform.emit("Saved to: " + filename)
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
if self.muted_ui:
|
||||
|
@ -755,8 +786,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
|
||||
self.plot2(self.axes, tooldia=self.options["tooldia"])
|
||||
|
||||
#self.app.plotcanvas.auto_adjust_axes()
|
||||
GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
||||
self.app.plotcanvas.auto_adjust_axes()
|
||||
|
||||
def convert_units(self, units):
|
||||
factor = CNCjob.convert_units(self, units)
|
||||
|
@ -770,26 +800,14 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
format.
|
||||
"""
|
||||
|
||||
ui_type = GeometryObjectUI
|
||||
|
||||
def __init__(self, name):
|
||||
FlatCAMObj.__init__(self, name, GeometryObjectUI())
|
||||
FlatCAMObj.__init__(self, name)
|
||||
Geometry.__init__(self)
|
||||
|
||||
self.kind = "geometry"
|
||||
|
||||
self.form_fields.update({
|
||||
"name": self.ui.name_entry,
|
||||
"plot": self.ui.plot_cb,
|
||||
# "solid": self.ui.sol,
|
||||
# "multicolored": self.ui.,
|
||||
"cutz": self.ui.cutz_entry,
|
||||
"travelz": self.ui.travelz_entry,
|
||||
"feedrate": self.ui.cncfeedrate_entry,
|
||||
"cnctooldia": self.ui.cnctooldia_entry,
|
||||
"painttooldia": self.ui.painttooldia_entry,
|
||||
"paintoverlap": self.ui.paintoverlap_entry,
|
||||
"paintmargin": self.ui.paintmargin_entry
|
||||
})
|
||||
|
||||
self.options.update({
|
||||
"plot": True,
|
||||
# "solid": False,
|
||||
|
@ -803,31 +821,34 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
"paintmargin": 0.01
|
||||
})
|
||||
|
||||
# self.form_kinds.update({
|
||||
# "plot": "cb",
|
||||
# "solid": "cb",
|
||||
# "multicolored": "cb",
|
||||
# "cutz": "entry_eval",
|
||||
# "travelz": "entry_eval",
|
||||
# "feedrate": "entry_eval",
|
||||
# "cnctooldia": "entry_eval",
|
||||
# "painttooldia": "entry_eval",
|
||||
# "paintoverlap": "entry_eval",
|
||||
# "paintmargin": "entry_eval"
|
||||
# })
|
||||
|
||||
# Attributes to be included in serialization
|
||||
# Always append to it because it carries contents
|
||||
# from predecessors.
|
||||
self.ser_attrs += ['options', 'kind']
|
||||
|
||||
def set_ui(self, ui):
|
||||
FlatCAMObj.set_ui(self, ui)
|
||||
|
||||
FlatCAMApp.App.log.debug("FlatCAMGeometry.set_ui()")
|
||||
|
||||
assert isinstance(self.ui, GeometryObjectUI)
|
||||
self.ui.plot_cb.connect('clicked', self.on_plot_cb_click)
|
||||
self.ui.plot_cb.connect('activate', self.on_plot_cb_click)
|
||||
self.ui.generate_cnc_button.connect('clicked', self.on_generatecnc_button_click)
|
||||
self.ui.generate_cnc_button.connect('activate', self.on_generatecnc_button_click)
|
||||
self.ui.generate_paint_button.connect('clicked', self.on_paint_button_click)
|
||||
self.ui.generate_paint_button.connect('activate', self.on_paint_button_click)
|
||||
|
||||
self.form_fields.update({
|
||||
"plot": self.ui.plot_cb,
|
||||
# "solid": self.ui.sol,
|
||||
# "multicolored": self.ui.,
|
||||
"cutz": self.ui.cutz_entry,
|
||||
"travelz": self.ui.travelz_entry,
|
||||
"feedrate": self.ui.cncfeedrate_entry,
|
||||
"cnctooldia": self.ui.cnctooldia_entry,
|
||||
"painttooldia": self.ui.painttooldia_entry,
|
||||
"paintoverlap": self.ui.paintoverlap_entry,
|
||||
"paintmargin": self.ui.paintmargin_entry
|
||||
})
|
||||
|
||||
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
|
||||
self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click)
|
||||
self.ui.generate_paint_button.clicked.connect(self.on_paint_button_click)
|
||||
|
||||
def on_paint_button_click(self, *args):
|
||||
self.app.info("Click inside the desired polygon.")
|
||||
|
@ -868,35 +889,41 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# Propagate options
|
||||
job_obj.options["tooldia"] = self.options["cnctooldia"]
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.2, "Creating CNC Job..."))
|
||||
app_obj.progress.emit(20)
|
||||
job_obj.z_cut = self.options["cutz"]
|
||||
job_obj.z_move = self.options["travelz"]
|
||||
job_obj.feedrate = self.options["feedrate"]
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.4, "Analyzing Geometry..."))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.4, "Analyzing Geometry..."))
|
||||
app_obj.progress.emit(40)
|
||||
# TODO: The tolerance should not be hard coded. Just for testing.
|
||||
job_obj.generate_from_geometry(self, tolerance=0.0005)
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.5, "Parsing G-Code..."))
|
||||
app_obj.progress.emit(50)
|
||||
job_obj.gcode_parse()
|
||||
|
||||
# TODO: job_obj.create_geometry creates stuff that is not used.
|
||||
#GLib.idle_add(lambda: app_obj.set_progress_bar(0.6, "Creating New Geometry..."))
|
||||
#job_obj.create_geometry()
|
||||
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(0.8, "Plotting..."))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(0.8, "Plotting..."))
|
||||
app_obj.progress.emit(80)
|
||||
|
||||
# To be run in separate thread
|
||||
def job_thread(app_obj):
|
||||
app_obj.new_object("cncjob", job_name, job_init)
|
||||
GLib.idle_add(lambda: app_obj.info("CNCjob created: %s" % job_name))
|
||||
GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
|
||||
GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, "Idle"))
|
||||
# GLib.idle_add(lambda: app_obj.info("CNCjob created: %s" % job_name))
|
||||
# GLib.idle_add(lambda: app_obj.set_progress_bar(1.0, "Done!"))
|
||||
# GLib.timeout_add_seconds(1, lambda: app_obj.set_progress_bar(0.0, "Idle"))
|
||||
app_obj.inform.emit("CNCjob created: %s" % job_name)
|
||||
app_obj.progress.emit(100)
|
||||
|
||||
# Send to worker
|
||||
self.app.worker.add_task(job_thread, [self.app])
|
||||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||
|
||||
def on_plot_cb_click(self, *args):
|
||||
def on_plot_cb_click(self, *args): # TODO: args not needed
|
||||
if self.muted_ui:
|
||||
return
|
||||
self.read_form_item('plot')
|
||||
|
@ -994,5 +1021,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
|
||||
FlatCAMApp.App.log.warning("Did not plot:", str(type(geo)))
|
||||
|
||||
#self.app.plotcanvas.auto_adjust_axes()
|
||||
GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
||||
self.app.plotcanvas.auto_adjust_axes()
|
||||
# GLib.idle_add(self.app.plotcanvas.auto_adjust_axes)
|
||||
# self.emit(QtCore.SIGNAL("plotChanged"), self)
|
|
@ -0,0 +1,258 @@
|
|||
from PyQt4 import QtGui, QtCore
|
||||
from shapely.geometry import Point
|
||||
from shapely import affinity
|
||||
from math import sqrt
|
||||
|
||||
import FlatCAMApp
|
||||
from GUIElements import *
|
||||
from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon
|
||||
|
||||
|
||||
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):
|
||||
self.menuAction = self.app.ui.menutool.addAction(self.toolName)
|
||||
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()
|
||||
|
||||
|
||||
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)
|
||||
form_layout.addRow("Bottom Layer:", self.object_combo)
|
||||
|
||||
## Axis
|
||||
self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'},
|
||||
{'label': 'Y', 'value': 'Y'}])
|
||||
form_layout.addRow("Mirror Axis:", self.mirror_axis)
|
||||
|
||||
## Axis Location
|
||||
self.axis_location = RadioSet([{'label': 'Point', 'value': 'point'},
|
||||
{'label': 'Box', 'value': 'box'}])
|
||||
form_layout.addRow("Axis Location:", self.axis_location)
|
||||
|
||||
## Point/Box
|
||||
self.point_box_container = QtGui.QVBoxLayout()
|
||||
form_layout.addRow("Point/Box:", 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)
|
||||
self.point_box_container.addWidget(self.box_combo)
|
||||
self.box_combo.hide()
|
||||
|
||||
## Alignment holes
|
||||
self.alignment_holes = EvalEntry()
|
||||
form_layout.addRow("Alignment Holes:", self.alignment_holes)
|
||||
|
||||
## Drill diameter for alignment holes
|
||||
self.drill_dia = LengthEntry()
|
||||
form_layout.addRow("Drill diam.:", self.drill_dia)
|
||||
|
||||
## Buttons
|
||||
hlay = QtGui.QHBoxLayout()
|
||||
self.layout.addLayout(hlay)
|
||||
hlay.addStretch()
|
||||
self.create_alignment_hole_button = QtGui.QPushButton("Create Alignment Drill")
|
||||
self.mirror_object_button = QtGui.QPushButton("Mirror Object")
|
||||
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()
|
||||
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):
|
||||
self.info("ERROR: Only Gerber and Excellon 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)
|
||||
|
||||
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()
|
||||
|
||||
|
||||
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):
|
||||
FlatCAMTool.install(self)
|
||||
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 == 'm':
|
||||
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()
|
||||
|
||||
|
||||
|
|
@ -1,43 +1,29 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
import threading
|
||||
import Queue
|
||||
from PyQt4 import QtCore
|
||||
#import Queue
|
||||
import FlatCAMApp
|
||||
|
||||
|
||||
class Worker(threading.Thread):
|
||||
class Worker(QtCore.QObject):
|
||||
"""
|
||||
Implements a queue of tasks to be carried out in order
|
||||
in a single independent thread.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, app, name=None):
|
||||
super(Worker, self).__init__()
|
||||
self.queue = Queue.Queue()
|
||||
self.stoprequest = threading.Event()
|
||||
self.app = app
|
||||
self.name = name
|
||||
|
||||
def run(self):
|
||||
while not self.stoprequest.isSet():
|
||||
try:
|
||||
task = self.queue.get(True, 0.05)
|
||||
self.do_task(task)
|
||||
except Queue.Empty:
|
||||
continue
|
||||
FlatCAMApp.App.log.debug("Worker Started!")
|
||||
self.app.worker_task.connect(self.do_worker_task)
|
||||
|
||||
@staticmethod
|
||||
def do_task(task):
|
||||
task['fcn'](*task['params'])
|
||||
return
|
||||
def do_worker_task(self, task):
|
||||
FlatCAMApp.App.log.debug("Running task: %s" % str(task))
|
||||
if 'worker_name' in task and task['worker_name'] == self.name:
|
||||
task['fcn'](*task['params'])
|
||||
return
|
||||
|
||||
def add_task(self, target, params=list()):
|
||||
self.queue.put({'fcn': target, 'params': params})
|
||||
return
|
||||
|
||||
def join(self, timeout=None):
|
||||
self.stoprequest.set()
|
||||
super(Worker, self).join()
|
||||
if 'worker_name' not in task and self.name is None:
|
||||
task['fcn'](*task['params'])
|
||||
return
|
|
@ -0,0 +1,46 @@
|
|||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class FCNoteBook(Gtk.Notebook):
|
||||
|
||||
def __init__(self):
|
||||
Gtk.Notebook.__init__(self, vexpand=True, vexpand_set=True, valign=1, expand=True)
|
||||
|
||||
###############
|
||||
### Project ###
|
||||
###############
|
||||
self.project_contents = Gtk.VBox(vexpand=True, valign=0, vexpand_set=True, expand=True)
|
||||
sw1 = Gtk.ScrolledWindow(vexpand=True, valign=0, vexpand_set=True, expand=True)
|
||||
sw1.add_with_viewport(self.project_contents)
|
||||
self.project_page_num = self.append_page(sw1, Gtk.Label("Project"))
|
||||
|
||||
################
|
||||
### Selected ###
|
||||
################
|
||||
self.selected_contents = Gtk.VBox()
|
||||
sw2 = Gtk.ScrolledWindow()
|
||||
sw2.add_with_viewport(self.selected_contents)
|
||||
self.selected_page_num = self.append_page(sw2, Gtk.Label("Selected"))
|
||||
|
||||
###############
|
||||
### Options ###
|
||||
###############
|
||||
self.options_contents_super = Gtk.VBox()
|
||||
sw3 = Gtk.ScrolledWindow()
|
||||
sw3.add_with_viewport(self.options_contents_super)
|
||||
self.options_page_num = self.append_page(sw3, Gtk.Label("Options"))
|
||||
|
||||
hb = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
ico = Gtk.Image.new_from_file("share/gear32.png")
|
||||
hb.pack_start(ico, expand=False, fill=False, padding=0)
|
||||
self.combo_options = Gtk.ComboBoxText()
|
||||
hb.pack_start(self.combo_options, expand=True, fill=True, padding=0)
|
||||
self.options_contents_super.pack_start(hb, expand=False, fill=False, padding=0)
|
||||
self.options_contents = Gtk.VBox()
|
||||
self.options_contents_super.pack_start(self.options_contents, expand=False, fill=False, padding=0)
|
||||
|
||||
############
|
||||
### Tool ###
|
||||
############
|
||||
self.tool_contents = Gtk.VBox()
|
||||
self.tool_page_num = self.append_page(self.tool_contents, Gtk.Label("Tool"))
|
|
@ -0,0 +1,15 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from FlatCAM_GTK.FlatCAMApp import *
|
||||
|
||||
|
||||
app = App()
|
||||
Gtk.main()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,303 @@
|
|||
from gi.repository import Gtk
|
||||
|
||||
from FlatCAM_GTK import FCNoteBook
|
||||
|
||||
|
||||
class FlatCAMGUI(Gtk.Window):
|
||||
|
||||
MENU = """
|
||||
<ui>
|
||||
<menubar name='MenuBar'>
|
||||
<menu action='FileMenu'>
|
||||
<menuitem action='FileNew'>
|
||||
<separator />
|
||||
|
||||
<menuitem action='FileQuit' />
|
||||
</menu>
|
||||
</menubar>
|
||||
<toolbar name='ToolBar'>
|
||||
<toolitem action='FileNewStandard' />
|
||||
<toolitem action='FileQuit' />
|
||||
</toolbar>
|
||||
</ui>
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
|
||||
:return: The FlatCAM window.
|
||||
:rtype: FlatCAM
|
||||
"""
|
||||
Gtk.Window.__init__(self, title="FlatCAM - 0.5")
|
||||
self.set_default_size(200, 200)
|
||||
|
||||
vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
### Menu
|
||||
# action_group = Gtk.ActionGroup("my_actions")
|
||||
# self.add_file_menu_actions(action_group)
|
||||
# #self.add_edit_menu_actions(action_group)
|
||||
# #self.add_choices_menu_actions(action_group)
|
||||
#
|
||||
# uimanager = self.create_ui_manager()
|
||||
# uimanager.insert_action_group(action_group)
|
||||
#
|
||||
# menubar = uimanager.get_widget("/MenuBar")
|
||||
# vbox1.pack_start(menubar, False, False, 0)
|
||||
#
|
||||
# toolbar = uimanager.get_widget("/ToolBar")
|
||||
# vbox1.pack_start(toolbar, False, False, 0)
|
||||
|
||||
menu = Gtk.MenuBar()
|
||||
|
||||
## File
|
||||
menufile = Gtk.MenuItem.new_with_label('File')
|
||||
menufile_menu = Gtk.Menu()
|
||||
menufile.set_submenu(menufile_menu)
|
||||
# New
|
||||
self.menufilenew = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_NEW, None)
|
||||
menufile_menu.append(self.menufilenew)
|
||||
menufile_menu.append(Gtk.SeparatorMenuItem())
|
||||
# Open recent
|
||||
self.menufilerecent = Gtk.ImageMenuItem("Open Recent", image=Gtk.Image(stock=Gtk.STOCK_OPEN))
|
||||
menufile_menu.append(self.menufilerecent)
|
||||
menufile_menu.append(Gtk.SeparatorMenuItem())
|
||||
# Open Gerber ...
|
||||
self.menufileopengerber = Gtk.ImageMenuItem("Open Gerber ...", image=Gtk.Image(stock=Gtk.STOCK_OPEN))
|
||||
menufile_menu.append(self.menufileopengerber)
|
||||
# Open Excellon ...
|
||||
self.menufileopenexcellon = Gtk.ImageMenuItem("Open Excellon ...", image=Gtk.Image(stock=Gtk.STOCK_OPEN))
|
||||
menufile_menu.append(self.menufileopenexcellon)
|
||||
# Open G-Code ...
|
||||
self.menufileopengcode = Gtk.ImageMenuItem("Open G-Code ...", image=Gtk.Image(stock=Gtk.STOCK_OPEN))
|
||||
menufile_menu.append(self.menufileopengcode)
|
||||
menufile_menu.append(Gtk.SeparatorMenuItem())
|
||||
# Open Project ...
|
||||
self.menufileopenproject = Gtk.ImageMenuItem("Open Project ...", image=Gtk.Image(stock=Gtk.STOCK_OPEN))
|
||||
menufile_menu.append(self.menufileopenproject)
|
||||
menufile_menu.append(Gtk.SeparatorMenuItem())
|
||||
# Save Project
|
||||
self.menufilesaveproject = Gtk.ImageMenuItem("Save Project", image=Gtk.Image(stock=Gtk.STOCK_SAVE))
|
||||
menufile_menu.append(self.menufilesaveproject)
|
||||
# Save Project As ...
|
||||
self.menufilesaveprojectas = Gtk.ImageMenuItem("Save Project As ...", image=Gtk.Image(stock=Gtk.STOCK_SAVE_AS))
|
||||
menufile_menu.append(self.menufilesaveprojectas)
|
||||
# Save Project Copy ...
|
||||
self.menufilesaveprojectcopy = Gtk.ImageMenuItem("Save Project Copy ...", image=Gtk.Image(stock=Gtk.STOCK_SAVE_AS))
|
||||
menufile_menu.append(self.menufilesaveprojectcopy)
|
||||
menufile_menu.append(Gtk.SeparatorMenuItem())
|
||||
# Save Defaults
|
||||
self.menufilesavedefaults = Gtk.ImageMenuItem("Save Defaults", image=Gtk.Image(stock=Gtk.STOCK_SAVE))
|
||||
menufile_menu.append(self.menufilesavedefaults)
|
||||
menufile_menu.append(Gtk.SeparatorMenuItem())
|
||||
# Quit
|
||||
self.menufilequit = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None)
|
||||
menufile_menu.append(self.menufilequit)
|
||||
menu.append(menufile)
|
||||
|
||||
## Edit
|
||||
menuedit = Gtk.MenuItem.new_with_label('Edit')
|
||||
menu.append(menuedit)
|
||||
menuedit_menu = Gtk.Menu()
|
||||
menuedit.set_submenu(menuedit_menu)
|
||||
# Delete
|
||||
self.menueditdelete = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_DELETE, None)
|
||||
menuedit_menu.append(self.menueditdelete)
|
||||
|
||||
## View
|
||||
menuview = Gtk.MenuItem.new_with_label('View')
|
||||
menu.append(menuview)
|
||||
menuview_menu = Gtk.Menu()
|
||||
menuview.set_submenu(menuview_menu)
|
||||
# Disable all plots
|
||||
self.menuviewdisableall = Gtk.ImageMenuItem("Disable all plots", image=Gtk.Image.new_from_file('share/clear_plot16.png'))
|
||||
menuview_menu.append(self.menuviewdisableall)
|
||||
self.menuviewdisableallbutthis = Gtk.ImageMenuItem("Disable all plots but this one", image=Gtk.Image.new_from_file('share/clear_plot16.png'))
|
||||
menuview_menu.append(self.menuviewdisableallbutthis)
|
||||
self.menuviewenableall = Gtk.ImageMenuItem("Enable all plots", image=Gtk.Image.new_from_file('share/replot16.png'))
|
||||
menuview_menu.append(self.menuviewenableall)
|
||||
|
||||
## Options
|
||||
menuoptions = Gtk.MenuItem.new_with_label('Options')
|
||||
menu.append(menuoptions)
|
||||
menuoptions_menu = Gtk.Menu()
|
||||
menuoptions.set_submenu(menuoptions_menu)
|
||||
# Transfer Options
|
||||
menutransferoptions = Gtk.ImageMenuItem("Transfer Options", image=Gtk.Image.new_from_file('share/copy16.png'))
|
||||
menuoptions_menu.append(menutransferoptions)
|
||||
menutransferoptions_menu = Gtk.Menu()
|
||||
menutransferoptions.set_submenu(menutransferoptions_menu)
|
||||
self.menutransferoptions_p2a = Gtk.ImageMenuItem("Project to App", image=Gtk.Image.new_from_file('share/copy16.png'))
|
||||
menutransferoptions_menu.append(self.menutransferoptions_p2a)
|
||||
self.menutransferoptions_a2p = Gtk.ImageMenuItem("App to Project", image=Gtk.Image.new_from_file('share/copy16.png'))
|
||||
menutransferoptions_menu.append(self.menutransferoptions_a2p)
|
||||
self.menutransferoptions_o2p = Gtk.ImageMenuItem("Object to Project", image=Gtk.Image.new_from_file('share/copy16.png'))
|
||||
menutransferoptions_menu.append(self.menutransferoptions_o2p)
|
||||
self.menutransferoptions_o2a = Gtk.ImageMenuItem("Object to App", image=Gtk.Image.new_from_file('share/copy16.png'))
|
||||
menutransferoptions_menu.append(self.menutransferoptions_o2a)
|
||||
self.menutransferoptions_p2o = Gtk.ImageMenuItem("Project to Object", image=Gtk.Image.new_from_file('share/copy16.png'))
|
||||
menutransferoptions_menu.append(self.menutransferoptions_p2o)
|
||||
self.menutransferoptions_a2o = Gtk.ImageMenuItem("App to Object", image=Gtk.Image.new_from_file('share/copy16.png'))
|
||||
menutransferoptions_menu.append(self.menutransferoptions_a2o)
|
||||
|
||||
## Tools
|
||||
menutools = Gtk.MenuItem.new_with_label('Tools')
|
||||
menu.append(menutools)
|
||||
menutools_menu = Gtk.Menu()
|
||||
menutools.set_submenu(menutools_menu)
|
||||
# Double Sided PCB tool
|
||||
self.menutools_dblsided = Gtk.ImageMenuItem("Double-Sided PCB Tool", image=Gtk.Image(stock=Gtk.STOCK_PREFERENCES))
|
||||
menutools_menu.append(self.menutools_dblsided)
|
||||
|
||||
## Help
|
||||
menuhelp = Gtk.MenuItem.new_with_label('Help')
|
||||
menu.append(menuhelp)
|
||||
menuhelp_menu = Gtk.Menu()
|
||||
menuhelp.set_submenu(menuhelp_menu)
|
||||
# About
|
||||
self.menuhelpabout = Gtk.ImageMenuItem("About", image=Gtk.Image(stock=Gtk.STOCK_ABOUT))
|
||||
menuhelp_menu.append(self.menuhelpabout)
|
||||
# Updates
|
||||
self.menuhelpupdates = Gtk.ImageMenuItem("Check for updates", image=Gtk.Image(stock=Gtk.STOCK_DIALOG_INFO))
|
||||
menuhelp_menu.append(self.menuhelpupdates)
|
||||
|
||||
vbox1.pack_start(menu, False, False, 0)
|
||||
### End of menu
|
||||
|
||||
###############
|
||||
### Toolbar ###
|
||||
###############
|
||||
self.toolbar = Gtk.Toolbar(toolbar_style=Gtk.ToolbarStyle.ICONS)
|
||||
vbox1.pack_start(self.toolbar, False, False, 0)
|
||||
|
||||
# Zoom fit
|
||||
zf_ico = Gtk.Image.new_from_file('share/zoom_fit32.png')
|
||||
self.zoom_fit_btn = Gtk.ToolButton.new(zf_ico, "")
|
||||
#zoom_fit.connect("clicked", self.on_zoom_fit)
|
||||
self.zoom_fit_btn.set_tooltip_markup("Zoom Fit.\n(Click on plot and hit <b>1</b>)")
|
||||
self.toolbar.insert(self.zoom_fit_btn, -1)
|
||||
|
||||
# Zoom out
|
||||
zo_ico = Gtk.Image.new_from_file('share/zoom_out32.png')
|
||||
self.zoom_out_btn = Gtk.ToolButton.new(zo_ico, "")
|
||||
#zoom_out.connect("clicked", self.on_zoom_out)
|
||||
self.zoom_out_btn.set_tooltip_markup("Zoom Out.\n(Click on plot and hit <b>2</b>)")
|
||||
self.toolbar.insert(self.zoom_out_btn, -1)
|
||||
|
||||
# Zoom in
|
||||
zi_ico = Gtk.Image.new_from_file('share/zoom_in32.png')
|
||||
self.zoom_in_btn = Gtk.ToolButton.new(zi_ico, "")
|
||||
#zoom_in.connect("clicked", self.on_zoom_in)
|
||||
self.zoom_in_btn.set_tooltip_markup("Zoom In.\n(Click on plot and hit <b>3</b>)")
|
||||
self.toolbar.insert(self.zoom_in_btn, -1)
|
||||
|
||||
# Clear plot
|
||||
cp_ico = Gtk.Image.new_from_file('share/clear_plot32.png')
|
||||
self.clear_plot_btn = Gtk.ToolButton.new(cp_ico, "")
|
||||
#clear_plot.connect("clicked", self.on_clear_plots)
|
||||
self.clear_plot_btn.set_tooltip_markup("Clear Plot")
|
||||
self.toolbar.insert(self.clear_plot_btn, -1)
|
||||
|
||||
# Replot
|
||||
rp_ico = Gtk.Image.new_from_file('share/replot32.png')
|
||||
self.replot_btn = Gtk.ToolButton.new(rp_ico, "")
|
||||
#replot.connect("clicked", self.on_toolbar_replot)
|
||||
self.replot_btn.set_tooltip_markup("Re-plot all")
|
||||
self.toolbar.insert(self.replot_btn, -1)
|
||||
|
||||
# Delete item
|
||||
del_ico = Gtk.Image.new_from_file('share/delete32.png')
|
||||
self.delete_btn = Gtk.ToolButton.new(del_ico, "")
|
||||
#delete.connect("clicked", self.on_delete)
|
||||
self.delete_btn.set_tooltip_markup("Delete selected\nobject.")
|
||||
self.toolbar.insert(self.delete_btn, -1)
|
||||
|
||||
#############
|
||||
### Paned ###
|
||||
#############
|
||||
hpane = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
|
||||
vbox1.pack_start(hpane, expand=True, fill=True, padding=0)
|
||||
|
||||
################
|
||||
### Notebook ###
|
||||
################
|
||||
self.notebook = FCNoteBook()
|
||||
hpane.pack1(self.notebook)
|
||||
|
||||
#################
|
||||
### Plot area ###
|
||||
#################
|
||||
# self.plotarea = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self.plotarea = Gtk.Grid()
|
||||
self.plotarea_super = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self.plotarea_super.pack_start(self.plotarea, expand=True, fill=True, padding=0)
|
||||
hpane.pack2(self.plotarea_super)
|
||||
|
||||
################
|
||||
### Info bar ###
|
||||
################
|
||||
infobox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
vbox1.pack_start(infobox, expand=False, fill=True, padding=0)
|
||||
## Frame
|
||||
frame = Gtk.Frame(margin=2, hexpand=True, halign=0)
|
||||
infobox.pack_start(frame, expand=True, fill=True, padding=0)
|
||||
self.info_label = Gtk.Label("Not started.", margin=2, hexpand=True)
|
||||
frame.add(self.info_label)
|
||||
## Coordinate Label
|
||||
self.position_label = Gtk.Label("X: 0.0 Y: 0.0", margin_left=4, margin_right=4)
|
||||
infobox.pack_start(self.position_label, expand=False, fill=False, padding=0)
|
||||
## Units label
|
||||
self.units_label = Gtk.Label("[in]", margin_left=4, margin_right=4)
|
||||
infobox.pack_start(self.units_label, expand=False, fill=False, padding=0)
|
||||
## Progress bar
|
||||
self.progress_bar = Gtk.ProgressBar(margin=2)
|
||||
infobox.pack_start(self.progress_bar, expand=False, fill=False, padding=0)
|
||||
|
||||
self.add(vbox1)
|
||||
self.show_all()
|
||||
|
||||
# def create_ui_manager(self):
|
||||
# uimanager = Gtk.UIManager()
|
||||
#
|
||||
# # Throws exception if something went wrong
|
||||
# uimanager.add_ui_from_string(FlatCAM.MENU)
|
||||
#
|
||||
# # Add the accelerator group to the toplevel window
|
||||
# accelgroup = uimanager.get_accel_group()
|
||||
# self.add_accel_group(accelgroup)
|
||||
# return uimanager
|
||||
#
|
||||
# def add_file_menu_actions(self, action_group):
|
||||
# action_filemenu = Gtk.Action("FileMenu", "File", None, None)
|
||||
# action_group.add_action(action_filemenu)
|
||||
#
|
||||
# action_filenewmenu = Gtk.Action("FileNew", None, None, Gtk.STOCK_NEW)
|
||||
# action_group.add_action(action_filenewmenu)
|
||||
#
|
||||
# action_new = Gtk.Action("FileNewStandard", "_New",
|
||||
# "Create a new file", Gtk.STOCK_NEW)
|
||||
# action_new.connect("activate", self.on_menu_file_new_generic)
|
||||
# action_group.add_action_with_accel(action_new, None)
|
||||
#
|
||||
# action_group.add_actions([
|
||||
# ("FileNewFoo", None, "New Foo", None, "Create new foo",
|
||||
# self.on_menu_file_new_generic),
|
||||
# ("FileNewGoo", None, "_New Goo", None, "Create new goo",
|
||||
# self.on_menu_file_new_generic),
|
||||
# ])
|
||||
#
|
||||
# action_filequit = Gtk.Action("FileQuit", None, None, Gtk.STOCK_QUIT)
|
||||
# action_filequit.connect("activate", self.on_menu_file_quit)
|
||||
# action_group.add_action(action_filequit)
|
||||
#
|
||||
# def on_menu_file_new_generic(self, widget):
|
||||
# print("A File|New menu item was selected.")
|
||||
#
|
||||
# def on_menu_file_quit(self, widget):
|
||||
# Gtk.main_quit()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
flatcam = FlatCAMGUI()
|
||||
Gtk.main()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
import threading
|
||||
import Queue
|
||||
|
||||
|
||||
class Worker(threading.Thread):
|
||||
"""
|
||||
Implements a queue of tasks to be carried out in order
|
||||
in a single independent thread.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(Worker, self).__init__()
|
||||
self.queue = Queue.Queue()
|
||||
self.stoprequest = threading.Event()
|
||||
|
||||
def run(self):
|
||||
while not self.stoprequest.isSet():
|
||||
try:
|
||||
task = self.queue.get(True, 0.05)
|
||||
self.do_task(task)
|
||||
except Queue.Empty:
|
||||
continue
|
||||
|
||||
@staticmethod
|
||||
def do_task(task):
|
||||
task['fcn'](*task['params'])
|
||||
return
|
||||
|
||||
def add_task(self, target, params=list()):
|
||||
self.queue.put({'fcn': target, 'params': params})
|
||||
return
|
||||
|
||||
def join(self, timeout=None):
|
||||
self.stoprequest.set()
|
||||
super(Worker, self).join()
|
|
@ -0,0 +1,249 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
import re
|
||||
from copy import copy
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from FlatCAM_GTK import FlatCAMApp
|
||||
|
||||
|
||||
class RadioSet(Gtk.Box):
|
||||
def __init__(self, choices):
|
||||
"""
|
||||
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.
|
||||
:type choices: list
|
||||
"""
|
||||
Gtk.Box.__init__(self)
|
||||
self.choices = copy(choices)
|
||||
self.group = None
|
||||
for choice in self.choices:
|
||||
if self.group is None:
|
||||
choice['radio'] = Gtk.RadioButton.new_with_label(None, choice['label'])
|
||||
self.group = choice['radio']
|
||||
else:
|
||||
choice['radio'] = Gtk.RadioButton.new_with_label_from_widget(self.group, choice['label'])
|
||||
self.pack_start(choice['radio'], expand=True, fill=False, padding=2)
|
||||
choice['radio'].connect('toggled', self.on_toggle)
|
||||
|
||||
self.group_toggle_fn = lambda x, y: None
|
||||
|
||||
def on_toggle(self, btn):
|
||||
if btn.get_active():
|
||||
self.group_toggle_fn(btn, self.get_value)
|
||||
return
|
||||
|
||||
def get_value(self):
|
||||
for choice in self.choices:
|
||||
if choice['radio'].get_active():
|
||||
return choice['value']
|
||||
FlatCAMApp.App.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'].set_active(True)
|
||||
return
|
||||
FlatCAMApp.App.log.error("Value given is not part of this RadioSet: %s" % str(val))
|
||||
|
||||
|
||||
class LengthEntry(Gtk.Entry):
|
||||
"""
|
||||
A text entry that interprets its string as a
|
||||
length, with or without specified units. When the user reads
|
||||
the value, it is interpreted and replaced by a floating
|
||||
point representation of the value in the default units. When
|
||||
the entry is activated, its string is repalced by the interpreted
|
||||
value.
|
||||
|
||||
Example:
|
||||
Default units are 'IN', input is "1.0 mm", value returned
|
||||
is 1.0/25.4 = 0.03937.
|
||||
"""
|
||||
|
||||
def __init__(self, output_units='IN'):
|
||||
"""
|
||||
|
||||
:param output_units: The default output units, 'IN' or 'MM'
|
||||
:return: LengthEntry
|
||||
"""
|
||||
|
||||
Gtk.Entry.__init__(self)
|
||||
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.connect('activate', self.on_activate)
|
||||
|
||||
def on_activate(self, *args):
|
||||
"""
|
||||
Entry "activate" callback. Replaces the text in the
|
||||
entry with the value returned by `get_value()`.
|
||||
|
||||
:param args: Ignored.
|
||||
:return: None.
|
||||
"""
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
else:
|
||||
FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
|
||||
|
||||
def get_value(self):
|
||||
"""
|
||||
Fetches, interprets and returns the value in the entry. The text
|
||||
is parsed to find the numerical expression and the (input) units (if any).
|
||||
The numerical expression is interpreted and scaled acording to the
|
||||
input and output units `self.output_units`.
|
||||
|
||||
:return: Floating point representation of the value in the entry.
|
||||
:rtype: float
|
||||
"""
|
||||
|
||||
raw = self.get_text().strip(' ')
|
||||
match = self.format_re.search(raw)
|
||||
if not match:
|
||||
return None
|
||||
try:
|
||||
if match.group(2) is not None and match.group(2).upper() in self.scales:
|
||||
return float(eval(match.group(1)))*self.scales[self.output_units][match.group(2).upper()]
|
||||
else:
|
||||
return float(eval(match.group(1)))
|
||||
except:
|
||||
FlatCAMApp.App.log.warning("Could not parse value in entry: %s" % str(raw))
|
||||
return None
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class FloatEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
|
||||
self.connect('activate', self.on_activate)
|
||||
|
||||
def on_activate(self, *args):
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
else:
|
||||
FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
|
||||
|
||||
def get_value(self):
|
||||
raw = self.get_text().strip(' ')
|
||||
try:
|
||||
evaled = eval(raw)
|
||||
except:
|
||||
FlatCAMApp.App.log.error("Could not evaluate: %s" % str(raw))
|
||||
return None
|
||||
|
||||
return float(evaled)
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class IntEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
|
||||
def get_value(self):
|
||||
return int(self.get_text())
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class FCEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
|
||||
def get_value(self):
|
||||
return self.get_text()
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class EvalEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
|
||||
def on_activate(self, *args):
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
else:
|
||||
FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
|
||||
|
||||
def get_value(self):
|
||||
raw = self.get_text().strip(' ')
|
||||
try:
|
||||
return eval(raw)
|
||||
except:
|
||||
FlatCAMApp.App.log.error("Could not evaluate: %s" % str(raw))
|
||||
return None
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
|
||||
|
||||
class FCCheckBox(Gtk.CheckButton):
|
||||
def __init__(self, label=''):
|
||||
Gtk.CheckButton.__init__(self, label=label)
|
||||
|
||||
def get_value(self):
|
||||
return self.get_active()
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_active(val)
|
||||
|
||||
|
||||
|
||||
|
||||
class FCTextArea(Gtk.ScrolledWindow):
|
||||
def __init__(self):
|
||||
# Gtk.ScrolledWindow.__init__(self)
|
||||
# FlatCAMApp.App.log.debug('Gtk.ScrolledWindow.__init__(self)')
|
||||
super(FCTextArea, self).__init__()
|
||||
FlatCAMApp.App.log.debug('super(FCTextArea, self).__init__()')
|
||||
self.set_size_request(250, 100)
|
||||
FlatCAMApp.App.log.debug('self.set_size_request(250, 100)')
|
||||
textview = Gtk.TextView()
|
||||
#print textview
|
||||
#FlatCAMApp.App.log.debug('self.textview = Gtk.TextView()')
|
||||
#self.textbuffer = self.textview.get_buffer()
|
||||
#FlatCAMApp.App.log.debug('self.textbuffer = self.textview.get_buffer()')
|
||||
#self.textbuffer.set_text("(Nothing here!)")
|
||||
#FlatCAMApp.App.log.debug('self.textbuffer.set_text("(Nothing here!)")')
|
||||
#self.add(self.textview)
|
||||
#FlatCAMApp.App.log.debug('self.add(self.textview)')
|
||||
#self.show()
|
||||
|
||||
def set_value(self, val):
|
||||
#self.textbuffer.set_text(str(val))
|
||||
return
|
||||
|
||||
def get_value(self):
|
||||
#return self.textbuffer.get_text()
|
||||
return ""
|
|
@ -0,0 +1,261 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 4/20/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
import inspect # TODO: Remove
|
||||
|
||||
from gi.repository import Gtk, GdkPixbuf, GLib
|
||||
|
||||
from FlatCAMObj import *
|
||||
from FlatCAM_GTK import FlatCAMApp
|
||||
|
||||
|
||||
class ObjectCollection:
|
||||
|
||||
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):
|
||||
|
||||
### Icons for the list view
|
||||
self.icons = {}
|
||||
for kind in ObjectCollection.icon_files:
|
||||
self.icons[kind] = GdkPixbuf.Pixbuf.new_from_file(ObjectCollection.icon_files[kind])
|
||||
|
||||
### GUI List components
|
||||
## Model
|
||||
self.store = Gtk.ListStore(FlatCAMObj)
|
||||
|
||||
## View
|
||||
self.view = Gtk.TreeView(model=self.store)
|
||||
#self.view.connect("row_activated", self.on_row_activated)
|
||||
self.tree_selection = self.view.get_selection()
|
||||
self.change_subscription = self.tree_selection.connect("changed", self.on_list_selection_change)
|
||||
|
||||
## Renderers
|
||||
# Icon
|
||||
renderer_pixbuf = Gtk.CellRendererPixbuf()
|
||||
column_pixbuf = Gtk.TreeViewColumn("Type", renderer_pixbuf)
|
||||
|
||||
def _set_cell_icon(column, cell, model, it, data):
|
||||
obj = model.get_value(it, 0)
|
||||
cell.set_property('pixbuf', self.icons[obj.kind])
|
||||
|
||||
column_pixbuf.set_cell_data_func(renderer_pixbuf, _set_cell_icon)
|
||||
self.view.append_column(column_pixbuf)
|
||||
|
||||
# Name
|
||||
renderer_text = Gtk.CellRendererText()
|
||||
column_text = Gtk.TreeViewColumn("Name", renderer_text)
|
||||
|
||||
def _set_cell_text(column, cell, model, it, data):
|
||||
obj = model.get_value(it, 0)
|
||||
cell.set_property('text', obj.options["name"])
|
||||
|
||||
column_text.set_cell_data_func(renderer_text, _set_cell_text)
|
||||
self.view.append_column(column_text)
|
||||
|
||||
def print_list(self):
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
print obj
|
||||
iterat = self.store.iter_next(iterat)
|
||||
|
||||
def delete_all(self):
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_all()")
|
||||
self.store.clear()
|
||||
|
||||
def delete_active(self):
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_active()")
|
||||
try:
|
||||
model, treeiter = self.tree_selection.get_selected()
|
||||
self.store.remove(treeiter)
|
||||
except:
|
||||
pass
|
||||
|
||||
def on_list_selection_change(self, selection):
|
||||
"""
|
||||
Callback for change in selection on the objects' list.
|
||||
Instructs the new selection to build the UI for its options.
|
||||
|
||||
:param selection: Ignored.
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.on_list_selection_change()")
|
||||
|
||||
active = self.get_active()
|
||||
active.build_ui()
|
||||
|
||||
def set_active(self, name):
|
||||
"""
|
||||
Sets an object as the active object in the program. Same
|
||||
as `set_list_selection()`.
|
||||
|
||||
:param name: Name of the object.
|
||||
:type name: str
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.set_active()")
|
||||
self.set_list_selection(name)
|
||||
|
||||
def get_active(self):
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_active()")
|
||||
try:
|
||||
model, treeiter = self.tree_selection.get_selected()
|
||||
return model[treeiter][0]
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def set_list_selection(self, name):
|
||||
"""
|
||||
Sets which object should be selected in the list.
|
||||
|
||||
:param name: Name of the object.
|
||||
:rtype name: str
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.set_list_selection()")
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None and self.store[iterat][0].options["name"] != name:
|
||||
iterat = self.store.iter_next(iterat)
|
||||
self.tree_selection.select_iter(iterat)
|
||||
|
||||
def append(self, obj, active=False):
|
||||
"""
|
||||
Add a FlatCAMObj the the collection. This method is thread-safe.
|
||||
|
||||
:param obj: FlatCAMObj to append
|
||||
:type obj: FlatCAMObj
|
||||
:param active: If it is to become the active object after appending
|
||||
:type active: bool
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.append()")
|
||||
|
||||
def guitask():
|
||||
self.store.append([obj])
|
||||
if active:
|
||||
self.set_list_selection(obj.options["name"])
|
||||
GLib.idle_add(guitask)
|
||||
|
||||
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()")
|
||||
|
||||
names = []
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
names.append(obj.options["name"])
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return names
|
||||
|
||||
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
|
||||
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
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.")
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return [xmin, ymin, xmax, ymax]
|
||||
|
||||
def get_list(self):
|
||||
"""
|
||||
Returns a list with all FlatCAMObj.
|
||||
|
||||
:return: List with all FlatCAMObj.
|
||||
:rtype: list
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_list()")
|
||||
collection_list = []
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
collection_list.append(obj)
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return collection_list
|
||||
|
||||
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()")
|
||||
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
if obj.options["name"] == name:
|
||||
return obj
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return None
|
||||
|
||||
# def change_name(self, old_name, new_name):
|
||||
# """
|
||||
# Changes the name of `FlatCAMObj` named `old_name` to `new_name`.
|
||||
#
|
||||
# :param old_name: Name of the object to change.
|
||||
# :type old_name: str
|
||||
# :param new_name: New name.
|
||||
# :type new_name: str
|
||||
# :return: True if name change succeeded, False otherwise. Will fail
|
||||
# if no object with `old_name` is found.
|
||||
# :rtype: bool
|
||||
# """
|
||||
# print inspect.stack()[1][3], "--> OC.change_name()"
|
||||
# iterat = self.store.get_iter_first()
|
||||
# while iterat is not None:
|
||||
# obj = self.store[iterat][0]
|
||||
# if obj.options["name"] == old_name:
|
||||
# obj.options["name"] = new_name
|
||||
# self.store.row_changed(0, iterat)
|
||||
# return True
|
||||
# iterat = self.store.iter_next(iterat)
|
||||
# return False
|
|
@ -0,0 +1,627 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from FlatCAM_GTK.GUIElements import *
|
||||
|
||||
|
||||
class ObjectUI(Gtk.VBox):
|
||||
"""
|
||||
Base class for the UI of FlatCAM objects. Deriving classes should
|
||||
put UI elements in ObjectUI.custom_box (Gtk.VBox).
|
||||
"""
|
||||
|
||||
def __init__(self, icon_file='share/flatcam_icon32.png', title='FlatCAM Object'):
|
||||
Gtk.VBox.__init__(self, spacing=3, margin=5, vexpand=False)
|
||||
|
||||
## Page Title box (spacing between children)
|
||||
self.title_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 2)
|
||||
self.pack_start(self.title_box, expand=False, fill=False, padding=2)
|
||||
|
||||
## Page Title icon
|
||||
self.icon = Gtk.Image.new_from_file(icon_file)
|
||||
self.title_box.pack_start(self.icon, expand=False, fill=False, padding=2)
|
||||
|
||||
## Title label
|
||||
self.title_label = Gtk.Label()
|
||||
self.title_label.set_markup("<b>" + title + "</b>")
|
||||
self.title_label.set_justify(Gtk.Justification.CENTER)
|
||||
self.title_box.pack_start(self.title_label, expand=False, fill=False, padding=2)
|
||||
|
||||
## Object name
|
||||
self.name_box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 2)
|
||||
self.pack_start(self.name_box, expand=False, fill=False, padding=2)
|
||||
name_label = Gtk.Label('Name:')
|
||||
name_label.set_justify(Gtk.Justification.RIGHT)
|
||||
self.name_box.pack_start(name_label,
|
||||
expand=False, fill=False, padding=2)
|
||||
self.name_entry = FCEntry()
|
||||
self.name_box.pack_start(self.name_entry, expand=True, fill=False, padding=2)
|
||||
|
||||
## Box box for custom widgets
|
||||
self.custom_box = Gtk.VBox(spacing=3, margin=0, vexpand=False)
|
||||
self.pack_start(self.custom_box, expand=False, fill=False, padding=0)
|
||||
|
||||
## Common to all objects
|
||||
## Scale
|
||||
self.scale_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.scale_label.set_markup('<b>Scale:</b>')
|
||||
self.scale_label.set_tooltip_markup(
|
||||
"Change the size of the object."
|
||||
)
|
||||
self.pack_start(self.scale_label, expand=False, fill=False, padding=2)
|
||||
|
||||
grid5 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.pack_start(grid5, expand=False, fill=False, padding=2)
|
||||
|
||||
# Factor
|
||||
l10 = Gtk.Label('Factor:', xalign=1)
|
||||
l10.set_tooltip_markup(
|
||||
"Factor by which to multiply\n"
|
||||
"geometric features of this object."
|
||||
)
|
||||
grid5.attach(l10, 0, 0, 1, 1)
|
||||
self.scale_entry = FloatEntry()
|
||||
self.scale_entry.set_text("1.0")
|
||||
grid5.attach(self.scale_entry, 1, 0, 1, 1)
|
||||
|
||||
# GO Button
|
||||
self.scale_button = Gtk.Button(label='Scale')
|
||||
self.scale_button.set_tooltip_markup(
|
||||
"Perform scaling operation."
|
||||
)
|
||||
self.pack_start(self.scale_button, expand=False, fill=False, padding=2)
|
||||
|
||||
## Offset
|
||||
self.offset_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.offset_label.set_markup('<b>Offset:</b>')
|
||||
self.offset_label.set_tooltip_markup(
|
||||
"Change the position of this object."
|
||||
)
|
||||
self.pack_start(self.offset_label, expand=False, fill=False, padding=2)
|
||||
|
||||
grid6 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.pack_start(grid6, expand=False, fill=False, padding=2)
|
||||
|
||||
# Vector
|
||||
l11 = Gtk.Label('Offset Vector:', xalign=1)
|
||||
l11.set_tooltip_markup(
|
||||
"Amount by which to move the object\n"
|
||||
"in the x and y axes in (x, y) format."
|
||||
)
|
||||
grid6.attach(l11, 0, 0, 1, 1)
|
||||
self.offsetvector_entry = EvalEntry()
|
||||
self.offsetvector_entry.set_text("(0.0, 0.0)")
|
||||
grid6.attach(self.offsetvector_entry, 1, 0, 1, 1)
|
||||
|
||||
self.offset_button = Gtk.Button(label='Scale')
|
||||
self.offset_button.set_tooltip_markup(
|
||||
"Perform the offset operation."
|
||||
)
|
||||
self.pack_start(self.offset_button, expand=False, fill=False, padding=2)
|
||||
|
||||
def set_field(self, name, value):
|
||||
getattr(self, name).set_value(value)
|
||||
|
||||
def get_field(self, name):
|
||||
return getattr(self, name).get_value()
|
||||
|
||||
|
||||
class CNCObjectUI(ObjectUI):
|
||||
"""
|
||||
User interface for CNCJob objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=False, fill=False, padding=2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
self.plot_cb.set_tooltip_markup(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 2, 1)
|
||||
|
||||
# Tool dia for plot
|
||||
l1 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
"Diameter of the tool to be\n"
|
||||
"rendered in the plot."
|
||||
)
|
||||
grid0.attach(l1, 0, 1, 1, 1)
|
||||
self.tooldia_entry = LengthEntry()
|
||||
grid0.attach(self.tooldia_entry, 1, 1, 1, 1)
|
||||
|
||||
# Update plot button
|
||||
self.updateplot_button = Gtk.Button(label='Update Plot')
|
||||
self.updateplot_button.set_tooltip_markup(
|
||||
"Update the plot."
|
||||
)
|
||||
self.custom_box.pack_start(self.updateplot_button, expand=False, fill=False, padding=2)
|
||||
|
||||
## Export G-Code
|
||||
self.export_gcode_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.export_gcode_label.set_markup("<b>Export G-Code:</b>")
|
||||
self.export_gcode_label.set_tooltip_markup(
|
||||
"Export and save G-Code to\n"
|
||||
"make this object to a file."
|
||||
)
|
||||
self.custom_box.pack_start(self.export_gcode_label, expand=False, fill=False, padding=2)
|
||||
|
||||
# Append text to Gerber
|
||||
l2 = Gtk.Label('Append to G-Code:')
|
||||
l2.set_tooltip_markup(
|
||||
"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.pack_start(l2, expand=False, fill=False, padding=2)
|
||||
#self.append_gtext = FCTextArea()
|
||||
#self.custom_box.pack_start(self.append_gtext, expand=False, fill=False, padding=2)
|
||||
|
||||
# GO Button
|
||||
self.export_gcode_button = Gtk.Button(label='Export G-Code')
|
||||
self.export_gcode_button.set_tooltip_markup(
|
||||
"Opens dialog to save G-Code\n"
|
||||
"file."
|
||||
)
|
||||
self.custom_box.pack_start(self.export_gcode_button, expand=False, fill=False, padding=2)
|
||||
|
||||
|
||||
class GeometryObjectUI(ObjectUI):
|
||||
"""
|
||||
User interface for Geometry objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Geometry Object', icon_file='share/geometry32.png')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
self.plot_cb.set_tooltip_markup(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
|
||||
## Create CNC Job
|
||||
self.cncjob_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.cncjob_label.set_markup('<b>Create CNC Job:</b>')
|
||||
self.cncjob_label.set_tooltip_markup(
|
||||
"Create a CNC Job object\n"
|
||||
"tracing the contours of this\n"
|
||||
"Geometry object."
|
||||
)
|
||||
self.custom_box.pack_start(self.cncjob_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid1 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid1, expand=True, fill=False, padding=2)
|
||||
|
||||
# Cut Z
|
||||
l1 = Gtk.Label('Cut Z:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
"Cutting depth (negative)\n"
|
||||
"below the copper surface."
|
||||
)
|
||||
grid1.attach(l1, 0, 0, 1, 1)
|
||||
self.cutz_entry = LengthEntry()
|
||||
grid1.attach(self.cutz_entry, 1, 0, 1, 1)
|
||||
|
||||
# Travel Z
|
||||
l2 = Gtk.Label('Travel Z:', xalign=1)
|
||||
l2.set_tooltip_markup(
|
||||
"Height of the tool when\n"
|
||||
"moving without cutting."
|
||||
)
|
||||
grid1.attach(l2, 0, 1, 1, 1)
|
||||
self.travelz_entry = LengthEntry()
|
||||
grid1.attach(self.travelz_entry, 1, 1, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Feed rate:', xalign=1)
|
||||
l3.set_tooltip_markup(
|
||||
"Cutting speed in the XY\n"
|
||||
"plane in units per minute"
|
||||
)
|
||||
grid1.attach(l3, 0, 2, 1, 1)
|
||||
self.cncfeedrate_entry = LengthEntry()
|
||||
grid1.attach(self.cncfeedrate_entry, 1, 2, 1, 1)
|
||||
|
||||
l4 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l4.set_tooltip_markup(
|
||||
"The diameter of the cutting\n"
|
||||
"tool (just for display)."
|
||||
)
|
||||
grid1.attach(l4, 0, 3, 1, 1)
|
||||
self.cnctooldia_entry = LengthEntry()
|
||||
grid1.attach(self.cnctooldia_entry, 1, 3, 1, 1)
|
||||
|
||||
self.generate_cnc_button = Gtk.Button(label='Generate')
|
||||
self.generate_cnc_button.set_tooltip_markup(
|
||||
"Generate the CNC Job object."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_cnc_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Paint Area
|
||||
self.paint_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.paint_label.set_markup('<b>Paint Area:</b>')
|
||||
self.paint_label.set_tooltip_markup(
|
||||
"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.pack_start(self.paint_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid2 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid2, expand=True, fill=False, padding=2)
|
||||
|
||||
# Tool dia
|
||||
l5 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l5.set_tooltip_markup(
|
||||
"Diameter of the tool to\n"
|
||||
"be used in the operation."
|
||||
)
|
||||
grid2.attach(l5, 0, 0, 1, 1)
|
||||
self.painttooldia_entry = LengthEntry()
|
||||
grid2.attach(self.painttooldia_entry, 1, 0, 1, 1)
|
||||
|
||||
# Overlap
|
||||
l6 = Gtk.Label('Overlap:', xalign=1)
|
||||
l6.set_tooltip_markup(
|
||||
"How much (fraction) of the tool\n"
|
||||
"width to overlap each tool pass."
|
||||
)
|
||||
grid2.attach(l6, 0, 1, 1, 1)
|
||||
self.paintoverlap_entry = LengthEntry()
|
||||
grid2.attach(self.paintoverlap_entry, 1, 1, 1, 1)
|
||||
|
||||
# Margin
|
||||
l7 = Gtk.Label('Margin:', xalign=1)
|
||||
l7.set_tooltip_markup(
|
||||
"Distance by which to avoid\n"
|
||||
"the edges of the polygon to\n"
|
||||
"be painted."
|
||||
)
|
||||
grid2.attach(l7, 0, 2, 1, 1)
|
||||
self.paintmargin_entry = LengthEntry()
|
||||
grid2.attach(self.paintmargin_entry, 1, 2, 1, 1)
|
||||
|
||||
# GO Button
|
||||
self.generate_paint_button = Gtk.Button(label='Generate')
|
||||
self.generate_paint_button.set_tooltip_markup(
|
||||
"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.pack_start(self.generate_paint_button, expand=True, fill=False, padding=2)
|
||||
|
||||
|
||||
class ExcellonObjectUI(ObjectUI):
|
||||
"""
|
||||
User interface for Excellon objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Excellon Object', icon_file='share/drill32.png')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
self.plot_cb.set_tooltip_markup(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
|
||||
self.solid_cb = FCCheckBox(label='Solid')
|
||||
self.solid_cb.set_tooltip_markup(
|
||||
"Solid circles."
|
||||
)
|
||||
grid0.attach(self.solid_cb, 1, 0, 1, 1)
|
||||
|
||||
## Create CNC Job
|
||||
self.cncjob_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.cncjob_label.set_markup('<b>Create CNC Job</b>')
|
||||
self.cncjob_label.set_tooltip_markup(
|
||||
"Create a CNC Job object\n"
|
||||
"for this drill object."
|
||||
)
|
||||
self.custom_box.pack_start(self.cncjob_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid1 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid1, expand=True, fill=False, padding=2)
|
||||
|
||||
l1 = Gtk.Label('Cut Z:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
"Drill depth (negative)\n"
|
||||
"below the copper surface."
|
||||
)
|
||||
grid1.attach(l1, 0, 0, 1, 1)
|
||||
self.cutz_entry = LengthEntry()
|
||||
grid1.attach(self.cutz_entry, 1, 0, 1, 1)
|
||||
|
||||
l2 = Gtk.Label('Travel Z:', xalign=1)
|
||||
l2.set_tooltip_markup(
|
||||
"Tool height when travelling\n"
|
||||
"across the XY plane."
|
||||
)
|
||||
grid1.attach(l2, 0, 1, 1, 1)
|
||||
self.travelz_entry = LengthEntry()
|
||||
grid1.attach(self.travelz_entry, 1, 1, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Feed rate:', xalign=1)
|
||||
l3.set_tooltip_markup(
|
||||
"Tool speed while drilling\n"
|
||||
"(in units per minute)."
|
||||
)
|
||||
grid1.attach(l3, 0, 2, 1, 1)
|
||||
self.feedrate_entry = LengthEntry()
|
||||
grid1.attach(self.feedrate_entry, 1, 2, 1, 1)
|
||||
|
||||
l4 = Gtk.Label('Tools:', xalign=1)
|
||||
l4.set_tooltip_markup(
|
||||
"Which tools to include\n"
|
||||
"in the CNC Job."
|
||||
)
|
||||
grid1.attach(l4, 0, 3, 1, 1)
|
||||
boxt = Gtk.Box()
|
||||
grid1.attach(boxt, 1, 3, 1, 1)
|
||||
self.tools_entry = FCEntry()
|
||||
boxt.pack_start(self.tools_entry, expand=True, fill=False, padding=2)
|
||||
self.choose_tools_button = Gtk.Button(label='Choose...')
|
||||
self.choose_tools_button.set_tooltip_markup(
|
||||
"Choose the tools\n"
|
||||
"from a list."
|
||||
)
|
||||
boxt.pack_start(self.choose_tools_button, expand=True, fill=False, padding=2)
|
||||
|
||||
self.generate_cnc_button = Gtk.Button(label='Generate')
|
||||
self.generate_cnc_button.set_tooltip_markup(
|
||||
"Generate the CNC Job."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_cnc_button, expand=True, fill=False, padding=2)
|
||||
|
||||
|
||||
class GerberObjectUI(ObjectUI):
|
||||
"""
|
||||
User interface for Gerber objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Gerber Object')
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
self.plot_cb.set_tooltip_markup(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
|
||||
# Solid CB
|
||||
self.solid_cb = FCCheckBox(label='Solid')
|
||||
self.solid_cb.set_tooltip_markup(
|
||||
"Solid color polygons."
|
||||
)
|
||||
grid0.attach(self.solid_cb, 1, 0, 1, 1)
|
||||
|
||||
# Multicolored CB
|
||||
self.multicolored_cb = FCCheckBox(label='Multicolored')
|
||||
self.multicolored_cb.set_tooltip_markup(
|
||||
"Draw polygons in different colors."
|
||||
)
|
||||
grid0.attach(self.multicolored_cb, 2, 0, 1, 1)
|
||||
|
||||
## Isolation Routing
|
||||
self.isolation_routing_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.isolation_routing_label.set_markup("<b>Isolation Routing:</b>")
|
||||
self.isolation_routing_label.set_tooltip_markup(
|
||||
"Create a Geometry object with\n"
|
||||
"toolpaths to cut outside polygons."
|
||||
)
|
||||
self.custom_box.pack_start(self.isolation_routing_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid, expand=True, fill=False, padding=2)
|
||||
|
||||
l1 = Gtk.Label('Tool diam:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
"Diameter of the cutting tool."
|
||||
)
|
||||
grid.attach(l1, 0, 0, 1, 1)
|
||||
self.iso_tool_dia_entry = LengthEntry()
|
||||
grid.attach(self.iso_tool_dia_entry, 1, 0, 1, 1)
|
||||
|
||||
l2 = Gtk.Label('Width (# passes):', xalign=1)
|
||||
l2.set_tooltip_markup(
|
||||
"Width of the isolation gap in\n"
|
||||
"number (integer) of tool widths."
|
||||
)
|
||||
grid.attach(l2, 0, 1, 1, 1)
|
||||
self.iso_width_entry = IntEntry()
|
||||
grid.attach(self.iso_width_entry, 1, 1, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Pass overlap:', xalign=1)
|
||||
l3.set_tooltip_markup(
|
||||
"How much (fraction of tool width)\n"
|
||||
"to overlap each pass."
|
||||
)
|
||||
grid.attach(l3, 0, 2, 1, 1)
|
||||
self.iso_overlap_entry = FloatEntry()
|
||||
grid.attach(self.iso_overlap_entry, 1, 2, 1, 1)
|
||||
|
||||
self.generate_iso_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_iso_button.set_tooltip_markup(
|
||||
"Create the Geometry Object\n"
|
||||
"for isolation routing."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_iso_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Board cuttout
|
||||
self.board_cutout_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.board_cutout_label.set_markup("<b>Board cutout:</b>")
|
||||
self.board_cutout_label.set_tooltip_markup(
|
||||
"Create toolpaths to cut around\n"
|
||||
"the PCB and separate it from\n"
|
||||
"the original board."
|
||||
)
|
||||
self.custom_box.pack_start(self.board_cutout_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid2 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid2, expand=True, fill=False, padding=2)
|
||||
|
||||
l4 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l4.set_tooltip_markup(
|
||||
"Diameter of the cutting tool."
|
||||
)
|
||||
grid2.attach(l4, 0, 0, 1, 1)
|
||||
self.cutout_tooldia_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_tooldia_entry, 1, 0, 1, 1)
|
||||
|
||||
l5 = Gtk.Label('Margin:', xalign=1)
|
||||
l5.set_tooltip_markup(
|
||||
"Distance from objects at which\n"
|
||||
"to draw the cutout."
|
||||
)
|
||||
grid2.attach(l5, 0, 1, 1, 1)
|
||||
self.cutout_margin_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_margin_entry, 1, 1, 1, 1)
|
||||
|
||||
l6 = Gtk.Label('Gap size:', xalign=1)
|
||||
l6.set_tooltip_markup(
|
||||
"Size of the gaps in the toolpath\n"
|
||||
"that will remain to hold the\n"
|
||||
"board in place."
|
||||
)
|
||||
grid2.attach(l6, 0, 2, 1, 1)
|
||||
self.cutout_gap_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_gap_entry, 1, 2, 1, 1)
|
||||
|
||||
l7 = Gtk.Label('Gaps:', xalign=1)
|
||||
l7.set_tooltip_markup(
|
||||
"Where to place the gaps, Top/Bottom\n"
|
||||
"Left/Rigt, or on all 4 sides."
|
||||
)
|
||||
grid2.attach(l7, 0, 3, 1, 1)
|
||||
self.gaps_radio = RadioSet([{'label': '2 (T/B)', 'value': 'tb'},
|
||||
{'label': '2 (L/R)', 'value': 'lr'},
|
||||
{'label': '4', 'value': '4'}])
|
||||
grid2.attach(self.gaps_radio, 1, 3, 1, 1)
|
||||
|
||||
self.generate_cutout_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_cutout_button.set_tooltip_markup(
|
||||
"Generate the geometry for\n"
|
||||
"the board cutout."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_cutout_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Non-copper regions
|
||||
self.noncopper_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.noncopper_label.set_markup("<b>Non-copper regions:</b>")
|
||||
self.noncopper_label.set_tooltip_markup(
|
||||
"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.pack_start(self.noncopper_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid3 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid3, expand=True, fill=False, padding=2)
|
||||
|
||||
l8 = Gtk.Label('Boundary margin:', xalign=1)
|
||||
l8.set_tooltip_markup(
|
||||
"Specify the edge of the PCB\n"
|
||||
"by drawing a box around all\n"
|
||||
"objects with this minimum\n"
|
||||
"distance."
|
||||
)
|
||||
grid3.attach(l8, 0, 0, 1, 1)
|
||||
self.noncopper_margin_entry = LengthEntry()
|
||||
grid3.attach(self.noncopper_margin_entry, 1, 0, 1, 1)
|
||||
|
||||
self.noncopper_rounded_cb = FCCheckBox(label="Rounded corners")
|
||||
self.noncopper_rounded_cb.set_tooltip_markup(
|
||||
"If the boundary of the board\n"
|
||||
"is to have rounded corners\n"
|
||||
"their radius is equal to the margin."
|
||||
)
|
||||
grid3.attach(self.noncopper_rounded_cb, 0, 1, 2, 1)
|
||||
|
||||
self.generate_noncopper_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_noncopper_button.set_tooltip_markup(
|
||||
"Creates a Geometry objects with polygons\n"
|
||||
"covering the copper-free areas of the PCB."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_noncopper_button, expand=True, fill=False, padding=2)
|
||||
|
||||
## Bounding box
|
||||
self.boundingbox_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.boundingbox_label.set_markup('<b>Bounding Box:</b>')
|
||||
self.boundingbox_label.set_tooltip_markup(
|
||||
"Create a Geometry object with a rectangle\n"
|
||||
"enclosing all polygons at a given distance."
|
||||
)
|
||||
self.custom_box.pack_start(self.boundingbox_label, expand=True, fill=False, padding=2)
|
||||
|
||||
grid4 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid4, expand=True, fill=False, padding=2)
|
||||
|
||||
l9 = Gtk.Label('Boundary Margin:', xalign=1)
|
||||
l9.set_tooltip_markup(
|
||||
"Distance of the edges of the box\n"
|
||||
"to the nearest polygon."
|
||||
)
|
||||
grid4.attach(l9, 0, 0, 1, 1)
|
||||
self.bbmargin_entry = LengthEntry()
|
||||
grid4.attach(self.bbmargin_entry, 1, 0, 1, 1)
|
||||
|
||||
self.bbrounded_cb = FCCheckBox(label="Rounded corners")
|
||||
self.bbrounded_cb.set_tooltip_markup(
|
||||
"If the bounding box is \n"
|
||||
"to have rounded corners\n"
|
||||
"their radius is equal to\n"
|
||||
"the margin."
|
||||
)
|
||||
grid4.attach(self.bbrounded_cb, 0, 1, 2, 1)
|
||||
|
||||
self.generate_bb_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_bb_button.set_tooltip_markup(
|
||||
"Genrate the Geometry object."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_bb_button, expand=True, fill=False, padding=2)
|
|
@ -0,0 +1,318 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from gi.repository import Gdk
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
|
||||
#from FlatCAMApp import *
|
||||
from FlatCAM_GTK import FlatCAMApp
|
||||
|
||||
|
||||
class PlotCanvas:
|
||||
"""
|
||||
Class handling the plotting area in the application.
|
||||
"""
|
||||
|
||||
def __init__(self, container):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
# 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)
|
||||
|
||||
# The canvas is the top level container (Gtk.DrawingArea)
|
||||
self.canvas = FigureCanvas(self.figure)
|
||||
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??
|
||||
|
||||
# Events
|
||||
self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
|
||||
self.canvas.connect('configure-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('key_press_event', self.on_key_down)
|
||||
self.canvas.mpl_connect('key_release_event', self.on_key_up)
|
||||
|
||||
self.mouse = [0, 0]
|
||||
self.key = None
|
||||
|
||||
def on_key_down(self, event):
|
||||
"""
|
||||
|
||||
:param event:
|
||||
:return:
|
||||
"""
|
||||
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 GTK 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.queue_draw()
|
||||
|
||||
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])
|
||||
|
||||
# Re-draw
|
||||
self.canvas.queue_draw()
|
||||
|
||||
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))
|
||||
|
||||
# Re-draw
|
||||
self.canvas.queue_draw()
|
||||
|
||||
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.queue_draw()
|
||||
|
||||
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, canvas, event):
|
||||
"""
|
||||
Scroll event handler.
|
||||
|
||||
:param canvas: The widget generating the event. Ignored.
|
||||
:param event: Event object containing the event information.
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# So it can receive key presses
|
||||
self.canvas.grab_focus()
|
||||
|
||||
# Event info
|
||||
z, direction = event.get_scroll_direction()
|
||||
|
||||
if self.key is None:
|
||||
|
||||
if direction is Gdk.ScrollDirection.UP:
|
||||
self.zoom(1.5, self.mouse)
|
||||
else:
|
||||
self.zoom(1/1.5, self.mouse)
|
||||
return
|
||||
|
||||
if self.key == 'shift':
|
||||
|
||||
if direction is Gdk.ScrollDirection.UP:
|
||||
self.pan(0.3, 0)
|
||||
else:
|
||||
self.pan(-0.3, 0)
|
||||
return
|
||||
|
||||
if self.key == 'ctrl+control':
|
||||
|
||||
if direction is Gdk.ScrollDirection.UP:
|
||||
self.pan(0, 0.3)
|
||||
else:
|
||||
self.pan(0, -0.3)
|
||||
return
|
||||
|
||||
def on_mouse_move(self, event):
|
||||
"""
|
||||
Mouse movement event hadler. Stores the coordinates.
|
||||
|
||||
:param event: Contains information about the event.
|
||||
:return: None
|
||||
"""
|
||||
self.mouse = [event.xdata, event.ydata]
|
221
GUIElements.py
221
GUIElements.py
|
@ -1,19 +1,11 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from gi.repository import Gtk
|
||||
import re
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from copy import copy
|
||||
import FlatCAMApp
|
||||
import re
|
||||
|
||||
|
||||
class RadioSet(Gtk.Box):
|
||||
def __init__(self, choices):
|
||||
class RadioSet(QtGui.QWidget):
|
||||
def __init__(self, choices, orientation='horizontal', parent=None):
|
||||
"""
|
||||
The choices are specified as a list of dictionaries containing:
|
||||
|
||||
|
@ -23,28 +15,37 @@ class RadioSet(Gtk.Box):
|
|||
:param choices: List of choices. See description.
|
||||
:type choices: list
|
||||
"""
|
||||
Gtk.Box.__init__(self)
|
||||
super(RadioSet, self).__init__(parent)
|
||||
self.choices = copy(choices)
|
||||
self.group = None
|
||||
|
||||
if orientation == 'horizontal':
|
||||
layout = QtGui.QHBoxLayout()
|
||||
else:
|
||||
layout = QtGui.QVBoxLayout()
|
||||
|
||||
group = QtGui.QButtonGroup(self)
|
||||
|
||||
for choice in self.choices:
|
||||
if self.group is None:
|
||||
choice['radio'] = Gtk.RadioButton.new_with_label(None, choice['label'])
|
||||
self.group = choice['radio']
|
||||
else:
|
||||
choice['radio'] = Gtk.RadioButton.new_with_label_from_widget(self.group, choice['label'])
|
||||
self.pack_start(choice['radio'], expand=True, fill=False, padding=2)
|
||||
choice['radio'].connect('toggled', self.on_toggle)
|
||||
choice['radio'] = QtGui.QRadioButton(choice['label'])
|
||||
group.addButton(choice['radio'])
|
||||
layout.addWidget(choice['radio'], stretch=0)
|
||||
choice['radio'].toggled.connect(self.on_toggle)
|
||||
|
||||
self.group_toggle_fn = lambda x, y: None
|
||||
layout.addStretch()
|
||||
self.setLayout(layout)
|
||||
|
||||
def on_toggle(self, btn):
|
||||
if btn.get_active():
|
||||
self.group_toggle_fn(btn, self.get_value)
|
||||
self.group_toggle_fn = lambda: None
|
||||
|
||||
def on_toggle(self):
|
||||
FlatCAMApp.App.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'].get_active():
|
||||
if choice['radio'].isChecked():
|
||||
return choice['value']
|
||||
FlatCAMApp.App.log.error("No button was toggled in RadioSet.")
|
||||
return None
|
||||
|
@ -52,33 +53,15 @@ class RadioSet(Gtk.Box):
|
|||
def set_value(self, val):
|
||||
for choice in self.choices:
|
||||
if choice['value'] == val:
|
||||
choice['radio'].set_active(True)
|
||||
choice['radio'].setChecked(True)
|
||||
return
|
||||
FlatCAMApp.App.log.error("Value given is not part of this RadioSet: %s" % str(val))
|
||||
|
||||
|
||||
class LengthEntry(Gtk.Entry):
|
||||
"""
|
||||
A text entry that interprets its string as a
|
||||
length, with or without specified units. When the user reads
|
||||
the value, it is interpreted and replaced by a floating
|
||||
point representation of the value in the default units. When
|
||||
the entry is activated, its string is repalced by the interpreted
|
||||
value.
|
||||
class LengthEntry(QtGui.QLineEdit):
|
||||
def __init__(self, output_units='IN', parent=None):
|
||||
super(LengthEntry, self).__init__(parent)
|
||||
|
||||
Example:
|
||||
Default units are 'IN', input is "1.0 mm", value returned
|
||||
is 1.0/25.4 = 0.03937.
|
||||
"""
|
||||
|
||||
def __init__(self, output_units='IN'):
|
||||
"""
|
||||
|
||||
:param output_units: The default output units, 'IN' or 'MM'
|
||||
:return: LengthEntry
|
||||
"""
|
||||
|
||||
Gtk.Entry.__init__(self)
|
||||
self.output_units = output_units
|
||||
self.format_re = re.compile(r"^([^\s]+)(?:\s([a-zA-Z]+))?$")
|
||||
|
||||
|
@ -90,40 +73,22 @@ class LengthEntry(Gtk.Entry):
|
|||
'MM': 1.0}
|
||||
}
|
||||
|
||||
self.connect('activate', self.on_activate)
|
||||
|
||||
def on_activate(self, *args):
|
||||
"""
|
||||
Entry "activate" callback. Replaces the text in the
|
||||
entry with the value returned by `get_value()`.
|
||||
|
||||
:param args: Ignored.
|
||||
:return: None.
|
||||
"""
|
||||
def returnPressed(self, *args, **kwargs):
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
self.set_text(QtCore.QString(str(val)))
|
||||
else:
|
||||
FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
|
||||
|
||||
def get_value(self):
|
||||
"""
|
||||
Fetches, interprets and returns the value in the entry. The text
|
||||
is parsed to find the numerical expression and the (input) units (if any).
|
||||
The numerical expression is interpreted and scaled acording to the
|
||||
input and output units `self.output_units`.
|
||||
|
||||
:return: Floating point representation of the value in the entry.
|
||||
:rtype: float
|
||||
"""
|
||||
|
||||
raw = self.get_text().strip(' ')
|
||||
raw = str(self.text()).strip(' ')
|
||||
match = self.format_re.search(raw)
|
||||
|
||||
if not match:
|
||||
return None
|
||||
try:
|
||||
if match.group(2) is not None and match.group(2).upper() in self.scales:
|
||||
return float(eval(match.group(1)))*self.scales[self.output_units][match.group(2).upper()]
|
||||
return float(eval(match.group(1)))*float(self.scales[self.output_units][match.group(2).upper()])
|
||||
else:
|
||||
return float(eval(match.group(1)))
|
||||
except:
|
||||
|
@ -131,24 +96,22 @@ class LengthEntry(Gtk.Entry):
|
|||
return None
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
self.setText(QtCore.QString(str(val)))
|
||||
|
||||
|
||||
class FloatEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
class FloatEntry(QtGui.QLineEdit):
|
||||
def __init__(self, parent=None):
|
||||
super(FloatEntry, self).__init__(parent)
|
||||
|
||||
self.connect('activate', self.on_activate)
|
||||
|
||||
def on_activate(self, *args):
|
||||
def returnPressed(self, *args, **kwargs):
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
self.set_text(QtCore.QString(str(val)))
|
||||
else:
|
||||
FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
|
||||
FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.text())
|
||||
|
||||
def get_value(self):
|
||||
raw = self.get_text().strip(' ')
|
||||
raw = str(self.text()).strip(' ')
|
||||
try:
|
||||
evaled = eval(raw)
|
||||
except:
|
||||
|
@ -158,44 +121,44 @@ class FloatEntry(Gtk.Entry):
|
|||
return float(evaled)
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
self.setText("%.6f"%val)
|
||||
|
||||
|
||||
class IntEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
class IntEntry(QtGui.QLineEdit):
|
||||
def __init__(self, parent=None):
|
||||
super(IntEntry, self).__init__(parent)
|
||||
|
||||
def get_value(self):
|
||||
return int(self.get_text())
|
||||
return int(self.text())
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
self.setText(QtCore.QString(str(val)))
|
||||
|
||||
|
||||
class FCEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
class FCEntry(QtGui.QLineEdit):
|
||||
def __init__(self, parent=None):
|
||||
super(FCEntry, self).__init__(parent)
|
||||
|
||||
def get_value(self):
|
||||
return self.get_text()
|
||||
return str(self.text())
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
self.setText(QtCore.QString(str(val)))
|
||||
|
||||
|
||||
class EvalEntry(Gtk.Entry):
|
||||
def __init__(self):
|
||||
Gtk.Entry.__init__(self)
|
||||
class EvalEntry(QtGui.QLineEdit):
|
||||
def __init__(self, parent=None):
|
||||
super(EvalEntry, self).__init__(parent)
|
||||
|
||||
def on_activate(self, *args):
|
||||
def returnPressed(self, *args, **kwargs):
|
||||
val = self.get_value()
|
||||
if val is not None:
|
||||
self.set_text(str(val))
|
||||
self.setText(QtCore.QString(str(val)))
|
||||
else:
|
||||
FlatCAMApp.App.log.warning("Could not interpret entry: %s" % self.get_text())
|
||||
|
||||
def get_value(self):
|
||||
raw = self.get_text().strip(' ')
|
||||
raw = str(self.text()).strip(' ')
|
||||
try:
|
||||
return eval(raw)
|
||||
except:
|
||||
|
@ -203,15 +166,65 @@ class EvalEntry(Gtk.Entry):
|
|||
return None
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_text(str(val))
|
||||
self.setText(QtCore.QString(str(val)))
|
||||
|
||||
|
||||
class FCCheckBox(Gtk.CheckButton):
|
||||
def __init__(self, label=''):
|
||||
Gtk.CheckButton.__init__(self, label=label)
|
||||
class FCCheckBox(QtGui.QCheckBox):
|
||||
def __init__(self, label='', parent=None):
|
||||
super(FCCheckBox, self).__init__(QtCore.QString(label), parent)
|
||||
|
||||
def get_value(self):
|
||||
return self.get_active()
|
||||
return self.isChecked()
|
||||
|
||||
def set_value(self, val):
|
||||
self.set_active(val)
|
||||
self.setChecked(val)
|
||||
|
||||
|
||||
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 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():
|
||||
# FlatCAMApp.App.log.debug("VerticalScrollArea: Widget resized:")
|
||||
# FlatCAMApp.App.log.debug(" minimumSizeHint().width() = %d" % self.widget().minimumSizeHint().width())
|
||||
# FlatCAMApp.App.log.debug(" verticalScrollBar().width() = %d" % self.verticalScrollBar().width())
|
||||
|
||||
self.setMinimumWidth(self.widget().sizeHint().width() +
|
||||
self.verticalScrollBar().sizeHint().width())
|
||||
|
||||
# if self.verticalScrollBar().isVisible():
|
||||
# FlatCAMApp.App.log.debug(" Scroll bar visible")
|
||||
# self.setMinimumWidth(self.widget().minimumSizeHint().width() +
|
||||
# self.verticalScrollBar().width())
|
||||
# else:
|
||||
# FlatCAMApp.App.log.debug(" Scroll bar hidden")
|
||||
# self.setMinimumWidth(self.widget().minimumSizeHint().width())
|
||||
return QtGui.QWidget.eventFilter(self, source, event)
|
|
@ -1,18 +1,11 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 4/20/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from PyQt4.QtCore import QModelIndex
|
||||
from FlatCAMObj import *
|
||||
from gi.repository import Gtk, GdkPixbuf
|
||||
import inspect # TODO: Remove
|
||||
import FlatCAMApp
|
||||
from PyQt4 import Qt, QtGui, QtCore
|
||||
|
||||
|
||||
class ObjectCollection:
|
||||
class ObjectCollection(QtCore.QAbstractListModel):
|
||||
|
||||
classdict = {
|
||||
"gerber": FlatCAMGerber,
|
||||
|
@ -28,130 +21,51 @@ class ObjectCollection:
|
|||
"geometry": "share/geometry16.png"
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
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] = GdkPixbuf.Pixbuf.new_from_file(ObjectCollection.icon_files[kind])
|
||||
self.icons[kind] = QtGui.QPixmap(ObjectCollection.icon_files[kind])
|
||||
|
||||
### GUI List components
|
||||
## Model
|
||||
self.store = Gtk.ListStore(FlatCAMObj)
|
||||
self.object_list = []
|
||||
|
||||
## View
|
||||
self.view = Gtk.TreeView(model=self.store)
|
||||
#self.view.connect("row_activated", self.on_row_activated)
|
||||
self.tree_selection = self.view.get_selection()
|
||||
self.change_subscription = self.tree_selection.connect("changed", self.on_list_selection_change)
|
||||
self.view = QtGui.QListView()
|
||||
self.view.setModel(self)
|
||||
self.view.selectionModel().selectionChanged.connect(self.on_list_selection_change)
|
||||
self.view.activated.connect(self.on_item_activated)
|
||||
|
||||
## Renderers
|
||||
# Icon
|
||||
renderer_pixbuf = Gtk.CellRendererPixbuf()
|
||||
column_pixbuf = Gtk.TreeViewColumn("Type", renderer_pixbuf)
|
||||
def rowCount(self, parent=QtCore.QModelIndex(), *args, **kwargs):
|
||||
return len(self.object_list)
|
||||
|
||||
def _set_cell_icon(column, cell, model, it, data):
|
||||
obj = model.get_value(it, 0)
|
||||
cell.set_property('pixbuf', self.icons[obj.kind])
|
||||
def columnCount(self, *args, **kwargs):
|
||||
return 1
|
||||
|
||||
column_pixbuf.set_cell_data_func(renderer_pixbuf, _set_cell_icon)
|
||||
self.view.append_column(column_pixbuf)
|
||||
|
||||
# Name
|
||||
renderer_text = Gtk.CellRendererText()
|
||||
column_text = Gtk.TreeViewColumn("Name", renderer_text)
|
||||
|
||||
def _set_cell_text(column, cell, model, it, data):
|
||||
obj = model.get_value(it, 0)
|
||||
cell.set_property('text', obj.options["name"])
|
||||
|
||||
column_text.set_cell_data_func(renderer_text, _set_cell_text)
|
||||
self.view.append_column(column_text)
|
||||
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]
|
||||
|
||||
def print_list(self):
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
for obj in self.object_list:
|
||||
print obj
|
||||
iterat = self.store.iter_next(iterat)
|
||||
|
||||
def delete_all(self):
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_all()")
|
||||
self.store.clear()
|
||||
|
||||
def delete_active(self):
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_active()")
|
||||
try:
|
||||
model, treeiter = self.tree_selection.get_selected()
|
||||
self.store.remove(treeiter)
|
||||
except:
|
||||
pass
|
||||
|
||||
def on_list_selection_change(self, selection):
|
||||
"""
|
||||
Callback for change in selection on the objects' list.
|
||||
Instructs the new selection to build the UI for its options.
|
||||
|
||||
:param selection: Ignored.
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.on_list_selection_change()")
|
||||
try:
|
||||
self.get_active().build_ui()
|
||||
except AttributeError: # For None being active
|
||||
pass
|
||||
|
||||
def set_active(self, name):
|
||||
"""
|
||||
Sets an object as the active object in the program. Same
|
||||
as `set_list_selection()`.
|
||||
|
||||
:param name: Name of the object.
|
||||
:type name: str
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.set_active()")
|
||||
self.set_list_selection(name)
|
||||
|
||||
def get_active(self):
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_active()")
|
||||
try:
|
||||
model, treeiter = self.tree_selection.get_selected()
|
||||
return model[treeiter][0]
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def set_list_selection(self, name):
|
||||
"""
|
||||
Sets which object should be selected in the list.
|
||||
|
||||
:param name: Name of the object.
|
||||
:rtype name: str
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.set_list_selection()")
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None and self.store[iterat][0].options["name"] != name:
|
||||
iterat = self.store.iter_next(iterat)
|
||||
self.tree_selection.select_iter(iterat)
|
||||
|
||||
def append(self, obj, active=False):
|
||||
"""
|
||||
Add a FlatCAMObj the the collection. This method is thread-safe.
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.append()")
|
||||
|
||||
:param obj: FlatCAMObj to append
|
||||
:type obj: FlatCAMObj
|
||||
:param active: If it is to become the active object after appending
|
||||
:type active: bool
|
||||
:return: None
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.append()")
|
||||
obj.set_ui(obj.ui_type())
|
||||
|
||||
def guitask():
|
||||
self.store.append([obj])
|
||||
if active:
|
||||
self.set_list_selection(obj.options["name"])
|
||||
GLib.idle_add(guitask)
|
||||
# Required before appending
|
||||
self.beginInsertRows(QtCore.QModelIndex(), len(self.object_list), len(self.object_list))
|
||||
|
||||
self.object_list.append(obj)
|
||||
|
||||
# Required after appending
|
||||
self.endInsertRows()
|
||||
|
||||
def get_names(self):
|
||||
"""
|
||||
|
@ -160,14 +74,9 @@ class ObjectCollection:
|
|||
:return: List of names.
|
||||
:rtype: list
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_names()")
|
||||
names = []
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
names.append(obj.options["name"])
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return names
|
||||
|
||||
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):
|
||||
"""
|
||||
|
@ -185,9 +94,7 @@ class ObjectCollection:
|
|||
xmax = -Inf
|
||||
ymax = -Inf
|
||||
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
for obj in self.object_list:
|
||||
try:
|
||||
gxmin, gymin, gxmax, gymax = obj.bounds()
|
||||
xmin = min([xmin, gxmin])
|
||||
|
@ -196,25 +103,9 @@ class ObjectCollection:
|
|||
ymax = max([ymax, gymax])
|
||||
except:
|
||||
FlatCAMApp.App.log.warning("DEV WARNING: Tried to get bounds of empty geometry.")
|
||||
iterat = self.store.iter_next(iterat)
|
||||
|
||||
return [xmin, ymin, xmax, ymax]
|
||||
|
||||
def get_list(self):
|
||||
"""
|
||||
Returns a list with all FlatCAMObj.
|
||||
|
||||
:return: List with all FlatCAMObj.
|
||||
:rtype: list
|
||||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_list()")
|
||||
collection_list = []
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
collection_list.append(obj)
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return collection_list
|
||||
|
||||
def get_by_name(self, name):
|
||||
"""
|
||||
Fetches the FlatCAMObj with the given `name`.
|
||||
|
@ -226,33 +117,57 @@ class ObjectCollection:
|
|||
"""
|
||||
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_by_name()")
|
||||
|
||||
iterat = self.store.get_iter_first()
|
||||
while iterat is not None:
|
||||
obj = self.store[iterat][0]
|
||||
if obj.options["name"] == name:
|
||||
for obj in self.object_list:
|
||||
if obj.options['name'] == name:
|
||||
return obj
|
||||
iterat = self.store.iter_next(iterat)
|
||||
return None
|
||||
|
||||
# def change_name(self, old_name, new_name):
|
||||
# """
|
||||
# Changes the name of `FlatCAMObj` named `old_name` to `new_name`.
|
||||
#
|
||||
# :param old_name: Name of the object to change.
|
||||
# :type old_name: str
|
||||
# :param new_name: New name.
|
||||
# :type new_name: str
|
||||
# :return: True if name change succeeded, False otherwise. Will fail
|
||||
# if no object with `old_name` is found.
|
||||
# :rtype: bool
|
||||
# """
|
||||
# print inspect.stack()[1][3], "--> OC.change_name()"
|
||||
# iterat = self.store.get_iter_first()
|
||||
# while iterat is not None:
|
||||
# obj = self.store[iterat][0]
|
||||
# if obj.options["name"] == old_name:
|
||||
# obj.options["name"] = new_name
|
||||
# self.store.row_changed(0, iterat)
|
||||
# return True
|
||||
# iterat = self.store.iter_next(iterat)
|
||||
# return False
|
||||
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.endRemoveRows()
|
||||
|
||||
def get_active(self):
|
||||
selections = self.view.selectedIndexes()
|
||||
if len(selections) == 0:
|
||||
return None
|
||||
row = selections[0].row()
|
||||
return self.object_list[row]
|
||||
|
||||
def set_active(self, name):
|
||||
iobj = self.createIndex(self.get_names().index(name))
|
||||
self.view.selectionModel().select(iobj, QtGui.QItemSelectionModel)
|
||||
|
||||
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_activated(self, index):
|
||||
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.object_list = []
|
||||
|
||||
self.endResetModel()
|
||||
|
||||
def get_list(self):
|
||||
return self.object_list
|
||||
|
||||
|
|
577
ObjectUI.py
577
ObjectUI.py
|
@ -1,114 +1,103 @@
|
|||
############################################################
|
||||
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||
# http://caram.cl/software/flatcam #
|
||||
# Author: Juan Pablo Caram (c) #
|
||||
# Date: 2/5/2014 #
|
||||
# MIT Licence #
|
||||
############################################################
|
||||
|
||||
from gi.repository import Gtk
|
||||
import sys
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from GUIElements import *
|
||||
|
||||
|
||||
class ObjectUI(Gtk.VBox):
|
||||
class ObjectUI(QtGui.QWidget):
|
||||
"""
|
||||
Base class for the UI of FlatCAM objects.
|
||||
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'):
|
||||
Gtk.VBox.__init__(self, spacing=3, margin=5, vexpand=False)
|
||||
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 = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 2)
|
||||
self.pack_start(self.title_box, expand=False, fill=False, padding=2)
|
||||
self.title_box = QtGui.QHBoxLayout()
|
||||
layout.addLayout(self.title_box)
|
||||
|
||||
## Page Title icon
|
||||
self.icon = Gtk.Image.new_from_file(icon_file)
|
||||
self.title_box.pack_start(self.icon, expand=False, fill=False, padding=2)
|
||||
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 = Gtk.Label()
|
||||
self.title_label.set_markup("<b>" + title + "</b>")
|
||||
self.title_label.set_justify(Gtk.Justification.CENTER)
|
||||
self.title_box.pack_start(self.title_label, expand=False, fill=False, padding=2)
|
||||
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 = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 2)
|
||||
self.pack_start(self.name_box, expand=False, fill=False, padding=2)
|
||||
name_label = Gtk.Label('Name:')
|
||||
name_label.set_justify(Gtk.Justification.RIGHT)
|
||||
self.name_box.pack_start(name_label,
|
||||
expand=False, fill=False, padding=2)
|
||||
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.pack_start(self.name_entry, expand=True, fill=False, padding=2)
|
||||
self.name_box.addWidget(self.name_entry)
|
||||
|
||||
## Box box for custom widgets
|
||||
self.custom_box = Gtk.VBox(spacing=3, margin=0, vexpand=False)
|
||||
self.pack_start(self.custom_box, expand=False, fill=False, padding=0)
|
||||
self.custom_box = QtGui.QVBoxLayout()
|
||||
layout.addLayout(self.custom_box)
|
||||
|
||||
## Common to all objects
|
||||
## Scale
|
||||
self.scale_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.scale_label.set_markup('<b>Scale:</b>')
|
||||
self.scale_label.set_tooltip_markup(
|
||||
self.scale_label = QtGui.QLabel('<b>Scale:</b>')
|
||||
self.scale_label.setToolTip(
|
||||
"Change the size of the object."
|
||||
)
|
||||
self.pack_start(self.scale_label, expand=False, fill=False, padding=2)
|
||||
layout.addWidget(self.scale_label)
|
||||
|
||||
grid5 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.pack_start(grid5, expand=False, fill=False, padding=2)
|
||||
grid1 = QtGui.QGridLayout()
|
||||
layout.addLayout(grid1)
|
||||
|
||||
# Factor
|
||||
l10 = Gtk.Label('Factor:', xalign=1)
|
||||
l10.set_tooltip_markup(
|
||||
faclabel = QtGui.QLabel('Factor:')
|
||||
faclabel.setToolTip(
|
||||
"Factor by which to multiply\n"
|
||||
"geometric features of this object."
|
||||
)
|
||||
grid5.attach(l10, 0, 0, 1, 1)
|
||||
grid1.addWidget(faclabel, 0, 0)
|
||||
self.scale_entry = FloatEntry()
|
||||
self.scale_entry.set_text("1.0")
|
||||
grid5.attach(self.scale_entry, 1, 0, 1, 1)
|
||||
self.scale_entry.set_value(1.0)
|
||||
grid1.addWidget(self.scale_entry, 0, 1)
|
||||
|
||||
# GO Button
|
||||
self.scale_button = Gtk.Button(label='Scale')
|
||||
self.scale_button.set_tooltip_markup(
|
||||
self.scale_button = QtGui.QPushButton('Scale')
|
||||
self.scale_button.setToolTip(
|
||||
"Perform scaling operation."
|
||||
)
|
||||
self.pack_start(self.scale_button, expand=False, fill=False, padding=2)
|
||||
layout.addWidget(self.scale_button)
|
||||
|
||||
## Offset
|
||||
self.offset_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.offset_label.set_markup('<b>Offset:</b>')
|
||||
self.offset_label.set_tooltip_markup(
|
||||
self.offset_label = QtGui.QLabel('<b>Offset:</b>')
|
||||
self.offset_label.setToolTip(
|
||||
"Change the position of this object."
|
||||
)
|
||||
self.pack_start(self.offset_label, expand=False, fill=False, padding=2)
|
||||
layout.addWidget(self.offset_label)
|
||||
|
||||
grid6 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.pack_start(grid6, expand=False, fill=False, padding=2)
|
||||
grid2 = QtGui.QGridLayout()
|
||||
layout.addLayout(grid2)
|
||||
|
||||
# Vector
|
||||
l11 = Gtk.Label('Offset Vector:', xalign=1)
|
||||
l11.set_tooltip_markup(
|
||||
self.offset_label = QtGui.QLabel('Vector:')
|
||||
self.offset_label.setToolTip(
|
||||
"Amount by which to move the object\n"
|
||||
"in the x and y axes in (x, y) format."
|
||||
)
|
||||
grid6.attach(l11, 0, 0, 1, 1)
|
||||
grid2.addWidget(self.offset_label, 0, 0)
|
||||
self.offsetvector_entry = EvalEntry()
|
||||
self.offsetvector_entry.set_text("(0.0, 0.0)")
|
||||
grid6.attach(self.offsetvector_entry, 1, 0, 1, 1)
|
||||
self.offsetvector_entry.setText("(0.0, 0.0)")
|
||||
grid2.addWidget(self.offsetvector_entry, 0, 1)
|
||||
|
||||
self.offset_button = Gtk.Button(label='Scale')
|
||||
self.offset_button.set_tooltip_markup(
|
||||
self.offset_button = QtGui.QPushButton('Offset')
|
||||
self.offset_button.setToolTip(
|
||||
"Perform the offset operation."
|
||||
)
|
||||
self.pack_start(self.offset_button, expand=False, fill=False, padding=2)
|
||||
layout.addWidget(self.offset_button)
|
||||
|
||||
def set_field(self, name, value):
|
||||
getattr(self, name).set_value(value)
|
||||
|
||||
def get_field(self, name):
|
||||
return getattr(self, name).get_value()
|
||||
layout.addStretch()
|
||||
|
||||
|
||||
class CNCObjectUI(ObjectUI):
|
||||
|
@ -116,57 +105,68 @@ class CNCObjectUI(ObjectUI):
|
|||
User interface for CNCJob objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png')
|
||||
def __init__(self, parent=None):
|
||||
ObjectUI.__init__(self, title='CNC Job Object', icon_file='share/cnc32.png', parent=parent)
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
self.plot_options_label = QtGui.QLabel("<b>Plot Options:</b>")
|
||||
self.custom_box.addWidget(self.plot_options_label)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=False, fill=False, padding=2)
|
||||
grid0 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid0)
|
||||
|
||||
# Plot CB
|
||||
self.plot_cb = FCCheckBox(label='Plot')
|
||||
self.plot_cb.set_tooltip_markup(
|
||||
# self.plot_cb = QtGui.QCheckBox('Plot')
|
||||
self.plot_cb = FCCheckBox('Plot')
|
||||
self.plot_cb.setToolTip(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 2, 1)
|
||||
grid0.addWidget(self.plot_cb, 0, 0)
|
||||
|
||||
# Tool dia for plot
|
||||
l1 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
tdlabel = QtGui.QLabel('Tool dia:')
|
||||
tdlabel.setToolTip(
|
||||
"Diameter of the tool to be\n"
|
||||
"rendered in the plot."
|
||||
)
|
||||
grid0.attach(l1, 0, 1, 1, 1)
|
||||
grid0.addWidget(tdlabel, 1, 0)
|
||||
self.tooldia_entry = LengthEntry()
|
||||
grid0.attach(self.tooldia_entry, 1, 1, 1, 1)
|
||||
grid0.addWidget(self.tooldia_entry, 1, 1)
|
||||
|
||||
# Update plot button
|
||||
self.updateplot_button = Gtk.Button(label='Update Plot')
|
||||
self.updateplot_button.set_tooltip_markup(
|
||||
self.updateplot_button = QtGui.QPushButton('Update Plot')
|
||||
self.updateplot_button.setToolTip(
|
||||
"Update the plot."
|
||||
)
|
||||
self.custom_box.pack_start(self.updateplot_button, expand=False, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.updateplot_button)
|
||||
|
||||
## Export G-Code
|
||||
self.export_gcode_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.export_gcode_label.set_markup("<b>Export G-Code:</b>")
|
||||
self.export_gcode_label.set_tooltip_markup(
|
||||
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.pack_start(self.export_gcode_label, expand=False, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.export_gcode_label)
|
||||
|
||||
# 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)
|
||||
|
||||
# GO Button
|
||||
self.export_gcode_button = Gtk.Button(label='Export G-Code')
|
||||
self.export_gcode_button.set_tooltip_markup(
|
||||
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.pack_start(self.export_gcode_button, expand=False, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.export_gcode_button)
|
||||
|
||||
|
||||
class GeometryObjectUI(ObjectUI):
|
||||
|
@ -174,135 +174,131 @@ class GeometryObjectUI(ObjectUI):
|
|||
User interface for Geometry objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Geometry Object', icon_file='share/geometry32.png')
|
||||
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 = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
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.set_tooltip_markup(
|
||||
self.plot_cb.setToolTip(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
self.custom_box.addWidget(self.plot_cb)
|
||||
|
||||
## Create CNC Job
|
||||
self.cncjob_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.cncjob_label.set_markup('<b>Create CNC Job:</b>')
|
||||
self.cncjob_label.set_tooltip_markup(
|
||||
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.pack_start(self.cncjob_label, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.cncjob_label)
|
||||
|
||||
grid1 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid1, expand=True, fill=False, padding=2)
|
||||
grid1 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid1)
|
||||
|
||||
# Cut Z
|
||||
l1 = Gtk.Label('Cut Z:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
cutzlabel = QtGui.QLabel('Cut Z:')
|
||||
cutzlabel.setToolTip(
|
||||
"Cutting depth (negative)\n"
|
||||
"below the copper surface."
|
||||
)
|
||||
grid1.attach(l1, 0, 0, 1, 1)
|
||||
grid1.addWidget(cutzlabel, 0, 0)
|
||||
self.cutz_entry = LengthEntry()
|
||||
grid1.attach(self.cutz_entry, 1, 0, 1, 1)
|
||||
grid1.addWidget(self.cutz_entry, 0, 1)
|
||||
|
||||
# Travel Z
|
||||
l2 = Gtk.Label('Travel Z:', xalign=1)
|
||||
l2.set_tooltip_markup(
|
||||
travelzlabel = QtGui.QLabel('Travel Z:')
|
||||
travelzlabel.setToolTip(
|
||||
"Height of the tool when\n"
|
||||
"moving without cutting."
|
||||
)
|
||||
grid1.attach(l2, 0, 1, 1, 1)
|
||||
grid1.addWidget(travelzlabel, 1, 0)
|
||||
self.travelz_entry = LengthEntry()
|
||||
grid1.attach(self.travelz_entry, 1, 1, 1, 1)
|
||||
grid1.addWidget(self.travelz_entry, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Feed rate:', xalign=1)
|
||||
l3.set_tooltip_markup(
|
||||
# Feedrate
|
||||
frlabel = QtGui.QLabel('Feed Rate:')
|
||||
frlabel.setToolTip(
|
||||
"Cutting speed in the XY\n"
|
||||
"plane in units per minute"
|
||||
)
|
||||
grid1.attach(l3, 0, 2, 1, 1)
|
||||
grid1.addWidget(frlabel, 2, 0)
|
||||
self.cncfeedrate_entry = LengthEntry()
|
||||
grid1.attach(self.cncfeedrate_entry, 1, 2, 1, 1)
|
||||
grid1.addWidget(self.cncfeedrate_entry, 2, 1)
|
||||
|
||||
l4 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l4.set_tooltip_markup(
|
||||
# Tooldia
|
||||
tdlabel = QtGui.QLabel('Tool dia:')
|
||||
tdlabel.setToolTip(
|
||||
"The diameter of the cutting\n"
|
||||
"tool (just for display)."
|
||||
)
|
||||
grid1.attach(l4, 0, 3, 1, 1)
|
||||
grid1.addWidget(tdlabel, 3, 0)
|
||||
self.cnctooldia_entry = LengthEntry()
|
||||
grid1.attach(self.cnctooldia_entry, 1, 3, 1, 1)
|
||||
grid1.addWidget(self.cnctooldia_entry, 3, 1)
|
||||
|
||||
self.generate_cnc_button = Gtk.Button(label='Generate')
|
||||
self.generate_cnc_button.set_tooltip_markup(
|
||||
self.generate_cnc_button = QtGui.QPushButton('Generate')
|
||||
self.generate_cnc_button.setToolTip(
|
||||
"Generate the CNC Job object."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_cnc_button, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.generate_cnc_button)
|
||||
|
||||
## Paint Area
|
||||
self.paint_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.paint_label.set_markup('<b>Paint Area:</b>')
|
||||
self.paint_label.set_tooltip_markup(
|
||||
## 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.pack_start(self.paint_label, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.paint_label)
|
||||
|
||||
grid2 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid2, expand=True, fill=False, padding=2)
|
||||
grid2 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid2)
|
||||
|
||||
# Tool dia
|
||||
l5 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l5.set_tooltip_markup(
|
||||
ptdlabel = QtGui.QLabel('Tool dia:')
|
||||
ptdlabel.setToolTip(
|
||||
"Diameter of the tool to\n"
|
||||
"be used in the operation."
|
||||
)
|
||||
grid2.attach(l5, 0, 0, 1, 1)
|
||||
grid2.addWidget(ptdlabel, 0, 0)
|
||||
|
||||
self.painttooldia_entry = LengthEntry()
|
||||
grid2.attach(self.painttooldia_entry, 1, 0, 1, 1)
|
||||
grid2.addWidget(self.painttooldia_entry, 0, 1)
|
||||
|
||||
# Overlap
|
||||
l6 = Gtk.Label('Overlap:', xalign=1)
|
||||
l6.set_tooltip_markup(
|
||||
ovlabel = QtGui.QLabel('Overlap:')
|
||||
ovlabel.setToolTip(
|
||||
"How much (fraction) of the tool\n"
|
||||
"width to overlap each tool pass."
|
||||
)
|
||||
grid2.attach(l6, 0, 1, 1, 1)
|
||||
grid2.addWidget(ovlabel, 1, 0)
|
||||
self.paintoverlap_entry = LengthEntry()
|
||||
grid2.attach(self.paintoverlap_entry, 1, 1, 1, 1)
|
||||
grid2.addWidget(self.paintoverlap_entry, 1, 1)
|
||||
|
||||
# Margin
|
||||
l7 = Gtk.Label('Margin:', xalign=1)
|
||||
l7.set_tooltip_markup(
|
||||
marginlabel = QtGui.QLabel('Margin:')
|
||||
marginlabel.setToolTip(
|
||||
"Distance by which to avoid\n"
|
||||
"the edges of the polygon to\n"
|
||||
"be painted."
|
||||
)
|
||||
grid2.attach(l7, 0, 2, 1, 1)
|
||||
grid2.addWidget(marginlabel, 2, 0)
|
||||
self.paintmargin_entry = LengthEntry()
|
||||
grid2.attach(self.paintmargin_entry, 1, 2, 1, 1)
|
||||
grid2.addWidget(self.paintmargin_entry)
|
||||
|
||||
# GO Button
|
||||
self.generate_paint_button = Gtk.Button(label='Generate')
|
||||
self.generate_paint_button.set_tooltip_markup(
|
||||
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.pack_start(self.generate_paint_button, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.generate_paint_button)
|
||||
|
||||
|
||||
class ExcellonObjectUI(ObjectUI):
|
||||
|
@ -310,90 +306,85 @@ class ExcellonObjectUI(ObjectUI):
|
|||
User interface for Excellon objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Excellon Object', icon_file='share/drill32.png')
|
||||
def __init__(self, parent=None):
|
||||
ObjectUI.__init__(self, title='Excellon Object', icon_file='share/drill32.png', parent=parent)
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
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.set_tooltip_markup(
|
||||
self.plot_cb.setToolTip(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
|
||||
grid0.addWidget(self.plot_cb, 0, 0)
|
||||
self.solid_cb = FCCheckBox(label='Solid')
|
||||
self.solid_cb.set_tooltip_markup(
|
||||
self.solid_cb.setToolTip(
|
||||
"Solid circles."
|
||||
)
|
||||
grid0.attach(self.solid_cb, 1, 0, 1, 1)
|
||||
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 = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.cncjob_label.set_markup('<b>Create CNC Job</b>')
|
||||
self.cncjob_label.set_tooltip_markup(
|
||||
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.pack_start(self.cncjob_label, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.cncjob_label)
|
||||
|
||||
grid1 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid1, expand=True, fill=False, padding=2)
|
||||
grid1 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid1)
|
||||
|
||||
l1 = Gtk.Label('Cut Z:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
cutzlabel = QtGui.QLabel('Cut Z:')
|
||||
cutzlabel.setToolTip(
|
||||
"Drill depth (negative)\n"
|
||||
"below the copper surface."
|
||||
)
|
||||
grid1.attach(l1, 0, 0, 1, 1)
|
||||
grid1.addWidget(cutzlabel, 0, 0)
|
||||
self.cutz_entry = LengthEntry()
|
||||
grid1.attach(self.cutz_entry, 1, 0, 1, 1)
|
||||
grid1.addWidget(self.cutz_entry, 0, 1)
|
||||
|
||||
l2 = Gtk.Label('Travel Z:', xalign=1)
|
||||
l2.set_tooltip_markup(
|
||||
travelzlabel = QtGui.QLabel('Travel Z:')
|
||||
travelzlabel.setToolTip(
|
||||
"Tool height when travelling\n"
|
||||
"across the XY plane."
|
||||
)
|
||||
grid1.attach(l2, 0, 1, 1, 1)
|
||||
grid1.addWidget(travelzlabel, 1, 0)
|
||||
self.travelz_entry = LengthEntry()
|
||||
grid1.attach(self.travelz_entry, 1, 1, 1, 1)
|
||||
grid1.addWidget(self.travelz_entry, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Feed rate:', xalign=1)
|
||||
l3.set_tooltip_markup(
|
||||
frlabel = QtGui.QLabel('Feed rate:')
|
||||
frlabel.setToolTip(
|
||||
"Tool speed while drilling\n"
|
||||
"(in units per minute)."
|
||||
)
|
||||
grid1.attach(l3, 0, 2, 1, 1)
|
||||
grid1.addWidget(frlabel, 2, 0)
|
||||
self.feedrate_entry = LengthEntry()
|
||||
grid1.attach(self.feedrate_entry, 1, 2, 1, 1)
|
||||
grid1.addWidget(self.feedrate_entry, 2, 1)
|
||||
|
||||
l4 = Gtk.Label('Tools:', xalign=1)
|
||||
l4.set_tooltip_markup(
|
||||
"Which tools to include\n"
|
||||
"in the CNC Job."
|
||||
choose_tools_label = QtGui.QLabel(
|
||||
"Select from the tools section above\n"
|
||||
"the tools you want to include."
|
||||
)
|
||||
grid1.attach(l4, 0, 3, 1, 1)
|
||||
boxt = Gtk.Box()
|
||||
grid1.attach(boxt, 1, 3, 1, 1)
|
||||
self.tools_entry = FCEntry()
|
||||
boxt.pack_start(self.tools_entry, expand=True, fill=False, padding=2)
|
||||
self.choose_tools_button = Gtk.Button(label='Choose...')
|
||||
self.choose_tools_button.set_tooltip_markup(
|
||||
"Choose the tools\n"
|
||||
"from a list."
|
||||
)
|
||||
boxt.pack_start(self.choose_tools_button, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(choose_tools_label)
|
||||
|
||||
self.generate_cnc_button = Gtk.Button(label='Generate')
|
||||
self.generate_cnc_button.set_tooltip_markup(
|
||||
self.generate_cnc_button = QtGui.QPushButton('Generate')
|
||||
self.generate_cnc_button.setToolTip(
|
||||
"Generate the CNC Job."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_cnc_button, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.generate_cnc_button)
|
||||
|
||||
|
||||
class GerberObjectUI(ObjectUI):
|
||||
|
@ -401,214 +392,210 @@ class GerberObjectUI(ObjectUI):
|
|||
User interface for Gerber objects.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ObjectUI.__init__(self, title='Gerber Object')
|
||||
def __init__(self, parent=None):
|
||||
ObjectUI.__init__(self, title='Gerber Object', parent=parent)
|
||||
|
||||
## Plot options
|
||||
self.plot_options_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.plot_options_label.set_markup("<b>Plot Options:</b>")
|
||||
self.custom_box.pack_start(self.plot_options_label, expand=False, fill=True, padding=2)
|
||||
|
||||
grid0 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid0, expand=True, fill=False, padding=2)
|
||||
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_cb.set_tooltip_markup(
|
||||
self.plot_options_label.setToolTip(
|
||||
"Plot (show) this object."
|
||||
)
|
||||
grid0.attach(self.plot_cb, 0, 0, 1, 1)
|
||||
grid0.addWidget(self.plot_cb, 0, 0)
|
||||
|
||||
# Solid CB
|
||||
self.solid_cb = FCCheckBox(label='Solid')
|
||||
self.solid_cb.set_tooltip_markup(
|
||||
self.solid_cb.setToolTip(
|
||||
"Solid color polygons."
|
||||
)
|
||||
grid0.attach(self.solid_cb, 1, 0, 1, 1)
|
||||
grid0.addWidget(self.solid_cb, 0, 1)
|
||||
|
||||
# Multicolored CB
|
||||
self.multicolored_cb = FCCheckBox(label='Multicolored')
|
||||
self.multicolored_cb.set_tooltip_markup(
|
||||
self.multicolored_cb.setToolTip(
|
||||
"Draw polygons in different colors."
|
||||
)
|
||||
grid0.attach(self.multicolored_cb, 2, 0, 1, 1)
|
||||
grid0.addWidget(self.multicolored_cb, 0, 2)
|
||||
|
||||
## Isolation Routing
|
||||
self.isolation_routing_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.isolation_routing_label.set_markup("<b>Isolation Routing:</b>")
|
||||
self.isolation_routing_label.set_tooltip_markup(
|
||||
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.pack_start(self.isolation_routing_label, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.isolation_routing_label)
|
||||
|
||||
grid = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid, expand=True, fill=False, padding=2)
|
||||
|
||||
l1 = Gtk.Label('Tool diam:', xalign=1)
|
||||
l1.set_tooltip_markup(
|
||||
grid1 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid1)
|
||||
tdlabel = QtGui.QLabel('Tool dia:')
|
||||
tdlabel.setToolTip(
|
||||
"Diameter of the cutting tool."
|
||||
)
|
||||
grid.attach(l1, 0, 0, 1, 1)
|
||||
grid1.addWidget(tdlabel, 0, 0)
|
||||
self.iso_tool_dia_entry = LengthEntry()
|
||||
grid.attach(self.iso_tool_dia_entry, 1, 0, 1, 1)
|
||||
grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
|
||||
|
||||
l2 = Gtk.Label('Width (# passes):', xalign=1)
|
||||
l2.set_tooltip_markup(
|
||||
passlabel = QtGui.QLabel('Width (# passes):')
|
||||
passlabel.setToolTip(
|
||||
"Width of the isolation gap in\n"
|
||||
"number (integer) of tool widths."
|
||||
)
|
||||
grid.attach(l2, 0, 1, 1, 1)
|
||||
grid1.addWidget(passlabel, 1, 0)
|
||||
self.iso_width_entry = IntEntry()
|
||||
grid.attach(self.iso_width_entry, 1, 1, 1, 1)
|
||||
grid1.addWidget(self.iso_width_entry, 1, 1)
|
||||
|
||||
l3 = Gtk.Label('Pass overlap:', xalign=1)
|
||||
l3.set_tooltip_markup(
|
||||
overlabel = QtGui.QLabel('Pass overlap:')
|
||||
overlabel.setToolTip(
|
||||
"How much (fraction of tool width)\n"
|
||||
"to overlap each pass."
|
||||
)
|
||||
grid.attach(l3, 0, 2, 1, 1)
|
||||
grid1.addWidget(overlabel, 2, 0)
|
||||
self.iso_overlap_entry = FloatEntry()
|
||||
grid.attach(self.iso_overlap_entry, 1, 2, 1, 1)
|
||||
grid1.addWidget(self.iso_overlap_entry, 2, 1)
|
||||
|
||||
self.generate_iso_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_iso_button.set_tooltip_markup(
|
||||
self.generate_iso_button = QtGui.QPushButton('Generate Geometry')
|
||||
self.generate_iso_button.setToolTip(
|
||||
"Create the Geometry Object\n"
|
||||
"for isolation routing."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_iso_button, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.generate_iso_button)
|
||||
|
||||
## Board cuttout
|
||||
self.board_cutout_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.board_cutout_label.set_markup("<b>Board cutout:</b>")
|
||||
self.board_cutout_label.set_tooltip_markup(
|
||||
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.pack_start(self.board_cutout_label, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.board_cutout_label)
|
||||
|
||||
grid2 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid2, expand=True, fill=False, padding=2)
|
||||
|
||||
l4 = Gtk.Label('Tool dia:', xalign=1)
|
||||
l4.set_tooltip_markup(
|
||||
grid2 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid2)
|
||||
tdclabel = QtGui.QLabel('Tool dia:')
|
||||
tdclabel.setToolTip(
|
||||
"Diameter of the cutting tool."
|
||||
)
|
||||
grid2.attach(l4, 0, 0, 1, 1)
|
||||
grid2.addWidget(tdclabel, 0, 0)
|
||||
self.cutout_tooldia_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_tooldia_entry, 1, 0, 1, 1)
|
||||
grid2.addWidget(self.cutout_tooldia_entry, 0, 1)
|
||||
|
||||
l5 = Gtk.Label('Margin:', xalign=1)
|
||||
l5.set_tooltip_markup(
|
||||
marginlabel = QtGui.QLabel('Margin:')
|
||||
marginlabel.setToolTip(
|
||||
"Distance from objects at which\n"
|
||||
"to draw the cutout."
|
||||
)
|
||||
grid2.attach(l5, 0, 1, 1, 1)
|
||||
grid2.addWidget(marginlabel, 1, 0)
|
||||
self.cutout_margin_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_margin_entry, 1, 1, 1, 1)
|
||||
grid2.addWidget(self.cutout_margin_entry, 1, 1)
|
||||
|
||||
l6 = Gtk.Label('Gap size:', xalign=1)
|
||||
l6.set_tooltip_markup(
|
||||
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.attach(l6, 0, 2, 1, 1)
|
||||
grid2.addWidget(gaplabel, 2, 0)
|
||||
self.cutout_gap_entry = LengthEntry()
|
||||
grid2.attach(self.cutout_gap_entry, 1, 2, 1, 1)
|
||||
grid2.addWidget(self.cutout_gap_entry, 2, 1)
|
||||
|
||||
l7 = Gtk.Label('Gaps:', xalign=1)
|
||||
l7.set_tooltip_markup(
|
||||
gapslabel = QtGui.QLabel('Gaps:')
|
||||
gapslabel.setToolTip(
|
||||
"Where to place the gaps, Top/Bottom\n"
|
||||
"Left/Rigt, or on all 4 sides."
|
||||
)
|
||||
grid2.attach(l7, 0, 3, 1, 1)
|
||||
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.attach(self.gaps_radio, 1, 3, 1, 1)
|
||||
grid2.addWidget(self.gaps_radio, 3, 1)
|
||||
|
||||
self.generate_cutout_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_cutout_button.set_tooltip_markup(
|
||||
self.generate_cutout_button = QtGui.QPushButton('Generate Geometry')
|
||||
self.generate_cutout_button.setToolTip(
|
||||
"Generate the geometry for\n"
|
||||
"the board cutout."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_cutout_button, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.generate_cutout_button)
|
||||
|
||||
## Non-copper regions
|
||||
self.noncopper_label = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.noncopper_label.set_markup("<b>Non-copper regions:</b>")
|
||||
self.noncopper_label.set_tooltip_markup(
|
||||
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.pack_start(self.noncopper_label, expand=True, fill=False, padding=2)
|
||||
self.custom_box.addWidget(self.noncopper_label)
|
||||
|
||||
grid3 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid3, expand=True, fill=False, padding=2)
|
||||
grid3 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid3)
|
||||
|
||||
l8 = Gtk.Label('Boundary margin:', xalign=1)
|
||||
l8.set_tooltip_markup(
|
||||
# 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.attach(l8, 0, 0, 1, 1)
|
||||
grid3.addWidget(bmlabel, 0, 0)
|
||||
self.noncopper_margin_entry = LengthEntry()
|
||||
grid3.attach(self.noncopper_margin_entry, 1, 0, 1, 1)
|
||||
grid3.addWidget(self.noncopper_margin_entry, 0, 1)
|
||||
|
||||
# Rounded corners
|
||||
self.noncopper_rounded_cb = FCCheckBox(label="Rounded corners")
|
||||
self.noncopper_rounded_cb.set_tooltip_markup(
|
||||
"If the boundary of the board\n"
|
||||
"is to have rounded corners\n"
|
||||
"their radius is equal to the margin."
|
||||
)
|
||||
grid3.attach(self.noncopper_rounded_cb, 0, 1, 2, 1)
|
||||
|
||||
self.generate_noncopper_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_noncopper_button.set_tooltip_markup(
|
||||
self.noncopper_rounded_cb.setToolTip(
|
||||
"Creates a Geometry objects with polygons\n"
|
||||
"covering the copper-free areas of the PCB."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_noncopper_button, expand=True, fill=False, padding=2)
|
||||
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 = Gtk.Label(justify=Gtk.Justification.LEFT, xalign=0, margin_top=5)
|
||||
self.boundingbox_label.set_markup('<b>Bounding Box:</b>')
|
||||
self.boundingbox_label.set_tooltip_markup(
|
||||
"Create a Geometry object with a rectangle\n"
|
||||
"enclosing all polygons at a given distance."
|
||||
)
|
||||
self.custom_box.pack_start(self.boundingbox_label, expand=True, fill=False, padding=2)
|
||||
self.boundingbox_label = QtGui.QLabel('<b>Bounding Box:</b>')
|
||||
self.custom_box.addWidget(self.boundingbox_label)
|
||||
|
||||
grid4 = Gtk.Grid(column_spacing=3, row_spacing=2)
|
||||
self.custom_box.pack_start(grid4, expand=True, fill=False, padding=2)
|
||||
grid4 = QtGui.QGridLayout()
|
||||
self.custom_box.addLayout(grid4)
|
||||
|
||||
l9 = Gtk.Label('Boundary Margin:', xalign=1)
|
||||
l9.set_tooltip_markup(
|
||||
bbmargin = QtGui.QLabel('Boundary Margin:')
|
||||
bbmargin.setToolTip(
|
||||
"Distance of the edges of the box\n"
|
||||
"to the nearest polygon."
|
||||
)
|
||||
grid4.attach(l9, 0, 0, 1, 1)
|
||||
grid4.addWidget(bbmargin, 0, 0)
|
||||
self.bbmargin_entry = LengthEntry()
|
||||
grid4.attach(self.bbmargin_entry, 1, 0, 1, 1)
|
||||
grid4.addWidget(self.bbmargin_entry, 0, 1)
|
||||
|
||||
self.bbrounded_cb = FCCheckBox(label="Rounded corners")
|
||||
self.bbrounded_cb.set_tooltip_markup(
|
||||
self.bbrounded_cb.setToolTip(
|
||||
"If the bounding box is \n"
|
||||
"to have rounded corners\n"
|
||||
"their radius is equal to\n"
|
||||
"the margin."
|
||||
)
|
||||
grid4.attach(self.bbrounded_cb, 0, 1, 2, 1)
|
||||
grid4.addWidget(self.bbrounded_cb, 1, 0, 1, 2)
|
||||
|
||||
self.generate_bb_button = Gtk.Button(label='Generate Geometry')
|
||||
self.generate_bb_button.set_tooltip_markup(
|
||||
self.generate_bb_button = QtGui.QPushButton('Generate Geometry')
|
||||
self.generate_bb_button.setToolTip(
|
||||
"Genrate the Geometry object."
|
||||
)
|
||||
self.custom_box.pack_start(self.generate_bb_button, expand=True, fill=False, padding=2)
|
||||
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()
|
|
@ -1,10 +1,26 @@
|
|||
from gi.repository import Gdk
|
||||
############################################################
|
||||
# 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
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
|
||||
#from FlatCAMApp import *
|
||||
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
|
||||
import FlatCAMApp
|
||||
|
||||
|
||||
# class FCFigureCanvas(FigureCanvas):
|
||||
#
|
||||
# resized = QtCore.pyqtSignal()
|
||||
#
|
||||
# def resizeEvent(self, event):
|
||||
# FigureCanvas.resizeEvent(self, event)
|
||||
# self.emit(QtCore.SIGNAL("resized"))
|
||||
# print self.geometry()
|
||||
|
||||
class PlotCanvas:
|
||||
"""
|
||||
Class handling the plotting area in the application.
|
||||
|
@ -38,18 +54,21 @@ class PlotCanvas:
|
|||
|
||||
# The canvas is the top level container (Gtk.DrawingArea)
|
||||
self.canvas = FigureCanvas(self.figure)
|
||||
self.canvas.set_hexpand(1)
|
||||
self.canvas.set_vexpand(1)
|
||||
self.canvas.set_can_focus(True) # For key press
|
||||
#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.attach(self.canvas, 0, 0, 600, 400) # TODO: Height and width are num. columns??
|
||||
self.container.addWidget(self.canvas) # Qt
|
||||
|
||||
# Events
|
||||
self.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
|
||||
self.canvas.connect('configure-event', self.auto_adjust_axes)
|
||||
self.canvas.add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK)
|
||||
self.canvas.connect("scroll-event", self.on_scroll)
|
||||
#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)
|
||||
|
||||
|
@ -62,6 +81,7 @@ class PlotCanvas:
|
|||
:param event:
|
||||
:return:
|
||||
"""
|
||||
FlatCAMApp.App.log.debug('on_key_down(): ' + str(event.key))
|
||||
self.key = event.key
|
||||
|
||||
def on_key_up(self, event):
|
||||
|
@ -125,7 +145,7 @@ class PlotCanvas:
|
|||
self.axes.grid(True)
|
||||
|
||||
# Re-draw
|
||||
self.canvas.queue_draw()
|
||||
self.canvas.draw()
|
||||
|
||||
def adjust_axes(self, xmin, ymin, xmax, ymax):
|
||||
"""
|
||||
|
@ -144,7 +164,7 @@ class PlotCanvas:
|
|||
:return: None
|
||||
"""
|
||||
|
||||
FlatCAMApp.App.log.debug("PC.adjust_axes()")
|
||||
# FlatCAMApp.App.log.debug("PC.adjust_axes()")
|
||||
|
||||
width = xmax - xmin
|
||||
height = ymax - ymin
|
||||
|
@ -182,7 +202,7 @@ class PlotCanvas:
|
|||
ax.set_position([x_ratio, y_ratio, 1 - 2 * x_ratio, 1 - 2 * y_ratio])
|
||||
|
||||
# Re-draw
|
||||
self.canvas.queue_draw()
|
||||
self.canvas.draw()
|
||||
|
||||
def auto_adjust_axes(self, *args):
|
||||
"""
|
||||
|
@ -234,7 +254,7 @@ class PlotCanvas:
|
|||
ax.set_ylim((ymin, ymax))
|
||||
|
||||
# Re-draw
|
||||
self.canvas.queue_draw()
|
||||
self.canvas.draw()
|
||||
|
||||
def pan(self, x, y):
|
||||
xmin, xmax = self.axes.get_xlim()
|
||||
|
@ -248,7 +268,7 @@ class PlotCanvas:
|
|||
ax.set_ylim((ymin + y*height, ymax + y*height))
|
||||
|
||||
# Re-draw
|
||||
self.canvas.queue_draw()
|
||||
self.canvas.draw()
|
||||
|
||||
def new_axes(self, name):
|
||||
"""
|
||||
|
@ -261,24 +281,24 @@ class PlotCanvas:
|
|||
|
||||
return self.figure.add_axes([0.05, 0.05, 0.9, 0.9], label=name)
|
||||
|
||||
def on_scroll(self, canvas, event):
|
||||
def on_scroll(self, event):
|
||||
"""
|
||||
Scroll event handler.
|
||||
|
||||
:param canvas: The widget generating the event. Ignored.
|
||||
:param event: Event object containing the event information.
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# So it can receive key presses
|
||||
self.canvas.grab_focus()
|
||||
# self.canvas.grab_focus()
|
||||
self.canvas.setFocus()
|
||||
|
||||
# Event info
|
||||
z, direction = event.get_scroll_direction()
|
||||
# z, direction = event.get_scroll_direction()
|
||||
|
||||
if self.key is None:
|
||||
|
||||
if direction is Gdk.ScrollDirection.UP:
|
||||
if event.button == 'up':
|
||||
self.zoom(1.5, self.mouse)
|
||||
else:
|
||||
self.zoom(1/1.5, self.mouse)
|
||||
|
@ -286,15 +306,15 @@ class PlotCanvas:
|
|||
|
||||
if self.key == 'shift':
|
||||
|
||||
if direction is Gdk.ScrollDirection.UP:
|
||||
if event.button == 'up':
|
||||
self.pan(0.3, 0)
|
||||
else:
|
||||
self.pan(-0.3, 0)
|
||||
return
|
||||
|
||||
if self.key == 'ctrl+control':
|
||||
if self.key == 'control':
|
||||
|
||||
if direction is Gdk.ScrollDirection.UP:
|
||||
if event.button == 'up':
|
||||
self.pan(0, 0.3)
|
||||
else:
|
||||
self.pan(0, -0.3)
|
||||
|
@ -308,3 +328,4 @@ class PlotCanvas:
|
|||
:return: None
|
||||
"""
|
||||
self.mouse = [event.xdata, event.ydata]
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"gerber_noncopperrounded": false, "geometry_paintoverlap": 0.15, "geometry_plot": true, "excellon_feedrate": 5.0, "gerber_plot": true, "excellon_drillz": -0.1, "geometry_feedrate": 3.0, "units": "IN", "excellon_travelz": 0.1, "gerber_multicolored": false, "gerber_solid": true, "gerber_isopasses": 1, "excellon_plot": true, "gerber_isotooldia": 0.016, "gerber_bboxmargin": 0.0, "cncjob_tooldia": 0.016, "geometry_travelz": 0.1, "gerber_cutoutmargin": 0.2, "excellon_solid": false, "geometry_paintmargin": 0.01, "geometry_cutz": -0.002, "geometry_cnctooldia": 0.016, "gerber_cutouttooldia": 0.07, "gerber_gaps": "4", "geometry_painttooldia": 0.0625, "cncjob_plot": true, "gerber_cutoutgapsize": 0.15, "gerber_isooverlap": 0.15, "gerber_bboxrounded": false, "gerber_noncoppermargin": 0.0}
|
||||
{"gerber_cutoutgapsize": 0.15, "gerber_noncopperrounded": false, "geometry_paintoverlap": 0.15, "excellon_plot": true, "gerber_isotooldia": 0.016, "gerber_plot": true, "excellon_drillz": -0.1, "geometry_feedrate": 3.0, "units": "IN", "excellon_travelz": 0.1, "gerber_multicolored": false, "gerber_solid": true, "gerber_isopasses": 1, "cncjob_append": "", "excellon_feedrate": 3.0, "cncjob_tooldia": 0.016, "geometry_travelz": 0.1, "gerber_cutoutmargin": 0.1, "excellon_solid": true, "geometry_paintmargin": 0.0, "geometry_cutz": -0.002, "gerber_noncoppermargin": 0.0, "gerber_cutouttooldia": 0.07, "gerber_gaps": "4", "gerber_bboxmargin": 0.0, "cncjob_plot": true, "geometry_plot": true, "gerber_isooverlap": 0.15, "gerber_bboxrounded": false, "geometry_cnctooldia": 0.016, "geometry_painttooldia": 0.07}
|
|
@ -1 +1 @@
|
|||
[{"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\pcbcam\\test_files\\bedini 7 coils capacitor discharge.gbr"}, {"kind": "cncjob", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\test.cnc"}, {"kind": "project", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Bridge2\\Bridge2_test.fcproj"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\Controller_Board_Shield\\CBS-B_Cu.gbl"}, {"kind": "cncjob", "filename": "C:\\Users\\jpcaram\\Dropbox\\CNC\\squarerpcb\\KiCad_Squarer.gcode"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Squarer\\KiCad_Squarer.drl"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Squarer\\KiCad_Squarer-F_Cu.gtl"}, {"kind": "project", "filename": "C:\\Users\\jpcaram\\Dropbox\\VNA\\KiCad_Bridge2\\Bridge2.fcproj"}, {"kind": "excellon", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\Kenney\\Project Outputs for AnalogPredistortion1\\apd.TXT"}, {"kind": "gerber", "filename": "C:\\Users\\jpcaram\\Dropbox\\PhD\\Kenney\\Project Outputs for AnalogPredistortion1\\apd.GTL"}]
|
||||
[{"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/FlatCam_Drilling_Test/FlatCam_Drilling_Test.drl"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/Excellon_Planck/X-Y CONTROLLER - Drill Data - Through Hole.drl"}, {"kind": "gerber", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/7V-PSU/7V PSU.GTL"}, {"kind": "cncjob", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/output.gcode"}, {"kind": "project", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/project_copy.fcproj"}, {"kind": "project", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/test_files/a_project.fcproj"}, {"kind": "project", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/FlatCAM_TestProject.fcproj"}, {"kind": "cncjob", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/FlatCAM_TestGCode.gcode"}, {"kind": "cncjob", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/CBS-B_Cu_ISOLATION_GCODE.ngc"}, {"kind": "excellon", "filename": "C:/Users/jpcaram/Dropbox/CNC/pcbcam/cirkuix/tests/FlatCAM_TestDrill.drl"}]
|
|
@ -1,783 +0,0 @@
|
|||
G04 (created by PCBNEW (2013-07-07 BZR 4022)-stable) date 11/20/2013 5:37:08 PM*
|
||||
%MOIN*%
|
||||
G04 Gerber Fmt 3.4, Leading zero omitted, Abs format*
|
||||
%FSLAX34Y34*%
|
||||
G01*
|
||||
G70*
|
||||
G90*
|
||||
G04 APERTURE LIST*
|
||||
%ADD10C,0.00590551*%
|
||||
%ADD11C,0.015*%
|
||||
%ADD12R,0.0393X0.0629*%
|
||||
%ADD13R,0.2125X0.244*%
|
||||
%ADD14R,0.02X0.03*%
|
||||
%ADD15R,0.144X0.08*%
|
||||
%ADD16R,0.04X0.08*%
|
||||
%ADD17R,0.0394X0.0354*%
|
||||
%ADD18R,0.0354X0.0394*%
|
||||
%ADD19R,0.063X0.071*%
|
||||
%ADD20R,0.071X0.063*%
|
||||
%ADD21C,0.1*%
|
||||
%ADD22R,0.08X0.08*%
|
||||
%ADD23C,0.08*%
|
||||
%ADD24C,0.04*%
|
||||
%ADD25C,0.02*%
|
||||
G04 APERTURE END LIST*
|
||||
G54D10*
|
||||
G54D11*
|
||||
X27233Y12376D02*
|
||||
X27728Y12376D01*
|
||||
X27461Y12071D01*
|
||||
X27576Y12071D01*
|
||||
X27652Y12033D01*
|
||||
X27690Y11995D01*
|
||||
X27728Y11919D01*
|
||||
X27728Y11728D01*
|
||||
X27690Y11652D01*
|
||||
X27652Y11614D01*
|
||||
X27576Y11576D01*
|
||||
X27347Y11576D01*
|
||||
X27271Y11614D01*
|
||||
X27233Y11652D01*
|
||||
X24021Y12300D02*
|
||||
X24059Y12338D01*
|
||||
X24135Y12376D01*
|
||||
X24326Y12376D01*
|
||||
X24402Y12338D01*
|
||||
X24440Y12300D01*
|
||||
X24478Y12223D01*
|
||||
X24478Y12147D01*
|
||||
X24440Y12033D01*
|
||||
X23983Y11576D01*
|
||||
X24478Y11576D01*
|
||||
X4578Y11701D02*
|
||||
X4121Y11701D01*
|
||||
X4350Y11701D02*
|
||||
X4350Y12501D01*
|
||||
X4273Y12386D01*
|
||||
X4197Y12310D01*
|
||||
X4121Y12272D01*
|
||||
X569Y1526D02*
|
||||
X1064Y1526D01*
|
||||
X797Y1221D01*
|
||||
X911Y1221D01*
|
||||
X988Y1183D01*
|
||||
X1026Y1145D01*
|
||||
X1064Y1069D01*
|
||||
X1064Y878D01*
|
||||
X1026Y802D01*
|
||||
X988Y764D01*
|
||||
X911Y726D01*
|
||||
X683Y726D01*
|
||||
X607Y764D01*
|
||||
X569Y802D01*
|
||||
X1407Y802D02*
|
||||
X1445Y764D01*
|
||||
X1407Y726D01*
|
||||
X1369Y764D01*
|
||||
X1407Y802D01*
|
||||
X1407Y726D01*
|
||||
X1711Y1526D02*
|
||||
X2207Y1526D01*
|
||||
X1940Y1221D01*
|
||||
X2054Y1221D01*
|
||||
X2130Y1183D01*
|
||||
X2169Y1145D01*
|
||||
X2207Y1069D01*
|
||||
X2207Y878D01*
|
||||
X2169Y802D01*
|
||||
X2130Y764D01*
|
||||
X2054Y726D01*
|
||||
X1826Y726D01*
|
||||
X1750Y764D01*
|
||||
X1711Y802D01*
|
||||
X2435Y1526D02*
|
||||
X2702Y726D01*
|
||||
X2969Y1526D01*
|
||||
X1672Y14551D02*
|
||||
X1291Y14551D01*
|
||||
X1253Y14170D01*
|
||||
X1291Y14208D01*
|
||||
X1367Y14246D01*
|
||||
X1558Y14246D01*
|
||||
X1634Y14208D01*
|
||||
X1672Y14170D01*
|
||||
X1710Y14094D01*
|
||||
X1710Y13903D01*
|
||||
X1672Y13827D01*
|
||||
X1634Y13789D01*
|
||||
X1558Y13751D01*
|
||||
X1367Y13751D01*
|
||||
X1291Y13789D01*
|
||||
X1253Y13827D01*
|
||||
X1939Y14551D02*
|
||||
X2205Y13751D01*
|
||||
X2472Y14551D01*
|
||||
X26525Y14651D02*
|
||||
X26677Y14651D01*
|
||||
X26753Y14613D01*
|
||||
X26829Y14536D01*
|
||||
X26867Y14384D01*
|
||||
X26867Y14117D01*
|
||||
X26829Y13965D01*
|
||||
X26753Y13889D01*
|
||||
X26677Y13851D01*
|
||||
X26525Y13851D01*
|
||||
X26448Y13889D01*
|
||||
X26372Y13965D01*
|
||||
X26334Y14117D01*
|
||||
X26334Y14384D01*
|
||||
X26372Y14536D01*
|
||||
X26448Y14613D01*
|
||||
X26525Y14651D01*
|
||||
X27210Y14651D02*
|
||||
X27210Y14003D01*
|
||||
X27248Y13927D01*
|
||||
X27286Y13889D01*
|
||||
X27363Y13851D01*
|
||||
X27515Y13851D01*
|
||||
X27591Y13889D01*
|
||||
X27629Y13927D01*
|
||||
X27667Y14003D01*
|
||||
X27667Y14651D01*
|
||||
X27934Y14651D02*
|
||||
X28391Y14651D01*
|
||||
X28163Y13851D02*
|
||||
X28163Y14651D01*
|
||||
X17800Y13451D02*
|
||||
X17952Y13451D01*
|
||||
X18028Y13413D01*
|
||||
X18104Y13336D01*
|
||||
X18142Y13184D01*
|
||||
X18142Y12917D01*
|
||||
X18104Y12765D01*
|
||||
X18028Y12689D01*
|
||||
X17952Y12651D01*
|
||||
X17800Y12651D01*
|
||||
X17723Y12689D01*
|
||||
X17647Y12765D01*
|
||||
X17609Y12917D01*
|
||||
X17609Y13184D01*
|
||||
X17647Y13336D01*
|
||||
X17723Y13413D01*
|
||||
X17800Y13451D01*
|
||||
X18485Y13451D02*
|
||||
X18485Y12803D01*
|
||||
X18523Y12727D01*
|
||||
X18561Y12689D01*
|
||||
X18638Y12651D01*
|
||||
X18790Y12651D01*
|
||||
X18866Y12689D01*
|
||||
X18904Y12727D01*
|
||||
X18942Y12803D01*
|
||||
X18942Y13451D01*
|
||||
X19209Y13451D02*
|
||||
X19666Y13451D01*
|
||||
X19438Y12651D02*
|
||||
X19438Y13451D01*
|
||||
X8900Y13326D02*
|
||||
X9052Y13326D01*
|
||||
X9128Y13288D01*
|
||||
X9204Y13211D01*
|
||||
X9242Y13059D01*
|
||||
X9242Y12792D01*
|
||||
X9204Y12640D01*
|
||||
X9128Y12564D01*
|
||||
X9052Y12526D01*
|
||||
X8900Y12526D01*
|
||||
X8823Y12564D01*
|
||||
X8747Y12640D01*
|
||||
X8709Y12792D01*
|
||||
X8709Y13059D01*
|
||||
X8747Y13211D01*
|
||||
X8823Y13288D01*
|
||||
X8900Y13326D01*
|
||||
X9585Y13326D02*
|
||||
X9585Y12678D01*
|
||||
X9623Y12602D01*
|
||||
X9661Y12564D01*
|
||||
X9738Y12526D01*
|
||||
X9890Y12526D01*
|
||||
X9966Y12564D01*
|
||||
X10004Y12602D01*
|
||||
X10042Y12678D01*
|
||||
X10042Y13326D01*
|
||||
X10309Y13326D02*
|
||||
X10766Y13326D01*
|
||||
X10538Y12526D02*
|
||||
X10538Y13326D01*
|
||||
X28880Y726D02*
|
||||
X28880Y1526D01*
|
||||
X29261Y726D02*
|
||||
X29261Y1526D01*
|
||||
X29719Y726D01*
|
||||
X29719Y1526D01*
|
||||
X23705Y2126D02*
|
||||
X23705Y2926D01*
|
||||
X24086Y2126D02*
|
||||
X24086Y2926D01*
|
||||
X24544Y2126D01*
|
||||
X24544Y2926D01*
|
||||
X3355Y3126D02*
|
||||
X3355Y3926D01*
|
||||
X3736Y3126D02*
|
||||
X3736Y3926D01*
|
||||
X4194Y3126D01*
|
||||
X4194Y3926D01*
|
||||
G54D12*
|
||||
X12278Y8961D03*
|
||||
X14072Y8961D03*
|
||||
G54D13*
|
||||
X13175Y11834D03*
|
||||
G54D14*
|
||||
X5850Y7450D03*
|
||||
X6600Y7450D03*
|
||||
X5850Y8450D03*
|
||||
X6225Y7450D03*
|
||||
X6600Y8450D03*
|
||||
X21075Y7400D03*
|
||||
X21825Y7400D03*
|
||||
X21075Y8400D03*
|
||||
X21450Y7400D03*
|
||||
X21825Y8400D03*
|
||||
G54D15*
|
||||
X15962Y2837D03*
|
||||
G54D16*
|
||||
X15962Y5437D03*
|
||||
X15062Y5437D03*
|
||||
X16862Y5437D03*
|
||||
G54D17*
|
||||
X23150Y6429D03*
|
||||
X23150Y7021D03*
|
||||
G54D18*
|
||||
X20546Y5800D03*
|
||||
X19954Y5800D03*
|
||||
G54D17*
|
||||
X22825Y7829D03*
|
||||
X22825Y8421D03*
|
||||
X21825Y9404D03*
|
||||
X21825Y9996D03*
|
||||
G54D18*
|
||||
X19579Y9325D03*
|
||||
X20171Y9325D03*
|
||||
X4454Y9325D03*
|
||||
X5046Y9325D03*
|
||||
G54D17*
|
||||
X6600Y9629D03*
|
||||
X6600Y10221D03*
|
||||
X7600Y8004D03*
|
||||
X7600Y8596D03*
|
||||
X8000Y6429D03*
|
||||
X8000Y7021D03*
|
||||
G54D18*
|
||||
X5171Y5850D03*
|
||||
X4579Y5850D03*
|
||||
G54D19*
|
||||
X12650Y7900D03*
|
||||
X13750Y7900D03*
|
||||
G54D20*
|
||||
X11025Y9750D03*
|
||||
X11025Y10850D03*
|
||||
X13737Y4387D03*
|
||||
X13737Y5487D03*
|
||||
G54D19*
|
||||
X16962Y6787D03*
|
||||
X15862Y6787D03*
|
||||
G54D17*
|
||||
X20175Y7804D03*
|
||||
X20175Y8396D03*
|
||||
X6225Y5854D03*
|
||||
X6225Y6446D03*
|
||||
G54D18*
|
||||
X16608Y7712D03*
|
||||
X16016Y7712D03*
|
||||
X23421Y9375D03*
|
||||
X22829Y9375D03*
|
||||
X24296Y7850D03*
|
||||
X23704Y7850D03*
|
||||
G54D17*
|
||||
X21450Y5804D03*
|
||||
X21450Y6396D03*
|
||||
G54D18*
|
||||
X9146Y8000D03*
|
||||
X8554Y8000D03*
|
||||
X8196Y9625D03*
|
||||
X7604Y9625D03*
|
||||
G54D17*
|
||||
X10100Y10104D03*
|
||||
X10100Y10696D03*
|
||||
X5050Y7854D03*
|
||||
X5050Y8446D03*
|
||||
G54D14*
|
||||
X28950Y6850D03*
|
||||
X29700Y6850D03*
|
||||
X28950Y7850D03*
|
||||
X29325Y6850D03*
|
||||
X29700Y7850D03*
|
||||
G54D21*
|
||||
X21825Y11925D03*
|
||||
X22825Y12925D03*
|
||||
X20825Y12925D03*
|
||||
X20825Y10925D03*
|
||||
X22825Y10925D03*
|
||||
X21450Y3475D03*
|
||||
X22450Y4475D03*
|
||||
X20450Y4475D03*
|
||||
X20450Y2475D03*
|
||||
X22450Y2475D03*
|
||||
X29325Y3475D03*
|
||||
X30325Y4475D03*
|
||||
X28325Y4475D03*
|
||||
X28325Y2475D03*
|
||||
X30325Y2475D03*
|
||||
X29700Y11925D03*
|
||||
X30700Y12925D03*
|
||||
X28700Y12925D03*
|
||||
X28700Y10925D03*
|
||||
X30700Y10925D03*
|
||||
G54D17*
|
||||
X27425Y8571D03*
|
||||
X27425Y7979D03*
|
||||
G54D22*
|
||||
X1375Y8325D03*
|
||||
G54D23*
|
||||
X1375Y9325D03*
|
||||
X1375Y10325D03*
|
||||
G54D22*
|
||||
X16375Y9625D03*
|
||||
G54D23*
|
||||
X16375Y10625D03*
|
||||
X16375Y11625D03*
|
||||
G54D22*
|
||||
X26375Y8425D03*
|
||||
G54D23*
|
||||
X26375Y9425D03*
|
||||
X26375Y10425D03*
|
||||
G54D22*
|
||||
X10225Y2475D03*
|
||||
G54D23*
|
||||
X11225Y2475D03*
|
||||
G54D22*
|
||||
X28375Y8850D03*
|
||||
G54D21*
|
||||
X10400Y6275D03*
|
||||
X25125Y5450D03*
|
||||
X6600Y11925D03*
|
||||
X7600Y12925D03*
|
||||
X5600Y12925D03*
|
||||
X5600Y10925D03*
|
||||
X7600Y10925D03*
|
||||
X6225Y3475D03*
|
||||
X7225Y4475D03*
|
||||
X5225Y4475D03*
|
||||
X5225Y2475D03*
|
||||
X7225Y2475D03*
|
||||
G54D17*
|
||||
X28225Y7254D03*
|
||||
X28225Y7846D03*
|
||||
G54D23*
|
||||
X12375Y2475D03*
|
||||
X30075Y6475D03*
|
||||
X28225Y6775D03*
|
||||
X20350Y7075D03*
|
||||
X22300Y6950D03*
|
||||
X7100Y6900D03*
|
||||
X19475Y5175D03*
|
||||
X24325Y9375D03*
|
||||
X25175Y7850D03*
|
||||
X17525Y7600D03*
|
||||
X13625Y3325D03*
|
||||
X11025Y8900D03*
|
||||
X10000Y8000D03*
|
||||
X8875Y9625D03*
|
||||
X4950Y7275D03*
|
||||
X3825Y5850D03*
|
||||
G54D24*
|
||||
X15962Y2837D02*
|
||||
X15962Y1162D01*
|
||||
X15962Y1162D02*
|
||||
X15975Y1150D01*
|
||||
X16375Y9625D02*
|
||||
X17450Y9625D01*
|
||||
X18650Y8425D02*
|
||||
X18650Y1150D01*
|
||||
X17450Y9625D02*
|
||||
X18650Y8425D01*
|
||||
X26375Y8425D02*
|
||||
X26375Y7775D01*
|
||||
X26350Y7800D02*
|
||||
X26375Y7775D01*
|
||||
X26375Y7775D02*
|
||||
X26975Y7175D01*
|
||||
X25700Y1150D02*
|
||||
X18650Y1150D01*
|
||||
X18650Y1150D02*
|
||||
X16925Y1150D01*
|
||||
X15975Y1150D02*
|
||||
X16925Y1150D01*
|
||||
X26975Y2425D02*
|
||||
X25700Y1150D01*
|
||||
X26975Y7175D02*
|
||||
X26975Y2425D01*
|
||||
X15962Y2837D02*
|
||||
X15962Y5437D01*
|
||||
X13175Y1150D02*
|
||||
X13550Y1150D01*
|
||||
X1375Y8325D02*
|
||||
X1375Y3774D01*
|
||||
X4000Y1150D02*
|
||||
X13175Y1150D01*
|
||||
X1375Y3774D02*
|
||||
X4000Y1150D01*
|
||||
X13550Y1150D02*
|
||||
X15975Y1150D01*
|
||||
G54D25*
|
||||
X16016Y7712D02*
|
||||
X15862Y7558D01*
|
||||
X15862Y7558D02*
|
||||
X15862Y6787D01*
|
||||
G54D24*
|
||||
X15962Y5437D02*
|
||||
X15962Y6112D01*
|
||||
X15862Y6212D02*
|
||||
X15862Y6787D01*
|
||||
X15962Y6112D02*
|
||||
X15862Y6212D01*
|
||||
X13175Y11834D02*
|
||||
X13175Y14200D01*
|
||||
X13150Y14225D02*
|
||||
X13150Y14200D01*
|
||||
X13175Y14200D02*
|
||||
X13150Y14225D01*
|
||||
X16375Y11625D02*
|
||||
X16400Y11650D01*
|
||||
X16400Y14175D02*
|
||||
X16400Y14200D01*
|
||||
X16400Y11650D02*
|
||||
X16400Y14175D01*
|
||||
X26375Y10425D02*
|
||||
X26375Y12750D01*
|
||||
X9775Y14200D02*
|
||||
X13150Y14200D01*
|
||||
X1375Y11475D02*
|
||||
X4100Y14200D01*
|
||||
X4100Y14200D02*
|
||||
X9775Y14200D01*
|
||||
X1375Y10325D02*
|
||||
X1375Y11475D01*
|
||||
X13150Y14200D02*
|
||||
X15025Y14200D01*
|
||||
X26375Y12750D02*
|
||||
X24925Y14200D01*
|
||||
X24925Y14200D02*
|
||||
X16400Y14200D01*
|
||||
X16400Y14200D02*
|
||||
X14825Y14200D01*
|
||||
X11025Y10850D02*
|
||||
X12191Y10850D01*
|
||||
X12191Y10850D02*
|
||||
X13175Y11834D01*
|
||||
G54D25*
|
||||
X10100Y10696D02*
|
||||
X10871Y10696D01*
|
||||
X10871Y10696D02*
|
||||
X11025Y10850D01*
|
||||
X6225Y3399D02*
|
||||
X6225Y5854D01*
|
||||
X6225Y5854D02*
|
||||
X5175Y5854D01*
|
||||
X5175Y5854D02*
|
||||
X5171Y5850D01*
|
||||
X21450Y3799D02*
|
||||
X21450Y5804D01*
|
||||
X20546Y5800D02*
|
||||
X21446Y5800D01*
|
||||
X21446Y5800D02*
|
||||
X21450Y5804D01*
|
||||
X6600Y10221D02*
|
||||
X6600Y11575D01*
|
||||
X21825Y11525D02*
|
||||
X21825Y9996D01*
|
||||
X8000Y7021D02*
|
||||
X8000Y7200D01*
|
||||
X7600Y7600D02*
|
||||
X7600Y8004D01*
|
||||
X8000Y7200D02*
|
||||
X7600Y7600D01*
|
||||
X7600Y8004D02*
|
||||
X8550Y8004D01*
|
||||
X8550Y8004D02*
|
||||
X8554Y8000D01*
|
||||
X10400Y6275D02*
|
||||
X9875Y6275D01*
|
||||
X9721Y6429D02*
|
||||
X9875Y6275D01*
|
||||
X9721Y6429D02*
|
||||
X8000Y6429D01*
|
||||
X6225Y6446D02*
|
||||
X6554Y6446D01*
|
||||
X7579Y6429D02*
|
||||
X8000Y6429D01*
|
||||
X7375Y6225D02*
|
||||
X7579Y6429D01*
|
||||
X6775Y6225D02*
|
||||
X7375Y6225D01*
|
||||
X6554Y6446D02*
|
||||
X6775Y6225D01*
|
||||
X8000Y6429D02*
|
||||
X8000Y6275D01*
|
||||
X8000Y6275D02*
|
||||
X8000Y6429D01*
|
||||
X6225Y6446D02*
|
||||
X6225Y7450D01*
|
||||
X6600Y9629D02*
|
||||
X6600Y8450D01*
|
||||
X7604Y9625D02*
|
||||
X6604Y9625D01*
|
||||
X6604Y9625D02*
|
||||
X6600Y9629D01*
|
||||
X7600Y8596D02*
|
||||
X7600Y9621D01*
|
||||
X7600Y9621D02*
|
||||
X7604Y9625D01*
|
||||
X23150Y7021D02*
|
||||
X23150Y7275D01*
|
||||
X22825Y7600D02*
|
||||
X22825Y7829D01*
|
||||
X23150Y7275D02*
|
||||
X22825Y7600D01*
|
||||
X22825Y7829D02*
|
||||
X23683Y7829D01*
|
||||
X23683Y7829D02*
|
||||
X23704Y7850D01*
|
||||
X24725Y6050D02*
|
||||
X25125Y5650D01*
|
||||
X24346Y6429D02*
|
||||
X24725Y6050D01*
|
||||
X23150Y6429D02*
|
||||
X24346Y6429D01*
|
||||
X25125Y5650D02*
|
||||
X25125Y5450D01*
|
||||
X21450Y6396D02*
|
||||
X21679Y6396D01*
|
||||
X23150Y6425D02*
|
||||
X23150Y6429D01*
|
||||
X22775Y6425D02*
|
||||
X23150Y6425D01*
|
||||
X22600Y6250D02*
|
||||
X22775Y6425D01*
|
||||
X21825Y6250D02*
|
||||
X22600Y6250D01*
|
||||
X21679Y6396D02*
|
||||
X21825Y6250D01*
|
||||
X21450Y7400D02*
|
||||
X21450Y6396D01*
|
||||
X21825Y8400D02*
|
||||
X21825Y9404D01*
|
||||
X21825Y9404D02*
|
||||
X21854Y9375D01*
|
||||
X21854Y9375D02*
|
||||
X22829Y9375D01*
|
||||
X22829Y9375D02*
|
||||
X22825Y9371D01*
|
||||
X22825Y9371D02*
|
||||
X22825Y8421D01*
|
||||
G54D24*
|
||||
X1375Y9325D02*
|
||||
X4454Y9325D01*
|
||||
X5050Y8725D02*
|
||||
X5050Y9321D01*
|
||||
X5050Y9321D02*
|
||||
X5046Y9325D01*
|
||||
X5050Y8446D02*
|
||||
X5050Y8725D01*
|
||||
X5850Y8725D02*
|
||||
X5850Y8450D01*
|
||||
X5050Y8725D02*
|
||||
X5850Y8725D01*
|
||||
X19579Y9325D02*
|
||||
X19400Y9325D01*
|
||||
X18100Y10625D02*
|
||||
X19400Y9325D01*
|
||||
X18100Y10625D02*
|
||||
X16375Y10625D01*
|
||||
X20171Y9325D02*
|
||||
X20171Y8400D01*
|
||||
X20171Y8400D02*
|
||||
X21075Y8400D01*
|
||||
X26375Y9425D02*
|
||||
X27050Y9425D01*
|
||||
X27425Y8625D02*
|
||||
X27425Y8571D01*
|
||||
X27425Y9050D02*
|
||||
X27425Y8625D01*
|
||||
X27050Y9425D02*
|
||||
X27425Y9050D01*
|
||||
G54D25*
|
||||
X28375Y8850D02*
|
||||
X28375Y7925D01*
|
||||
X28375Y7925D02*
|
||||
X28225Y7846D01*
|
||||
G54D24*
|
||||
X28225Y7846D02*
|
||||
X28921Y7846D01*
|
||||
X28925Y7850D02*
|
||||
X28950Y7850D01*
|
||||
X28921Y7846D02*
|
||||
X28925Y7850D01*
|
||||
X28225Y7846D02*
|
||||
X27454Y7846D01*
|
||||
X27425Y7875D02*
|
||||
X27425Y7979D01*
|
||||
X27454Y7846D02*
|
||||
X27425Y7875D01*
|
||||
X10225Y2475D02*
|
||||
X10225Y2950D01*
|
||||
X12175Y5550D02*
|
||||
X12162Y5550D01*
|
||||
X12175Y4900D02*
|
||||
X12175Y5550D01*
|
||||
X10225Y2950D02*
|
||||
X12175Y4900D01*
|
||||
X13750Y7900D02*
|
||||
X14072Y8222D01*
|
||||
X12162Y5487D02*
|
||||
X12162Y5550D01*
|
||||
X12162Y5550D02*
|
||||
X12162Y6362D01*
|
||||
X14072Y7147D02*
|
||||
X14072Y8961D01*
|
||||
X13650Y6725D02*
|
||||
X14072Y7147D01*
|
||||
X12525Y6725D02*
|
||||
X13650Y6725D01*
|
||||
X12162Y6362D02*
|
||||
X12525Y6725D01*
|
||||
X12162Y5487D02*
|
||||
X13737Y5487D01*
|
||||
X15062Y5437D02*
|
||||
X13787Y5437D01*
|
||||
X13787Y5437D02*
|
||||
X13737Y5487D01*
|
||||
X14072Y8222D02*
|
||||
X14072Y8961D01*
|
||||
X11225Y2475D02*
|
||||
X12375Y2475D01*
|
||||
G54D25*
|
||||
X29700Y6850D02*
|
||||
X29800Y6750D01*
|
||||
X29800Y6750D02*
|
||||
X29700Y6850D01*
|
||||
G54D24*
|
||||
X29825Y6725D02*
|
||||
X30100Y6450D01*
|
||||
X28225Y6800D02*
|
||||
X28800Y6800D01*
|
||||
X28850Y6800D02*
|
||||
X28825Y6775D01*
|
||||
X28800Y6800D02*
|
||||
X28850Y6800D01*
|
||||
X28225Y7254D02*
|
||||
X28225Y6800D01*
|
||||
X28225Y6800D02*
|
||||
X28225Y6800D01*
|
||||
X28225Y6800D02*
|
||||
X28225Y6775D01*
|
||||
G54D25*
|
||||
X21075Y7400D02*
|
||||
X20900Y7400D01*
|
||||
G54D24*
|
||||
X20900Y7400D02*
|
||||
X20625Y7400D01*
|
||||
G54D25*
|
||||
X5850Y7450D02*
|
||||
X5725Y7450D01*
|
||||
G54D24*
|
||||
X5050Y7475D02*
|
||||
X5075Y7450D01*
|
||||
X5075Y7450D02*
|
||||
X5725Y7450D01*
|
||||
X5050Y7475D02*
|
||||
X5050Y7854D01*
|
||||
X20625Y7400D02*
|
||||
X20350Y7125D01*
|
||||
X20350Y7125D02*
|
||||
X20350Y7075D01*
|
||||
G54D25*
|
||||
X21825Y7400D02*
|
||||
X21850Y7400D01*
|
||||
X21850Y7400D02*
|
||||
X22300Y6950D01*
|
||||
X6600Y7450D02*
|
||||
X6625Y7450D01*
|
||||
X7125Y6925D02*
|
||||
X7100Y6900D01*
|
||||
X7150Y6925D02*
|
||||
X7125Y6925D01*
|
||||
X6625Y7450D02*
|
||||
X7150Y6925D01*
|
||||
G54D24*
|
||||
X19954Y5800D02*
|
||||
X19479Y5179D01*
|
||||
X19479Y5179D02*
|
||||
X19475Y5175D01*
|
||||
X23421Y9375D02*
|
||||
X24325Y9375D01*
|
||||
X24296Y7850D02*
|
||||
X25175Y7850D01*
|
||||
X16962Y6787D02*
|
||||
X16962Y7037D01*
|
||||
X16962Y7037D02*
|
||||
X17525Y7600D01*
|
||||
X13737Y4387D02*
|
||||
X13737Y3437D01*
|
||||
X13737Y3437D02*
|
||||
X13625Y3325D01*
|
||||
X11025Y9750D02*
|
||||
X11025Y8900D01*
|
||||
X9146Y8000D02*
|
||||
X10000Y8000D01*
|
||||
X8196Y9625D02*
|
||||
X8875Y9625D01*
|
||||
X5050Y7854D02*
|
||||
X5050Y7375D01*
|
||||
X5050Y7375D02*
|
||||
X4950Y7275D01*
|
||||
X4579Y5850D02*
|
||||
X3825Y5850D01*
|
||||
G54D25*
|
||||
X16608Y7712D02*
|
||||
X16962Y7358D01*
|
||||
X16962Y7358D02*
|
||||
X16962Y6787D01*
|
||||
G54D24*
|
||||
X16862Y5437D02*
|
||||
X16962Y5537D01*
|
||||
X16962Y5537D02*
|
||||
X16962Y6787D01*
|
||||
X12278Y8961D02*
|
||||
X12278Y8272D01*
|
||||
X12278Y8272D02*
|
||||
X12650Y7900D01*
|
||||
X11025Y9750D02*
|
||||
X11489Y9750D01*
|
||||
X11489Y9750D02*
|
||||
X12278Y8961D01*
|
||||
G54D25*
|
||||
X10100Y10104D02*
|
||||
X10454Y9750D01*
|
||||
X10454Y9750D02*
|
||||
X11025Y9750D01*
|
||||
G54D24*
|
||||
X20175Y7804D02*
|
||||
X20175Y7750D01*
|
||||
X20525Y7400D02*
|
||||
X20625Y7400D01*
|
||||
X20175Y7750D02*
|
||||
X20525Y7400D01*
|
||||
G54D25*
|
||||
X29325Y6850D02*
|
||||
X29325Y3475D01*
|
||||
X29700Y11925D02*
|
||||
X29700Y7850D01*
|
||||
M02*
|
|
@ -1,77 +0,0 @@
|
|||
M48
|
||||
;DRILL file {Pcbnew (2013-07-07 BZR 4022)-stable} date 11/20/2013 1:52:19 PM
|
||||
;FORMAT={2:4/ absolute / inch / keep zeros}
|
||||
FMAT,2
|
||||
INCH,TZ
|
||||
T1C0.030
|
||||
T2C0.040
|
||||
T3C0.060
|
||||
%
|
||||
G90
|
||||
G05
|
||||
M72
|
||||
T1
|
||||
X010400Y006275
|
||||
X025125Y005450
|
||||
T2
|
||||
X001375Y010325
|
||||
X001375Y009325
|
||||
X001375Y008325
|
||||
X003825Y005850
|
||||
X004950Y007275
|
||||
X007100Y006900
|
||||
X008875Y009625
|
||||
X010000Y008000
|
||||
X010225Y002475
|
||||
X011025Y008900
|
||||
X011225Y002475
|
||||
X012375Y002475
|
||||
X013625Y003325
|
||||
X016375Y011625
|
||||
X016375Y010625
|
||||
X016375Y009625
|
||||
X017525Y007600
|
||||
X019475Y005175
|
||||
X020350Y007075
|
||||
X022300Y006950
|
||||
X024325Y009375
|
||||
X025175Y007850
|
||||
X026375Y010425
|
||||
X026375Y009425
|
||||
X026375Y008425
|
||||
X028225Y006775
|
||||
X028375Y008850
|
||||
X030075Y006475
|
||||
T3
|
||||
X005225Y004475
|
||||
X005225Y002475
|
||||
X005600Y012925
|
||||
X005600Y010925
|
||||
X006225Y003475
|
||||
X006600Y011925
|
||||
X007225Y004475
|
||||
X007225Y002475
|
||||
X007600Y012925
|
||||
X007600Y010925
|
||||
X020450Y004475
|
||||
X020450Y002475
|
||||
X020825Y012925
|
||||
X020825Y010925
|
||||
X021450Y003475
|
||||
X021825Y011925
|
||||
X022450Y004475
|
||||
X022450Y002475
|
||||
X022825Y012925
|
||||
X022825Y010925
|
||||
X028325Y004475
|
||||
X028325Y002475
|
||||
X028700Y012925
|
||||
X028700Y010925
|
||||
X029325Y003475
|
||||
X029700Y011925
|
||||
X030325Y004475
|
||||
X030325Y002475
|
||||
X030700Y012925
|
||||
X030700Y010925
|
||||
T0
|
||||
M30
|
Loading…
Reference in New Issue