From 9a664eea65e2a5113e21b7a8f6ddfcb82266551b Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Fri, 1 Oct 2021 18:52:15 +0200 Subject: [PATCH 01/17] BoM use json for userconfig make description for all i bellieve infopartUi make user config autofilling fonction.... --- .gitignore | 2 - InfoKeys.py | 150 ++++++++++++++++++ InfoKeysInit.py | 23 --- infoPartCmd.py | 411 ++++++++++++++++++++++++++++++++++++++---------- makeBomCmd.py | 381 +++++++++++--------------------------------- 5 files changed, 570 insertions(+), 397 deletions(-) create mode 100644 InfoKeys.py delete mode 100644 InfoKeysInit.py diff --git a/.gitignore b/.gitignore index 6e341fc4..2708f228 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,3 @@ __pycache__/* *.directory AnimationLib_translated.py .gitignore -InfoKeys.py -InfoScript.py diff --git a/InfoKeys.py b/InfoKeys.py new file mode 100644 index 00000000..42ad43da --- /dev/null +++ b/InfoKeys.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# coding: utf-8 +# +# LGPL +# +# libraries for FreeCAD's Assembly 4 workbench + +import os +import json + +import FreeCAD as App + +# protection against update of user configuration + +### to have the dir of external configuration file +ConfUserDir = os.path.join(App.getUserAppDataDir(),'Asm4_UserConf') +ConfUserFilename = "infoConfUser.json" +ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) + + +### try to open existing external configuration file of user +try : + file = open(ConfUserFilejson, 'r') + file.close() +### else make the default external configuration file +except : + partInfoDef = dict() + for prop in InfoKeys.partInfo: + partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True}) + os.mkdir(ConfUserDir) + file = open(ConfUserFilejson, 'x') + json.dump(partInfoDef,file) + file.close() + +### now user configuration is : +file = open(ConfUserFilejson, 'r') +infoKeysUser = json.load(file).copy() +file.close() + + +import infoPartCmd + +# Autofilling info ref + +partInfo =[ 'LabelDoc', \ + 'LabelPart' ] + +infoToolTip = {'LabelDoc':'Return the Label of Document','LabelPart':'Return the Label of Part'} + +def infoDefault(self): + ### auto filling module + + ### part variable creation + try : + self.TypeId + PART=self + except AttributeError: + PART=self.part + ### you have PART + DOC=PART.Document + ### you have DOC + ### research + for i in range(len(PART.Group)): + if PART.Group[i].TypeId == 'PartDesign::Body' : + BODY=PART.Group[i] + ### you have BODY + for i in range(len(BODY.Group)): + if BODY.Group[i].TypeId == 'PartDesign::Pad' : + PAD=BODY.Group[i] + ### you have PAD + try : + SKETCH=PAD.Profile[0] + ### you have SKETCH + except NameError : + print('there is no Sketch on a Pad of : ',PART.FullName ) + + + ### start all autoinfofield + LabelDoc(self,PART,DOC) + LabelPart(self,PART) + +""" +how make a new autoinfofield : + +ref newautoinfofield name in partInfo[] + +make a description in infoToolTip = {} + +put newautoinfofield name in infoDefault() at the end + +write new def like that : + +def newautoinfofield(self,PART (opt : DOC , BODY , PAD , SKETCH): +###you can use DOC - PART - BODY - PAD - SKETCH + auto_info = string you want to write in field + try: + ### if the command comes from makeBom write autoinfo directly on Part + self.TypeId + auto_info = string + setattr(PART,'newautoinfofield name',auto_info) + except AttributeError: + ### if the command comes from infoPartUI write autoinfo on autofilling field on UI + try : + ### if field is actived + for i in range(len(self.infoTable)): + if self.infoTable[i][0]=='newautoinfofield name': + self.infos[i].setText(auto_info) + except AttributeError: + ### if field is not actived + pass + +""" + +def LabelDoc(self,PART,DOC): + docLabel = infoKeysUser.get('LabelDoc').get('userData') + try: + ### if the command comes from makeBom write autoinfo directly on Part + self.TypeId + setattr(PART,docLabel,DOC.Label) + except AttributeError: + ### if the command comes from infoPartUI write autoinfo on autofilling field on UI + try : + ### if field is actived + for i in range(len(self.infoTable)): + if self.infoTable[i][0]==docLabel: + self.infos[i].setText(DOC.Label) + except AttributeError: + ### if field is not actived + pass + +def LabelPart(self,PART): + partLabel = infoKeysUser.get('LabelPart').get('userData') + try: + ### if the command comes from makeBom write autoinfo directly on Part + self.TypeId + setattr(PART,partLabel,PART.Label) + except AttributeError: + ### if the command comes from infoPartUI write autoinfo on autofilling field on UI + try : + ### if field is actived + for i in range(len(self.infoTable)): + if self.infoTable[i][0]== partLabel: + self.infos[i].setText(PART.Label) + except AttributeError: + ### if field is not actived + pass + + + + pass \ No newline at end of file diff --git a/InfoKeysInit.py b/InfoKeysInit.py deleted file mode 100644 index 3f713718..00000000 --- a/InfoKeysInit.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 -# -# LGPL -# -# InfoKeysInit.py - - - -""" - +-----------------------------------------------+ - | customizable file | - +-----------------------------------------------+ -""" - -# Entitled of the informations for your part - -partInfo = [ 'PartID', \ - 'PartName', \ - 'PartDescription', \ - 'PartSupplier'] - - diff --git a/infoPartCmd.py b/infoPartCmd.py index 5c5b1b94..a4670ec0 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -8,7 +8,8 @@ -import os, shutil +import os +import json from PySide import QtGui, QtCore import FreeCADGui as Gui @@ -16,71 +17,53 @@ from FreeCAD import Console as FCC import Asm4_libs as Asm4 +import InfoKeys + +# protection against update of user configuration ### to have the dir of external configuration file -#wbPath = os.path.dirname(__file__) -wbPath = Asm4.wbPath -InfoKeysFile = os.path.join( wbPath, 'InfoKeys.py' ) -# InfoScript = os.path.join( wbPath, 'InfoScript.py' ) -InfoKeysFileInit = os.path.join( wbPath, 'InfoKeysInit.py' ) -# InfoScriptInit = os.path.join( wbPath, 'InfoScriptInit.py' ) +ConfUserDir = os.path.join(App.getUserAppDataDir(),'Asm4_UserConf') +ConfUserFilename = "infoConfUser.json" +ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) ### try to open existing external configuration file of user try : - fichier = open(InfoKeysFile, 'r') - fichier.close() - import InfoKeys as InfoKeys -### else make the default external configuration file -except : - shutil.copyfile( InfoKeysFileInit , InfoKeysFile ) - import InfoKeys as InfoKeys -### try to open existing external configuration file of user -''' -try : - fichier = open(InfoScript, 'r') - fichier.close() - import InfoScript as autoInfo + file = open(ConfUserFilejson, 'r') + file.close() ### else make the default external configuration file except : - shutil.copyfile( InfoScriptInit ,InfoScript) - import InfoScript as autoInfo -''' - - + partInfoDef = dict() + for prop in InfoKeys.partInfo: + partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True}) + os.mkdir(ConfUserDir) + file = open(ConfUserFilejson, 'x') + json.dump(partInfoDef,file) + file.close() + """ - +-----------------------------------------------+ - | Helper functions | - +-----------------------------------------------+ +now user configuration is : +file = open(ConfUserFilejson, 'r') +user configuration = json.load(file).copy() +file.close() """ -''' -def checkPart(): - # allowed types to edit info - partTypes = [ 'App::Part', 'PartDesign::Body'] - selectedPart = None - # if an App::Part is selected - if len(Gui.Selection.getSelection())==1: - selObj = Gui.Selection.getSelection()[0] - if selObj.TypeId in partTypes: - selectedPart = selObj - return selectedPart -''' - """ +-----------------------------------------------+ | The command | +-----------------------------------------------+ """ + + class infoPartCmd(): def __init__(self): super(infoPartCmd,self).__init__() def GetResources(self): tooltip = "Edit part information. " - tooltip += "The default part information keys are in the file " - tooltip += "\"FreeCAD/Mod/Assembly4/InfoKeys.py\", edit as you need" + tooltip += "The default part information can be configured" + tooltip += "use the Config button" iconFile = os.path.join( Asm4.iconPath, 'Asm4_PartInfo.svg' ) return {"MenuText": "Edit Part Information", "ToolTip": tooltip, "Pixmap": iconFile } @@ -96,7 +79,6 @@ def Activated(self): - """ +-----------------------------------------------+ | The UI and functions in the Task panel | @@ -106,18 +88,17 @@ class infoPartUI(): def __init__(self): self.base = QtGui.QWidget() - self.form = self.base + self.form = self.base iconFile = os.path.join( Asm4.iconPath , 'Asm4_PartInfo.svg') self.form.setWindowIcon(QtGui.QIcon( iconFile )) self.form.setWindowTitle("Edit Part Information") # hey-ho, let's go self.part = Asm4.getSelectedContainer() - self.infoKeys = InfoKeys.partInfo - # the attribute PartName is mandatory in the info-keys - if not 'PartName' in self.infoKeys: - self.infoKeys.append('PartName') - self.makePartInfo() + file = open(ConfUserFilejson, 'r') + self.infoKeysUser = json.load(file).copy() + file.close() + self.makePartInfo(self,self.part) self.infoTable = [] self.getPartInfo() @@ -134,17 +115,19 @@ def getPartInfo(self): self.infoTable.append([prop,value]) # add the default part information - def makePartInfo( self, reset=False ): - for info in self.infoKeys: - try : - self.part - if not hasattr(self.part,info): - #object with part - self.part.addProperty( 'App::PropertyString', info, 'PartInfo' ) - except AttributeError : - if self.TypeId == 'App::Part' : - #object part - self.addProperty( 'App::PropertyString', info, 'PartInfo' ) + def makePartInfo( self, object , reset=False ): + for info in self.infoKeysUser: + if self.infoKeysUser.get(info).get('active'): + try : + object.part + if not hasattr(object.part,self.infoKeysUser.get(info).get('userData')): + #object with part + object.part.addProperty( 'App::PropertyString', self.infoKeysUser.get(info).get('userData'), 'PartInfo' ) + except AttributeError : + if object.TypeId == 'App::Part' : + #object part + if not hasattr(object,self.infoKeysUser.get(info).get('userData')): + object.addProperty( 'App::PropertyString', self.infoKeysUser.get(info).get('userData'), 'PartInfo' ) return # AddNew @@ -152,17 +135,22 @@ def addNew(self): for i,prop in enumerate(self.infoTable): if self.part.getGroupOfProperty(prop[0])=='PartInfo' : if self.part.getTypeIdOfProperty(prop[0])=='App::PropertyString' : - text=self.infos[i].text() - setattr(self.part,prop[0],str(text)) + for propuser in self.infoKeysUser : + if self.infoKeysUser.get(propuser).get('userData') == prop[0] : + if self.infoKeysUser.get(propuser).get('active'): + text=self.infos[i].text() + setattr(self.part,prop[0],str(text)) # edit info keys def editKeys(self): - pass + Gui.Control.closeDialog() + Gui.Control.showDialog( infoPartConfUI() ) + #pass # InfoDefault def infoDefault(self): - #autoInfo.infoDefault(self) - pass + InfoKeys.infoDefault(self) + #pass # close def finish(self): @@ -174,11 +162,13 @@ def getStandardButtons(self): # Cancel def reject(self): + print("info cancel") self.finish() # OK: we insert the selected part def accept(self): self.addNew() + print("info save") self.finish() @@ -189,36 +179,295 @@ def drawUI(self): self.formLayout = QtGui.QFormLayout() self.infos=[] for i,prop in enumerate(self.infoTable): - checkLayout = QtGui.QHBoxLayout() - propValue = QtGui.QLineEdit() - propValue.setText( prop[1] ) - checked = QtGui.QCheckBox() - checkLayout.addWidget(propValue) - checkLayout.addWidget(checked) - self.formLayout.addRow(QtGui.QLabel(prop[0]),checkLayout) - self.infos.append(propValue) + for propuser in self.infoKeysUser : + if self.infoKeysUser.get(propuser).get('userData') == prop[0] : + if self.infoKeysUser.get(propuser).get('active'): + checkLayout = QtGui.QHBoxLayout() + propValue = QtGui.QLineEdit() + propValue.setText( prop[1] ) + checkLayout.addWidget(propValue) + self.formLayout.addRow(QtGui.QLabel(prop[0]),checkLayout) + self.infos.append(propValue) self.mainLayout.addLayout(self.formLayout) self.mainLayout.addWidget(QtGui.QLabel()) # Buttons self.buttonsLayout = QtGui.QHBoxLayout() - self.editFields = QtGui.QPushButton('Edit Fields') - self.loadTemplate = QtGui.QPushButton('Load Template') - self.buttonsLayout.addWidget(self.editFields) + self.confFields = QtGui.QPushButton('Config') + self.autoFill = QtGui.QPushButton('auto-filling') + self.buttonsLayout.addWidget(self.confFields) self.buttonsLayout.addStretch() - self.buttonsLayout.addWidget(self.loadTemplate) + self.buttonsLayout.addWidget(self.autoFill) self.mainLayout.addLayout(self.buttonsLayout) - self.form.setLayout(self.mainLayout) + #self.form.setLayout(self.mainLayout) # Actions - self.editFields.clicked.connect(self.editKeys) - self.loadTemplate.clicked.connect(self.infoDefault) + self.confFields.clicked.connect(self.editKeys) + self.autoFill.clicked.connect(self.infoDefault) + + if self.infoTable[0][1]=='': + self.infoDefault() + self.addNew() +class infoPartConfUI(): + def __init__(self): + self.base = QtGui.QWidget() + self.form = self.base + iconFile = os.path.join( Asm4.iconPath , 'Asm4_PartInfo.svg') + self.form.setWindowIcon(QtGui.QIcon( iconFile )) + self.form.setWindowTitle("Edit Part Info Configuration") + + # hey-ho, let's go + self.infoKeysDefault = InfoKeys.partInfo.copy() + self.infoToolTip = InfoKeys.infoToolTip.copy() + file = open(ConfUserFilejson, 'r') + self.infoKeysUser = json.load(file).copy() + file.close() + # create a dict() of defaultinfokeys and userinfokeys + self.confTemplate = dict() + self.confTemplate = self.infoKeysUser.copy() + + # the GUI objects are defined later down + self.drawConfUI() + + + # close + def finish(self): + Gui.Control.closeDialog() + print('exit config') + # standard panel UI buttons + def getStandardButtons(self): + return int(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) + + # Cancel + def reject(self): + print("Cancel") + self.finish() + # OK: we write a new config + def accept(self): + # init i + i=0 + # verification of field + for prop in self.confTemplate: + if self.infos[i].text() == '': + mb = QtGui.QMessageBox() + mb.setText("YOU CAN NOT LEAVE A FIELD BLANK \n DISABLE IT OR DELETE IT") + mb.setWindowTitle("WRITTING OF NEW CONFIG") + mb.exec_() + return + i+=1 + # init i + i=0 + # creation of dict() for user config file + config=dict() + for prop in self.confTemplate: + config.setdefault(prop,{'userData':self.infos[i].text().replace(" ", "_"),'active':self.checker[i].isChecked()}) + i+=1 + # write user config file + file = open(ConfUserFilejson, 'w') + json.dump(config,file) + file.close() + # write in infoKeysUser + file = open(ConfUserFilejson, 'r') + self.infoKeysUser = json.load(file).copy() + file.close() + # message for user + mb = QtGui.QMessageBox() + mb.setText("Your configuration \n has been saved") + mb.setWindowTitle("WRITTING OF NEW CONFIG") + mb.exec_() + # close + self.finish() + + def addNewManField(self): + # init new default name + nameref='man' + indexref=1 + newref = nameref + str(indexref) + while newref in self.confTemplate : + indexref+=1 + newref = nameref + str(indexref) + # init new user name + newField=self.newOne.text() + self.newOne.setText('') + self.addNewField(newref,newField) + + def addNewField(self,newref,newField): + # write new field on confTemplate + self.confTemplate.setdefault(newref,{'userData': newField,'active': True }) + # write new field on line + # Label + newLab = QtGui.QLabel(newref) + self.gridLayout.addWidget(newLab,self.i,0) + self.label.append(newLab) + # Qline + newOne = QtGui.QLineEdit() + newOne.setText(newField) + self.gridLayout.addWidget(newOne,self.i,1) + self.infos.append(newOne) + # checkbox + checkLayout = QtGui.QVBoxLayout() + checked = QtGui.QCheckBox() + checked.setChecked(self.confTemplate.get(newref).get('active') ) + self.gridLayout.addWidget(checked,self.i,2) + self.checker.append(checked) + # suppcombo + self.suppCombo.addItem(newField) + self.i+=1 + + # Define the deleteField action + def deleteField(self): + # delete all ref line and infos.text() + delField = self.suppCombo.currentText() + # door for not delete Asm4 autofield + for prop in self.confTemplate: + if self.confTemplate.get(prop).get('userData') == delField: + if prop[0:3] != 'man' : + mb = QtGui.QMessageBox() + mb.setText(" You can not DELETE \n Asm4 Auto-infoField \n But you can DISABLE IT") + mb.setWindowTitle("UNABLE TO DELETE") + mb.exec_() + return + i=0 + for prop in self.confTemplate: + if self.confTemplate.get(prop).get('userData') == delField: + self.label[i].deleteLater() + self.label.remove( self.label[i]) + self.infos[i].deleteLater() + self.infos.remove(self.infos[i]) + self.checker[i].deleteLater() + self.checker.remove(self.checker[i]) + self.suppCombo.removeItem(i) + self.refField = str(prop) + i+=1 + # delete it on confTemplate + self.confTemplate.pop(self.refField) + return + + # fonction of return if autofield list is update or no and what is new + def updateAutoFieldlist(self): + # init list + listUser=[] + for li in self.infoKeysUser : + listUser.append(li) + listDefault=self.infoKeysDefault.copy() + for li in listUser: + try : + listDefault.remove(li) + except: + pass + if listDefault == [] : + return None + else : + return listDefault + + # Define the update autoField module + # if infoKeys.py Update have new-autoField + # the user must be able to install them + def updateAutoField(self): + upField = self.upCombo.currentText() + self.upCombo.removeItem(self.upCombo.currentIndex()) + self.addNewField(upField,upField) + + + # Define the iUI + def drawConfUI(self): + # init container + self.label=[] + self.infos=[] + self.checker=[] + self.combo=[] + self.upcombo=[] + # Place the widgets with layouts + # make multiple layout + self.mainLayout = QtGui.QVBoxLayout(self.form) + self.gridLayout = QtGui.QGridLayout() + self.gridLayoutButtons = QtGui.QGridLayout() + self.gridLayoutUpdate = QtGui.QGridLayout() + + # make a first column with default data + default = QtGui.QLabel('default Data') + self.gridLayout.addWidget(default,0,0) + i=1 + for prop in self.confTemplate: + default = QtGui.QLabel(prop) + default.setToolTip(self.infoToolTip.get(prop)) + self.gridLayout.addWidget(default,i,0) + self.label.append(default) + i+=1 + # make add and delete label + self.addnewLab = QtGui.QLabel('add New') + self.gridLayoutButtons.addWidget(self.addnewLab,0,0) + self.suppLab = QtGui.QLabel('Delete') + self.gridLayoutButtons.addWidget(self.suppLab,1,0) + + # make a second column with user data in QLinEdit + user = QtGui.QLabel('user Data') + self.gridLayout.addWidget(user,0,1) + i=1 + for prop in self.confTemplate: + propValue = QtGui.QLineEdit() + propValue.setText( self.confTemplate.get(prop).get('userData') ) + self.gridLayout.addWidget(propValue,i,1) + self.infos.append(propValue) + i+=1 + # make add qline and delete combobox + # add + self.newOne = QtGui.QLineEdit() + self.i=i + self.gridLayoutButtons.addWidget(self.newOne,0,1) + # delete + self.suppCombo = QtGui.QComboBox() + for prop in self.confTemplate: + self.suppCombo.addItem(self.confTemplate.get(prop).get('userData')) + self.gridLayoutButtons.addWidget(self.suppCombo,1,1) + + # make a third column of QCheckBox for activate or not + active = QtGui.QLabel('active') + self.gridLayout.addWidget(active,0,2) + i=1 + for prop in self.confTemplate: + checkLayout = QtGui.QVBoxLayout() + checked = QtGui.QCheckBox() + checked.setChecked(self.confTemplate.get(prop).get('active') ) + self.gridLayout.addWidget(checked,i,2) + self.checker.append(checked) + i+=1 + self.addnew = QtGui.QPushButton('add New') + self.gridLayoutButtons.addWidget(self.addnew,0,2) + self.suppBut = QtGui.QPushButton('Delete') + self.gridLayoutButtons.addWidget(self.suppBut,1,2) + # Actions + self.addnew.clicked.connect(self.addNewManField) + self.suppBut.clicked.connect(self.deleteField) + + # insert layout in mainlayout + self.mainLayout.addLayout(self.gridLayout) + self.mainLayout.addLayout(self.gridLayoutButtons) + + # If are there update show there + if self.updateAutoFieldlist() != None : + updateLab = QtGui.QLabel('Update New Auto-infoField') + self.gridLayoutUpdate.addWidget(updateLab,0,0) + self.upCombo = QtGui.QComboBox() + self.gridLayoutUpdate.addWidget(self.upCombo,1,0) + for prop in self.updateAutoFieldlist() : + self.upCombo.addItem(prop) + self.upBut = QtGui.QPushButton('Update') + self.gridLayoutUpdate.addWidget(self.upBut,1,1) + # Actions + self.upBut.clicked.connect(self.updateAutoField) + + # insert layout in mainlayout + self.mainLayout.addLayout(self.gridLayoutUpdate) + + + """ +-----------------------------------------------+ diff --git a/makeBomCmd.py b/makeBomCmd.py index 23999885..b054d72d 100644 --- a/makeBomCmd.py +++ b/makeBomCmd.py @@ -8,17 +8,45 @@ import os +import json from PySide import QtGui, QtCore import FreeCADGui as Gui import FreeCAD as App -from FreeCAD import Console as FCC import Asm4_libs as Asm4 -#import infoPartCmd +import infoPartCmd import InfoKeys -#crea = infoPartCmd.infoPartUI.makePartInfo -#rempli = infoPartCmd.infoPartUI.infoDefault + +# protection against update of user configuration + +### to have the dir of external configuration file +ConfUserDir = os.path.join(App.getUserAppDataDir(),'Asm4_UserConf') +ConfUserFilename = "infoConfUser.json" +ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) + + +### try to open existing external configuration file of user +try : + file = open(ConfUserFilejson, 'r') + file.close() +### else make the default external configuration file +except : + partInfoDef = dict() + for prop in InfoKeys.partInfo: + partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True}) + os.mkdir(ConfUserDir) + file = open(ConfUserFilejson, 'x') + json.dump(partInfoDef,file) + file.close() + +### now user configuration is : +file = open(ConfUserFilejson, 'r') +infoKeysUser = json.load(file).copy() +file.close() + +crea = infoPartCmd.infoPartUI.makePartInfo +fill = infoPartCmd.infoPartUI.infoDefault @@ -34,140 +62,19 @@ +-----------------------------------------------+ | prints a parts list | +-----------------------------------------------+ -def Partlist(object,level=0): - indent = ' '*level - # list the Variables - if object.Name=='Variables': - print(indent+'Variables:') - vars = object - for prop in vars.PropertiesList: - if vars.getGroupOfProperty(prop)=='Variables' : - propValue = vars.getPropertyByName(prop) - print(indent+' '+prop+' = '+str(propValue)) - # if its a link, look for the linked object - elif object.TypeId=='App::Link': - print(indent+object.Label+' -> '+object.LinkedObject.Document.Name+'#'+object.LinkedObject.Label) - Partlist(object.LinkedObject,level+1) - # everything else - else: - print(indent+object.Label+' ('+object.TypeId+')') - # if it's a part, look for sub-objects - if object.TypeId=='App::Part': - for objname in object.getSubObjects(): - subobj = object.Document.getObject( objname[0:-1] ) - Partlist(subobj,level+1) - return - - - - -import FreeCAD,Draft -import csv - -forbbox = ('PartDesign::Body', 'Part::Feature', 'Part::FeaturePython') - -def Partlist(object,level=0,tab=None): - indent = ' '*level - if tab is None: - tab = {} - if object.TypeId=='App::Link': - print(indent+object.Label+' -> '+object.LinkedObject.Document.Name+'#'+object.LinkedObject.Label+' => '+object.LinkedObject.FullName) - Partlist(object.LinkedObject,level+1,tab) - else: - print(indent+object.Label+' ('+object.TypeId+')') - if hasattr(object, 'Shape') and object.TypeId in forbbox: - if object.FullName in tab: - tab[object.FullName]["count"] = tab[object.FullName]["count"] + 1 - else: - tab[object.FullName] = {} - tab[object.FullName]["var"] = {} - tab[object.FullName]["count"] = 1 - - tab[object.FullName]['label'] = object.Label - tab[object.FullName]['fullname'] = object.FullName - tab[object.FullName]['Id'] = object.Label - bb=object.Shape.BoundBox - tab[object.FullName]['xlen'] = bb.XLength - tab[object.FullName]['ylen'] = bb.YLength - tab[object.FullName]['zlen'] = bb.ZLength - tab[object.FullName]['volume'] = str(object.Shape.Volume) - - if hasattr(object, 'AttachedTo'): - tab[object.FullName]['attachedto'] = object.AttachedTo - - print (indent+" => BBox: "+str(object.Shape.BoundBox)) - print (indent+" => Volume: "+str(object.Shape.Volume)) - if object.TypeId=='App::Part': - # look for Variables - if object.Document.getObject( 'Variables' ): - print(indent+' Variables:') - vars = object.Document.getObject( 'Variables' ) - for prop in vars.PropertiesList: - if vars.getGroupOfProperty(prop)=='Variables' : - propValue = vars.getPropertyByName(prop) - print(indent+' '+prop+' = '+str(propValue)) - if not object.FullName in tab: - tab[object.FullName] = {} - tab[object.FullName]['fullname'] = object.FullName - tab[object.FullName]["var"] = {} - tab[object.FullName]["var"][prop] = str(propValue) - # look for sub-objects - for objname in object.getSubObjects(): - subobj = object.Document.getObject( objname[0:-1] ) - Partlist(subobj,level+1, tab) - return tab - - -def dictoarr(tab): - keys = {} - for obj in tab.keys(): - if isinstance(tab[obj], dict): - for key in tab[obj].keys(): - if isinstance(tab[obj][key], dict): - for inner_key in tab[obj][key].keys(): - keys[key+"."+inner_key] = {}; - keys[key+"."+inner_key][0] = key; - keys[key+"."+inner_key][1] = inner_key; - else: - keys[key] = 1; - headings = sorted(keys.keys()) - - arr = [ headings ] - for obj in sorted(tab.keys()): - line = [] - for head in headings: - value = '' - lookup = keys[head] - if isinstance(lookup, dict): - if lookup[0] in tab[obj] and lookup[1] in tab[obj][lookup[0]]: - value = tab[obj][lookup[0]][lookup[1]] - else: - if head in tab[obj]: - value = tab[obj][head] - line.append(value) - arr.append(line) - return arr - - -a = Partlist(FreeCAD.ActiveDocument.getObject("Model"), 0) -print("\n\n") -print(a) -t = dictoarr(a) -print("\n\n") -print(t) - -with open('/tmp/table.csv', 'w') as csvfile: - writer = csv.writer(csvfile, delimiter="\t") - writer.writerows(t) - """ + class makeBOM: def __init__(self): super(makeBOM,self).__init__() + file = open(ConfUserFilejson, 'r') + self.infoKeysUser = json.load(file).copy() + file.close() def GetResources(self): - tooltip = "EXPERIMENTAL !!! " + tooltip = "Bill of Materials" tooltip += "Create the Bill of Materials of an Assembly" + tooltip += "With the Info and Config of Edit Part Information" iconFile = os.path.join( Asm4.iconPath, 'Asm4_PartsList.svg' ) return {"MenuText": "Create Part List", "ToolTip": tooltip, "Pixmap": iconFile } @@ -192,201 +99,98 @@ def Activated(self): print("legacy Assembly4 Model") except: print("Hum, this might not work") - ''' - try : - self.model = self.modelDoc.Model - except: - print("nouveau Assembly") - ''' self.drawUI() self.UI.show() self.BOM.clear() + self.Verbose=str() self.PartsList = {} self.listParts(self.model) - self.BOM.setPlainText(str(self.PartsList)) + self.inSpreadsheet() + self.BOM.setPlainText(self.Verbose) -### def listParts by FarmingSoul - use of Part info Edit +### def listParts use of Part info Edit def listParts(self,object,level=0): - forbbox = ('PartDesign::Body', 'Part::Feature', 'Part::FeaturePython') + file = open(ConfUserFilejson, 'r') + self.infoKeysUser = json.load(file).copy() + file.close() if object == None: return if self.PartsList == None: self.PartsList = {} + # research App::Part because the partInfo attribute is on if object.TypeId=='App::Link': self.listParts(object.LinkedObject,level+1) else: if object.TypeId=='App::Part': if level > 0: + # write PartsList + # test if the part already exist on PartsList if object.Label in self.PartsList: + # if already exist =+ 1 in qty of this part count self.PartsList[object.Label]['Qty.'] = self.PartsList[object.Label]['Qty.'] + 1 else: + # if not exist , create a dict() for this part self.PartsList[object.Label] = dict() + for prop in self.infoKeysUser: + if self.infoKeysUser.get(prop).get('active'): + try: + # try to get partInfo in part + getattr(object,self.infoKeysUser.get(prop).get('userData')) + except AttributeError: + self.Verbose+='you don\'t have fill the info of this Part :'+ object.Label +'\n' + crea(self,object) + self.Verbose+='info create for :'+ object.Label +'\n' + fill(object) + self.Verbose+='info auto filled for :'+ object.Label+'\n' + self.PartsList[object.Label][self.infoKeysUser.get(prop).get('userData')] = getattr(object,self.infoKeysUser.get(prop).get('userData')) self.PartsList[object.Label]['Qty.'] = 1 - for prop in InfoKeys.partInfo: - try: - getattr(object,prop) - self.PartsList[object.Label][prop] = getattr(object,prop) - except AttributeError: - print ('L\'object \"',object.Label,'\" n\'a pas d\'info \"',prop,'\"') - # create an entry for that part under its name - if prop=='PartName': - self.PartsList[object.Label][prop] = object.Label - # crea(object) - # rempli(object) - - '''tab[object.FullName]["var"] = {} ''' - - '''# look for Variables - if object.Document.getObject( 'Variables' ): - print(indent+' Variables:') - vars = object.Document.getObject( 'Variables' ) - for prop in vars.PropertiesList: - if vars.getGroupOfProperty(prop)=='Variables' : - propValue = vars.getPropertyByName(prop) - print(indent+' '+prop+' = '+str(propValue)) - if not object.FullName in tab: - tab[object.FullName] = {} - tab[object.FullName]['fullname'] = object.FullName - tab[object.FullName]["var"] = {} - tab[object.FullName]["var"][prop] = str(propValue) ''' # look for sub-objects for objname in object.getSubObjects(): subobj = object.Document.getObject( objname[0:-1] ) self.listParts(subobj,level+1) - return - -### def listParts by Zolko - not use of Part info Edit -### def listParts( self, obj, level=0 ): -### indent = '\n'+'\t'*level -### if obj.Document == self.modelDoc: -### docName = '' -### else: -### docName = obj.Document.Name+'#' -### #partBB = App.BoundBox() -### # list the Variables -### if obj.Name=='Variables': -### #print(indent+'Variables:') -### self.PartsList += indent+'Variables:' -### for prop in obj.PropertiesList: -### if obj.getGroupOfProperty(prop)=='Variables' : -### propValue = obj.getPropertyByName(prop) -### self.PartsList += indent+'\t'+prop+' = '+str(propValue) -### # if it's part we look for sub-objects -### elif obj.TypeId=='App::Part': -### self.PartsList += indent +docName +obj.Label -### for objname in obj.getSubObjects(): -### subobj = obj.Document.getObject( objname[0:-1] ) -### self.listParts( subobj, level+1 ) -### # if its a link, look for the linked object -### elif obj.TypeId=='App::Link': -### self.PartsList += indent+obj.Label+' -> ' -### self.listParts( obj.LinkedObject, level ) -### # if its a Body container we also add the document name and the size -### elif obj.TypeId=='PartDesign::Body': -### self.PartsList += indent +docName +obj.Label -### if obj.Label2: -### self.PartsList += ' ('+obj.Label2+')' -### bb = obj.Shape.BoundBox -### if abs(max(bb.XLength,bb.YLength,bb.ZLength)) < 1e+10: -### Xsize = str(int((bb.XLength * 10)+0.099)/10) -### Ysize = str(int((bb.YLength * 10)+0.099)/10) -### Zsize = str(int((bb.ZLength * 10)+0.099)/10) -### self.PartsList += ', Size: '+Xsize+' x '+Ysize+' x '+Zsize -### # everything else except datum objects -### elif obj.TypeId not in Asm4.datumTypes: -### self.PartsList += indent+obj.Label -### if obj.Label2: -### self.PartsList += ' ('+obj.Label2+')' -### else: -### self.PartsList += ' ('+obj.TypeId+')' -### # if the object has a shape, add it at the end of the line -### if hasattr(obj,'Shape') and obj.Shape.BoundBox.isValid(): -### bb = obj.Shape.BoundBox -### if max(bb.XLength,bb.YLength,bb.ZLength) < 1e+10: -### Xsize = str(int((bb.XLength * 10)+0.099)/10) -### Ysize = str(int((bb.YLength * 10)+0.099)/10) -### Zsize = str(int((bb.ZLength * 10)+0.099)/10) -### self.PartsList += ', Size: '+Xsize+' x '+Ysize+' x '+Zsize -### return - - def onSave(self): - #pass - """Saves ASCII tree to user system file""" - _path = QtGui.QFileDialog.getSaveFileName() - if _path[0]: - save_file = QtCore.QFile(_path[0]) - if save_file.open(QtCore.QFile.ReadWrite): - save_fileContent = QtCore.QTextStream(save_file) - save_fileContent << self.BOM - save_file.flush() - save_file.close() - self.BOM.setPlainText("Saved to file : " + _path[0]) - else: - #FCC.PrintError("ERROR : Can't open file : "+ _path[0]+'\n') - self.BOM.setPlainText("ERROR : Can't open file : " + _path[0]) - else: - self.BOM.setPlainText("ERROR : Can't open file : " + _path[0]) - QtCore.QTimer.singleShot(3000, lambda:self.BOM.setPlainText(self.PartsList)) - #self.UI.close() - - - def isReal( bb ): - # check if the BoundingBox is a real one - if bb.isValid() and abs(max(bb.XLength,bb.YLength,bb.ZLength)) < 1e+10: - return True - else: - return False - - ''' - def checkModel(self): - # check whether there is already a Model in the document - # Returns True if there is an object called 'Assembly' or 'Model' for old version - if App.ActiveDocument and App.ActiveDocument.getObject('Assembly') and App.ActiveDocument.Assembly.TypeId == 'App::Part': - return(True) - elif App.ActiveDocument and App.ActiveDocument.getObject('Model') and App.ActiveDocument.Model.TypeId == 'App::Part': - return(True) - else: - return(False) - ''' + + return + self.Verbose+='Your Bill of Materials is Done\n' -### def onCopy by FarmingSoul - Copy on Spreadsheet +### def Copy - Copy on Spreadsheet - def onCopy(self): - """Copies Parts List to Spreadsheet""" + def inSpreadsheet(self): + #Copies Parts List to Spreadsheet document = App.ActiveDocument + # init plist whit dict() PartsList plist = self.PartsList if len(plist) == 0: return - + # BOM on Spreadsheet if not hasattr(document, 'BOM'): spreadsheet = document.addObject('Spreadsheet::Sheet','BOM') else: spreadsheet = document.BOM spreadsheet.Label = "BOM" + # clean the BOM spreadsheet.clearAll() - + # to write line in spreadsheet def wrow(drow: [str], row: int): for i, d in enumerate(drow): spreadsheet.set(str(chr(ord('a') + i)).upper()+str(row+1), str(d)) - - for i, (_,data) in enumerate(plist.items(), start=1): - wrow(data.keys(),0) - wrow(data.values(),i) + # to make list of values of dict() plist + data = list(plist.values()) + # to write first line with keys + wrow(data[0].keys(),0) + # to write line by line BoM in Spreadsheet + for i,_ in enumerate(data): + wrow(data[i].values(),i+1) document.recompute() -### def onCopy by Zolko - Copy on clipboard -### def onCopy(self): -### '''Copies Parts List to clipboard''' -### self.BOM.selectAll() -### self.BOM.copy() -### self.BOM.setPlainText("Copied BoM to clipboard") -### QtCore.QTimer.singleShot(3000, lambda:self.BOM.setPlainText(self.PartsList)) + self.Verbose+='Your Bill of Materials is Write on BOM Spreadsheet\n' def onOK(self): + document = App.ActiveDocument + Gui.Selection.addSelection(document.Name,'BOM') self.UI.close() @@ -403,24 +207,21 @@ def drawUI(self): self.UI.setModal(False) # set main window widgets layout self.mainLayout = QtGui.QVBoxLayout(self.UI) - - # The list, is a plain text field + + # Help and Log : + self.LabelBOM = QtGui.QLabel('BoM:\n\nThis tool make BoM with the Info and Config of Edit Part Information. \n\nIf you have auto-infoField in your Config you can use BoM directly.\nBoM complet automaticaly your auto-infoField.\n\nLog :') + self.mainLayout.addWidget(self.LabelBOM) + + # The Log (Verbose) is a plain text field self.BOM = QtGui.QPlainTextEdit() - self.BOM.setMinimumSize(600,500) self.BOM.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) self.mainLayout.addWidget(self.BOM) # the button row definition self.buttonLayout = QtGui.QHBoxLayout() - self.buttonLayout.addStretch() - # Save button - self.CopyButton = QtGui.QPushButton('In Spreadsheet') - self.buttonLayout.addWidget(self.CopyButton) - # Save button - #self.SaveButton = QtGui.QPushButton('Save') - #self.buttonLayout.addWidget(self.SaveButton) + # OK button - self.OkButton = QtGui.QPushButton('Close') + self.OkButton = QtGui.QPushButton('OK') self.OkButton.setDefault(True) self.buttonLayout.addWidget(self.OkButton) self.mainLayout.addLayout(self.buttonLayout) @@ -429,8 +230,6 @@ def drawUI(self): self.UI.setLayout(self.mainLayout) # Actions - self.CopyButton.clicked.connect(self.onCopy) - #self.SaveButton.clicked.connect(self.onSave) self.OkButton.clicked.connect(self.onOK) # add the command to the workbench From 0798f92d06dffd816c2d63d43d69e0ae87be7e09 Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Sun, 3 Oct 2021 22:53:39 +0200 Subject: [PATCH 02/17] fixed bug in InfoKeys --- InfoKeys.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/InfoKeys.py b/InfoKeys.py index 42ad43da..44dd6105 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -5,13 +5,19 @@ # # libraries for FreeCAD's Assembly 4 workbench -import os -import json +import os, json import FreeCAD as App +import infoPartCmd -# protection against update of user configuration +# Autofilling info ref +partInfo =[ 'LabelDoc', \ + 'LabelPart' ] + +infoToolTip = {'LabelDoc':'Return the Label of Document','LabelPart':'Return the Label of Part'} + +# protection against update of user configuration ### to have the dir of external configuration file ConfUserDir = os.path.join(App.getUserAppDataDir(),'Asm4_UserConf') ConfUserFilename = "infoConfUser.json" @@ -25,7 +31,7 @@ ### else make the default external configuration file except : partInfoDef = dict() - for prop in InfoKeys.partInfo: + for prop in partInfo: partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True}) os.mkdir(ConfUserDir) file = open(ConfUserFilejson, 'x') @@ -38,15 +44,6 @@ file.close() -import infoPartCmd - -# Autofilling info ref - -partInfo =[ 'LabelDoc', \ - 'LabelPart' ] - -infoToolTip = {'LabelDoc':'Return the Label of Document','LabelPart':'Return the Label of Part'} - def infoDefault(self): ### auto filling module @@ -144,7 +141,4 @@ def LabelPart(self,PART): except AttributeError: ### if field is not actived pass - - - - pass \ No newline at end of file + pass From f8c177ceef994b5cc090d1d0ae3378f97e2c206c Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Sun, 3 Oct 2021 22:59:31 +0200 Subject: [PATCH 03/17] changed infoConfUserIdr from Asm4_UserConf to Templates --- InfoKeys.py | 2 +- infoPartCmd.py | 2 +- makeBomCmd.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InfoKeys.py b/InfoKeys.py index 44dd6105..f796ef6c 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -19,7 +19,7 @@ # protection against update of user configuration ### to have the dir of external configuration file -ConfUserDir = os.path.join(App.getUserAppDataDir(),'Asm4_UserConf') +ConfUserDir = os.path.join(App.getUserAppDataDir(),'Templates') ConfUserFilename = "infoConfUser.json" ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) diff --git a/infoPartCmd.py b/infoPartCmd.py index a4670ec0..6121ea59 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -22,7 +22,7 @@ # protection against update of user configuration ### to have the dir of external configuration file -ConfUserDir = os.path.join(App.getUserAppDataDir(),'Asm4_UserConf') +ConfUserDir = os.path.join(App.getUserAppDataDir(),'Templates') ConfUserFilename = "infoConfUser.json" ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) diff --git a/makeBomCmd.py b/makeBomCmd.py index b054d72d..f459bf3f 100644 --- a/makeBomCmd.py +++ b/makeBomCmd.py @@ -21,7 +21,7 @@ # protection against update of user configuration ### to have the dir of external configuration file -ConfUserDir = os.path.join(App.getUserAppDataDir(),'Asm4_UserConf') +ConfUserDir = os.path.join(App.getUserAppDataDir(),'Templates') ConfUserFilename = "infoConfUser.json" ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) From 9f1af6820d69b83d8a516eedc6daa90c24a25cc9 Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Sun, 3 Oct 2021 23:33:25 +0200 Subject: [PATCH 04/17] changed the user conf file infoConfUser.json > Asm4_infoPartConf.json --- InfoKeys.py | 7 +++++-- infoPartCmd.py | 2 +- makeBomCmd.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/InfoKeys.py b/InfoKeys.py index f796ef6c..676ba3ea 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -20,7 +20,7 @@ # protection against update of user configuration ### to have the dir of external configuration file ConfUserDir = os.path.join(App.getUserAppDataDir(),'Templates') -ConfUserFilename = "infoConfUser.json" +ConfUserFilename = "Asm4_infoPartConf.json" ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) @@ -33,7 +33,10 @@ partInfoDef = dict() for prop in partInfo: partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True}) - os.mkdir(ConfUserDir) + try: + os.mkdir(ConfUserDir) + except: + pass file = open(ConfUserFilejson, 'x') json.dump(partInfoDef,file) file.close() diff --git a/infoPartCmd.py b/infoPartCmd.py index 6121ea59..e0bf3d14 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -23,7 +23,7 @@ ### to have the dir of external configuration file ConfUserDir = os.path.join(App.getUserAppDataDir(),'Templates') -ConfUserFilename = "infoConfUser.json" +ConfUserFilename = "Asm4_infoPartConf.json" ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) diff --git a/makeBomCmd.py b/makeBomCmd.py index f459bf3f..cfa2ad61 100644 --- a/makeBomCmd.py +++ b/makeBomCmd.py @@ -22,7 +22,7 @@ ### to have the dir of external configuration file ConfUserDir = os.path.join(App.getUserAppDataDir(),'Templates') -ConfUserFilename = "infoConfUser.json" +ConfUserFilename = "Asm4_infoPartConf.json" ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename) From a3c56445643e78507a862b7ea5b4f95d188d99f3 Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Sun, 3 Oct 2021 23:33:43 +0200 Subject: [PATCH 05/17] whitespace --- InfoKeys.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/InfoKeys.py b/InfoKeys.py index 676ba3ea..746981dc 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -35,8 +35,6 @@ partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True}) try: os.mkdir(ConfUserDir) - except: - pass file = open(ConfUserFilejson, 'x') json.dump(partInfoDef,file) file.close() From ac2e4511c64ec3267d98676f21e536a6550f2e2a Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Mon, 4 Oct 2021 01:17:57 +0200 Subject: [PATCH 06/17] fixed infokeys --- InfoKeys.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InfoKeys.py b/InfoKeys.py index 746981dc..676ba3ea 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -35,6 +35,8 @@ partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True}) try: os.mkdir(ConfUserDir) + except: + pass file = open(ConfUserFilejson, 'x') json.dump(partInfoDef,file) file.close() From a84ecb74bd38b673a9753ff35db8ceb3fb7b4e73 Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Mon, 4 Oct 2021 18:54:17 +0200 Subject: [PATCH 07/17] Add re-intialize of PartInfo properties --- infoPartCmd.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/infoPartCmd.py b/infoPartCmd.py index e0bf3d14..9a886ebb 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -146,6 +146,26 @@ def editKeys(self): Gui.Control.closeDialog() Gui.Control.showDialog( infoPartConfUI() ) #pass + + def reInit(self): + #init of list of all Properties + List = self.part.PropertiesList + listpi=[] + #make list of PartInfo Properties + for prop in List : + if self.part.getGroupOfProperty(prop) == 'PartInfo' : + listpi.append(prop) + # delete all PartInfo Properties + for suppr in listpi : + self.part.removeProperty(suppr) + # message for user + mb = QtGui.QMessageBox() + mb.setText("Your fields \n has been re-initilize") + mb.setWindowTitle("RE-INITIALISATION") + mb.exec_() + # close + self.finish() + # InfoDefault def infoDefault(self): @@ -195,9 +215,11 @@ def drawUI(self): # Buttons self.buttonsLayout = QtGui.QHBoxLayout() self.confFields = QtGui.QPushButton('Config') + self.reinit = QtGui.QPushButton('re-init') + self.reinit.setToolTip('To re-initialize your PartInfo Field of your part') self.autoFill = QtGui.QPushButton('auto-filling') self.buttonsLayout.addWidget(self.confFields) - self.buttonsLayout.addStretch() + self.buttonsLayout.addWidget(self.reinit) self.buttonsLayout.addWidget(self.autoFill) self.mainLayout.addLayout(self.buttonsLayout) @@ -205,6 +227,7 @@ def drawUI(self): # Actions self.confFields.clicked.connect(self.editKeys) + self.reinit.clicked.connect(self.reInit) self.autoFill.clicked.connect(self.infoDefault) if self.infoTable[0][1]=='': From f967ee2893a99d385b07738fc035561152e15f42 Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Tue, 5 Oct 2021 13:25:16 +0200 Subject: [PATCH 08/17] fix bug with infotable - re-init there is a problem for have a right info face to the right label sometime. now is done --- InfoKeys.py | 109 +++++++++++++++++++++++++++++++++++++++++-------- infoPartCmd.py | 19 ++++++--- 2 files changed, 105 insertions(+), 23 deletions(-) diff --git a/InfoKeys.py b/InfoKeys.py index 676ba3ea..2231cd30 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -13,9 +13,12 @@ # Autofilling info ref partInfo =[ 'LabelDoc', \ - 'LabelPart' ] + 'LabelPart', \ + 'PadLength', \ + 'ShapeLength', \ + 'ShapeWidth' ] -infoToolTip = {'LabelDoc':'Return the Label of Document','LabelPart':'Return the Label of Part'} +infoToolTip = {'LabelDoc':'Return the Label of Document','LabelPart':'Return the Label of Part','PadLength':'Return the Length of Pad','ShapeLength':'Return the Length of Shape','ShapeWidth':'Return the Width of Shape'} # protection against update of user configuration ### to have the dir of external configuration file @@ -41,15 +44,18 @@ json.dump(partInfoDef,file) file.close() +''' ### now user configuration is : file = open(ConfUserFilejson, 'r') infoKeysUser = json.load(file).copy() file.close() - - +''' def infoDefault(self): ### auto filling module - + ### load infoKeysUser + file = open(ConfUserFilejson, 'r') + self.infoKeysUser = json.load(file).copy() + file.close() ### part variable creation try : self.TypeId @@ -74,11 +80,12 @@ def infoDefault(self): except NameError : print('there is no Sketch on a Pad of : ',PART.FullName ) - ### start all autoinfofield LabelDoc(self,PART,DOC) LabelPart(self,PART) - + PadLength(self,PART,PAD) + ShapeLength(self,PART,SKETCH) + ShapeWidth(self,PART,SKETCH) """ how make a new autoinfofield : @@ -86,33 +93,98 @@ def infoDefault(self): make a description in infoToolTip = {} -put newautoinfofield name in infoDefault() at the end +put newautoinfofield name in infoDefault() at the end with the right arg (PAD,SKETCH...) write new def like that : -def newautoinfofield(self,PART (opt : DOC , BODY , PAD , SKETCH): +def newautoinfofieldname(self,PART(option : DOC , BODY , PAD , SKETCH): ###you can use DOC - PART - BODY - PAD - SKETCH - auto_info = string you want to write in field + auto_info_field = self.infoKeysUser.get('newautoinfofieldname').get('userData') + auto_info_fill = newautoinfofield information try: ### if the command comes from makeBom write autoinfo directly on Part self.TypeId - auto_info = string - setattr(PART,'newautoinfofield name',auto_info) + setattr(PART,auto_info_field,str(auto_info_fill)) except AttributeError: ### if the command comes from infoPartUI write autoinfo on autofilling field on UI try : ### if field is actived for i in range(len(self.infoTable)): - if self.infoTable[i][0]=='newautoinfofield name': - self.infos[i].setText(auto_info) + if self.infoTable[i][0]== auto_info_field : + self.infos[i].setText(str(auto_info_fill)) except AttributeError: ### if field is not actived pass """ + +def ShapeLength(self,PART,SKETCH): +###you can use DOC - PART - BODY - PAD - SKETCH + try : + auto_info_field = self.infoKeysUser.get('ShapeLength').get('userData') + auto_info_fill = SKETCH.Shape.Length + except AttributeError: + return + try: + ### if the command comes from makeBom write autoinfo directly on Part + self.TypeId + setattr(PART,auto_info_field,str(auto_info_fill)) + except AttributeError: + ### if the command comes from infoPartUI write autoinfo on autofilling field on UI + try : + ### if field is actived + for i in range(len(self.infoTable)): + if self.infoTable[i][0]== auto_info_field : + self.infos[i].setText(str(auto_info_fill)) + except AttributeError: + ### if field is not actived + pass + +def ShapeWidth(self,PART,SKETCH): +###you can use DOC - PART - BODY - PAD - SKETCH + try : + auto_info_field = self.infoKeysUser.get('ShapeWidth').get('userData') + auto_info_fill = SKETCH.Shape.Width + except AttributeError: + return + try: + ### if the command comes from makeBom write autoinfo directly on Part + self.TypeId + setattr(PART,auto_info_field,str(auto_info_fill)) + except AttributeError: + ### if the command comes from infoPartUI write autoinfo on autofilling field on UI + try : + ### if field is actived + for i in range(len(self.infoTable)): + if self.infoTable[i][0]== auto_info_field : + self.infos[i].setText(str(auto_info_fill)) + except AttributeError: + ### if field is not actived + pass + +def PadLength(self,PART,PAD): +###you can use DOC - PART - BODY - PAD - SKETCH + auto_info_field = self.infoKeysUser.get('PadLength').get('userData') + auto_info_fill = PAD.Length + try: + ### if the command comes from makeBom write autoinfo directly on Part + self.TypeId + setattr(PART,auto_info_field,str(auto_info_fill)) + except AttributeError: + ### if the command comes from infoPartUI write autoinfo on autofilling field on UI + try : + ### if field is actived + for i in range(len(self.infoTable)): + if self.infoTable[i][0]== auto_info_field : + self.infos[i].setText(str(auto_info_fill)) + except AttributeError: + ### if field is not actived + pass + + def LabelDoc(self,PART,DOC): - docLabel = infoKeysUser.get('LabelDoc').get('userData') + docLabel = self.infoKeysUser.get('LabelDoc').get('userData') try: ### if the command comes from makeBom write autoinfo directly on Part self.TypeId @@ -129,7 +201,7 @@ def LabelDoc(self,PART,DOC): pass def LabelPart(self,PART): - partLabel = infoKeysUser.get('LabelPart').get('userData') + partLabel = self.infoKeysUser.get('LabelPart').get('userData') try: ### if the command comes from makeBom write autoinfo directly on Part self.TypeId @@ -144,4 +216,7 @@ def LabelPart(self,PART): except AttributeError: ### if field is not actived pass - pass + + + + pass \ No newline at end of file diff --git a/infoPartCmd.py b/infoPartCmd.py index 9a886ebb..168c099d 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -8,8 +8,7 @@ -import os -import json +import os, json from PySide import QtGui, QtCore import FreeCADGui as Gui @@ -111,8 +110,11 @@ def getPartInfo(self): for prop in self.part.PropertiesList: if self.part.getGroupOfProperty(prop)=='PartInfo' : if self.part.getTypeIdOfProperty(prop)=='App::PropertyString' : - value = self.part.getPropertyByName(prop) - self.infoTable.append([prop,value]) + for propuser in self.infoKeysUser : + if self.infoKeysUser.get(propuser).get('userData') == prop : + if self.infoKeysUser.get(propuser).get('active'): + value = self.part.getPropertyByName(prop) + self.infoTable.append([prop,value]) # add the default part information def makePartInfo( self, object , reset=False ): @@ -229,8 +231,13 @@ def drawUI(self): self.confFields.clicked.connect(self.editKeys) self.reinit.clicked.connect(self.reInit) self.autoFill.clicked.connect(self.infoDefault) - - if self.infoTable[0][1]=='': + test=False + try: + if self.infoTable[0][1]=='': + test=True + except IndexError: + test=True + if test : self.infoDefault() self.addNew() From 4d4a3613a86ab3ed331c137ab0fcb07293e7b6bc Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Tue, 5 Oct 2021 13:30:59 +0200 Subject: [PATCH 09/17] delete false fieldexample ShapeWidth --- InfoKeys.py | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/InfoKeys.py b/InfoKeys.py index 2231cd30..11494c69 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -15,10 +15,9 @@ partInfo =[ 'LabelDoc', \ 'LabelPart', \ 'PadLength', \ - 'ShapeLength', \ - 'ShapeWidth' ] + 'ShapeLength'] -infoToolTip = {'LabelDoc':'Return the Label of Document','LabelPart':'Return the Label of Part','PadLength':'Return the Length of Pad','ShapeLength':'Return the Length of Shape','ShapeWidth':'Return the Width of Shape'} +infoToolTip = {'LabelDoc':'Return the Label of Document','LabelPart':'Return the Label of Part','PadLength':'Return the Length of Pad','ShapeLength':'Return the Length of Shape'} # protection against update of user configuration ### to have the dir of external configuration file @@ -85,7 +84,6 @@ def infoDefault(self): LabelPart(self,PART) PadLength(self,PART,PAD) ShapeLength(self,PART,SKETCH) - ShapeWidth(self,PART,SKETCH) """ how make a new autoinfofield : @@ -120,8 +118,8 @@ def newautoinfofieldname(self,PART(option : DOC , BODY , PAD , SKETCH): def ShapeLength(self,PART,SKETCH): ###you can use DOC - PART - BODY - PAD - SKETCH + auto_info_field = self.infoKeysUser.get('ShapeLength').get('userData') try : - auto_info_field = self.infoKeysUser.get('ShapeLength').get('userData') auto_info_fill = SKETCH.Shape.Length except AttributeError: return @@ -140,32 +138,14 @@ def ShapeLength(self,PART,SKETCH): ### if field is not actived pass -def ShapeWidth(self,PART,SKETCH): -###you can use DOC - PART - BODY - PAD - SKETCH - try : - auto_info_field = self.infoKeysUser.get('ShapeWidth').get('userData') - auto_info_fill = SKETCH.Shape.Width - except AttributeError: - return - try: - ### if the command comes from makeBom write autoinfo directly on Part - self.TypeId - setattr(PART,auto_info_field,str(auto_info_fill)) - except AttributeError: - ### if the command comes from infoPartUI write autoinfo on autofilling field on UI - try : - ### if field is actived - for i in range(len(self.infoTable)): - if self.infoTable[i][0]== auto_info_field : - self.infos[i].setText(str(auto_info_fill)) - except AttributeError: - ### if field is not actived - pass def PadLength(self,PART,PAD): ###you can use DOC - PART - BODY - PAD - SKETCH auto_info_field = self.infoKeysUser.get('PadLength').get('userData') - auto_info_fill = PAD.Length + try : + auto_info_fill = PAD.Length + except AttributeError: + return try: ### if the command comes from makeBom write autoinfo directly on Part self.TypeId From 2430cf15c7ff1391b7adc89c4be075bc9017ae89 Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Tue, 5 Oct 2021 15:25:26 +0200 Subject: [PATCH 10/17] protection agains't non ascii char of field label --- infoPartCmd.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/infoPartCmd.py b/infoPartCmd.py index 168c099d..7e1b35c5 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -324,6 +324,14 @@ def addNewManField(self): newref = nameref + str(indexref) # init new user name newField=self.newOne.text() + if newField != newField.encode('ascii', 'replace').decode() : + # message for user + mb = QtGui.QMessageBox() + mb.setText("You can only use\nASCII charactere\nfor your field label\nsorry") + mb.setWindowTitle("WRITTING OF NEW CONFIG") + mb.exec_() + # return + return self.newOne.setText('') self.addNewField(newref,newField) From 6c40a6bd0ad9616e60743929368193694f97ed27 Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Tue, 5 Oct 2021 15:56:11 +0200 Subject: [PATCH 11/17] fixing BoM bug --- InfoKeys.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/InfoKeys.py b/InfoKeys.py index 11494c69..7c00c732 100644 --- a/InfoKeys.py +++ b/InfoKeys.py @@ -43,17 +43,17 @@ json.dump(partInfoDef,file) file.close() -''' + ### now user configuration is : file = open(ConfUserFilejson, 'r') infoKeysUser = json.load(file).copy() file.close() -''' + def infoDefault(self): ### auto filling module ### load infoKeysUser file = open(ConfUserFilejson, 'r') - self.infoKeysUser = json.load(file).copy() + infoKeysUser = json.load(file).copy() file.close() ### part variable creation try : @@ -97,7 +97,7 @@ def infoDefault(self): def newautoinfofieldname(self,PART(option : DOC , BODY , PAD , SKETCH): ###you can use DOC - PART - BODY - PAD - SKETCH - auto_info_field = self.infoKeysUser.get('newautoinfofieldname').get('userData') + auto_info_field = infoKeysUser.get('newautoinfofieldname').get('userData') auto_info_fill = newautoinfofield information try: ### if the command comes from makeBom write autoinfo directly on Part @@ -118,7 +118,7 @@ def newautoinfofieldname(self,PART(option : DOC , BODY , PAD , SKETCH): def ShapeLength(self,PART,SKETCH): ###you can use DOC - PART - BODY - PAD - SKETCH - auto_info_field = self.infoKeysUser.get('ShapeLength').get('userData') + auto_info_field = infoKeysUser.get('ShapeLength').get('userData') try : auto_info_fill = SKETCH.Shape.Length except AttributeError: @@ -141,7 +141,7 @@ def ShapeLength(self,PART,SKETCH): def PadLength(self,PART,PAD): ###you can use DOC - PART - BODY - PAD - SKETCH - auto_info_field = self.infoKeysUser.get('PadLength').get('userData') + auto_info_field = infoKeysUser.get('PadLength').get('userData') try : auto_info_fill = PAD.Length except AttributeError: @@ -164,7 +164,7 @@ def PadLength(self,PART,PAD): def LabelDoc(self,PART,DOC): - docLabel = self.infoKeysUser.get('LabelDoc').get('userData') + docLabel = infoKeysUser.get('LabelDoc').get('userData') try: ### if the command comes from makeBom write autoinfo directly on Part self.TypeId @@ -181,7 +181,7 @@ def LabelDoc(self,PART,DOC): pass def LabelPart(self,PART): - partLabel = self.infoKeysUser.get('LabelPart').get('userData') + partLabel = infoKeysUser.get('LabelPart').get('userData') try: ### if the command comes from makeBom write autoinfo directly on Part self.TypeId From 5392dcb93504679da188a49841213bcc76357052 Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Tue, 5 Oct 2021 18:28:15 +0200 Subject: [PATCH 12/17] make possible to use non-ascii character in info field add writeXml and decodeXml --- infoPartCmd.py | 19 ++++++++++++++++--- makeBomCmd.py | 5 ++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/infoPartCmd.py b/infoPartCmd.py index 7e1b35c5..29514f9e 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -47,6 +47,18 @@ file.close() """ +""" + +-----------------------------------------------+ + | The Help Tools | + +-----------------------------------------------+ +""" +def writeXml(text): + text=text.encode('unicode_escape').decode().replace('\\','_x_m_l_') + return text + +def decodeXml(text): + text=text.replace('_x_m_l_','\\').encode().decode('unicode_escape') + return text """ +-----------------------------------------------+ @@ -208,7 +220,7 @@ def drawUI(self): propValue = QtGui.QLineEdit() propValue.setText( prop[1] ) checkLayout.addWidget(propValue) - self.formLayout.addRow(QtGui.QLabel(prop[0]),checkLayout) + self.formLayout.addRow(QtGui.QLabel(decodeXml(prop[0])),checkLayout) self.infos.append(propValue) self.mainLayout.addLayout(self.formLayout) @@ -296,7 +308,8 @@ def accept(self): # creation of dict() for user config file config=dict() for prop in self.confTemplate: - config.setdefault(prop,{'userData':self.infos[i].text().replace(" ", "_"),'active':self.checker[i].isChecked()}) + uData=writeXml(self.infos[i].text()) + config.setdefault(prop,{'userData':uData.replace(" ", "_"),'active':self.checker[i].isChecked()}) i+=1 # write user config file file = open(ConfUserFilejson, 'w') @@ -450,7 +463,7 @@ def drawConfUI(self): i=1 for prop in self.confTemplate: propValue = QtGui.QLineEdit() - propValue.setText( self.confTemplate.get(prop).get('userData') ) + propValue.setText( decodeXml(self.confTemplate.get(prop).get('userData')) ) self.gridLayout.addWidget(propValue,i,1) self.infos.append(propValue) i+=1 diff --git a/makeBomCmd.py b/makeBomCmd.py index cfa2ad61..8daadcac 100644 --- a/makeBomCmd.py +++ b/makeBomCmd.py @@ -174,7 +174,10 @@ def inSpreadsheet(self): # to write line in spreadsheet def wrow(drow: [str], row: int): for i, d in enumerate(drow): - spreadsheet.set(str(chr(ord('a') + i)).upper()+str(row+1), str(d)) + if row==0: + spreadsheet.set(str(chr(ord('a') + i)).upper()+str(row+1),infoPartCmd.decodeXml(str(d))) + else : + spreadsheet.set(str(chr(ord('a') + i)).upper()+str(row+1),str(d)) # to make list of values of dict() plist data = list(plist.values()) # to write first line with keys From 9203236ada928b487a976376e2c486bac875129b Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Tue, 5 Oct 2021 18:59:56 +0200 Subject: [PATCH 13/17] improvement of delete field fonction --- infoPartCmd.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/infoPartCmd.py b/infoPartCmd.py index 29514f9e..7fb6c4bf 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -374,16 +374,7 @@ def addNewField(self,newref,newField): # Define the deleteField action def deleteField(self): # delete all ref line and infos.text() - delField = self.suppCombo.currentText() - # door for not delete Asm4 autofield - for prop in self.confTemplate: - if self.confTemplate.get(prop).get('userData') == delField: - if prop[0:3] != 'man' : - mb = QtGui.QMessageBox() - mb.setText(" You can not DELETE \n Asm4 Auto-infoField \n But you can DISABLE IT") - mb.setWindowTitle("UNABLE TO DELETE") - mb.exec_() - return + delField = writeXml(self.suppCombo.currentText()) i=0 for prop in self.confTemplate: if self.confTemplate.get(prop).get('userData') == delField: @@ -393,7 +384,7 @@ def deleteField(self): self.infos.remove(self.infos[i]) self.checker[i].deleteLater() self.checker.remove(self.checker[i]) - self.suppCombo.removeItem(i) + self.suppCombo.removeItem(i-len(self.infoKeysDefault)) self.refField = str(prop) i+=1 # delete it on confTemplate @@ -475,7 +466,8 @@ def drawConfUI(self): # delete self.suppCombo = QtGui.QComboBox() for prop in self.confTemplate: - self.suppCombo.addItem(self.confTemplate.get(prop).get('userData')) + if prop[0:3] == 'man' : + self.suppCombo.addItem(decodeXml(self.confTemplate.get(prop).get('userData'))) self.gridLayoutButtons.addWidget(self.suppCombo,1,1) # make a third column of QCheckBox for activate or not From 8fb7ad58e0c71cd72926a00e3b9afdfd6e67739a Mon Sep 17 00:00:00 2001 From: FarmingSoul Date: Tue, 5 Oct 2021 19:06:14 +0200 Subject: [PATCH 14/17] delete of a non-ascii protect on add button --- infoPartCmd.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/infoPartCmd.py b/infoPartCmd.py index 7fb6c4bf..9a4ae1ae 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -337,14 +337,6 @@ def addNewManField(self): newref = nameref + str(indexref) # init new user name newField=self.newOne.text() - if newField != newField.encode('ascii', 'replace').decode() : - # message for user - mb = QtGui.QMessageBox() - mb.setText("You can only use\nASCII charactere\nfor your field label\nsorry") - mb.setWindowTitle("WRITTING OF NEW CONFIG") - mb.exec_() - # return - return self.newOne.setText('') self.addNewField(newref,newField) From 979605d026f4cd5d2d4b3e12f2562b0b476c1cab Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Thu, 7 Oct 2021 08:41:35 +0200 Subject: [PATCH 15/17] minor code cleanup --- infoPartCmd.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/infoPartCmd.py b/infoPartCmd.py index e0bf3d14..342409fc 100644 --- a/infoPartCmd.py +++ b/infoPartCmd.py @@ -82,6 +82,7 @@ def Activated(self): """ +-----------------------------------------------+ | The UI and functions in the Task panel | + | edit the part info field contents | +-----------------------------------------------+ """ class infoPartUI(): @@ -206,11 +207,18 @@ def drawUI(self): # Actions self.confFields.clicked.connect(self.editKeys) self.autoFill.clicked.connect(self.infoDefault) - + if self.infoTable[0][1]=='': self.infoDefault() self.addNew() + +""" + +-----------------------------------------------+ + | The UI and functions in the Task panel | + | edit the part info keys | + +-----------------------------------------------+ +""" class infoPartConfUI(): def __init__(self): @@ -237,7 +245,7 @@ def __init__(self): # close def finish(self): Gui.Control.closeDialog() - print('exit config') + print('exit PartInfo config') # standard panel UI buttons def getStandardButtons(self): @@ -245,7 +253,6 @@ def getStandardButtons(self): # Cancel def reject(self): - print("Cancel") self.finish() # OK: we write a new config From 8feb21830712698051a81f25fec9ce180169281b Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Fri, 8 Oct 2021 10:33:16 +0200 Subject: [PATCH 16/17] added "Open File" to insertLink --- insertLinkCmd.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/insertLinkCmd.py b/insertLinkCmd.py index 6689d2e2..6919de02 100644 --- a/insertLinkCmd.py +++ b/insertLinkCmd.py @@ -67,10 +67,10 @@ def Activated(self): self.rootAssembly = None self.origLink = None self.brokenLink = False - self.allParts = [] - self.partsDoc = [] + #self.allParts = [] + #self.partsDoc = [] self.filterPartList.clear() - self.partList.clear() + #self.partList.clear() self.linkNameInput.clear() # if an Asm4 Assembly is present, that's where we put the link @@ -106,6 +106,10 @@ def Activated(self): Asm4.warningBox( 'Please create an Assembly' ) return + # build the list of available parts + self.lookForParts() + + ''' # Search for all App::Parts and PartDesign::Body in all open documents # Also store the document of the part for doc in App.listDocuments().values(): @@ -130,6 +134,7 @@ def Activated(self): newItem.setText( part.Document.Name +"#"+ Asm4.labelName(part) ) newItem.setIcon(part.ViewObject.Icon) self.partList.addItem(newItem) + ''' # if an existing valid App::Link was selected if self.origLink and not self.brokenLink: @@ -156,6 +161,74 @@ def Activated(self): # show the UI self.UI.show() + # Search for all App::Parts and PartDesign::Body in all open documents + # Also store the document of the part + def lookForParts( self, doc=None ): + self.allParts = [] + self.partsDoc = [] + self.partList.clear() + if doc is None: + docList = App.listDocuments().values() + else: + docList = [doc] + for doc in docList: + # don't consider temporary documents + if not doc.Temporary: + for obj in doc.findObjects("App::Part"): + # we don't want to link to itself to the 'Model' object + # other App::Part in the same document are OK + # but only those at top level (not nested inside other containers) + if obj != self.rootAssembly and obj.getParentGeoFeatureGroup() is None: + self.allParts.append( obj ) + self.partsDoc.append( doc ) + for obj in doc.findObjects("PartDesign::Body"): + # but only those at top level (not nested inside other containers) + if obj.getParentGeoFeatureGroup() is None: + self.allParts.append( obj ) + self.partsDoc.append( doc ) + # build the list + for part in self.allParts: + newItem = QtGui.QListWidgetItem() + newItem.setText( part.Document.Name +"#"+ Asm4.labelName(part) ) + newItem.setIcon(part.ViewObject.Icon) + self.partList.addItem(newItem) + + + # from A2+ + def openFile(self): + filename = None + importDoc = None + importDocIsOpen = False + dialog = QtGui.QFileDialog( QtGui.QApplication.activeWindow(), + "Select FreeCAD document to import part from" ) + # set option "DontUseNativeDialog"=True, as native Filedialog shows + # misbehavior on Unbuntu 18.04 LTS. It works case sensitively, what is not wanted... + ''' + if a2plib.getNativeFileManagerUsage(): + dialog.setOption(QtGui.QFileDialog.DontUseNativeDialog, False) + else: + dialog.setOption(QtGui.QFileDialog.DontUseNativeDialog, True) + ''' + dialog.setNameFilter("Supported Formats *.FCStd *.fcstd (*.FCStd *.fcstd);;All files (*.*)") + if dialog.exec_(): + filename = str(dialog.selectedFiles()[0]) + # look only for filenames, not paths, as there are problems on WIN10 (Address-translation??) + requestedFile = os.path.split(filename)[1] + # see whether the file is already open + for d in App.listDocuments().values(): + recentFile = os.path.split(d.FileName)[1] + if requestedFile == recentFile: + importDoc = d # file is already open... + importDocIsOpen = True + break + # if not, open it + if not importDocIsOpen: + if filename.lower().endswith('.fcstd'): + importDoc = App.openDocument(filename) + App.setActiveDocument( self.activeDoc.Name ) + # update the part list + self.lookForParts(importDoc) + return """ @@ -279,6 +352,8 @@ def drawUI(self): self.linkNameInput = QtGui.QLineEdit(self.UI) # Cancel button self.cancelButton = QtGui.QPushButton('Cancel', self.UI) + # Cancel button + self.openFileButton = QtGui.QPushButton('Open file', self.UI) # Insert Link button self.insertButton = QtGui.QPushButton('Insert', self.UI) self.insertButton.setDefault(True) @@ -295,12 +370,15 @@ def drawUI(self): self.buttonsLayout = QtGui.QHBoxLayout() self.buttonsLayout.addWidget(self.cancelButton) self.buttonsLayout.addStretch() + self.buttonsLayout.addWidget(self.openFileButton) + self.buttonsLayout.addStretch() self.buttonsLayout.addWidget(self.insertButton) self.mainLayout.addLayout(self.buttonsLayout) self.UI.setLayout(self.mainLayout) # Actions self.cancelButton.clicked.connect(self.onCancel) + self.openFileButton.clicked.connect(self.openFile) self.insertButton.clicked.connect(self.onCreateLink) self.partList.itemClicked.connect( self.onItemClicked) self.filterPartList.textChanged.connect(self.onFilterChange) From 64e55412fa79c9bb1493f6bc3a0739d09b859496 Mon Sep 17 00:00:00 2001 From: Zoltan Hubert Date: Fri, 8 Oct 2021 10:34:26 +0200 Subject: [PATCH 17/17] v0.11.2 --- README.md | 6 +++++- VERSION | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c3a6744a..caaabc1e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # FreeCAD Assembly 4 workbench -Current version 0.11.1 +Current version 0.11.2 @@ -52,6 +52,10 @@ You can get more information in the [user instructions](INSTRUCTIONS.md), the [t ## Release notes +* 2021.10.08 (**0.11.2**) : +added "Open File" to insertLink +BOM and infoPart improvements + * 2021.10.01 (**0.11.1**) : reverted Asm4.QUnitSpinBox() to QtGui.QDoubleSpinBox() because of incompatibilites with some locale (',' comma decimal separators) diff --git a/VERSION b/VERSION index 21e4e39d..ccb9063d 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ Assembly 4 Workbench for FreeCAD -v0.11.1 +v0.11.2