Erste Version

This commit is contained in:
Stefan Ritter 2023-10-12 14:23:35 +02:00
commit fcf086bdee
11 changed files with 455 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.idea
__pycache__
venv
Dicom Extractor 2023.exe

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar
14 rue de Plaisance, 75014 Paris, France
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

BIN
Resources/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

11
compile.bat Normal file
View File

@ -0,0 +1,11 @@
python -m nuitka^
--onefile^
--disable-console^
--windows-icon-from-ico=Resources\icon.ico^
--include-data-files=Resources\icon.ico=icon.ico^
--plugin-enable=pyqt6^
--include-module=pydicom.encoders.gdcm^
--include-module=pydicom.encoders.pylibjpeg^
--output-filename="Dicom Extractor 2023.exe"^
--remove-output^
main.py

19
drives.py Normal file
View File

@ -0,0 +1,19 @@
from ctypes import windll
from string import ascii_uppercase
class Drives:
def __init__(self):
self.list = self.getDrives()
@staticmethod
def getDrives():
drives = []
bitmask = windll.kernel32.GetLogicalDrives()
for letter in ascii_uppercase:
if bitmask & 1:
drives.append(letter)
bitmask >>= 1
return drives

10
main.py Normal file
View File

@ -0,0 +1,10 @@
from PyQt6.QtWidgets import QApplication
from wmain import wMain
version = "20231012"
if __name__ == "__main__":
app = QApplication([])
app.setStyle("Fusion")
win = wMain(version)
app.exec()

49
registry.py Normal file
View File

@ -0,0 +1,49 @@
import winreg
class Registry:
def __init__(self):
self.regInit()
self.ArchivePath, self.CDRomPath = self.regRead()
# Initialize registry with values if there are none
@staticmethod
def regInit():
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
# Key
try:
key = winreg.OpenKeyEx(reg, r"SOFTWARE\Dicom Extractor 2023", 0, winreg.KEY_ALL_ACCESS)
except FileNotFoundError:
winreg.CreateKey(reg, r"SOFTWARE\Dicom Extractor 2023")
key = winreg.OpenKeyEx(reg, r"SOFTWARE\Dicom Extractor 2023", 0, winreg.KEY_ALL_ACCESS)
# Value ArchivePath
try:
_ = winreg.QueryValueEx(key, "ArchivePath")
except FileNotFoundError:
winreg.SetValueEx(key, "ArchivePath", 0, winreg.REG_SZ, "Init")
# Value CDRomPath
try:
_ = winreg.QueryValueEx(key, "CDRomPath")
except FileNotFoundError:
winreg.SetValueEx(key, "CDRomPath", 0, winreg.REG_SZ, "C")
# Read values
@staticmethod
def regRead():
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
key = winreg.OpenKeyEx(reg, r"SOFTWARE\Dicom Extractor 2023", 0, winreg.KEY_ALL_ACCESS)
archivePath = winreg.QueryValueEx(key, "ArchivePath")
cdromPath = winreg.QueryValueEx(key, "CDRomPath")
return archivePath[0], cdromPath[0]
# Write values
@staticmethod
def regWrite(ArchivePath, CDRomPath):
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
key = winreg.OpenKeyEx(reg, r"SOFTWARE\Dicom Extractor 2023", 0, winreg.KEY_ALL_ACCESS)
winreg.SetValueEx(key, "ArchivePath", 0, winreg.REG_SZ, ArchivePath)
winreg.SetValueEx(key, "CDRomPath", 0, winreg.REG_SZ, CDRomPath)

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
PyQt6
pydicom
nuitka

213
warchive.py Normal file
View File

