302 lines
12 KiB
Python
302 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# ***************************************************************************
|
|
# * *
|
|
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
|
# * *
|
|
# * This program is free software; you can redistribute it and/or modify *
|
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
# * as published by the Free Software Foundation; either version 2 of *
|
|
# * the License, or (at your option) any later version. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program is distributed in the hope that it will be useful, *
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
# * GNU Library General Public License for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Library General Public *
|
|
# * License along with this program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
# * USA *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
|
|
import FreeCADGui
|
|
import PathScripts.PathLog as PathLog
|
|
import PathScripts.PathPreferences as PathPreferences
|
|
import PathScripts.PathToolBit as PathToolBit
|
|
import PathScripts.PathToolBitGui as PathToolBitGui
|
|
import PySide
|
|
import json
|
|
import os
|
|
import traceback
|
|
import uuid
|
|
|
|
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
|
PathLog.trackModule(PathLog.thisModule())
|
|
|
|
_UuidRole = PySide.QtCore.Qt.UserRole + 1
|
|
_PathRole = PySide.QtCore.Qt.UserRole + 2
|
|
|
|
class _TableView(PySide.QtGui.QTableView):
|
|
'''Subclass of QTableView to support rearrange and copying of ToolBits'''
|
|
|
|
def __init__(self, parent):
|
|
PySide.QtGui.QTableView.__init__(self, parent)
|
|
self.setDragEnabled(True)
|
|
self.setAcceptDrops(True)
|
|
self.setDropIndicatorShown(True)
|
|
self.setDragDropMode(PySide.QtGui.QAbstractItemView.InternalMove)
|
|
self.setDefaultDropAction(PySide.QtCore.Qt.MoveAction)
|
|
self.setSortingEnabled(True)
|
|
self.setSelectionBehavior(PySide.QtGui.QAbstractItemView.SelectRows)
|
|
self.verticalHeader().hide()
|
|
|
|
def supportedDropActions(self):
|
|
return [PySide.QtCore.Qt.CopyAction, PySide.QtCore.Qt.MoveAction]
|
|
|
|
def _uuidOfRow(self, row):
|
|
model = self.model()
|
|
return model.data(model.index(row, 0), _UuidRole)
|
|
|
|
def _rowWithUuid(self, uuid):
|
|
model = self.model()
|
|
for row in range(model.rowCount()):
|
|
if self._uuidOfRow(row) == uuid:
|
|
return row
|
|
return None
|
|
|
|
def _copyTool(self, uuid_, dstRow):
|
|
model = self.model()
|
|
items = []
|
|
model.insertRow(dstRow)
|
|
srcRow = self._rowWithUuid(uuid_)
|
|
for col in range(model.columnCount()):
|
|
srcItem = model.item(srcRow, col)
|
|
|
|
model.setData(model.index(dstRow, col), srcItem.data(PySide.QtCore.Qt.EditRole), PySide.QtCore.Qt.EditRole)
|
|
if col == 0:
|
|
model.setData(model.index(dstRow, col), srcItem.data(_PathRole), _PathRole)
|
|
# Even a clone of a tool gets its own uuid so it can be identified when
|
|
# rearranging the order or inserting/deleting rows
|
|
model.setData(model.index(dstRow, col), uuid.uuid4(), _UuidRole)
|
|
else:
|
|
model.item(dstRow, col).setEditable(False)
|
|
|
|
def _copyTools(self, uuids, dst):
|
|
for i, uuid in enumerate(uuids):
|
|
self._copyTool(uuid, dst + i)
|
|
|
|
def dropEvent(self, event):
|
|
PathLog.track()
|
|
mime = event.mimeData()
|
|
data = mime.data('application/x-qstandarditemmodeldatalist')
|
|
stream = PySide.QtCore.QDataStream(data)
|
|
srcRows = []
|
|
while not stream.atEnd():
|
|
row = stream.readInt32()
|
|
srcRows.append(row)
|
|
col = stream.readInt32()
|
|
#PathLog.track(row, col)
|
|
cnt = stream.readInt32()
|
|
for i in range(cnt):
|
|
key = stream.readInt32()
|
|
val = stream.readQVariant()
|
|
#PathLog.track(' ', i, key, val, type(val))
|
|
# I have no idea what these three integers are,
|
|
# or if they even are three integers,
|
|
# but it seems to work out this way.
|
|
i0 = stream.readInt32()
|
|
i1 = stream.readInt32()
|
|
i2 = stream.readInt32()
|
|
#PathLog.track(' ', i0, i1, i2)
|
|
|
|
# get the uuids of all srcRows
|
|
model = self.model()
|
|
srcUuids = [self._uuidOfRow(row) for row in set(srcRows)]
|
|
destRow = self.rowAt(event.pos().y())
|
|
|
|
self._copyTools(srcUuids, destRow)
|
|
if PySide.QtCore.Qt.DropAction.MoveAction == event.proposedAction():
|
|
for uuid in srcUuids:
|
|
model.removeRow(self._rowWithUuid(uuid))
|
|
|
|
class ToolBitLibrary(object):
|
|
'''ToolBitLibrary is the controller for displaying/selecting/creating/editing a collection of ToolBits.'''
|
|
|
|
def __init__(self, path=None):
|
|
self.path = path
|
|
self.form = FreeCADGui.PySideUic.loadUi(':/panels/ToolBitLibraryEdit.ui')
|
|
self.toolTableView = _TableView(self.form.toolTableGroup)
|
|
self.form.toolTableGroup.layout().replaceWidget(self.form.toolTable, self.toolTableView)
|
|
self.form.toolTable.hide()
|
|
self.setupUI()
|
|
self.title = self.form.windowTitle()
|
|
if path:
|
|
self.libraryLoad(path)
|
|
|
|
def _toolAdd(self, nr, tool, path):
|
|
toolNr = PySide.QtGui.QStandardItem()
|
|
toolNr.setData(nr, PySide.QtCore.Qt.EditRole)
|
|
toolNr.setData(path, _PathRole)
|
|
toolNr.setData(uuid.uuid4(), _UuidRole)
|
|
|
|
toolName = PySide.QtGui.QStandardItem()
|
|
toolName.setData(tool['name'], PySide.QtCore.Qt.EditRole)
|
|
toolName.setEditable(False)
|
|
|
|
toolTemplate = PySide.QtGui.QStandardItem()
|
|
toolTemplate.setData(os.path.splitext(os.path.basename(tool['template']))[0], PySide.QtCore.Qt.EditRole)
|
|
toolTemplate.setEditable(False)
|
|
|
|
toolDiameter = PySide.QtGui.QStandardItem()
|
|
toolDiameter.setData(tool['parameter']['Diameter'], PySide.QtCore.Qt.EditRole)
|
|
toolDiameter.setEditable(False)
|
|
|
|
self.model.appendRow([toolNr, toolName, toolTemplate, toolDiameter])
|
|
|
|
def toolAdd(self):
|
|
PathLog.track()
|
|
try:
|
|
nr = 0
|
|
for row in range(self.model.rowCount()):
|
|
itemNr = int(self.model.item(row, 0).data(PySide.QtCore.Qt.EditRole))
|
|
nr = max(nr, itemNr)
|
|
nr += 1
|
|
|
|
for i, foo in enumerate(PathToolBitGui.GetToolFiles(self.form)):
|
|
tool = PathToolBit.Declaration(foo)
|
|
self._toolAdd(nr + i, tool, foo)
|
|
self.toolTableView.resizeColumnsToContents()
|
|
except:
|
|
PathLog.error('something happened')
|
|
PathLog.error(traceback.print_exc())
|
|
|
|
def selectedOrAllTools(self):
|
|
selectedRows = set([index.row() for index in self.toolTableView.selectedIndexes()])
|
|
if not selectedRows:
|
|
selectedRows = list(range(self.model.rowCount()))
|
|
tools = []
|
|
for row in selectedRows:
|
|
item = self.model.item(row, 0)
|
|
toolNr = int(item.data(PySide.QtCore.Qt.EditRole))
|
|
toolPath = item.data(_PathRole)
|
|
tools.append((toolNr, PathToolBit.Factory.CreateFrom(toolPath)))
|
|
return tools
|
|
|
|
def toolDelete(self):
|
|
PathLog.track()
|
|
selectedRows = set([index.row() for index in self.toolTableView.selectedIndexes()])
|
|
for row in sorted(list(selectedRows), key = lambda r: -r):
|
|
self.model.removeRows(row, 1)
|
|
|
|
def toolEnumerate(self):
|
|
PathLog.track()
|
|
for row in range(self.model.rowCount()):
|
|
self.model.setData(self.model.index(row, 0), row + 1, PySide.QtCore.Qt.EditRole)
|
|
|
|
def toolSelect(self, selected, deselected):
|
|
self.form.toolDelete.setEnabled(len(self.toolTableView.selectedIndexes()) > 0)
|
|
|
|
def open(self, path=None, dialog=False):
|
|
'''open(path=None, dialog=False) ... load library stored in path and bring up ui.
|
|
Returns 1 if user pressed OK, 0 otherwise.'''
|
|
if path:
|
|
fullPath = PathToolBit.findLibrary(path)
|
|
if fullPath:
|
|
self.libraryLoad(fullPath)
|
|
else:
|
|
self.libraryOpen()
|
|
elif dialog:
|
|
self.libraryOpen()
|
|
return self.form.exec_()
|
|
|
|
def updateToolbar(self):
|
|
if self.path:
|
|
self.form.librarySave.setEnabled(True)
|
|
else:
|
|
self.form.librarySave.setEnabled(False)
|
|
|
|
def libraryOpen(self):
|
|
PathLog.track()
|
|
foo = PySide.QtGui.QFileDialog.getOpenFileName(self.form, 'Tool Library', PathPreferences.lastPathToolLibrary(), '*.fctl')
|
|
if foo and foo[0]:
|
|
path = foo[0]
|
|
PathPreferences.setLastPathToolLibrary(os.path.dirname(path))
|
|
self.libraryLoad(path)
|
|
|
|
def libraryLoad(self, path):
|
|
self.toolTableView.setUpdatesEnabled(False)
|
|
self.model.clear()
|
|
self.model.setHorizontalHeaderLabels(self.columnNames())
|
|
if path:
|
|
with open(path) as fp:
|
|
library = json.load(fp)
|
|
for toolBit in library['tools']:
|
|
nr = toolBit['nr']
|
|
bit = PathToolBit.findBit(toolBit['path'])
|
|
if bit:
|
|
PathLog.track(bit)
|
|
tool = PathToolBit.Declaration(bit)
|
|
self._toolAdd(nr, tool, bit)
|
|
else:
|
|
PathLog.error("Could not find tool #{}: {}".format(nr, library['tools'][nr]))
|
|
self.toolTableView.resizeColumnsToContents()
|
|
self.toolTableView.setUpdatesEnabled(True)
|
|
|
|
self.form.setWindowTitle("{} - {}".format(self.title, os.path.basename(path) if path else ''))
|
|
self.path = path
|
|
self.updateToolbar()
|
|
|
|
def libraryNew(self):
|
|
self.libraryLoad(None)
|
|
|
|
def librarySave(self):
|
|
library = {}
|
|
tools = []
|
|
library['version'] = 1
|
|
library['tools'] = tools
|
|
for row in range(self.model.rowCount()):
|
|
toolNr = self.model.data(self.model.index(row, 0), PySide.QtCore.Qt.EditRole)
|
|
toolPath = self.model.data(self.model.index(row, 0), _PathRole)
|
|
tools.append({'nr': toolNr, 'path': toolPath})
|
|
|
|
with open(self.path, 'w') as fp:
|
|
json.dump(library, fp, sort_keys=True, indent=2)
|
|
|
|
def librarySaveAs(self):
|
|
foo = PySide.QtGui.QFileDialog.getSaveFileName(self.form, 'Tool Library', PathPreferences.lastPathToolLibrary(), '*.fctl')
|
|
if foo and foo[0]:
|
|
path = foo[0] if foo[0].endswith('.fctl') else "{}.fctl".format(foo[0])
|
|
PathPreferences.setLastPathToolLibrary(os.path.dirname(path))
|
|
self.path = path
|
|
self.librarySave()
|
|
self.updateToolbar()
|
|
|
|
def columnNames(self):
|
|
return ['Nr', 'Tool', 'Template', 'Diameter']
|
|
|
|
def setupUI(self):
|
|
PathLog.track('+')
|
|
self.model = PySide.QtGui.QStandardItemModel(0, len(self.columnNames()), self.toolTableView)
|
|
self.model.setHorizontalHeaderLabels(self.columnNames())
|
|
|
|
self.toolTableView.setModel(self.model)
|
|
self.toolTableView.resizeColumnsToContents()
|
|
self.toolTableView.selectionModel().selectionChanged.connect(self.toolSelect)
|
|
|
|
self.form.toolAdd.clicked.connect(self.toolAdd)
|
|
self.form.toolDelete.clicked.connect(self.toolDelete)
|
|
self.form.toolEnumerate.clicked.connect(self.toolEnumerate)
|
|
|
|
self.form.libraryNew.clicked.connect(self.libraryNew)
|
|
self.form.libraryOpen.clicked.connect(self.libraryOpen)
|
|
self.form.librarySave.clicked.connect(self.librarySave)
|
|
self.form.librarySaveAs.clicked.connect(self.librarySaveAs)
|
|
|
|
self.toolSelect([], [])
|
|
self.updateToolbar()
|
|
PathLog.track('-')
|