PyQt5で作るSTEPファイルのパーツの名前を変更するツール

2021/04/27 categories:TOOL| tags:TOOL|PyQt5|Python|STEP|QTreeView|

STEPファイルの構造

STEPファイルはテキストデータのようで、テキストエディタで開いてみると以下のような構造になっています。

ISO-10303-21;
HEADER;
...
ENDSEC;
DATA;
...
ENDSEC;
END-ISO-10303-21;

ヘッダ部はHEADER; … ENDSEC;に記載されていて、データ部はDATA; … ENDSEC;に記載されています。STEPデータ内の部品名などはデータ部に含まれているようです。

STEPファイル内の部品名

CADで部品名に特徴的な名前を付けたモデルを作成して、STEPファイルにエクスポートし、そのSTEPファイルをテキストエディタで開いて文字列の検索をすると以下のように、部品名と思われる部分が見つかります。

DATA;

...

#393=PRODUCT('DDDDDDD','DDDDDDD',' ',(#402));
#394=PRODUCT('CCCCCCC','CCCCCCC',' ',(#403));
#395=PRODUCT('BBBBBB','BBBBBB',' ',(#404));
#396=PRODUCT('AAAAAAA','AAAAAAA',' ',(#405));

...

ENDSEC;

このPRODUCTという項目から’AAAAAAA’などの部品名を取得するプログラムを作成します。

STEPファイルからデータ部を取得

with open(filePath, mode='r') as f:
    text = f.read()

上記のコードで、STEPデータをテキストファイルとして開いて、textに文字列を格納します。

splited = text.split('DATA;\n')
data = splited[1]

格納した文字列を’DATA;\n’で分割して、分割した2番目の要素がデータ部のテキストデータとなり、dataにその文字列を格納します。

returnData = {}

for i in data.split(';\n'):
    line = i.strip().replace('\n', '')
    splitIndex = line.find('=')
    lineNumber = line[:splitIndex].strip()
    lineText = line[splitIndex + 1:].strip()

    dataType = lineText[ : lineText.find('(') ].strip()

    if not dataType == '':
        dataInner = lineText[ lineText.find('(') + 1 : lineText.rfind(')') ].strip()
        datas = [ i.strip().replace("'", '') for i in dataInner.split(',') ]
        if dataType == 'PRODUCT':
            self.products[lineNumber] = [ dataType, datas ]

    returnData[lineNumber] = [ dataType, datas ]

return returnData

データは1行ごとにセミコロンで区切られていて、#番号=名前(データ);のような構造で書かれているということが分かるので、データに対してそれぞれ以下のように処理を行います。

動作の様子

ソースコード

import sys
from pathlib import Path
from PyQt5 import QtWidgets, QtCore, QtGui

class StepFileRenamer(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(StepFileRenamer, self).__init__(parent)
        self.resize(800, 400)
        self.filePath = None

        self.model = QtGui.QStandardItemModel(self)
        self.view = QtWidgets.QTableView(self)
        self.view.setModel(self.model)

        self.setCentralWidget( QtWidgets.QWidget(self) )
        self.centralWidget().setLayout( QtWidgets.QVBoxLayout() )
        self.centralWidget().layout().addWidget(self.view)

        self.toolBar = QtWidgets.QToolBar()
        self.toolBar.addAction('Open', self.open)
        self.toolBar.addAction('Save', self.save)
        self.addToolBar(self.toolBar)

    def open(self):
        filePath, _ = QtWidgets.QFileDialog.getOpenFileName(None, 'Open file', '', 'STEP file(*.stp *.step)')

        if filePath == '':
            return
        
        with open(filePath, mode='r') as f:
            self.step = STEP( f.read() )
            self.filePath = Path(filePath)
        
        self.model.clear()
        self.model.setHorizontalHeaderLabels(['Before', 'After'])
        self.view.setColumnWidth(0, 400)
        self.view.horizontalHeader().setStretchLastSection(True)

        for lineNumber in self.step.products:
            part_name = self.step.products[lineNumber][1][0]
            self.model.appendRow([ QtGui.QStandardItem(part_name), QtGui.QStandardItem(part_name) ])

    def save(self):
        filePath, _ = QtWidgets.QFileDialog.getSaveFileName(None, 'Save file', self.filePath.name, 'STEP file(*.stp )')
        
        if filePath == '':
            return
        
        text = self.step.text
        for row in range( self.model.rowCount() ):
            source, target = self.model.item(row, 0), self.model.item(row, 1)
            if not source == target:
                text = text.replace( source.text(), target.text() )

        with open( Path(filePath).with_suffix('.stp'), mode='w' ) as f:
            f.write(text)

class STEP():

    class Item():
        def __init__(self, name, parent=None):
            self.name = name
            self.parent = parent
            self.children = []

    def __init__(self, text):
        self.text = text
        splited = text.split('DATA;\n')

        self.products = {}
        self.header = self.loadHeader( splited[0].split('HEADER;\n')[1] )
        self.data   = self.loadData(   splited[1] )
        
    def loadHeader(self, headerText):
        return [ i.strip().replace('\n', '') for i in headerText.split(';\n') ]

    def loadData(self, dataText):
        returnData = {}

        for i in dataText.split(';\n'):
            line = i.strip().replace('\n', '')
            splitIndex = line.find('=')
            lineNumber = line[:splitIndex].strip()
            lineText = line[splitIndex + 1:].strip()

            dataType = lineText[ : lineText.find('(') ].strip()

            if not dataType == '':
                dataInner = lineText[ lineText.find('(') + 1 : lineText.rfind(')') ].strip()
                datas = [ i.strip().replace("'", '') for i in dataInner.split(',') ]
                if dataType == 'PRODUCT':
                    self.products[lineNumber] = [ dataType, datas ]

            returnData[lineNumber] = [ dataType, datas ]

        return returnData

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    view = StepFileRenamer()
    view.show()
    app.exec()

Share post

Related Posts

コメント