@ -0,0 +1,213 @@
import os
import glob
import threading
import time
import pydicom
import shutil
import random
from registry import Registry
from PyQt6.QtWidgets import QDialog
from PyQt6.QtWidgets import QLabel
from PyQt6.QtWidgets import QPushButton
from PyQt6.QtWidgets import QStyle
class wArchive(QDialog):
def __init__(self):
super().__init__()
self.setFixedSize(400, 400)
self.setWindowTitle("Archiviere...")
self.setStyleSheet("background-color: white")
pixmap = QStyle.StandardPixmap.SP_DriveNetIcon
icon = self.style().standardIcon(pixmap)
self.setWindowIcon(icon)
column1w = 180
column2s = column1w + 20
column2w = 400 - column2s - 10
self.lLine1 = QLabel("Starte Archivierung:", self)
self.lLine1.setGeometry(10, 10, column1w, 20)
self.tLine1 = QLabel("", self)
self.tLine1.setGeometry(column2s, 10, column2w, 20)
self.lLine2 = QLabel("Pfad zum Archiv:", self)
self.lLine2.setGeometry(10, 30, column1w, 20)
self.tLine2 = QLabel("", self)
self.tLine2.setGeometry(column2s, 30, column2w, 20)
self.lLine3 = QLabel("CD-Rom-Laufwerk:", self)
self.lLine3.setGeometry(10, 50, column1w, 20)
self.tLine3 = QLabel("", self)
self.tLine3.setGeometry(column2s, 50, column2w, 20)
self.lLine4 = QLabel("DICOMDIR-Datei gefunden:", self)
self.lLine4.setGeometry(10, 70, column1w, 20)
self.tLine4 = QLabel("", self)
self.tLine4.setGeometry(column2s, 70, column2w, 20)
self.lLine5 = QLabel("DICOM-Verzeichnis gefunden:", self)
self.lLine5.setGeometry(10, 90, column1w, 20)
self.tLine5 = QLabel("", self)
self.tLine5.setGeometry(column2s, 90, column2w, 20)
self.lLine6 = QLabel("Archiv beschreibbar:", self)
self.lLine6.setGeometry(10, 110, column1w, 20)
self.tLine6 = QLabel("", self)
self.tLine6.setGeometry(column2s, 110, column2w, 20)
self.lLine7 = QLabel("DICOM-Dateien gefunden:", self)
self.lLine7.setGeometry(10, 130, column1w, 20)
self.tLine7 = QLabel("", self)
self.tLine7.setGeometry(column2s, 130, column2w, 20)
self.lLine8 = QLabel("Name:", self)
self.lLine8.setGeometry(10, 150, column1w, 20)
self.tLine8 = QLabel("", self)
self.tLine8.setGeometry(column2s, 150, column2w, 20)
self.lLine9 = QLabel("Geburtsdatum:", self)
self.lLine9.setGeometry(10, 170, column1w, 20)
self.tLine9 = QLabel("", self)
self.tLine9.setGeometry(column2s, 170, column2w, 20)
self.lLine10 = QLabel("Dateien:", self)
self.lLine10.setGeometry(10, 190, column1w, 20)
self.tLine10 = QLabel("", self)
self.tLine10.setGeometry(column2s, 190, column2w, 20)
self.bArchive = QPushButton("Archivieren", self)
self.bArchive.setGeometry(10, 350, 380, 40)
self.bArchive.setEnabled(False)
self.bArchive.clicked.connect(self.copyData)
archive = threading.Thread(target=self.archive)
archive.start()
time.sleep(0.1)
self.exec()
def archive(self):
okay = ""
notokay = ""
self.tLine1.setText(okay)
self.tLine1.setStyleSheet("color: green")
reg = Registry()
self.ArchivePath, self.CDRomPath = reg.regRead()
self.tLine2.setText(self.ArchivePath)
self.tLine3.setText(self.CDRomPath + ":\\")
# Logic
dicomfile = False
dicomdir = False
target_writable = False
has_dicom_files = False
has_pName = False
has_pBirth = False
# Check if DICOMDIR file exists
if os.path.isfile(os.path.join(self.CDRomPath + ":", os.sep, "DICOMDIR")):
dicomfile = True
self.tLine4.setText(okay)
self.tLine4.setStyleSheet("color: green")
else:
self.tLine4.setText(notokay)
self.tLine4.setStyleSheet("color: red")
# Check if DICOM directory exists
if os.path.isdir(os.path.join(self.CDRomPath + ":", os.sep, "DICOM")):
dicomdir = True
self.tLine5.setText(okay)
self.tLine5.setStyleSheet("color: green")
else:
self.tLine5.setText(notokay)
self.tLine5.setStyleSheet("color: red")
# Check if ArchivePath is writable
if os.access(self.ArchivePath, os.W_OK) is True:
target_writable = True
self.tLine6.setText(okay)
self.tLine6.setStyleSheet("color: green")
else:
self.tLine6.setText(notokay)
self.tLine6.setStyleSheet("color: red")
# List of all files under DICOM directory
path = os.path.join(self.CDRomPath + ":", os.sep, "DICOM")
self.files = []
for x in os.walk(path):
for y in glob.glob(os.path.join(x[0], "*")):
if os.path.isfile(y):
self.files.append(y)
# Get PatientsName from first file
self.pName = ""
self.pBirth = ""
try:
dicom = pydicom.read_file(self.files[0])
self.pName = str(dicom.PatientName)
self.pBirth = str(dicom.PatientBirthDate)
has_dicom_files = True
has_pName = True
has_pBirth = True
self.tLine7.setText(okay)
self.tLine7.setStyleSheet("color: green")
except:
self.tLine7.setText(notokay)
self.tLine7.setStyleSheet("color: red")
# Output pName and pBirth
if self.pName and self.pBirth:
self.tLine8.setText(self.pName)
self.tLine8.setStyleSheet("color: green")
self.tLine9.setText(self.pBirth)
self.tLine9.setStyleSheet("color: green")
else:
self.tLine8.setText(notokay)
self.tLine8.setStyleSheet("color: red")
self.tLine9.setText(notokay)
self.tLine9.setStyleSheet("color: red")
# Output file count
self.tLine10.setText(str(len(self.files)))
# Activate bArchive
if dicomfile and dicomdir and target_writable and has_dicom_files and has_pBirth and has_pName:
self.bArchive.setEnabled(True)
def copyData(self):
self.bArchive.setEnabled(False)
source = self.files
# LastName^FirstName^Birth
mainFolder = os.path.join(self.ArchivePath, self.pName + "^" + self.pBirth)
try:
os.mkdir(mainFolder)
except:
pass
# LastName^FirstName^Birth\1234
subFolder = self.randomString(4)
os.mkdir(os.path.join(mainFolder, subFolder))
for file in source:
shutil.copy(file, os.path.join(mainFolder, subFolder))
self.close()
@staticmethod
def randomString(length):
string = ""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
for _ in range(length):
char = random.choice(chars)
string = string + char
return string

