corrosim 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
corrosim/__init__.py ADDED
@@ -0,0 +1,19 @@
1
+ """
2
+ CorroSim - Professional Corrosion Analysis Platform
3
+
4
+ A comprehensive tool for electrochemical corrosion analysis including:
5
+ - Tafel polarization analysis
6
+ - Lifetime prediction
7
+ - Multi-sample comparison
8
+ - Data import/export
9
+ """
10
+
11
+ __version__ = "1.0.0"
12
+ __author__ = "Your Name"
13
+ __description__ = "Professional Corrosion Analysis Platform"
14
+
15
+ from .tafel_engine import TafelEngine
16
+ from .database import Database
17
+ from .theme import Theme
18
+
19
+ __all__ = ['TafelEngine', 'Database', 'Theme']
corrosim/app.py ADDED
@@ -0,0 +1,206 @@
1
+ """Main Application Window"""
2
+
3
+ from PyQt6.QtWidgets import *
4
+ from PyQt6.QtCore import *
5
+ from PyQt6.QtGui import *
6
+
7
+ from .theme import Theme, GLOBAL_STYLE, SIDEBAR_STYLE
8
+ from .database import Database
9
+ from .tabs import ImportTab, TafelTab, PredictionTab, ComparisonTab
10
+
11
+
12
+ class MainWindow(QMainWindow):
13
+ """Main application window for CorroSim"""
14
+
15
+ def __init__(self):
16
+ super().__init__()
17
+ self.setWindowTitle("CorroSim Analysis Platform")
18
+ self.setMinimumSize(1200, 800)
19
+ self.resize(1400, 900)
20
+
21
+ # Initialize components
22
+ self.db = Database()
23
+ self.nav_buttons = []
24
+
25
+ self.setStyleSheet(GLOBAL_STYLE)
26
+ self._setup_ui()
27
+ self._setup_menu()
28
+ self.statusBar().showMessage("Ready - Import data to begin")
29
+
30
+ def _setup_ui(self):
31
+ """Setup the user interface"""
32
+ central = QWidget()
33
+ self.setCentralWidget(central)
34
+ main_layout = QHBoxLayout(central)
35
+ main_layout.setContentsMargins(0, 0, 0, 0)
36
+ main_layout.setSpacing(0)
37
+
38
+ self._create_sidebar()
39
+ self._create_tabs()
40
+
41
+ main_layout.addWidget(self.sidebar)
42
+ main_layout.addWidget(self.tabs, 1)
43
+
44
+ self.switch_tab(0)
45
+
46
+ def _create_sidebar(self):
47
+ """Create navigation sidebar"""
48
+ self.sidebar = QWidget()
49
+ self.sidebar.setObjectName("sidebar")
50
+ self.sidebar.setFixedWidth(200)
51
+ self.sidebar.setStyleSheet(SIDEBAR_STYLE)
52
+
53
+ layout = QVBoxLayout(self.sidebar)
54
+ layout.setContentsMargins(12, 20, 12, 20)
55
+ layout.setSpacing(6)
56
+
57
+ # Logo
58
+ logo = QLabel("⚡ CorroSim")
59
+ logo.setStyleSheet("font-size: 18px; font-weight: 700; color: white; background: transparent;")
60
+ layout.addWidget(logo)
61
+ layout.addSpacing(20)
62
+
63
+ # Navigation label
64
+ nav_label = QLabel("NAVIGATION")
65
+ nav_label.setStyleSheet("color: #94A3B8; font-size: 10px; font-weight: 600; background: transparent;")
66
+ layout.addWidget(nav_label)
67
+
68
+ # Navigation buttons
69
+ tabs = [
70
+ ("📁", "Import", 0),
71
+ ("⚡", "Tafel", 1),
72
+ ("🔮", "Prediction", 2),
73
+ ("📊", "Compare", 3)
74
+ ]
75
+
76
+ for icon, text, idx in tabs:
77
+ btn = QPushButton(f" {icon} {text}")
78
+ btn.setProperty("active", "false")
79
+ btn.clicked.connect(lambda checked, i=idx: self.switch_tab(i))
80
+ self.nav_buttons.append(btn)
81
+ layout.addWidget(btn)
82
+
83
+ layout.addStretch()
84
+
85
+ # Status indicator
86
+ sep = QFrame()
87
+ sep.setFrameShape(QFrame.Shape.HLine)
88
+ sep.setStyleSheet("background-color: #334155; max-height: 1px;")
89
+ layout.addWidget(sep)
90
+
91
+ status = QLabel("● Database Connected")
92
+ status.setStyleSheet("color: #10B981; font-size: 10px; background: transparent; padding: 8px;")
93
+ layout.addWidget(status)
94
+
95
+ def _create_tabs(self):
96
+ """Create and setup all tabs"""
97
+ self.tabs = QTabWidget()
98
+ self.tabs.tabBar().setVisible(False)
99
+
100
+ # Create tab widgets
101
+ self.import_widget = QWidget()
102
+ self.tafel_widget = QWidget()
103
+ self.prediction_widget = QWidget()
104
+ self.comparison_widget = QWidget()
105
+
106
+ self.tabs.addTab(self.import_widget, "Import")
107
+ self.tabs.addTab(self.tafel_widget, "Tafel")
108
+ self.tabs.addTab(self.prediction_widget, "Prediction")
109
+ self.tabs.addTab(self.comparison_widget, "Compare")
110
+
111
+ # Setup Import Tab
112
+ self.import_tab = ImportTab()
113
+ self.import_tab.setup(
114
+ parent=self.import_widget,
115
+ db=self.db,
116
+ switch_tab_callback=self.switch_tab,
117
+ load_tafel_callback=self._load_tafel_data
118
+ )
119
+
120
+ # Setup Tafel Tab
121
+ self.tafel_tab = TafelTab()
122
+ self.tafel_tab.setup(
123
+ parent=self.tafel_widget,
124
+ db=self.db
125
+ )
126
+
127
+ # Setup Prediction Tab
128
+ self.prediction_tab = PredictionTab()
129
+ self.prediction_tab.setup(
130
+ parent=self.prediction_widget
131
+ )
132
+
133
+ # Setup Comparison Tab
134
+ self.comparison_tab = ComparisonTab()
135
+ self.comparison_tab.setup(
136
+ parent=self.comparison_widget,
137
+ db=self.db
138
+ )
139
+
140
+ def switch_tab(self, index):
141
+ """Switch active tab"""
142
+ self.tabs.setCurrentIndex(index)
143
+ for i, btn in enumerate(self.nav_buttons):
144
+ btn.setProperty("active", "true" if i == index else "false")
145
+ btn.style().unpolish(btn)
146
+ btn.style().polish(btn)
147
+
148
+ def _load_tafel_data(self):
149
+ """Load latest data into Tafel tab"""
150
+ if hasattr(self, 'tafel_tab'):
151
+ self.tafel_tab._load_data()
152
+
153
+ def _setup_menu(self):
154
+ """Setup menu bar"""
155
+ menubar = self.menuBar()
156
+ menubar.setStyleSheet(f"""
157
+ QMenuBar {{
158
+ background-color: {Theme.BG_WHITE};
159
+ border-bottom: 1px solid {Theme.BORDER};
160
+ padding: 2px;
161
+ }}
162
+ QMenuBar::item {{
163
+ padding: 6px 12px;
164
+ border-radius: 4px;
165
+ }}
166
+ QMenuBar::item:selected {{
167
+ background-color: {Theme.PRIMARY_LIGHT};
168
+ }}
169
+ """)
170
+
171
+ file_menu = menubar.addMenu("File")
172
+
173
+ exit_action = QAction("Exit", self)
174
+ exit_action.setShortcut("Ctrl+Q")
175
+ exit_action.triggered.connect(self.close)
176
+ file_menu.addAction(exit_action)
177
+
178
+ help_menu = menubar.addMenu("Help")
179
+
180
+ about_action = QAction("About", self)
181
+ about_action.triggered.connect(self._show_about)
182
+ help_menu.addAction(about_action)
183
+
184
+ def _show_about(self):
185
+ """Show about dialog"""
186
+ QMessageBox.about(
187
+ self,
188
+ "About CorroSim",
189
+ "<h3>⚡ CorroSim Analysis Platform</h3>"
190
+ "<p>Version 1.0.0</p>"
191
+ "<p>Professional Corrosion Analysis Tool</p>"
192
+ "<hr>"
193
+ "<p><b>Features:</b></p>"
194
+ "<ul>"
195
+ "<li>Tafel Polarization Analysis</li>"
196
+ "<li>Lifetime Prediction Models</li>"
197
+ "<li>Multi-Sample Comparison</li>"
198
+ "<li>Data Export (PNG, PDF, Excel)</li>"
199
+ "</ul>"
200
+ "<p>© 2026 CorroSim By NanoStack-Lab</p>"
201
+ )
202
+
203
+ def closeEvent(self, event):
204
+ """Handle window close event"""
205
+ self.db.close()
206
+ event.accept()
corrosim/database.py ADDED
@@ -0,0 +1,85 @@
1
+ """Database module for CorroSim"""
2
+
3
+ import sqlite3
4
+ import uuid
5
+ import datetime
6
+ import pandas as pd
7
+ from io import StringIO
8
+
9
+
10
+ class Database:
11
+ """SQLite database for storing corrosion analysis samples"""
12
+
13
+ def __init__(self, db_path='corrosion.db'):
14
+ self.db_path = db_path
15
+ self.conn = sqlite3.connect(db_path)
16
+ self.cursor = self.conn.cursor()
17
+ self._create_tables()
18
+
19
+ def _create_tables(self):
20
+ """Create database tables if they don't exist"""
21
+ self.cursor.execute('''
22
+ CREATE TABLE IF NOT EXISTS samples (
23
+ id TEXT PRIMARY KEY,
24
+ name TEXT,
25
+ test_type TEXT,
26
+ date TEXT,
27
+ data TEXT,
28
+ ecorr REAL,
29
+ icorr REAL,
30
+ cr REAL,
31
+ ba REAL,
32
+ bc REAL
33
+ )
34
+ ''')
35
+ self.conn.commit()
36
+
37
+ def save(self, name, test_type, data):
38
+ """Save a new sample to the database"""
39
+ sid = str(uuid.uuid4())[:8]
40
+ data_csv = data.to_csv(index=False)
41
+ date = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
42
+
43
+ self.cursor.execute(
44
+ "INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
45
+ (sid, name, test_type, date, data_csv, None, None, None, None, None)
46
+ )
47
+ self.conn.commit()
48
+ return sid
49
+
50
+ def update(self, sid, ecorr, icorr, cr, ba, bc):
51
+ """Update analysis results for a sample"""
52
+ self.cursor.execute(
53
+ "UPDATE samples SET ecorr=?, icorr=?, cr=?, ba=?, bc=? WHERE id=?",
54
+ (ecorr, icorr, cr, ba, bc, sid)
55
+ )
56
+ self.conn.commit()
57
+
58
+ def get_all(self):
59
+ """Get all samples ordered by date"""
60
+ self.cursor.execute("SELECT * FROM samples ORDER BY date DESC")
61
+ return self.cursor.fetchall()
62
+
63
+ def get_latest(self):
64
+ """Get the most recent sample"""
65
+ self.cursor.execute("SELECT * FROM samples ORDER BY date DESC LIMIT 1")
66
+ return self.cursor.fetchone()
67
+
68
+ def get_by_id(self, sid):
69
+ """Get a specific sample by ID"""
70
+ self.cursor.execute("SELECT * FROM samples WHERE id=?", (sid,))
71
+ return self.cursor.fetchone()
72
+
73
+ def delete(self, sid):
74
+ """Delete a sample by ID"""
75
+ self.cursor.execute("DELETE FROM samples WHERE id=?", (sid,))
76
+ self.conn.commit()
77
+
78
+ def get_count(self):
79
+ """Get total number of samples"""
80
+ self.cursor.execute("SELECT COUNT(*) FROM samples")
81
+ return self.cursor.fetchone()[0]
82
+
83
+ def close(self):
84
+ """Close database connection"""
85
+ self.conn.close()
corrosim/main.py ADDED
@@ -0,0 +1,37 @@
1
+ """Main entry point for CorroSim"""
2
+
3
+ import sys
4
+ import time
5
+ from PyQt6.QtWidgets import QApplication
6
+
7
+ from .splash_screen import SplashScreen
8
+ from .app import MainWindow
9
+
10
+
11
+ def main():
12
+ """Application entry point"""
13
+ app = QApplication(sys.argv)
14
+ app.setStyle('Fusion')
15
+
16
+ # Show splash screen with loading sequence
17
+ messages = [
18
+ "Initializing database...",
19
+ "Loading analysis modules...",
20
+ "Setting up user interface...",
21
+ "Preparing Tafel engine...",
22
+ "Starting application..."
23
+ ]
24
+ delays = [0.3, 0.3, 0.3, 0.3, 0.2]
25
+
26
+ splash = SplashScreen.show_loading_sequence(messages, delays)
27
+
28
+ # Create and show main window
29
+ window = MainWindow()
30
+ splash.close()
31
+ window.show()
32
+
33
+ sys.exit(app.exec())
34
+
35
+
36
+ if __name__ == "__main__":
37
+ main()
@@ -0,0 +1,234 @@
1
+ """Professional Splash Screen for CorroSim"""
2
+
3
+ import time
4
+ from PyQt6.QtWidgets import QSplashScreen, QApplication
5
+ from PyQt6.QtCore import Qt, QRect, QRectF
6
+ from PyQt6.QtGui import QPixmap, QPainter, QColor, QFont, QLinearGradient, QPen, QBrush
7
+
8
+
9
+ class SplashScreen(QSplashScreen):
10
+ """Professional splash screen with gradient design and progress bar"""
11
+
12
+ # Configuration
13
+ WIDTH = 600
14
+ HEIGHT = 400
15
+ BRAND_NAME = "CorroSim"
16
+ TAGLINE = "Advanced Corrosion Analysis Platform"
17
+ VERSION = "Version 1.0.0"
18
+ COPYRIGHT = "© 2026 CorroSim • NanoStack-Lab"
19
+ WEBSITE = "github.com/khadev/corrosim"
20
+
21
+ def __init__(self):
22
+ pixmap = self._create_pixmap(0, "Initializing...")
23
+ super().__init__(pixmap)
24
+ self.setWindowFlags(
25
+ Qt.WindowType.WindowStaysOnTopHint |
26
+ Qt.WindowType.FramelessWindowHint |
27
+ Qt.WindowType.SplashScreen
28
+ )
29
+ self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False)
30
+ self._center_on_screen()
31
+ self.show()
32
+ QApplication.processEvents()
33
+
34
+ def _draw_gradient_background(self, painter, pixmap):
35
+ """Draw professional gradient background"""
36
+ gradient = QLinearGradient(0, 0, self.WIDTH, self.HEIGHT)
37
+ gradient.setColorAt(0.0, QColor("#0F172A")) # Dark navy top
38
+ gradient.setColorAt(0.4, QColor("#1E293B")) # Slate middle
39
+ gradient.setColorAt(0.7, QColor("#1E3A5F")) # Blue-tinted
40
+ gradient.setColorAt(1.0, QColor("#0F172A")) # Dark navy bottom
41
+
42
+ painter.setBrush(QBrush(gradient))
43
+ painter.setPen(Qt.PenStyle.NoPen)
44
+ painter.drawRect(pixmap.rect())
45
+
46
+ def _draw_decorative_circles(self, painter):
47
+ """Draw decorative background circles"""
48
+ # Large subtle circle top-right
49
+ painter.setBrush(QColor(37, 99, 235, 15)) # Theme blue, very transparent
50
+ painter.setPen(Qt.PenStyle.NoPen)
51
+ painter.drawEllipse(QRect(self.WIDTH - 150, -80, 300, 300))
52
+
53
+ # Small circle bottom-left
54
+ painter.setBrush(QColor(5, 150, 105, 10)) # Theme green, very transparent
55
+ painter.drawEllipse(QRect(-60, self.HEIGHT - 120, 200, 200))
56
+
57
+ # Medium circle center accent
58
+ painter.setBrush(QColor(124, 58, 237, 8)) # Purple accent
59
+ painter.drawEllipse(QRect(self.WIDTH//2 - 100, 50, 250, 250))
60
+
61
+ def _draw_logo_icon(self, painter):
62
+ """Draw modern logo icon"""
63
+ # Outer circle
64
+ painter.setBrush(QColor(37, 99, 235, 30))
65
+ painter.setPen(QPen(QColor("#2563EB"), 2))
66
+ painter.drawEllipse(QRect(self.WIDTH//2 - 35, 70, 70, 70))
67
+
68
+ # Lightning bolt symbol
69
+ painter.setPen(Qt.PenStyle.NoPen)
70
+ painter.setBrush(QColor("#3B82F6"))
71
+
72
+ # Draw a simple lightning bolt using polygons
73
+ bolt = [
74
+ (self.WIDTH//2 + 8, 78),
75
+ (self.WIDTH//2 - 8, 102),
76
+ (self.WIDTH//2 + 2, 102),
77
+ (self.WIDTH//2 - 5, 120),
78
+ (self.WIDTH//2 + 12, 96),
79
+ (self.WIDTH//2, 96),
80
+ (self.WIDTH//2 + 10, 78)
81
+ ]
82
+
83
+ from PyQt6.QtGui import QPolygon
84
+ from PyQt6.QtCore import QPoint
85
+
86
+ points = [QPoint(x, y) for x, y in bolt]
87
+ polygon = QPolygon(points)
88
+ painter.drawPolygon(polygon)
89
+
90
+ def _draw_brand_text(self, painter):
91
+ """Draw brand name and tagline"""
92
+ # Brand name
93
+ painter.setPen(QColor("#F8FAFC"))
94
+ font_title = QFont("Segoe UI", 36, QFont.Weight.Bold)
95
+ font_title.setLetterSpacing(QFont.SpacingType.AbsoluteSpacing, 2)
96
+ painter.setFont(font_title)
97
+ painter.drawText(
98
+ QRect(0, 145, self.WIDTH, 50),
99
+ Qt.AlignmentFlag.AlignCenter,
100
+ self.BRAND_NAME
101
+ )
102
+
103
+ # Tagline
104
+ painter.setPen(QColor("#94A3B8"))
105
+ font_tag = QFont("Segoe UI", 13, QFont.Weight.Normal)
106
+ painter.setFont(font_tag)
107
+ painter.drawText(
108
+ QRect(0, 200, self.WIDTH, 25),
109
+ Qt.AlignmentFlag.AlignCenter,
110
+ self.TAGLINE
111
+ )
112
+
113
+ # Version badge
114
+ painter.setPen(Qt.PenStyle.NoPen)
115
+ painter.setBrush(QColor(37, 99, 235, 40))
116
+ badge_rect = QRect(self.WIDTH//2 - 50, 232, 100, 24)
117
+ painter.drawRoundedRect(QRectF(badge_rect), 12, 12)
118
+
119
+ painter.setPen(QColor("#60A5FA"))
120
+ font_ver = QFont("Segoe UI", 10, QFont.Weight.Medium)
121
+ painter.setFont(font_ver)
122
+ painter.drawText(badge_rect, Qt.AlignmentFlag.AlignCenter, self.VERSION)
123
+
124
+ def _draw_progress_section(self, painter, progress, text):
125
+ """Draw modern progress bar and status text"""
126
+ # Section separator line
127
+ painter.setPen(QPen(QColor("#334155"), 1))
128
+ painter.drawLine(80, 285, self.WIDTH - 80, 285)
129
+
130
+ # Status text
131
+ painter.setPen(QColor("#10B981"))
132
+ font_status = QFont("Segoe UI", 10, QFont.Weight.Normal)
133
+ painter.setFont(font_status)
134
+ painter.drawText(
135
+ QRect(0, 290, self.WIDTH, 25),
136
+ Qt.AlignmentFlag.AlignCenter,
137
+ text
138
+ )
139
+
140
+ # Progress bar background
141
+ bar_x = 80
142
+ bar_y = 322
143
+ bar_width = self.WIDTH - 160
144
+ bar_height = 5
145
+
146
+ painter.setPen(Qt.PenStyle.NoPen)
147
+ painter.setBrush(QColor("#1E293B"))
148
+ painter.drawRoundedRect(QRectF(bar_x, bar_y, bar_width, bar_height), 3, 3)
149
+
150
+ # Progress bar fill with gradient
151
+ if progress > 0:
152
+ fill_width = int(bar_width * progress / 100)
153
+ fill_gradient = QLinearGradient(bar_x, 0, bar_x + fill_width, 0)
154
+ fill_gradient.setColorAt(0.0, QColor("#2563EB"))
155
+ fill_gradient.setColorAt(1.0, QColor("#059669"))
156
+
157
+ painter.setBrush(QBrush(fill_gradient))
158
+ painter.drawRoundedRect(QRectF(bar_x, bar_y, fill_width, bar_height), 3, 3)
159
+
160
+ # Glow dot at progress end
161
+ if fill_width > 5:
162
+ painter.setBrush(QColor("#F8FAFC"))
163
+ painter.drawEllipse(QRect(bar_x + fill_width - 5, bar_y - 3, 10, 10))
164
+
165
+ def _draw_footer(self, painter):
166
+ """Draw footer with copyright and website"""
167
+ # Copyright
168
+ painter.setPen(QColor("#64748B"))
169
+ font_footer = QFont("Segoe UI", 9, QFont.Weight.Normal)
170
+ painter.setFont(font_footer)
171
+ painter.drawText(
172
+ QRect(0, self.HEIGHT - 35, self.WIDTH, 20),
173
+ Qt.AlignmentFlag.AlignCenter,
174
+ self.COPYRIGHT
175
+ )
176
+
177
+ # Website
178
+ painter.setPen(QColor("#475569"))
179
+ font_web = QFont("Segoe UI", 8, QFont.Weight.Light)
180
+ painter.setFont(font_web)
181
+ painter.drawText(
182
+ QRect(0, self.HEIGHT - 18, self.WIDTH, 15),
183
+ Qt.AlignmentFlag.AlignCenter,
184
+ self.WEBSITE
185
+ )
186
+
187
+ def _create_pixmap(self, progress, text):
188
+ """Create complete splash screen pixmap"""
189
+ pixmap = QPixmap(self.WIDTH, self.HEIGHT)
190
+
191
+ painter = QPainter(pixmap)
192
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
193
+
194
+ # Draw all layers
195
+ self._draw_gradient_background(painter, pixmap)
196
+ self._draw_decorative_circles(painter)
197
+ self._draw_logo_icon(painter)
198
+ self._draw_brand_text(painter)
199
+ self._draw_progress_section(painter, progress, text)
200
+ self._draw_footer(painter)
201
+
202
+ painter.end()
203
+ return pixmap
204
+
205
+ def _center_on_screen(self):
206
+ """Center splash on screen"""
207
+ screen = QApplication.primaryScreen().geometry()
208
+ x = (screen.width() - self.WIDTH) // 2
209
+ y = (screen.height() - self.HEIGHT) // 2
210
+ self.move(x, y)
211
+
212
+ def update_progress(self, value, text):
213
+ """Update progress bar and status text"""
214
+ pixmap = self._create_pixmap(value, text)
215
+ self.setPixmap(pixmap)
216
+ QApplication.processEvents()
217
+
218
+ @staticmethod
219
+ def show_loading_sequence(messages, delays):
220
+ """
221
+ Show animated loading sequence.
222
+
223
+ Parameters:
224
+ - messages: List of status messages
225
+ - delays: List of delays (seconds) between messages
226
+ """
227
+ splash = SplashScreen()
228
+
229
+ for i, (msg, delay) in enumerate(zip(messages, delays)):
230
+ progress = int((i + 1) / len(messages) * 100)
231
+ splash.update_progress(progress, msg)
232
+ time.sleep(delay)
233
+
234
+ return splash
@@ -0,0 +1,8 @@
1
+ """Tab modules for CorroSim"""
2
+
3
+ from .import_tab import ImportTab
4
+ from .tafel_tab import TafelTab
5
+ from .prediction_tab import PredictionTab
6
+ from .comparison_tab import ComparisonTab
7
+
8
+ __all__ = ['ImportTab', 'TafelTab', 'PredictionTab', 'ComparisonTab']