# ============================================================================================================
# ============================================================================================================
# == ==
# == alias Manager ==
# == ==
# ============================================================================================================
# ============================================================================================================
# ABOUT
# ============================================================================================================
# version v1.0
# Macro developed for FreeCAD (http://www.freecadweb.org/).
# This macro helps managing aliases inside FreeCAD Spreadsheet workbench. It is able to:
# - create aliases based on first column (A column)
# - delete aliases placed on a column
# - move aliases from one column to other one
# - create a "part family", that's it, create different files for each column in a range. It will add
# to the original name a suffix based on first row (1)
# More information might be found on FreeCAD forums: http://forum.freecadweb.org/
#
#
# LICENSE
# ============================================================================================================
# Original work done by tarihatari (https://github.com/tarihatari/FreeCAD_Macros)
# Improved by Pablo Gil Fernandez
#
# Copyright (c) 2016 tarihatari & Pablo Gil Fernandez
#
# This work is licensed under GNU Lesser General Public License (LGPL).
# To view a copy of this license, visit https://www.gnu.org/licenses/lgpl-3.0.html.
#
# ============================================================================================================
__title__ = "alias manager"
__author__ = "Pablo Gil Fernandez"
__version__ = "01.00"
__date__ = "20/11/2016"
__Comment__ = "This macro helps managing aliases inside FreeCAD Spreadsheet workbench. It is able to create, delete, move aliases and create a 'part family' group of files"
__Wiki__ = "https://github.com/pgilfernandez/FreeCAD_AliasManager"
__Help__ = "https://github.com/pgilfernandez/FreeCAD_AliasManager"
__Status__ = "stable"
__Requires__ = "FreeCAD 0.16"
"""
Added features:
* Column and row range are found automatically on startup. The first empty cell in column A will stop the autofind_columnrange. The first empty cell in the first row will stop autofind_rowrange functions.
* The clear function will clear all alias fields in the specified (automatically found) range.
*
* A dictionary is returned containing the childnames the filenames of the of all children (family-members), their alias/parameters and the column-names.
* A autorun parameter can be set on startup to generate a partfamily in the automatically found range without opening GUI elements.
* A automatically check is done if a alias column is set in the automatically found column-range.
* If a alias is already set: go to family_generation combo-box-entry and change the seleced columns
* If no alias is found in the automatically found region run automatically set_alias combo-box-entry (set alias to first column 'B')
"""
#~ Todo:
#~ # test if other Spreadsheet-names than 'Spreadsheet' are allowed
from PySide import QtGui, QtCore
from FreeCAD import Gui
import os
import string
import FreeCAD
import FreeCADGui
App = FreeCAD
Gui = FreeCADGui
# ========================================================
# ===== Info popup window ================================
# ========================================================
class infoPopup(QtGui.QDialog):
def __init__(self, parent=None):
self.dialog = None
self.dialog = QtGui.QDialog()
self.dialog.resize(360,400)
self.dialog.setWindowTitle("About...")
info = QtGui.QTextEdit("
INFORMATION
This macro helps managing aliases inside FreeCAD Spreadsheet workbench. It is able to create, delete, move aliases and create a 'part family' group of files.
USAGE
- set aliases: based on first column (A column), it will create aliases for the range given. If an alias is already set for any other cell the command won't work, it will be needed to clear them before setting them again.
- Clear aliases in range: it will clear all aliases inside the given range of cells (only one column).
- Move aliases: it will clear and set aliases from a given column to a new one.
- Generate part family: it will create different files for each column in a range. It will add to the original name a suffix based on first row. If an alias is already set for any other cell the command won't work, it will be needed to clear them before running the command.
LICENCE
Original work done by tarihatari (https://github.com/tarihatari/FreeCAD_Macros)
Improved by Pablo Gil Fernandez
Copyright (c) 2016 tarihatari & Pablo Gil Fernandez
This work is licensed under GNU Lesser General Public License (LGPL).
To view a copy of this license, visit https://www.gnu.org/licenses/lgpl-3.0.html.
")
info.setReadOnly(True)
info.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
okbox = QtGui.QDialogButtonBox(self.dialog)
okbox.setOrientation(QtCore.Qt.Horizontal)
okbox.setStandardButtons(QtGui.QDialogButtonBox.Close)
okbox.setFocus()
grid2 = QtGui.QGridLayout()
grid2.setSpacing(10)
grid2.addWidget(info, 0, 0)
grid2.addWidget(okbox, 1, 0)
self.dialog.setLayout(grid2)
QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), self.close)
QtCore.QMetaObject.connectSlotsByName(self.dialog)
self.dialog.show()
self.dialog.exec_()
def close(self):
self.dialog.close()
# ========================================================
# ===== Main code ========================================
# ========================================================
# ===== Global variables ==============================================
alphabet_list = list(string.ascii_uppercase)
column_list = []
for i in range(0,26):
column_list.append(alphabet_list[i])
for i in range(0,26):
for j in range(0,26):
column_list.append(alphabet_list[i] + alphabet_list[j])
class partfamily():
def get_family(self):
family = self.family
return family
def get_automatically(self):
automatically = self.automatically
return automatically
def char_range(self, c1, c2):
"""Generates the characters from `c1` to `c2`, inclusive."""
#~ TODO: This conversion does not work for 'column-name' inputs like 'AB'
for c in xrange(ord(c1), ord(c2)+1):
yield str.capitalize(chr(c))
def aliasManager(self):
try:
# ===== Variables ==============================================
mode = self.d1.currentText()
column_from = self.d2.currentText()
column_to = self.d3.currentText()
row_from = self.d4.value()
row_to = self.d5.value()
# ===== Mode - Set ==============================================
if mode == "Set aliases":
for i in range(row_from,row_to+1):
cell_from = 'A' + str(i)
cell_to = str(column_from) + str(i)
App.ActiveDocument.Spreadsheet.setAlias(cell_to, '')
App.ActiveDocument.Spreadsheet.setAlias(cell_to, App.ActiveDocument.Spreadsheet.getContents(cell_from))
App.ActiveDocument.recompute()
FreeCAD.Console.PrintMessage("\nAliases set\n")
# ===== Mode - Clear ==============================================
elif mode == "Clear aliases in range":
col_range = []
for c in self.char_range(str(column_from), str(column_to)):
col_range.append(c)
#~ FreeCAD.Console.PrintMessage(col_range)
for index in range(len(col_range)):
for i in range(row_from,row_to+1):
cell = str(col_range[index]) + str(i)
cell_to = str(column_from) + str(i)
#~ FreeCAD.Console.PrintMessage(cell)
App. ActiveDocument.Spreadsheet.setAlias(cell, '')
App.ActiveDocument.recompute()
FreeCAD.Console.PrintMessage("\nAliases cleared\n")
self.d1.setCurrentIndex(0) # set mode-combobox to 'Set aliases'
self.d2.setCurrentIndex(1) # set colum "from"- combobox to 'B'
# ===== Mode - Move ==============================================
elif mode == "Move aliases":
self.d3.setDisabled(False)
for i in range(row_from,row_to+1):
cell_reference = 'A'+ str(i)
cell_from = column_from + str(i)
cell_to = column_to + str(i)
App.ActiveDocument.Spreadsheet.setAlias(cell_from, '')
App.ActiveDocument.recompute()
App.ActiveDocument.Spreadsheet.setAlias(cell_to, App.ActiveDocument.Spreadsheet.getContents(cell_reference))
App.ActiveDocument.recompute()
FreeCAD.Console.PrintMessage("\nAliases moved\n")
# ===== Mode - Generate part family ==============================================
elif mode == "Generate part family":
doc = FreeCAD.ActiveDocument
if not doc.FileName:
FreeCAD.Console.PrintError('\nMust save project first\n')
docDir, docFilename = os.path.split(doc.FileName)
filePrefix = os.path.splitext(docFilename)[0]
fam_range = []
for c in self.char_range(str(column_from), str(column_to)):
fam_range.append(c)
#~ FreeCAD.Console.PrintMessage(fam_range)
for index in range(len(fam_range)):
# set aliases
alias_values = {} # for storing alias values
for i in range(row_from,row_to+1):
cell_reference = 'A' + str(i)
cell_from = str(fam_range[index-1]) + str(i)
cell_to = str(fam_range[index]) + str(i)
App.ActiveDocument.Spreadsheet.setAlias(cell_from, '')
App.ActiveDocument.recompute()
App.ActiveDocument.Spreadsheet.setAlias(cell_to, App.ActiveDocument.Spreadsheet.getContents(cell_reference))
App.ActiveDocument.recompute()
sfx = str(fam_range[index]) + '1'
alias_values[App.ActiveDocument.Spreadsheet.get(cell_reference)] = App.ActiveDocument.Spreadsheet.get(cell_to)
# save file
suffix = App.ActiveDocument.Spreadsheet.getContents(sfx)
filename = filePrefix + '_' + suffix + '.fcstd'
filePath = os.path.join(docDir, filename)
FreeCAD.Console.PrintMessage("\nSaving view to %s\n" % filePath)
App.ActiveDocument.saveCopy(filePath)
# Store information about the created family
self.family['filenames'].append(filePath)
self.family['childnames'].append(suffix)
self.family['alias_values'].append(alias_values)
self.family['column'].append(fam_range[index])
FreeCAD.Console.PrintMessage("\nPart family files generated\n")
#~ FreeCAD.Console.PrintMessage(self.family)
FreeCAD.Console.PrintMessage("\nClearing alias for reset.\n")
# Clear lastcol
for i in range(row_from,row_to+1):
cell_to = str(column_to) + str(i)
App.ActiveDocument.Spreadsheet.setAlias(cell_to, '')
App.ActiveDocument.recompute()
FreeCAD.Console.PrintMessage("\nResetting alias.\n")
# Reset alias to column A
for i in range(row_from,row_to+1):
cell_from = 'A' + str(i)
cell_to = str(column_from) + str(i)
App.ActiveDocument.Spreadsheet.setAlias(cell_to, '')
App.ActiveDocument.Spreadsheet.setAlias(cell_to, App.ActiveDocument.Spreadsheet.getContents(cell_from))
App.ActiveDocument.recompute()
App.ActiveDocument.recompute()
# ===== If errors ==============================================
else:
FreeCAD.Console.PrintError("\nError or 'TODO'\n")
except:
FreeCAD.Console.PrintError("\nUnable to complete task\n")
self.close()
def close(self):
self.dialog.hide()
def autofind_rowrange(self, curr_row=1):
# Walk through all rows in column A and test if the cell is empty
values_present = True
while values_present:
curr_row += 1
cellDef = 'A' + str(curr_row)
try:
# FreeCAD.Console.PrintMessage(cellDef + " holds : " + App.ActiveDocument.Spreadsheet.getContents(cellDef) + "\n")
if App.ActiveDocument.Spreadsheet.getContents(cellDef)=='':
values_present = False
except:
values_present = False
last_filled_row = curr_row -1
#~ FreeCAD.Console.PrintMessage("\nFound entries up to row "+ str(last_filled_row) + "\n")
return last_filled_row
def autofind_columnrange(self):
# Walk through all columns in row 1 and test if the cell is empty
values_present = True
curr_column = 0
while values_present:
curr_column += 1
cellDef = column_list[curr_column] + '1'
#~ FreeCAD.Console.PrintMessage(cellDef + " holds : " + App.ActiveDocument.Spreadsheet.getContents(cellDef) + "\n")
try:
if App.ActiveDocument.Spreadsheet.getContents(cellDef)=='':
values_present = False
except:
values_present = False
last_filled_column = curr_column -1
#~ FreeCAD.Console.PrintMessage("Found entries up to column "+ str(column_list[last_filled_column] + "\n"))
return last_filled_column
def autofind_alias(self, columnrange=[column_list[0], column_list[-1]]):
# Walk through all columns in row 2 and test if a alias is set
alias_present = False
curr_column = 0
fam_range = []
for c in self.char_range(columnrange[0], columnrange[-1]):
fam_range.append(c)
for ind in range(len(fam_range)):
cellDef = fam_range[ind] + '2'
#~ FreeCAD.Console.PrintMessage(cellDef + " holds : " + str(App.ActiveDocument.Spreadsheet.getAlias(cellDef)) + "\n")
try:
if App.ActiveDocument.Spreadsheet.getAlias(cellDef) is not None:
alias_present = True
break
except:
alias_present = False
if alias_present:
alias_column = ind
#~ FreeCAD.Console.PrintMessage("Found alias entries in column "+ str(column_list[alias_column] + "\n"))
else:
alias_column = None
FreeCAD.Console.PrintMessage("Found no alias entries.\n")
return alias_column
# ========================================================
# ===== GUI menu ========================================
# ========================================================
def __init__(self, automatically=False):
self.automatically = automatically
infoIcon = ['16 16 3 1',
' c None',
'+ c #444444',
'. c #e6e6e6',
' ...... ',
' .......... ',
' ......++.... ',
' .......++.....',
' ..............',
'.....+++++......',
'....+++++.......',
'.......++.......',
'.......++.......',
'.......+........',
'......++........',
' .....++.+.....',
' .....++++.....',
' .....++..... ',
' .......... ',
' ...... ']
# Hide/show "to column" label and spinbox based on mode type
def disableWidget(currentIndex):
if self.d1.currentText() == "Set aliases":
iN3.hide()
self.d3.setEnabled(False)
self.d3.hide()
elif self.d1.currentText() == "Clear aliases in range":
iN3.show()
self.d3.setEnabled(True)
self.d3.show()
#~ self.d3.hide()
else:
iN3.show()
self.d3.setEnabled(True)
self.d3.show()
family = {}
family['childnames'] = []
family['filenames'] = []
family['alias_values'] = []
family['column'] = []
self.family = family
self.dialog = None
self.dialog = QtGui.QDialog()
self.dialog.resize(400,140)
self.dialog.setWindowTitle("Alias manager")
iN1 = QtGui.QLabel("mode:")
iN1.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.d1 = QtGui.QComboBox()
self.d1.addItem("Set aliases")
self.d1.addItem("Clear aliases in range")
self.d1.addItem("Move aliases")
self.d1.addItem("Generate part family")
self.d1.setCurrentIndex(3) # set default item
self.d1.currentIndexChanged['QString'].connect(disableWidget)
iN2a = QtGui.QLabel("column:")
iN2a.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
iN2b = QtGui.QLabel("from")
iN2b.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter)
self.d2 = QtGui.QComboBox()
self.d2.addItems(column_list)
self.d2.setCurrentIndex(1) # set default item
iN3 = QtGui.QLabel("to")
iN3.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter)
iN3.hide() # set initial state hidden
self.d3 = QtGui.QComboBox()
self.d3.addItems(column_list)
self.d3.setCurrentIndex(3) # set default item
#~ self.d3.setEnabled(False) # set initial state to not editable
#~ self.d3.hide() # set initial state hidden
# Find range of columns to use
try:
last_filled_column = self.autofind_columnrange()
if last_filled_column>0:
self.d2.setCurrentIndex(1) # set default item
self.d3.setCurrentIndex(last_filled_column) # set default item
except:
FreeCAD.Console.PrintMessage("Automatically found values for columns cound not be used!\n")
iN4 = QtGui.QLabel("from row:")
iN4.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.d4 = QtGui.QSpinBox()
self.d4.setValue(2.0) # set default item
self.d4.setSingleStep(1.0)
self.d4.setMinimum(1.0)
iN5 = QtGui.QLabel("to row:")
iN5.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.d5 = QtGui.QSpinBox()
self.d5.setValue(4.0) # set default item
self.d5.setSingleStep(1.0)
self.d5.setMinimum(1.0)
# Find range of rows to use
try:
last_filled_row = self.autofind_rowrange()
if last_filled_row>1:
self.d4.setValue(2) # set default item
self.d5.setValue(last_filled_row) # set default item
except:
FreeCAD.Console.PrintMessage("Automatically found values for rows cound not be used!\n")
# Looking for already set alias-columns and set dialog values accordingly
alias_column = self.autofind_alias([column_list[0], column_list[last_filled_column]])
if alias_column is None:
FreeCAD.Console.PrintMessage("\nNo alias found resetting.\n")
self.d1.setCurrentIndex(0)
self.aliasManager()
else:
self.d2.setCurrentIndex(alias_column)
# Info button
self.d6 = QtGui.QPushButton("")
self.d6.setFixedWidth(40)
self.d6.setIcon(QtGui.QIcon(QtGui.QPixmap(infoIcon)))
self.d6.clicked.connect(self.popup)
okbox = QtGui.QDialogButtonBox(self.dialog)
okbox.setOrientation(QtCore.Qt.Horizontal)
okbox.setStandardButtons(QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Close)
grid = QtGui.QGridLayout()
grid.setSpacing(10)
# Mode
grid.addWidget(self.d1, 0, 0, 1, 3)
# column, column from
grid.addWidget(iN2a, 2, 0, 1, 1)
grid.addWidget(iN2b, 1, 1, 1, 1)
grid.addWidget(self.d2, 2, 1, 1, 1)
# column to
grid.addWidget(iN3, 1, 2, 1, 1)
grid.addWidget(self.d3, 2, 2, 1, 1)
# from row
grid.addWidget(iN4, 3, 0, 1, 1)
grid.addWidget(self.d4, 3, 1, 1, 1)
# to row
grid.addWidget(iN5, 4, 0, 1, 1)
grid.addWidget(self.d5, 4, 1)
# + info
grid.addWidget(self.d6, 6, 0, 1, 1)
# cancel, OK
grid.addWidget(okbox, 6, 1, 1, 2)
self.dialog.setLayout(grid)
QtCore.QObject.connect(okbox, QtCore.SIGNAL("rejected()"), self.close)
QtCore.QObject.connect(okbox, QtCore.SIGNAL("accepted()"), self.aliasManager)
QtCore.QMetaObject.connectSlotsByName(self.dialog)
if automatically:
FreeCAD.Console.PrintMessage("\nAutorun selected.\n")
self.d1.setCurrentIndex(3)
self.aliasManager()
self.close()
else:
self.dialog.show()
self.dialog.exec_()
def popup(self):
self.dialog2 = infoPopup()
self.dialog2.exec_()
if __name__ == "__main__":
# For direct use in the FreeCAD- "Python console" use:
"""
import aliasManager_popup_HoWil as amp
p_loc = amp.partfamily()
fam = p_loc.getfamily()
"""
p_loc = partfamily()
FreeCAD.Console.PrintMessage(p_loc.get_family())