ich bitte euch mal wieder, falls jemand etwas Zeit übrig hat, meinen Code sehr kritisch anzuschauen und jeden möglich (noch so kleinen) Kritikpunkt zu melden.
Ich wollte einem Bekannten einen Gefallen tun und habe das gleich dazu genutzt, endlich mal ein GUI mit Qt zu erstellen. Vor 2(?) Jahren oder so, habe ich das schon mal versucht, das lief aber nur, weil __deets__ und __blackjack__ starke Nerven hatten. (Danke!) Jetzt habe ich mich noch mal, ohne Vorlage, rangesetzt, die GUI mit dem Creator zusammen geklickt und es ist ein Programm rausgekommen, das genau das macht, was ich will. Kann aber auch Zufall sein, ich bin gespannt.
Es geht darum Teile oder Baugruppen die von SolidWorks kommen in STEP-Dateien zu wandeln oder Zeichnungen von SolidWorks in PDF-Dateien zu wandeln. Es gibt viele Programme die das auch können oder man kann es auch für jede Datei manuell exportieren, wie man es von anderen Programmen kennt. Die Zusatzprogramme kosten viel Geld, können viel mehr(was man vielleicht nicht braucht) und die zweite Variante ist bei mehreren Dateien nervig. Ob und was am sinnvollsten ist, lasse ich mal so stehen, auf jeden Fall habe ich gesehen, das SolidWorks ein API hat und habe das mal umgesetzt.
Den Ablauf stelle ich mir so vor:
Der Benutzer darf alle Dateien im Format "sldprt", "sldasm" und "slddrw" auswählen, von denen er entweder STEP-Dateien oder PDF-Dateien benötigt. Um das so anwenderfreundlich wie möglich zu machen, ist es beim Auswahlprozess total egal, ob auch Dateien ausgewählt werden die nicht in STEP exportiert werden können, auch wenn man nur dieses Format will. Wenn man nach dem Auswählen merkt, das eine Datei zuviel drin ist, lässt sich die durch anwählen und "Entfernen"-Button wieder entfernen.
Es gibt zwei weitere Buttons: "PDF" und "STEP".
Wird auf den einen geklickt, werden die Dateien aus der Liste ausgesucht die in das Format gewandelt werden können und beim anderen gleich.
Ich will, dass die Dateien im gleichen Pfad wie die Ursprungsdatei liegt, allerdings in einem Ordner der nach dem Dateienformat benannt ist.
Ich habe mir überlegt, das es sein könnte, dass der Benutzer die Dateien auswählt, aus welchen Gründen auch immer eine der ausgewählten Dateien umbennen/löscht/verschiebt, daher prüfe ich vor dem exportieren noch, ob die Datei existiert und wenn nicht, starte ich den Export nicht, sondern öffne ein Popup-Fenster mit einer entsprechenden Meldung.
Unsicher bin ich mir bei der Klasse `SolidWorks`, weil eigentlich brauche ich die nicht. Ich habe, da ich nicht dauerhaft Zugang zu dem PC mit SolidWorks habe, erst den Code geschrieben, der nur die Dateien exportiert. Der bestand nur aus Funktionen, aber ich fand das schon irgendwie unübersichtlich, auch wenn es nicht so viele Argumente sind, die rumgereicht wurden. Deswegen jetzt die Klasse. Dann bin ich mir nicht sicher, ob ich die Instanz der Klasse vielleicht nur einmal beim öffnen des Programms hätte erstellen sollen, aber ich war mir nicht sicher wie sich das verhält, wenn man das Programm offen lässt und manuel mit SolidWorks arbeitet.
Habe ich irgendwelche Fehlerquellen übersehen?
Der Code:
Code: Alles auswählen
import sys
from contextlib import contextmanager
from functools import partial
from pathlib import Path
from queue import Empty, Queue
from threading import Thread
import pythoncom
import win32com.client
from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QWidget
from ui_form import Ui_Widget
SUFFIX_TO_OPEN_METHODE = {".sldprt": 1, ".sldasm": 2, ".slddrw": 3}
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_Widget()
self.ui.setupUi(self)
self.chosen_files = set()
self.export_progress = Queue()
self.ui.choose_file.clicked.connect(self.open_file_name_dialog)
self.ui.remove_file.clicked.connect(self.remove_file_from_view)
self.ui.create_pdf.clicked.connect(
partial(self.export_files, [".slddrw"], ".pdf")
)
self.ui.create_step.clicked.connect(
partial(self.export_files, [".sldprt", ".sldasm"], ".step")
)
def on_process(self, percentage, file=Path.home()):
self.export_progress.put((percentage, file))
def show_process_update(self):
try:
percent, progressing_file = self.export_progress.get()
except Empty:
self.ui.progress.setText("Starte SolidWorks")
else:
self.ui.progress_bar.setValue(percent)
self.ui.progress.setText(f"Exporting: {progressing_file.name}")
try:
self.chosen_files.remove(progressing_file)
self.update_file_view()
except KeyError:
pass
if percent == 100:
self.change_button_state(True)
self.ui.progress.setText("Bereit")
return
QTimer.singleShot(10, self.show_process_update)
def open_file_name_dialog(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
file_names, _ = QFileDialog.getOpenFileNames(
self,
"Datei(en) auswählen",
str(Path.home()),
"SolidWorks (*.slddrw *.sldprt *.sldasm)",
options=options,
)
self.chosen_files.update(list(map(Path, file_names)))
self.update_file_view()
def update_file_view(self):
self.ui.user_choice.clear()
self.ui.user_choice.addItems([file.name for file in self.chosen_files])
def remove_file_from_view(self):
to_remove = self.ui.user_choice.currentItem()
if to_remove is None:
return
for file in self.chosen_files:
if to_remove.text() == file.name:
self.chosen_files.remove(file)
break
self.update_file_view()
def change_button_state(self, state):
self.ui.create_step.setEnabled(state)
self.ui.create_pdf.setEnabled(state)
self.ui.remove_file.setEnabled(state)
self.ui.choose_file.setEnabled(state)
def search_missing_file(self):
for file in self.chosen_files:
if not file.exists():
return file
return
def export_files(self, valid_file_formats, type_of_export):
if not self.chosen_files:
return
missing_file = self.search_missing_file()
if missing_file:
self.show_warning(missing_file.name)
return
self.change_button_state(False)
Thread(
target=control_export_process,
args=[
self.chosen_files,
valid_file_formats,
type_of_export,
self.on_process,
],
).start()
self.show_process_update()
@staticmethod
def show_warning(file):
pop_up = QMessageBox()
pop_up.setIcon(QMessageBox.Warning)
pop_up.setText(f'"{file}" wurde nicht gefunden')
pop_up.setWindowTitle("Datei nicht gefunden")
pop_up.setStandardButtons(QMessageBox.Ok)
pop_up.exec()
def control_export_process(
chosen_files, valid_file_formats, type_of_export, on_progress
):
on_progress(0)
solidworks = SoldiWorks()
files_to_export = [
file for file in chosen_files if file.suffix.lower() in valid_file_formats
]
for number, file in enumerate(files_to_export, 1):
on_progress(number // len(files_to_export) * 100, file)
solidworks.file_path = file
destination_path = file.parent / type_of_export[1:].upper()
destination_path.mkdir(exist_ok=True)
solidworks.destination_path = destination_path
solidworks.export_file(type_of_export)
class SoldiWorks:
def __init__(self):
# 2023 is the SolidWorks version.
self.solid_works = win32com.client.Dispatch(
f"SldWorks.Application.{2023 - 2012 + 20}"
)
self.destination_path = None
self.file_path = None
def _activate_doc(self):
return self.solid_works.ActivateDoc3(
self.file_path.name,
False,
2,
win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0),
)
@contextmanager
def open_model(self):
open_methode = SUFFIX_TO_OPEN_METHODE[str(self.file_path.suffix).lower()]
try:
yield self.solid_works.OpenDoc6(
win32com.client.VARIANT(pythoncom.VT_BSTR, self.file_path),
win32com.client.VARIANT(pythoncom.VT_I4, open_methode),
win32com.client.VARIANT(pythoncom.VT_I4, 1),
"",
win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 2),
win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 128),
)
finally:
self._close_model()
def _close_model(self):
return self.solid_works.CloseDoc(str(self.file_path))
@staticmethod
def _save_as(model, export_file_path):
return model.Extension.SaveAs2(
str(export_file_path),
0,
1,
win32com.client.VARIANT(pythoncom.VT_DISPATCH, None),
"",
win32com.client.VARIANT(pythoncom.VT_BOOL, 0),
win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0),
win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0),
)
def export_file(self, export_format):
with self.open_model():
model = self._activate_doc()
export_file_path = (
self.destination_path / f"{self.file_path.stem}{export_format}"
)
return self._save_as(model, export_file_path)
def main():
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
Code: Alles auswählen
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'form.ui'
##
## Created by: Qt User Interface Compiler version 6.6.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QLabel, QListWidget, QListWidgetItem,
QProgressBar, QPushButton, QSizePolicy, QWidget)
class Ui_Widget(object):
def setupUi(self, Widget):
if not Widget.objectName():
Widget.setObjectName(u"Widget")
Widget.resize(800, 600)
self.create_step = QPushButton(Widget)
self.create_step.setObjectName(u"create_step")
self.create_step.setGeometry(QRect(450, 390, 331, 191))
self.create_pdf = QPushButton(Widget)
self.create_pdf.setObjectName(u"create_pdf")
self.create_pdf.setGeometry(QRect(20, 390, 331, 191))
self.choose_file = QPushButton(Widget)
self.choose_file.setObjectName(u"choose_file")
self.choose_file.setGeometry(QRect(540, 290, 211, 51))
self.user_choice = QListWidget(Widget)
self.user_choice.setObjectName(u"user_choice")
self.user_choice.setGeometry(QRect(40, 20, 256, 192))
self.remove_file = QPushButton(Widget)
self.remove_file.setObjectName(u"remove_file")
self.remove_file.setGeometry(QRect(330, 140, 81, 51))
self.progress_bar = QProgressBar(Widget)
self.progress_bar.setObjectName(u"progress_bar")
self.progress_bar.setGeometry(QRect(40, 300, 341, 71))
self.progress_bar.setValue(0)
self.progress = QLabel(Widget)
self.progress.setObjectName(u"progress")
self.progress.setGeometry(QRect(40, 250, 341, 41))
self.retranslateUi(Widget)
QMetaObject.connectSlotsByName(Widget)
# setupUi
def retranslateUi(self, Widget):
Widget.setWindowTitle(QCoreApplication.translate("Widget", u"SW-Helper", None))
self.create_step.setText(QCoreApplication.translate("Widget", u"STEP", None))
self.create_pdf.setText(QCoreApplication.translate("Widget", u"PDF", None))
self.choose_file.setText(QCoreApplication.translate("Widget", u"Datei(en) ausw\u00e4hlen", None))
self.remove_file.setText(QCoreApplication.translate("Widget", u"Entfernen", None))
self.progress.setText(QCoreApplication.translate("Widget", u"Bereit", None))
# retranslateUi
https://help.solidworks.com/2023/Englis ... elcome.htm
Das Ansprechen von SolidWorks habe ich mir hier etwas abgeschaut, weil ich das auch noch nicht ganz verstanden habe:
https://github.com/ThomasNeve/pySldWrap/tree/main
Vielen Dank vorab!
Grüße
Dennis