80
wconfig.py Normal file
View File

@ -0,0 +1,80 @@
from registry import Registry
from drives import Drives
from PyQt6.QtWidgets import QPushButton
from PyQt6.QtWidgets import QLabel
from PyQt6.QtWidgets import QLineEdit
from PyQt6.QtWidgets import QComboBox
from PyQt6.QtWidgets import QStyle
from PyQt6.QtWidgets import QDialog
from PyQt6 import QtCore
class wConfig(QDialog):
def __init__(self):
super().__init__()
self.reg = Registry()
self.drives = Drives()
self.setWindowTitle("Konfiguration")
self.setFixedSize(300, 120)
pixmap = QStyle.StandardPixmap.SP_DriveNetIcon
icon = self.style().standardIcon(pixmap)
self.setWindowIcon(icon)
# Window should stay on top and only close button is visible
self.setWindowFlags(QtCore.Qt.WindowType.WindowStaysOnTopHint | QtCore.Qt.WindowType.WindowCloseButtonHint)
# ArchivePath label and input field
self.lArchivePath = QLabel("Pfad zum Archiv:", self)
self.lArchivePath.setGeometry(10, 10, 100, 20)
self.leArchivePath = QLineEdit(self)
self.leArchivePath.setText(self.reg.ArchivePath)
self.leArchivePath.setGeometry(155, 10, 135, 20)
# CDRomPath label and drop down menu
self.lCDRomPath = QLabel("CD-Rom-Laufwerk:", self)
self.lCDRomPath.setGeometry(10, 40, 100, 20)
self.ddCDRomPath = QComboBox(self)
self.ddCDRomPath.addItems(self.drives.list)
self.ddCDRomPath.setCurrentText(self.reg.CDRomPath)
self.ddCDRomPath.setGeometry(155, 40, 135, 20)
# Apply
self.bApply = QPushButton("Übernehmen", self)
self.bApply.setGeometry(10, 70, 135, 40)
self.bApply.clicked.connect(self.bApplyFunction)
# Cancel / Close
self.bClose = QPushButton("Schließen", self)
self.bClose.setGeometry(155, 70, 135, 40)
self.bClose.clicked.connect(self.bCloseFunction)
self.exec()
def bApplyFunction(self):
# Get text from ArchivePath input field
ArchivePath = self.leArchivePath.text()
# Remove all backslashes from the end of the string
while ArchivePath[-1] == "\\":
ArchivePath = ArchivePath[:-1]
# Get selected item from CDRomPath drop down menu
CDRomPath = self.ddCDRomPath.currentText()
# Set both variables in the running app
self.reg.ArchivePath = ArchivePath
self.reg.CDRomPath = CDRomPath
# Write both variables to the registry
self.reg.regWrite(ArchivePath, CDRomPath)
def bCloseFunction(self):
self.close()
def closeEvent(self, event):
self.close()

53
wmain.py Normal file
View File

@ -0,0 +1,53 @@
from warchive import wArchive
from wconfig import wConfig
from PyQt6.QtWidgets import QMainWindow
from PyQt6.QtWidgets import QPushButton
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QLabel
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import Qt
class wMain(QMainWindow):
def __init__(self, version):
super().__init__()
self.setWindowTitle("DICOM Extractor 2023")
self.setFixedSize(400, 180)
self.setWindowIcon(QIcon("Resources\\icon.ico"))
# Archive button
self.bArchive = QPushButton("Archivieren", self)
self.bArchive.setGeometry(10, 10, 380, 100)
self.bArchive.clicked.connect(self.bArchiveFunction)
# Config button
self.bConfig = QPushButton("Konfiguration", self)
self.bConfig.setGeometry(10, 120, 185, 40)
self.bConfig.clicked.connect(self.bConfigFunction)
# Quit button
self.bQuit = QPushButton("Beenden", self)
self.bQuit.setGeometry(205, 120, 185, 40)
self.bQuit.clicked.connect(self.bQuitFunction)
# Version
self.lVersion = QLabel("Version " + version, self)
self.lVersion.setGeometry(10, 162, 380, 20)
self.lVersion.setAlignment(Qt.AlignmentFlag.AlignHCenter)
self.setStyleSheet("QLabel{ color: grey; font-size: 10px; }")
self.show()
def bArchiveFunction(self):
self.wArchive = wArchive()
def bConfigFunction(self):
self.wConfig = wConfig()
@staticmethod
def bQuitFunction():
QApplication.quit()
def closeEvent(self, event):
QApplication.quit()