Delete styling
Browse files- styling/charts.py +0 -282
- styling/enhanced_ui.py +0 -586
- styling/icons.py +0 -609
- styling/theme.py +0 -541
styling/charts.py
DELETED
@@ -1,282 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
مولد الرسوم البيانية لنظام إدارة المناقصات
|
3 |
-
"""
|
4 |
-
|
5 |
-
import os
|
6 |
-
import numpy as np
|
7 |
-
import matplotlib.pyplot as plt
|
8 |
-
import matplotlib as mpl
|
9 |
-
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
10 |
-
import tkinter as tk
|
11 |
-
import customtkinter as ctk
|
12 |
-
|
13 |
-
class ChartGenerator:
|
14 |
-
"""فئة مولد الرسوم البيانية"""
|
15 |
-
|
16 |
-
def __init__(self, theme):
|
17 |
-
"""تهيئة مولد الرسوم البيانية"""
|
18 |
-
self.theme = theme
|
19 |
-
|
20 |
-
# تحديد مسار مجلد الرسوم البيانية
|
21 |
-
self.charts_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "charts")
|
22 |
-
|
23 |
-
# إنشاء مجلد الرسوم البيانية إذا لم يكن موجودًا
|
24 |
-
os.makedirs(self.charts_dir, exist_ok=True)
|
25 |
-
|
26 |
-
# تهيئة نمط الرسوم البيانية
|
27 |
-
self._setup_chart_style()
|
28 |
-
|
29 |
-
def _setup_chart_style(self):
|
30 |
-
"""إعداد نمط الرسوم البيانية"""
|
31 |
-
# تعيين نمط الرسوم البيانية
|
32 |
-
plt.style.use('ggplot')
|
33 |
-
|
34 |
-
# تعيين الخط
|
35 |
-
plt.rcParams['font.family'] = 'sans-serif'
|
36 |
-
plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Liberation Sans', 'Bitstream Vera Sans', 'sans-serif']
|
37 |
-
|
38 |
-
# تعيين حجم الخط
|
39 |
-
plt.rcParams['font.size'] = 10
|
40 |
-
plt.rcParams['axes.titlesize'] = 14
|
41 |
-
plt.rcParams['axes.labelsize'] = 12
|
42 |
-
plt.rcParams['xtick.labelsize'] = 10
|
43 |
-
plt.rcParams['ytick.labelsize'] = 10
|
44 |
-
plt.rcParams['legend.fontsize'] = 10
|
45 |
-
|
46 |
-
# تعيين الألوان
|
47 |
-
if self.theme.current_theme == "light":
|
48 |
-
plt.rcParams['figure.facecolor'] = self.theme.LIGHT_CARD_BG_COLOR
|
49 |
-
plt.rcParams['axes.facecolor'] = self.theme.LIGHT_BG_COLOR
|
50 |
-
plt.rcParams['axes.edgecolor'] = self.theme.LIGHT_BORDER_COLOR
|
51 |
-
plt.rcParams['axes.labelcolor'] = self.theme.LIGHT_FG_COLOR
|
52 |
-
plt.rcParams['xtick.color'] = self.theme.LIGHT_FG_COLOR
|
53 |
-
plt.rcParams['ytick.color'] = self.theme.LIGHT_FG_COLOR
|
54 |
-
plt.rcParams['text.color'] = self.theme.LIGHT_FG_COLOR
|
55 |
-
plt.rcParams['grid.color'] = self.theme.LIGHT_BORDER_COLOR
|
56 |
-
else:
|
57 |
-
plt.rcParams['figure.facecolor'] = self.theme.DARK_CARD_BG_COLOR
|
58 |
-
plt.rcParams['axes.facecolor'] = self.theme.DARK_BG_COLOR
|
59 |
-
plt.rcParams['axes.edgecolor'] = self.theme.DARK_BORDER_COLOR
|
60 |
-
plt.rcParams['axes.labelcolor'] = self.theme.DARK_FG_COLOR
|
61 |
-
plt.rcParams['xtick.color'] = self.theme.DARK_FG_COLOR
|
62 |
-
plt.rcParams['ytick.color'] = self.theme.DARK_FG_COLOR
|
63 |
-
plt.rcParams['text.color'] = self.theme.DARK_FG_COLOR
|
64 |
-
plt.rcParams['grid.color'] = self.theme.DARK_BORDER_COLOR
|
65 |
-
|
66 |
-
def create_bar_chart(self, data, title, xlabel, ylabel):
|
67 |
-
"""إنشاء رسم بياني شريطي"""
|
68 |
-
# إنشاء الشكل والمحاور
|
69 |
-
fig, ax = plt.subplots(figsize=(8, 5), dpi=100)
|
70 |
-
|
71 |
-
# رسم الرسم البياني الشريطي
|
72 |
-
bars = ax.bar(data['labels'], data['values'], color=self.theme.PRIMARY_COLOR[self.theme.current_theme])
|
73 |
-
|
74 |
-
# إضافة القيم فوق الأشرطة
|
75 |
-
for bar in bars:
|
76 |
-
height = bar.get_height()
|
77 |
-
ax.text(bar.get_x() + bar.get_width() / 2., height + 0.1 * max(data['values']),
|
78 |
-
f'{height:,.0f}', ha='center', va='bottom')
|
79 |
-
|
80 |
-
# تعيين العنوان والتسميات
|
81 |
-
ax.set_title(title)
|
82 |
-
ax.set_xlabel(xlabel)
|
83 |
-
ax.set_ylabel(ylabel)
|
84 |
-
|
85 |
-
# تعيين حدود المحور y
|
86 |
-
ax.set_ylim(0, max(data['values']) * 1.2)
|
87 |
-
|
88 |
-
# إضافة الشبكة
|
89 |
-
ax.grid(True, linestyle='--', alpha=0.7)
|
90 |
-
|
91 |
-
# تضييق الشكل
|
92 |
-
fig.tight_layout()
|
93 |
-
|
94 |
-
return fig
|
95 |
-
|
96 |
-
def create_line_chart(self, data, title, xlabel, ylabel):
|
97 |
-
"""إنشاء رسم بياني خطي"""
|
98 |
-
# إنشاء الشكل والمحاور
|
99 |
-
fig, ax = plt.subplots(figsize=(8, 5), dpi=100)
|
100 |
-
|
101 |
-
# رسم الرسم البياني الخطي
|
102 |
-
line = ax.plot(data['labels'], data['values'], marker='o', linestyle='-', linewidth=2,
|
103 |
-
color=self.theme.PRIMARY_COLOR[self.theme.current_theme],
|
104 |
-
markersize=8, markerfacecolor=self.theme.SECONDARY_COLOR[self.theme.current_theme])
|
105 |
-
|
106 |
-
# إضافة القيم فوق النقاط
|
107 |
-
for i, value in enumerate(data['values']):
|
108 |
-
ax.text(i, value + 0.05 * max(data['values']), f'{value:,.0f}', ha='center', va='bottom')
|
109 |
-
|
110 |
-
# تعيين العنوان والتسميات
|
111 |
-
ax.set_title(title)
|
112 |
-
ax.set_xlabel(xlabel)
|
113 |
-
ax.set_ylabel(ylabel)
|
114 |
-
|
115 |
-
# تعيي�� حدود المحور y
|
116 |
-
ax.set_ylim(0, max(data['values']) * 1.2)
|
117 |
-
|
118 |
-
# إضافة الشبكة
|
119 |
-
ax.grid(True, linestyle='--', alpha=0.7)
|
120 |
-
|
121 |
-
# تضييق الشكل
|
122 |
-
fig.tight_layout()
|
123 |
-
|
124 |
-
return fig
|
125 |
-
|
126 |
-
def create_pie_chart(self, data, title):
|
127 |
-
"""إنشاء رسم بياني دائري"""
|
128 |
-
# إنشاء الشكل والمحاور
|
129 |
-
fig, ax = plt.subplots(figsize=(8, 5), dpi=100)
|
130 |
-
|
131 |
-
# تعيين الألوان
|
132 |
-
colors = [
|
133 |
-
self.theme.PRIMARY_COLOR[self.theme.current_theme],
|
134 |
-
self.theme.SECONDARY_COLOR[self.theme.current_theme],
|
135 |
-
self.theme.ACCENT_COLOR[self.theme.current_theme],
|
136 |
-
self.theme.WARNING_COLOR[self.theme.current_theme],
|
137 |
-
self.theme.SUCCESS_COLOR[self.theme.current_theme]
|
138 |
-
]
|
139 |
-
|
140 |
-
# رسم الرسم البياني الدائري
|
141 |
-
wedges, texts, autotexts = ax.pie(
|
142 |
-
data['values'],
|
143 |
-
labels=data['labels'],
|
144 |
-
autopct='%1.1f%%',
|
145 |
-
startangle=90,
|
146 |
-
colors=colors,
|
147 |
-
wedgeprops={'edgecolor': 'white', 'linewidth': 1},
|
148 |
-
textprops={'color': self.theme.get_color('fg_color')}
|
149 |
-
)
|
150 |
-
|
151 |
-
# تعيين خصائص النص
|
152 |
-
for autotext in autotexts:
|
153 |
-
autotext.set_color('white')
|
154 |
-
autotext.set_fontweight('bold')
|
155 |
-
|
156 |
-
# تعيين العنوان
|
157 |
-
ax.set_title(title)
|
158 |
-
|
159 |
-
# جعل الرسم البياني دائريًا
|
160 |
-
ax.axis('equal')
|
161 |
-
|
162 |
-
# تضييق الشكل
|
163 |
-
fig.tight_layout()
|
164 |
-
|
165 |
-
return fig
|
166 |
-
|
167 |
-
def create_stacked_bar_chart(self, data, title, xlabel, ylabel):
|
168 |
-
"""إنشاء رسم بياني شريطي متراكم"""
|
169 |
-
# إنشاء الشكل والمحاور
|
170 |
-
fig, ax = plt.subplots(figsize=(8, 5), dpi=100)
|
171 |
-
|
172 |
-
# تعيين الألوان
|
173 |
-
colors = [
|
174 |
-
self.theme.PRIMARY_COLOR[self.theme.current_theme],
|
175 |
-
self.theme.SECONDARY_COLOR[self.theme.current_theme],
|
176 |
-
self.theme.ACCENT_COLOR[self.theme.current_theme],
|
177 |
-
self.theme.WARNING_COLOR[self.theme.current_theme],
|
178 |
-
self.theme.SUCCESS_COLOR[self.theme.current_theme]
|
179 |
-
]
|
180 |
-
|
181 |
-
# رسم الرسم البياني الشريطي المتراكم
|
182 |
-
bottom = np.zeros(len(data['labels']))
|
183 |
-
for i, category in enumerate(data['categories']):
|
184 |
-
values = data['values'][i]
|
185 |
-
bars = ax.bar(data['labels'], values, bottom=bottom, label=category, color=colors[i % len(colors)])
|
186 |
-
bottom += values
|
187 |
-
|
188 |
-
# تعيين العنوان والتسميات
|
189 |
-
ax.set_title(title)
|
190 |
-
ax.set_xlabel(xlabel)
|
191 |
-
ax.set_ylabel(ylabel)
|
192 |
-
|
193 |
-
# إضافة وسيلة إيضاح
|
194 |
-
ax.legend()
|
195 |
-
|
196 |
-
# إضافة الشبكة
|
197 |
-
ax.grid(True, linestyle='--', alpha=0.7)
|
198 |
-
|
199 |
-
# تضييق الشكل
|
200 |
-
fig.tight_layout()
|
201 |
-
|
202 |
-
return fig
|
203 |
-
|
204 |
-
def create_risk_matrix(self, data, title):
|
205 |
-
"""إنشاء مصفوفة المخاطر"""
|
206 |
-
# إنشاء الشكل والمحاور
|
207 |
-
fig, ax = plt.subplots(figsize=(8, 5), dpi=100)
|
208 |
-
|
209 |
-
# تعيين الألوان
|
210 |
-
colors = {
|
211 |
-
'منخفض': self.theme.SUCCESS_COLOR[self.theme.current_theme],
|
212 |
-
'متوسط': self.theme.WARNING_COLOR[self.theme.current_theme],
|
213 |
-
'عالي': self.theme.ERROR_COLOR[self.theme.current_theme]
|
214 |
-
}
|
215 |
-
|
216 |
-
# تعيين قيم المحاور
|
217 |
-
probability_values = {'منخفض': 1, 'متوسط': 2, 'عالي': 3}
|
218 |
-
impact_values = {'منخفض': 1, 'متوسط': 2, 'عالي': 3}
|
219 |
-
|
220 |
-
# رسم المصفوفة
|
221 |
-
for risk in data['risks']:
|
222 |
-
prob = probability_values[risk['probability']]
|
223 |
-
impact = impact_values[risk['impact']]
|
224 |
-
color = colors[risk['probability']] if prob > impact else colors[risk['impact']]
|
225 |
-
ax.scatter(impact, prob, color=color, s=100, alpha=0.7)
|
226 |
-
ax.annotate(risk['name'], (impact, prob), xytext=(5, 5), textcoords='offset points')
|
227 |
-
|
228 |
-
# تعيين حدود المحاور
|
229 |
-
ax.set_xlim(0.5, 3.5)
|
230 |
-
ax.set_ylim(0.5, 3.5)
|
231 |
-
|
232 |
-
# تعيين تسميات المحاور
|
233 |
-
ax.set_xticks([1, 2, 3])
|
234 |
-
ax.set_xticklabels(['منخفض', 'متوسط', 'عالي'])
|
235 |
-
ax.set_yticks([1, 2, 3])
|
236 |
-
ax.set_yticklabels(['منخفض', 'متوسط', 'عالي'])
|
237 |
-
|
238 |
-
# تعيين العنوان والتسميات
|
239 |
-
ax.set_title(title)
|
240 |
-
ax.set_xlabel('التأثير')
|
241 |
-
ax.set_ylabel('الاحتمالية')
|
242 |
-
|
243 |
-
# إضافة الشبكة
|
244 |
-
ax.grid(True, linestyle='--', alpha=0.7)
|
245 |
-
|
246 |
-
# إضافة مناطق المخاطر
|
247 |
-
# منطقة المخاطر المنخفضة (أخضر)
|
248 |
-
ax.add_patch(plt.Rectangle((0.5, 0.5), 1, 1, fill=True, color=self.theme.SUCCESS_COLOR[self.theme.current_theme], alpha=0.1))
|
249 |
-
# منطقة المخاطر المتوسطة (أصفر)
|
250 |
-
ax.add_patch(plt.Rectangle((1.5, 0.5), 1, 1, fill=True, color=self.theme.WARNING_COLOR[self.theme.current_theme], alpha=0.1))
|
251 |
-
ax.add_patch(plt.Rectangle((0.5, 1.5), 1, 1, fill=True, color=self.theme.WARNING_COLOR[self.theme.current_theme], alpha=0.1))
|
252 |
-
# منطقة المخاطر العالية (أحمر)
|
253 |
-
ax.add_patch(plt.Rectangle((2.5, 0.5), 1, 3, fill=True, color=self.theme.ERROR_COLOR[self.theme.current_theme], alpha=0.1))
|
254 |
-
ax.add_patch(plt.Rectangle((0.5, 2.5), 2, 1, fill=True, color=self.theme.ERROR_COLOR[self.theme.current_theme], alpha=0.1))
|
255 |
-
ax.add_patch(plt.Rectangle((1.5, 1.5), 1, 1, fill=True, color=self.theme.ERROR_COLOR[self.theme.current_theme], alpha=0.1))
|
256 |
-
|
257 |
-
# تضييق الشكل
|
258 |
-
fig.tight_layout()
|
259 |
-
|
260 |
-
return fig
|
261 |
-
|
262 |
-
def embed_chart_in_frame(self, parent, fig):
|
263 |
-
"""تضمين الرسم البياني في إطار"""
|
264 |
-
# إنشاء إطار للرسم البياني
|
265 |
-
chart_frame = ctk.CTkFrame(parent, fg_color="transparent")
|
266 |
-
|
267 |
-
# تضمين الرسم البياني في الإطار
|
268 |
-
canvas = FigureCanvasTkAgg(fig, master=chart_frame)
|
269 |
-
canvas.draw()
|
270 |
-
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
|
271 |
-
|
272 |
-
return chart_frame
|
273 |
-
|
274 |
-
def save_chart(self, fig, name):
|
275 |
-
"""حفظ الرسم البياني"""
|
276 |
-
# تحديد مسار الملف
|
277 |
-
file_path = os.path.join(self.charts_dir, f"{name}.png")
|
278 |
-
|
279 |
-
# حفظ الرسم البياني
|
280 |
-
fig.savefig(file_path, dpi=100, bbox_inches='tight')
|
281 |
-
|
282 |
-
return file_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
styling/enhanced_ui.py
DELETED
@@ -1,586 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
محسن واجهة المستخدم - نظام تحليل المناقصات
|
3 |
-
"""
|
4 |
-
|
5 |
-
import streamlit as st
|
6 |
-
import pandas as pd
|
7 |
-
import numpy as np
|
8 |
-
import base64
|
9 |
-
from pathlib import Path
|
10 |
-
import os
|
11 |
-
|
12 |
-
class UIEnhancer:
|
13 |
-
"""فئة لتحسين واجهة المستخدم وتوحيد التصميم عبر النظام"""
|
14 |
-
|
15 |
-
# ألوان النظام
|
16 |
-
COLORS = {
|
17 |
-
'primary': '#1E88E5', # أزرق
|
18 |
-
'secondary': '#5E35B1', # بنفسجي
|
19 |
-
'success': '#43A047', # أخضر
|
20 |
-
'warning': '#FB8C00', # برتقالي
|
21 |
-
'danger': '#E53935', # أحمر
|
22 |
-
'info': '#00ACC1', # سماوي
|
23 |
-
'light': '#F5F5F5', # رمادي فاتح
|
24 |
-
'dark': '#212121', # رمادي داكن
|
25 |
-
'accent': '#FF4081', # وردي
|
26 |
-
'background': '#FFFFFF', # أبيض
|
27 |
-
'text': '#212121', # أسود
|
28 |
-
'border': '#E0E0E0' # رمادي حدود
|
29 |
-
}
|
30 |
-
|
31 |
-
# أحجام الخطوط
|
32 |
-
FONT_SIZES = {
|
33 |
-
'xs': '0.75rem',
|
34 |
-
'sm': '0.875rem',
|
35 |
-
'md': '1rem',
|
36 |
-
'lg': '1.125rem',
|
37 |
-
'xl': '1.25rem',
|
38 |
-
'2xl': '1.5rem',
|
39 |
-
'3xl': '1.875rem',
|
40 |
-
'4xl': '2.25rem',
|
41 |
-
'5xl': '3rem'
|
42 |
-
}
|
43 |
-
|
44 |
-
def __init__(self, page_title="نظام تحليل المناقصات", page_icon="📊"):
|
45 |
-
"""تهيئة محسن واجهة المستخدم"""
|
46 |
-
self.page_title = page_title
|
47 |
-
self.page_icon = page_icon
|
48 |
-
self.theme_mode = "light" # الوضع الافتراضي هو الوضع الفاتح
|
49 |
-
|
50 |
-
def apply_global_styles(self):
|
51 |
-
"""تطبيق التنسيقات العامة على الصفحة"""
|
52 |
-
# تعريف CSS العام
|
53 |
-
css = f"""
|
54 |
-
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700&display=swap');
|
55 |
-
|
56 |
-
* {{
|
57 |
-
font-family: 'Tajawal', sans-serif;
|
58 |
-
direction: rtl;
|
59 |
-
}}
|
60 |
-
|
61 |
-
h1, h2, h3, h4, h5, h6 {{
|
62 |
-
font-family: 'Tajawal', sans-serif;
|
63 |
-
font-weight: 700;
|
64 |
-
color: {self.COLORS['dark']};
|
65 |
-
}}
|
66 |
-
|
67 |
-
.module-title {{
|
68 |
-
color: {self.COLORS['primary']};
|
69 |
-
font-size: {self.FONT_SIZES['3xl']};
|
70 |
-
margin-bottom: 1rem;
|
71 |
-
border-bottom: 2px solid {self.COLORS['primary']};
|
72 |
-
padding-bottom: 0.5rem;
|
73 |
-
}}
|
74 |
-
|
75 |
-
.stTabs [data-baseweb="tab-list"] {{
|
76 |
-
gap: 2px;
|
77 |
-
}}
|
78 |
-
|
79 |
-
.stTabs [data-baseweb="tab"] {{
|
80 |
-
height: 50px;
|
81 |
-
white-space: pre-wrap;
|
82 |
-
background-color: {self.COLORS['light']};
|
83 |
-
border-radius: 4px 4px 0 0;
|
84 |
-
gap: 1px;
|
85 |
-
padding-top: 10px;
|
86 |
-
padding-bottom: 10px;
|
87 |
-
}}
|
88 |
-
|
89 |
-
.stTabs [aria-selected="true"] {{
|
90 |
-
background-color: {self.COLORS['primary']};
|
91 |
-
color: white;
|
92 |
-
}}
|
93 |
-
|
94 |
-
div[data-testid="stSidebarNav"] li div a span {{
|
95 |
-
direction: rtl;
|
96 |
-
text-align: right;
|
97 |
-
font-family: 'Tajawal', sans-serif;
|
98 |
-
}}
|
99 |
-
|
100 |
-
div[data-testid="stSidebarNav"] {{
|
101 |
-
background-color: {self.COLORS['light']};
|
102 |
-
}}
|
103 |
-
|
104 |
-
div[data-testid="stSidebarNav"] li div {{
|
105 |
-
margin-right: 0;
|
106 |
-
margin-left: auto;
|
107 |
-
}}
|
108 |
-
|
109 |
-
div[data-testid="stSidebarNav"] li div a {{
|
110 |
-
padding-right: 10px;
|
111 |
-
padding-left: 0;
|
112 |
-
}}
|
113 |
-
|
114 |
-
div[data-testid="stSidebarNav"] li div a:hover {{
|
115 |
-
background-color: {self.COLORS['primary'] + '20'};
|
116 |
-
}}
|
117 |
-
|
118 |
-
div[data-testid="stSidebarNav"] li div[aria-selected="true"] {{
|
119 |
-
background-color: {self.COLORS['primary'] + '40'};
|
120 |
-
}}
|
121 |
-
|
122 |
-
div[data-testid="stSidebarNav"] li div[aria-selected="true"] a span {{
|
123 |
-
color: {self.COLORS['primary']};
|
124 |
-
font-weight: 500;
|
125 |
-
}}
|
126 |
-
|
127 |
-
.metric-card {{
|
128 |
-
background-color: white;
|
129 |
-
border-radius: 10px;
|
130 |
-
padding: 20px;
|
131 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
132 |
-
text-align: center;
|
133 |
-
transition: transform 0.3s ease;
|
134 |
-
}}
|
135 |
-
|
136 |
-
.metric-card:hover {{
|
137 |
-
transform: translateY(-5px);
|
138 |
-
}}
|
139 |
-
|
140 |
-
.metric-value {{
|
141 |
-
font-size: 2.5rem;
|
142 |
-
font-weight: 700;
|
143 |
-
margin: 10px 0;
|
144 |
-
}}
|
145 |
-
|
146 |
-
.metric-label {{
|
147 |
-
font-size: 1rem;
|
148 |
-
color: #666;
|
149 |
-
}}
|
150 |
-
|
151 |
-
.metric-change {{
|
152 |
-
font-size: 0.9rem;
|
153 |
-
margin-top: 5px;
|
154 |
-
}}
|
155 |
-
|
156 |
-
.metric-change-positive {{
|
157 |
-
color: {self.COLORS['success']};
|
158 |
-
}}
|
159 |
-
|
160 |
-
.metric-change-negative {{
|
161 |
-
color: {self.COLORS['danger']};
|
162 |
-
}}
|
163 |
-
|
164 |
-
.custom-button {{
|
165 |
-
background-color: {self.COLORS['primary']};
|
166 |
-
color: white;
|
167 |
-
border: none;
|
168 |
-
border-radius: 5px;
|
169 |
-
padding: 10px 20px;
|
170 |
-
font-size: 1rem;
|
171 |
-
cursor: pointer;
|
172 |
-
transition: background-color 0.3s ease;
|
173 |
-
}}
|
174 |
-
|
175 |
-
.custom-button:hover {{
|
176 |
-
background-color: {self.COLORS['secondary']};
|
177 |
-
}}
|
178 |
-
|
179 |
-
.custom-card {{
|
180 |
-
background-color: white;
|
181 |
-
border-radius: 10px;
|
182 |
-
padding: 20px;
|
183 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
184 |
-
margin-bottom: 20px;
|
185 |
-
}}
|
186 |
-
|
187 |
-
.header-container {{
|
188 |
-
display: flex;
|
189 |
-
justify-content: space-between;
|
190 |
-
align-items: center;
|
191 |
-
margin-bottom: 2rem;
|
192 |
-
padding-bottom: 1rem;
|
193 |
-
border-bottom: 1px solid {self.COLORS['border']};
|
194 |
-
}}
|
195 |
-
|
196 |
-
.header-title {{
|
197 |
-
color: {self.COLORS['primary']};
|
198 |
-
font-size: {self.FONT_SIZES['3xl']};
|
199 |
-
margin: 0;
|
200 |
-
}}
|
201 |
-
|
202 |
-
.header-subtitle {{
|
203 |
-
color: {self.COLORS['dark']};
|
204 |
-
font-size: {self.FONT_SIZES['lg']};
|
205 |
-
margin: 0;
|
206 |
-
}}
|
207 |
-
|
208 |
-
.header-actions {{
|
209 |
-
display: flex;
|
210 |
-
gap: 10px;
|
211 |
-
}}
|
212 |
-
|
213 |
-
/* تنسيق الجداول */
|
214 |
-
div[data-testid="stTable"] table {{
|
215 |
-
width: 100%;
|
216 |
-
border-collapse: collapse;
|
217 |
-
}}
|
218 |
-
|
219 |
-
div[data-testid="stTable"] thead tr th {{
|
220 |
-
background-color: {self.COLORS['primary']};
|
221 |
-
color: white;
|
222 |
-
text-align: right;
|
223 |
-
padding: 12px;
|
224 |
-
}}
|
225 |
-
|
226 |
-
div[data-testid="stTable"] tbody tr:nth-child(even) {{
|
227 |
-
background-color: {self.COLORS['light']};
|
228 |
-
}}
|
229 |
-
|
230 |
-
div[data-testid="stTable"] tbody tr:hover {{
|
231 |
-
background-color: {self.COLORS['primary'] + '10'};
|
232 |
-
}}
|
233 |
-
|
234 |
-
div[data-testid="stTable"] tbody tr td {{
|
235 |
-
padding: 10px;
|
236 |
-
text-align: right;
|
237 |
-
}}
|
238 |
-
|
239 |
-
/* تنسيق النماذج */
|
240 |
-
div[data-testid="stForm"] {{
|
241 |
-
background-color: white;
|
242 |
-
border-radius: 10px;
|
243 |
-
padding: 20px;
|
244 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
245 |
-
}}
|
246 |
-
|
247 |
-
button[kind="primaryFormSubmit"] {{
|
248 |
-
background-color: {self.COLORS['primary']};
|
249 |
-
color: white;
|
250 |
-
}}
|
251 |
-
|
252 |
-
button[kind="secondaryFormSubmit"] {{
|
253 |
-
background-color: {self.COLORS['light']};
|
254 |
-
color: {self.COLORS['dark']};
|
255 |
-
border: 1px solid {self.COLORS['border']};
|
256 |
-
}}
|
257 |
-
|
258 |
-
/* تنسيق الرسوم البيانية */
|
259 |
-
div[data-testid="stVegaLiteChart"] {{
|
260 |
-
background-color: white;
|
261 |
-
border-radius: 10px;
|
262 |
-
padding: 20px;
|
263 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
264 |
-
}}
|
265 |
-
"""
|
266 |
-
|
267 |
-
# تطبيق CSS
|
268 |
-
st.markdown(f'<style>{css}</style>', unsafe_allow_html=True)
|
269 |
-
|
270 |
-
def apply_theme_colors(self):
|
271 |
-
"""تطبيق ألوان السمة الحالية"""
|
272 |
-
# تحديد ألوان السمة بناءً على الوضع
|
273 |
-
if self.theme_mode == "dark":
|
274 |
-
self.COLORS['background'] = '#121212'
|
275 |
-
self.COLORS['text'] = '#FFFFFF'
|
276 |
-
self.COLORS['border'] = '#333333'
|
277 |
-
else:
|
278 |
-
self.COLORS['background'] = '#FFFFFF'
|
279 |
-
self.COLORS['text'] = '#212121'
|
280 |
-
self.COLORS['border'] = '#E0E0E0'
|
281 |
-
|
282 |
-
# تطبيق CSS للسمة
|
283 |
-
theme_css = f"""
|
284 |
-
body {{
|
285 |
-
background-color: {self.COLORS['background']};
|
286 |
-
color: {self.COLORS['text']};
|
287 |
-
}}
|
288 |
-
"""
|
289 |
-
|
290 |
-
st.markdown(f'<style>{theme_css}</style>', unsafe_allow_html=True)
|
291 |
-
|
292 |
-
def toggle_theme(self):
|
293 |
-
"""تبديل وضع السمة بين الفاتح والداكن"""
|
294 |
-
if self.theme_mode == "light":
|
295 |
-
self.theme_mode = "dark"
|
296 |
-
else:
|
297 |
-
self.theme_mode = "light"
|
298 |
-
|
299 |
-
self.apply_theme_colors()
|
300 |
-
|
301 |
-
def create_sidebar(self, menu_items):
|
302 |
-
"""إنشاء الشريط الجانبي مع قائمة العناصر"""
|
303 |
-
# إنشاء معرف فريد للزر بناءً على عنوان الصفحة
|
304 |
-
button_key = f"toggle_theme_button_{self.page_title}"
|
305 |
-
|
306 |
-
with st.sidebar:
|
307 |
-
# إضافة الشعار
|
308 |
-
st.markdown(
|
309 |
-
f"""
|
310 |
-
<div style="text-align: center; margin-bottom: 20px;">
|
311 |
-
<h2 style="color: {self.COLORS['primary']};">{self.page_icon} {self.page_title}</h2>
|
312 |
-
</div>
|
313 |
-
""",
|
314 |
-
unsafe_allow_html=True
|
315 |
-
)
|
316 |
-
|
317 |
-
# إضافة معلومات المستخدم
|
318 |
-
st.markdown(
|
319 |
-
f"""
|
320 |
-
<div style="text-align: center; margin-bottom: 20px;">
|
321 |
-
<div style="width: 60px; height: 60px; border-radius: 50%; background-color: {self.COLORS['primary']}; color: white; display: flex; align-items: center; justify-content: center; margin: 0 auto; font-size: 24px; font-weight: bold;">
|
322 |
-
م
|
323 |
-
</div>
|
324 |
-
<p style="margin-top: 10px; font-weight: bold;">محمد أحمد</p>
|
325 |
-
<p style="margin-top: -15px; font-size: 0.8rem; color: #666;">مدير المشاريع</p>
|
326 |
-
</div>
|
327 |
-
""",
|
328 |
-
unsafe_allow_html=True
|
329 |
-
)
|
330 |
-
|
331 |
-
st.divider()
|
332 |
-
|
333 |
-
# إنشاء القائمة
|
334 |
-
selected = st.radio(
|
335 |
-
"القائمة الرئيسية",
|
336 |
-
[item["name"] for item in menu_items],
|
337 |
-
format_func=lambda x: x,
|
338 |
-
label_visibility="collapsed"
|
339 |
-
)
|
340 |
-
|
341 |
-
st.divider()
|
342 |
-
|
343 |
-
# إضافة زر تبديل السمة مع معرف فريد
|
344 |
-
if st.button("تبديل السمة 🌓", key=button_key):
|
345 |
-
self.toggle_theme()
|
346 |
-
st.experimental_rerun()
|
347 |
-
|
348 |
-
# إضافة معلومات النظام
|
349 |
-
st.markdown(
|
350 |
-
"""
|
351 |
-
<div style="position: absolute; bottom: 20px; left: 20px; right: 20px; text-align: center;">
|
352 |
-
<p style="font-size: 0.8rem; color: #666;">نظام تحليل المناقصات | الإصدار 2.0.0</p>
|
353 |
-
<p style="font-size: 0.7rem; color: #888;">© 2025 جميع الحقوق محفوظة</p>
|
354 |
-
</div>
|
355 |
-
""",
|
356 |
-
unsafe_allow_html=True
|
357 |
-
)
|
358 |
-
|
359 |
-
return selected
|
360 |
-
|
361 |
-
def create_header(self, title, subtitle=None, show_actions=True):
|
362 |
-
"""إنشاء ترويسة الصفحة"""
|
363 |
-
# إنشاء معرفات فريدة للأزرار
|
364 |
-
add_button_key = f"add_button_{title}"
|
365 |
-
update_button_key = f"update_button_{title}"
|
366 |
-
|
367 |
-
col1, col2 = st.columns([3, 1])
|
368 |
-
|
369 |
-
with col1:
|
370 |
-
st.markdown(f'<h1 class="header-title">{title}</h1>', unsafe_allow_html=True)
|
371 |
-
if subtitle:
|
372 |
-
st.markdown(f'<p class="header-subtitle">{subtitle}</p>', unsafe_allow_html=True)
|
373 |
-
|
374 |
-
if show_actions:
|
375 |
-
with col2:
|
376 |
-
col2_1, col2_2 = st.columns(2)
|
377 |
-
with col2_1:
|
378 |
-
st.button("إضافة جديد", key=add_button_key)
|
379 |
-
with col2_2:
|
380 |
-
st.button("تحديث", key=update_button_key)
|
381 |
-
|
382 |
-
st.divider()
|
383 |
-
|
384 |
-
def create_metric_card(self, label, value, change=None, color=None):
|
385 |
-
"""إنشاء بطاقة مقياس"""
|
386 |
-
if color is None:
|
387 |
-
color = self.COLORS['primary']
|
388 |
-
|
389 |
-
change_html = ""
|
390 |
-
if change is not None:
|
391 |
-
if change.startswith("+"):
|
392 |
-
change_class = "metric-change-positive"
|
393 |
-
change_icon = "↑"
|
394 |
-
elif change.startswith("-"):
|
395 |
-
change_class = "metric-change-negative"
|
396 |
-
change_icon = "↓"
|
397 |
-
else:
|
398 |
-
change_class = ""
|
399 |
-
change_icon = ""
|
400 |
-
|
401 |
-
change_html = f'<div class="metric-change {change_class}">{change_icon} {change}</div>'
|
402 |
-
|
403 |
-
st.markdown(
|
404 |
-
f"""
|
405 |
-
<div class="metric-card" style="border-top: 4px solid {color};">
|
406 |
-
<div class="metric-label">{label}</div>
|
407 |
-
<div class="metric-value" style="color: {color};">{value}</div>
|
408 |
-
{change_html}
|
409 |
-
</div>
|
410 |
-
""",
|
411 |
-
unsafe_allow_html=True
|
412 |
-
)
|
413 |
-
|
414 |
-
def create_card(self, title, content, color=None):
|
415 |
-
"""إنشاء بطاقة عامة"""
|
416 |
-
if color is None:
|
417 |
-
color = self.COLORS['primary']
|
418 |
-
|
419 |
-
st.markdown(
|
420 |
-
f"""
|
421 |
-
<div class="custom-card" style="border-top: 4px solid {color};">
|
422 |
-
<h3 style="color: {color}; margin-top: 0;">{title}</h3>
|
423 |
-
<div>{content}</div>
|
424 |
-
</div>
|
425 |
-
""",
|
426 |
-
unsafe_allow_html=True
|
427 |
-
)
|
428 |
-
|
429 |
-
def create_button(self, label, color=None, icon=None, key=None):
|
430 |
-
"""إنشاء زر مخصص"""
|
431 |
-
if color is None:
|
432 |
-
color = self.COLORS['primary']
|
433 |
-
|
434 |
-
# إنشاء معرف فريد للزر إذا لم يتم توفيره
|
435 |
-
if key is None:
|
436 |
-
key = f"button_{label}_{hash(label)}"
|
437 |
-
|
438 |
-
icon_html = f"{icon} " if icon else ""
|
439 |
-
|
440 |
-
return st.button(
|
441 |
-
f"{icon_html}{label}",
|
442 |
-
key=key
|
443 |
-
)
|
444 |
-
|
445 |
-
def create_tabs(self, tab_names):
|
446 |
-
"""إنشاء تبويبات"""
|
447 |
-
return st.tabs(tab_names)
|
448 |
-
|
449 |
-
def create_expander(self, title, expanded=False, key=None):
|
450 |
-
"""إنشاء عنصر قابل للتوسيع"""
|
451 |
-
# إنشاء معرف فريد للعنصر إذا لم يتم توفيره
|
452 |
-
if key is None:
|
453 |
-
key = f"expander_{title}_{hash(title)}"
|
454 |
-
|
455 |
-
return st.expander(title, expanded=expanded, key=key)
|
456 |
-
|
457 |
-
def create_data_table(self, data, use_container_width=True, hide_index=True):
|
458 |
-
"""إنشاء جدول بيانات"""
|
459 |
-
return st.dataframe(data, use_container_width=use_container_width, hide_index=hide_index)
|
460 |
-
|
461 |
-
def create_chart(self, chart_type, data, **kwargs):
|
462 |
-
"""إنشاء رسم بياني"""
|
463 |
-
if chart_type == "bar":
|
464 |
-
return st.bar_chart(data, **kwargs)
|
465 |
-
elif chart_type == "line":
|
466 |
-
return st.line_chart(data, **kwargs)
|
467 |
-
elif chart_type == "area":
|
468 |
-
return st.area_chart(data, **kwargs)
|
469 |
-
else:
|
470 |
-
return st.bar_chart(data, **kwargs)
|
471 |
-
|
472 |
-
def create_form(self, title, key=None):
|
473 |
-
"""إنشاء نموذج"""
|
474 |
-
# إنشاء معرف فريد للنموذج إذا لم يتم توفيره
|
475 |
-
if key is None:
|
476 |
-
key = f"form_{title}_{hash(title)}"
|
477 |
-
|
478 |
-
return st.form(key=key)
|
479 |
-
|
480 |
-
def create_file_uploader(self, label, types=None, key=None):
|
481 |
-
"""إنشاء أداة رفع الملفات"""
|
482 |
-
# إنشاء معرف فريد لأداة رفع الملفات إذا لم يتم توفيره
|
483 |
-
if key is None:
|
484 |
-
key = f"file_uploader_{label}_{hash(label)}"
|
485 |
-
|
486 |
-
return st.file_uploader(label, type=types, key=key)
|
487 |
-
|
488 |
-
def create_date_input(self, label, value=None, key=None):
|
489 |
-
"""إنشاء حقل إدخال تاريخ"""
|
490 |
-
# إنشاء معرف فريد لحقل إدخال التاريخ إذا لم يتم توفيره
|
491 |
-
if key is None:
|
492 |
-
key = f"date_input_{label}_{hash(label)}"
|
493 |
-
|
494 |
-
return st.date_input(label, value=value, key=key)
|
495 |
-
|
496 |
-
def create_select_box(self, label, options, index=0, key=None):
|
497 |
-
"""إنشاء قائمة منسدلة"""
|
498 |
-
# إنشاء معرف فريد للقائمة المنسدلة إذا لم يتم توفيره
|
499 |
-
if key is None:
|
500 |
-
key = f"select_box_{label}_{hash(label)}"
|
501 |
-
|
502 |
-
return st.selectbox(label, options, index=index, key=key)
|
503 |
-
|
504 |
-
def create_multi_select(self, label, options, default=None, key=None):
|
505 |
-
"""إنشاء قائمة اختيار متعدد"""
|
506 |
-
# إنشاء معرف فريد لقائمة الاختيار المتعدد إذا لم يتم توفيره
|
507 |
-
if key is None:
|
508 |
-
key = f"multi_select_{label}_{hash(label)}"
|
509 |
-
|
510 |
-
return st.multiselect(label, options, default=default, key=key)
|
511 |
-
|
512 |
-
def create_slider(self, label, min_value, max_value, value=None, step=1, key=None):
|
513 |
-
"""إنشاء شريط تمرير"""
|
514 |
-
# إنشاء معرف فريد لشريط التمرير إذا لم يتم توفيره
|
515 |
-
if key is None:
|
516 |
-
key = f"slider_{label}_{hash(label)}"
|
517 |
-
|
518 |
-
return st.slider(label, min_value=min_value, max_value=max_value, value=value, step=step, key=key)
|
519 |
-
|
520 |
-
def create_text_input(self, label, value="", key=None):
|
521 |
-
"""إنشاء حقل إدخال نصي"""
|
522 |
-
# إنشاء معرف فريد لحقل الإدخال النصي إذا لم يتم توفيره
|
523 |
-
if key is None:
|
524 |
-
key = f"text_input_{label}_{hash(label)}"
|
525 |
-
|
526 |
-
return st.text_input(label, value=value, key=key)
|
527 |
-
|
528 |
-
def create_text_area(self, label, value="", height=None, key=None):
|
529 |
-
"""إنشاء منطقة نص"""
|
530 |
-
# إنشاء معرف فريد لمنطقة النص إذا لم يتم توفيره
|
531 |
-
if key is None:
|
532 |
-
key = f"text_area_{label}_{hash(label)}"
|
533 |
-
|
534 |
-
return st.text_area(label, value=value, height=height, key=key)
|
535 |
-
|
536 |
-
def create_number_input(self, label, min_value=None, max_value=None, value=0, step=1, key=None):
|
537 |
-
"""إنشاء حقل إدخال رقمي"""
|
538 |
-
# إنشاء معرف فريد لحقل الإدخال الرقمي إذا لم يتم توفيره
|
539 |
-
if key is None:
|
540 |
-
key = f"number_input_{label}_{hash(label)}"
|
541 |
-
|
542 |
-
return st.number_input(label, min_value=min_value, max_value=max_value, value=value, step=step, key=key)
|
543 |
-
|
544 |
-
def create_checkbox(self, label, value=False, key=None):
|
545 |
-
"""إنشاء خانة اختيار"""
|
546 |
-
# إنشاء معرف فريد لخانة الاختيار إذا لم يتم توفيره
|
547 |
-
if key is None:
|
548 |
-
key = f"checkbox_{label}_{hash(label)}"
|
549 |
-
|
550 |
-
return st.checkbox(label, value=value, key=key)
|
551 |
-
|
552 |
-
def create_radio(self, label, options, index=0, key=None):
|
553 |
-
"""إنشاء أزرار راديو"""
|
554 |
-
# إنشاء معرف فريد لأزرار الراديو إذا لم يتم توفيره
|
555 |
-
if key is None:
|
556 |
-
key = f"radio_{label}_{hash(label)}"
|
557 |
-
|
558 |
-
return st.radio(label, options, index=index, key=key)
|
559 |
-
|
560 |
-
def create_progress_bar(self, value, key=None):
|
561 |
-
"""إنشاء شريط تقدم"""
|
562 |
-
# إنشاء معرف فريد لشريط التقدم إذا لم يتم توفيره
|
563 |
-
if key is None:
|
564 |
-
key = f"progress_bar_{value}_{hash(str(value))}"
|
565 |
-
|
566 |
-
return st.progress(value, key=key)
|
567 |
-
|
568 |
-
def create_spinner(self, text="جاري التحميل..."):
|
569 |
-
"""إنشاء مؤشر تحميل"""
|
570 |
-
return st.spinner(text)
|
571 |
-
|
572 |
-
def create_success_message(self, message):
|
573 |
-
"""إنشاء رسالة نجاح"""
|
574 |
-
return st.success(message)
|
575 |
-
|
576 |
-
def create_error_message(self, message):
|
577 |
-
"""إنشاء رسالة خطأ"""
|
578 |
-
return st.error(message)
|
579 |
-
|
580 |
-
def create_warning_message(self, message):
|
581 |
-
"""إنشاء رسالة تحذير"""
|
582 |
-
return st.warning(message)
|
583 |
-
|
584 |
-
def create_info_message(self, message):
|
585 |
-
"""إنشاء رسالة معلومات"""
|
586 |
-
return st.info(message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
styling/icons.py
DELETED
@@ -1,609 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
مولد الأيقونات لنظام إدارة المناقصات
|
3 |
-
"""
|
4 |
-
|
5 |
-
import os
|
6 |
-
import math
|
7 |
-
from PIL import Image, ImageDraw, ImageFont
|
8 |
-
|
9 |
-
class IconGenerator:
|
10 |
-
"""فئة مولد الأيقونات"""
|
11 |
-
|
12 |
-
def __init__(self):
|
13 |
-
"""تهيئة مولد الأيقونات"""
|
14 |
-
# تحديد مسار مجلد الأيقونات
|
15 |
-
self.icons_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "assets", "icons")
|
16 |
-
|
17 |
-
# إنشاء مجلد الأيقونات إذا لم يكن موجودًا
|
18 |
-
os.makedirs(self.icons_dir, exist_ok=True)
|
19 |
-
|
20 |
-
# تحديد حجم الأيقونة الافتراضي
|
21 |
-
self.icon_size = (64, 64)
|
22 |
-
|
23 |
-
# تحديد الألوان الافتراضية
|
24 |
-
self.colors = {
|
25 |
-
"primary": "#2980B9",
|
26 |
-
"secondary": "#1ABC9C",
|
27 |
-
"accent": "#9B59B6",
|
28 |
-
"warning": "#F39C12",
|
29 |
-
"error": "#E74C3C",
|
30 |
-
"success": "#2ECC71",
|
31 |
-
"white": "#FFFFFF",
|
32 |
-
"black": "#333333",
|
33 |
-
"gray": "#95A5A6"
|
34 |
-
}
|
35 |
-
|
36 |
-
def generate_icon(self, name, color=None, background_color=None, size=None):
|
37 |
-
"""توليد أيقونة"""
|
38 |
-
# تحديد الألوان
|
39 |
-
if color is None:
|
40 |
-
color = self.colors["primary"]
|
41 |
-
|
42 |
-
if background_color is None:
|
43 |
-
background_color = self.colors["white"]
|
44 |
-
|
45 |
-
# تحديد الحجم
|
46 |
-
if size is None:
|
47 |
-
size = self.icon_size
|
48 |
-
|
49 |
-
# إنشاء صورة جديدة
|
50 |
-
icon = Image.new("RGBA", size, background_color)
|
51 |
-
draw = ImageDraw.Draw(icon)
|
52 |
-
|
53 |
-
# رسم الأيقونة بناءً على الاسم
|
54 |
-
if name == "dashboard":
|
55 |
-
self._draw_dashboard_icon(draw, size, color)
|
56 |
-
elif name == "projects":
|
57 |
-
self._draw_projects_icon(draw, size, color)
|
58 |
-
elif name == "documents":
|
59 |
-
self._draw_documents_icon(draw, size, color)
|
60 |
-
elif name == "pricing":
|
61 |
-
self._draw_pricing_icon(draw, size, color)
|
62 |
-
elif name == "resources":
|
63 |
-
self._draw_resources_icon(draw, size, color)
|
64 |
-
elif name == "risk":
|
65 |
-
self._draw_risk_icon(draw, size, color)
|
66 |
-
elif name == "reports":
|
67 |
-
self._draw_reports_icon(draw, size, color)
|
68 |
-
elif name == "ai":
|
69 |
-
self._draw_ai_icon(draw, size, color)
|
70 |
-
elif name == "settings":
|
71 |
-
self._draw_settings_icon(draw, size, color)
|
72 |
-
elif name == "logout":
|
73 |
-
self._draw_logout_icon(draw, size, color)
|
74 |
-
elif name == "search":
|
75 |
-
self._draw_search_icon(draw, size, color)
|
76 |
-
elif name == "add":
|
77 |
-
self._draw_add_icon(draw, size, color)
|
78 |
-
elif name == "upload":
|
79 |
-
self._draw_upload_icon(draw, size, color)
|
80 |
-
elif name == "import":
|
81 |
-
self._draw_import_icon(draw, size, color)
|
82 |
-
elif name == "export":
|
83 |
-
self._draw_export_icon(draw, size, color)
|
84 |
-
elif name == "save":
|
85 |
-
self._draw_save_icon(draw, size, color)
|
86 |
-
else:
|
87 |
-
# أيقونة افتراضية
|
88 |
-
self._draw_default_icon(draw, size, color)
|
89 |
-
|
90 |
-
# حفظ الأيقونة
|
91 |
-
icon_path = os.path.join(self.icons_dir, f"{name}.png")
|
92 |
-
icon.save(icon_path)
|
93 |
-
|
94 |
-
return icon_path
|
95 |
-
|
96 |
-
def _draw_dashboard_icon(self, draw, size, color):
|
97 |
-
"""رسم أيقونة لوحة التحكم"""
|
98 |
-
width, height = size
|
99 |
-
padding = width // 8
|
100 |
-
|
101 |
-
# رسم المربعات الأربعة
|
102 |
-
box_size = (width - 3 * padding) // 2
|
103 |
-
|
104 |
-
# المربع العلوي الأيسر
|
105 |
-
draw.rectangle(
|
106 |
-
[(padding, padding), (padding + box_size, padding + box_size)],
|
107 |
-
fill=color
|
108 |
-
)
|
109 |
-
|
110 |
-
# المربع العلوي الأيمن
|
111 |
-
draw.rectangle(
|
112 |
-
[(2 * padding + box_size, padding), (2 * padding + 2 * box_size, padding + box_size)],
|
113 |
-
fill=color
|
114 |
-
)
|
115 |
-
|
116 |
-
# المربع السفلي الأيسر
|
117 |
-
draw.rectangle(
|
118 |
-
[(padding, 2 * padding + box_size), (padding + box_size, 2 * padding + 2 * box_size)],
|
119 |
-
fill=color
|
120 |
-
)
|
121 |
-
|
122 |
-
# المربع السفلي الأيمن
|
123 |
-
draw.rectangle(
|
124 |
-
[(2 * padding + box_size, 2 * padding + box_size), (2 * padding + 2 * box_size, 2 * padding + 2 * box_size)],
|
125 |
-
fill=color
|
126 |
-
)
|
127 |
-
|
128 |
-
def _draw_projects_icon(self, draw, size, color):
|
129 |
-
"""رسم أيقونة المشاريع"""
|
130 |
-
width, height = size
|
131 |
-
padding = width // 8
|
132 |
-
|
133 |
-
# رسم مجلد
|
134 |
-
folder_points = [
|
135 |
-
(padding, height // 3),
|
136 |
-
(width // 3, height // 3),
|
137 |
-
(width // 2, padding),
|
138 |
-
(width - padding, padding),
|
139 |
-
(width - padding, height - padding),
|
140 |
-
(padding, height - padding)
|
141 |
-
]
|
142 |
-
draw.polygon(folder_points, fill=color)
|
143 |
-
|
144 |
-
def _draw_documents_icon(self, draw, size, color):
|
145 |
-
"""رسم أيقونة المستندات"""
|
146 |
-
width, height = size
|
147 |
-
padding = width // 8
|
148 |
-
|
149 |
-
# رسم ورقة
|
150 |
-
draw.rectangle(
|
151 |
-
[(padding, padding), (width - padding, height - padding)],
|
152 |
-
fill=color
|
153 |
-
)
|
154 |
-
|
155 |
-
# رسم خطوط النص
|
156 |
-
line_padding = height // 8
|
157 |
-
line_height = height // 20
|
158 |
-
for i in range(4):
|
159 |
-
y = padding + line_padding + i * (line_height + line_padding)
|
160 |
-
draw.rectangle(
|
161 |
-
[(padding * 2, y), (width - padding * 2, y + line_height)],
|
162 |
-
fill=self.colors["white"]
|
163 |
-
)
|
164 |
-
|
165 |
-
def _draw_pricing_icon(self, draw, size, color):
|
166 |
-
"""رسم أيقونة التسعير"""
|
167 |
-
width, height = size
|
168 |
-
padding = width // 8
|
169 |
-
|
170 |
-
# رسم علامة الدولار
|
171 |
-
center_x = width // 2
|
172 |
-
center_y = height // 2
|
173 |
-
radius = min(width, height) // 3
|
174 |
-
|
175 |
-
# رسم دائرة
|
176 |
-
draw.ellipse(
|
177 |
-
[(center_x - radius, center_y - radius), (center_x + radius, center_y + radius)],
|
178 |
-
fill=color
|
179 |
-
)
|
180 |
-
|
181 |
-
# رسم علامة الدولار
|
182 |
-
line_width = radius // 4
|
183 |
-
draw.rectangle(
|
184 |
-
[(center_x - line_width // 2, center_y - radius * 2 // 3), (center_x + line_width // 2, center_y + radius * 2 // 3)],
|
185 |
-
fill=self.colors["white"]
|
186 |
-
)
|
187 |
-
draw.rectangle(
|
188 |
-
[(center_x - radius * 2 // 3, center_y - line_width // 2), (center_x + radius * 2 // 3, center_y + line_width // 2)],
|
189 |
-
fill=self.colors["white"]
|
190 |
-
)
|
191 |
-
|
192 |
-
def _draw_resources_icon(self, draw, size, color):
|
193 |
-
"""رسم أيقونة الموارد"""
|
194 |
-
width, height = size
|
195 |
-
padding = width // 8
|
196 |
-
|
197 |
-
# رسم ثلاثة أشخاص
|
198 |
-
center_x = width // 2
|
199 |
-
center_y = height // 2
|
200 |
-
radius = min(width, height) // 10
|
201 |
-
|
202 |
-
# الشخص الأول (في الوسط)
|
203 |
-
head_center_y = center_y - radius * 2
|
204 |
-
draw.ellipse(
|
205 |
-
[(center_x - radius, head_center_y - radius), (center_x + radius, head_center_y + radius)],
|
206 |
-
fill=color
|
207 |
-
)
|
208 |
-
draw.polygon(
|
209 |
-
[
|
210 |
-
(center_x, head_center_y + radius),
|
211 |
-
(center_x - radius * 2, center_y + radius * 3),
|
212 |
-
(center_x + radius * 2, center_y + radius * 3)
|
213 |
-
],
|
214 |
-
fill=color
|
215 |
-
)
|
216 |
-
|
217 |
-
# الشخص الثاني (على اليسار)
|
218 |
-
left_center_x = center_x - radius * 4
|
219 |
-
head_center_y = center_y - radius * 2
|
220 |
-
draw.ellipse(
|
221 |
-
[(left_center_x - radius, head_center_y - radius), (left_center_x + radius, head_center_y + radius)],
|
222 |
-
fill=color
|
223 |
-
)
|
224 |
-
draw.polygon(
|
225 |
-
[
|
226 |
-
(left_center_x, head_center_y + radius),
|
227 |
-
(left_center_x - radius * 2, center_y + radius * 3),
|
228 |
-
(left_center_x + radius * 2, center_y + radius * 3)
|
229 |
-
],
|
230 |
-
fill=color
|
231 |
-
)
|
232 |
-
|
233 |
-
# الشخص الثالث (على اليمين)
|
234 |
-
right_center_x = center_x + radius * 4
|
235 |
-
head_center_y = center_y - radius * 2
|
236 |
-
draw.ellipse(
|
237 |
-
[(right_center_x - radius, head_center_y - radius), (right_center_x + radius, head_center_y + radius)],
|
238 |
-
fill=color
|
239 |
-
)
|
240 |
-
draw.polygon(
|
241 |
-
[
|
242 |
-
(right_center_x, head_center_y + radius),
|
243 |
-
(right_center_x - radius * 2, center_y + radius * 3),
|
244 |
-
(right_center_x + radius * 2, center_y + radius * 3)
|
245 |
-
],
|
246 |
-
fill=color
|
247 |
-
)
|
248 |
-
|
249 |
-
def _draw_risk_icon(self, draw, size, color):
|
250 |
-
"""رسم أيقونة المخاطر"""
|
251 |
-
width, height = size
|
252 |
-
padding = width // 8
|
253 |
-
|
254 |
-
# رسم علامة تحذير (مثلث)
|
255 |
-
draw.polygon(
|
256 |
-
[
|
257 |
-
(width // 2, padding),
|
258 |
-
(padding, height - padding),
|
259 |
-
(width - padding, height - padding)
|
260 |
-
],
|
261 |
-
fill=color
|
262 |
-
)
|
263 |
-
|
264 |
-
# رسم علامة التعجب
|
265 |
-
exclamation_width = width // 10
|
266 |
-
exclamation_height = height // 3
|
267 |
-
center_x = width // 2
|
268 |
-
center_y = height // 2
|
269 |
-
|
270 |
-
# الجزء العلوي من علامة التعجب
|
271 |
-
draw.rectangle(
|
272 |
-
[
|
273 |
-
(center_x - exclamation_width // 2, center_y - exclamation_height),
|
274 |
-
(center_x + exclamation_width // 2, center_y)
|
275 |
-
],
|
276 |
-
fill=self.colors["white"]
|
277 |
-
)
|
278 |
-
|
279 |
-
# النقطة السفلية من علامة التعجب
|
280 |
-
dot_radius = exclamation_width
|
281 |
-
draw.ellipse(
|
282 |
-
[
|
283 |
-
(center_x - dot_radius // 2, center_y + exclamation_height // 4),
|
284 |
-
(center_x + dot_radius // 2, center_y + exclamation_height // 4 + dot_radius)
|
285 |
-
],
|
286 |
-
fill=self.colors["white"]
|
287 |
-
)
|
288 |
-
|
289 |
-
def _draw_reports_icon(self, draw, size, color):
|
290 |
-
"""رسم أيقونة التقارير"""
|
291 |
-
width, height = size
|
292 |
-
padding = width // 8
|
293 |
-
|
294 |
-
# رسم ورقة
|
295 |
-
draw.rectangle(
|
296 |
-
[(padding, padding), (width - padding, height - padding)],
|
297 |
-
fill=color
|
298 |
-
)
|
299 |
-
|
300 |
-
# رسم رسم بياني
|
301 |
-
chart_padding = width // 6
|
302 |
-
chart_width = width - 2 * chart_padding
|
303 |
-
chart_height = height // 2
|
304 |
-
chart_bottom = height - chart_padding
|
305 |
-
|
306 |
-
# رسم الأعمدة
|
307 |
-
bar_width = chart_width // 5
|
308 |
-
bar_spacing = bar_width // 2
|
309 |
-
|
310 |
-
for i in range(4):
|
311 |
-
bar_height = (i + 1) * chart_height // 4
|
312 |
-
bar_x = chart_padding + i * (bar_width + bar_spacing)
|
313 |
-
bar_y = chart_bottom - bar_height
|
314 |
-
|
315 |
-
draw.rectangle(
|
316 |
-
[(bar_x, bar_y), (bar_x + bar_width, chart_bottom)],
|
317 |
-
fill=self.colors["white"]
|
318 |
-
)
|
319 |
-
|
320 |
-
def _draw_ai_icon(self, draw, size, color):
|
321 |
-
"""رسم أيقونة الذكاء الاصطناعي"""
|
322 |
-
width, height = size
|
323 |
-
padding = width // 8
|
324 |
-
|
325 |
-
# رسم دماغ (مجرد تمثيل مبسط)
|
326 |
-
center_x = width // 2
|
327 |
-
center_y = height // 2
|
328 |
-
brain_width = width - 2 * padding
|
329 |
-
brain_height = height - 2 * padding
|
330 |
-
|
331 |
-
# رسم الجزء الخارجي من الدماغ
|
332 |
-
draw.ellipse(
|
333 |
-
[(center_x - brain_width // 2, center_y - brain_height // 2), (center_x + brain_width // 2, center_y + brain_height // 2)],
|
334 |
-
fill=color
|
335 |
-
)
|
336 |
-
|
337 |
-
# رسم خطوط الدماغ
|
338 |
-
line_width = brain_width // 10
|
339 |
-
line_spacing = brain_width // 8
|
340 |
-
|
341 |
-
for i in range(-2, 3):
|
342 |
-
y = center_y + i * line_spacing
|
343 |
-
draw.line(
|
344 |
-
[(center_x - brain_width // 3, y), (center_x + brain_width // 3, y)],
|
345 |
-
fill=self.colors["white"],
|
346 |
-
width=line_width
|
347 |
-
)
|
348 |
-
|
349 |
-
def _draw_settings_icon(self, draw, size, color):
|
350 |
-
"""رسم أيقونة الإعدادات"""
|
351 |
-
width, height = size
|
352 |
-
padding = width // 8
|
353 |
-
|
354 |
-
# رسم ترس
|
355 |
-
center_x = width // 2
|
356 |
-
center_y = height // 2
|
357 |
-
outer_radius = min(width, height) // 2 - padding
|
358 |
-
inner_radius = outer_radius * 2 // 3
|
359 |
-
|
360 |
-
# رسم الدائرة الداخلية
|
361 |
-
draw.ellipse(
|
362 |
-
[(center_x - inner_radius, center_y - inner_radius), (center_x + inner_radius, center_y + inner_radius)],
|
363 |
-
fill=color
|
364 |
-
)
|
365 |
-
|
366 |
-
# رسم الأسنان
|
367 |
-
num_teeth = 8
|
368 |
-
tooth_width = outer_radius - inner_radius
|
369 |
-
|
370 |
-
for i in range(num_teeth):
|
371 |
-
angle = 2 * math.pi * i / num_teeth
|
372 |
-
tooth_center_x = center_x + (inner_radius + tooth_width // 2) * math.cos(angle)
|
373 |
-
tooth_center_y = center_y + (inner_radius + tooth_width // 2) * math.sin(angle)
|
374 |
-
|
375 |
-
draw.ellipse(
|
376 |
-
[
|
377 |
-
(tooth_center_x - tooth_width // 2, tooth_center_y - tooth_width // 2),
|
378 |
-
(tooth_center_x + tooth_width // 2, tooth_center_y + tooth_width // 2)
|
379 |
-
],
|
380 |
-
fill=color
|
381 |
-
)
|
382 |
-
|
383 |
-
def _draw_logout_icon(self, draw, size, color):
|
384 |
-
"""رسم أيقونة تسجيل الخروج"""
|
385 |
-
width, height = size
|
386 |
-
padding = width // 8
|
387 |
-
|
388 |
-
# رسم سهم الخروج
|
389 |
-
arrow_width = width - 2 * padding
|
390 |
-
arrow_height = height - 2 * padding
|
391 |
-
|
392 |
-
# رسم المستطيل الرئيسي
|
393 |
-
draw.rectangle(
|
394 |
-
[(padding, padding), (width // 2, height - padding)],
|
395 |
-
fill=color
|
396 |
-
)
|
397 |
-
|
398 |
-
# رسم السهم
|
399 |
-
arrow_points = [
|
400 |
-
(width // 2, height // 3),
|
401 |
-
(width - padding, height // 2),
|
402 |
-
(width // 2, height * 2 // 3),
|
403 |
-
(width // 2, height // 2 + height // 8),
|
404 |
-
(width // 2 + width // 4, height // 2 + height // 8),
|
405 |
-
(width // 2 + width // 4, height // 2 - height // 8),
|
406 |
-
(width // 2, height // 2 - height // 8)
|
407 |
-
]
|
408 |
-
draw.polygon(arrow_points, fill=color)
|
409 |
-
|
410 |
-
def _draw_search_icon(self, draw, size, color):
|
411 |
-
"""رسم أيقونة البحث"""
|
412 |
-
width, height = size
|
413 |
-
padding = width // 8
|
414 |
-
|
415 |
-
# رسم دائرة البحث
|
416 |
-
center_x = width // 2 - padding
|
417 |
-
center_y = height // 2 - padding
|
418 |
-
radius = min(width, height) // 3
|
419 |
-
|
420 |
-
draw.ellipse(
|
421 |
-
[(center_x - radius, center_y - radius), (center_x + radius, center_y + radius)],
|
422 |
-
outline=color,
|
423 |
-
width=radius // 3
|
424 |
-
)
|
425 |
-
|
426 |
-
# رسم مقبض البحث
|
427 |
-
handle_width = radius // 3
|
428 |
-
handle_length = radius
|
429 |
-
handle_angle = math.pi / 4 # 45 درجة
|
430 |
-
|
431 |
-
handle_start_x = center_x + radius * math.cos(handle_angle)
|
432 |
-
handle_start_y = center_y + radius * math.sin(handle_angle)
|
433 |
-
handle_end_x = handle_start_x + handle_length * math.cos(handle_angle)
|
434 |
-
handle_end_y = handle_start_y + handle_length * math.sin(handle_angle)
|
435 |
-
|
436 |
-
draw.line(
|
437 |
-
[(handle_start_x, handle_start_y), (handle_end_x, handle_end_y)],
|
438 |
-
fill=color,
|
439 |
-
width=handle_width
|
440 |
-
)
|
441 |
-
|
442 |
-
def _draw_add_icon(self, draw, size, color):
|
443 |
-
"""رسم أيقونة الإضافة"""
|
444 |
-
width, height = size
|
445 |
-
padding = width // 8
|
446 |
-
|
447 |
-
# رسم علامة الزائد
|
448 |
-
center_x = width // 2
|
449 |
-
center_y = height // 2
|
450 |
-
line_length = min(width, height) - 2 * padding
|
451 |
-
line_width = line_length // 5
|
452 |
-
|
453 |
-
# الخط الأفقي
|
454 |
-
draw.rectangle(
|
455 |
-
[
|
456 |
-
(center_x - line_length // 2, center_y - line_width // 2),
|
457 |
-
(center_x + line_length // 2, center_y + line_width // 2)
|
458 |
-
],
|
459 |
-
fill=color
|
460 |
-
)
|
461 |
-
|
462 |
-
# الخط الرأسي
|
463 |
-
draw.rectangle(
|
464 |
-
[
|
465 |
-
(center_x - line_width // 2, center_y - line_length // 2),
|
466 |
-
(center_x + line_width // 2, center_y + line_length // 2)
|
467 |
-
],
|
468 |
-
fill=color
|
469 |
-
)
|
470 |
-
|
471 |
-
def _draw_upload_icon(self, draw, size, color):
|
472 |
-
"""رسم أيقونة التحميل"""
|
473 |
-
width, height = size
|
474 |
-
padding = width // 8
|
475 |
-
|
476 |
-
# رسم سهم لأعلى
|
477 |
-
center_x = width // 2
|
478 |
-
arrow_width = width // 3
|
479 |
-
arrow_height = height // 2
|
480 |
-
|
481 |
-
# رسم السهم
|
482 |
-
arrow_points = [
|
483 |
-
(center_x, padding),
|
484 |
-
(center_x + arrow_width, padding + arrow_height),
|
485 |
-
(center_x + arrow_width // 2, padding + arrow_height),
|
486 |
-
(center_x + arrow_width // 2, height - padding),
|
487 |
-
(center_x - arrow_width // 2, height - padding),
|
488 |
-
(center_x - arrow_width // 2, padding + arrow_height),
|
489 |
-
(center_x - arrow_width, padding + arrow_height)
|
490 |
-
]
|
491 |
-
draw.polygon(arrow_points, fill=color)
|
492 |
-
|
493 |
-
def _draw_import_icon(self, draw, size, color):
|
494 |
-
"""رسم أيقونة الاستيراد"""
|
495 |
-
width, height = size
|
496 |
-
padding = width // 8
|
497 |
-
|
498 |
-
# رسم سهم للداخل
|
499 |
-
center_y = height // 2
|
500 |
-
arrow_width = width // 2
|
501 |
-
arrow_height = height // 3
|
502 |
-
|
503 |
-
# رسم المستطيل
|
504 |
-
draw.rectangle(
|
505 |
-
[(width - padding - arrow_width // 2, padding), (width - padding, height - padding)],
|
506 |
-
fill=color
|
507 |
-
)
|
508 |
-
|
509 |
-
# رسم السهم
|
510 |
-
arrow_points = [
|
511 |
-
(padding, center_y),
|
512 |
-
(padding + arrow_width, center_y - arrow_height // 2),
|
513 |
-
(padding + arrow_width, center_y - arrow_height // 4),
|
514 |
-
(width - padding - arrow_width // 2, center_y - arrow_height // 4),
|
515 |
-
(width - padding - arrow_width // 2, center_y + arrow_height // 4),
|
516 |
-
(padding + arrow_width, center_y + arrow_height // 4),
|
517 |
-
(padding + arrow_width, center_y + arrow_height // 2)
|
518 |
-
]
|
519 |
-
draw.polygon(arrow_points, fill=color)
|
520 |
-
|
521 |
-
def _draw_export_icon(self, draw, size, color):
|
522 |
-
"""رسم أيقونة التصدير"""
|
523 |
-
width, height = size
|
524 |
-
padding = width // 8
|
525 |
-
|
526 |
-
# رسم سهم للخارج
|
527 |
-
center_y = height // 2
|
528 |
-
arrow_width = width // 2
|
529 |
-
arrow_height = height // 3
|
530 |
-
|
531 |
-
# رسم المستطيل
|
532 |
-
draw.rectangle(
|
533 |
-
[(padding, padding), (padding + arrow_width // 2, height - padding)],
|
534 |
-
fill=color
|
535 |
-
)
|
536 |
-
|
537 |
-
# رسم السهم
|
538 |
-
arrow_points = [
|
539 |
-
(width - padding, center_y),
|
540 |
-
(width - padding - arrow_width, center_y - arrow_height // 2),
|
541 |
-
(width - padding - arrow_width, center_y - arrow_height // 4),
|
542 |
-
(padding + arrow_width // 2, center_y - arrow_height // 4),
|
543 |
-
(padding + arrow_width // 2, center_y + arrow_height // 4),
|
544 |
-
(width - padding - arrow_width, center_y + arrow_height // 4),
|
545 |
-
(width - padding - arrow_width, center_y + arrow_height // 2)
|
546 |
-
]
|
547 |
-
draw.polygon(arrow_points, fill=color)
|
548 |
-
|
549 |
-
def _draw_save_icon(self, draw, size, color):
|
550 |
-
"""رسم أيقونة الحفظ"""
|
551 |
-
width, height = size
|
552 |
-
padding = width // 8
|
553 |
-
|
554 |
-
# رسم أيقونة القرص
|
555 |
-
draw.rectangle(
|
556 |
-
[(padding, padding), (width - padding, height - padding)],
|
557 |
-
fill=color
|
558 |
-
)
|
559 |
-
|
560 |
-
# رسم الشريط العلوي
|
561 |
-
draw.rectangle(
|
562 |
-
[(padding * 2, padding * 2), (width - padding * 2, padding * 4)],
|
563 |
-
fill=self.colors["white"]
|
564 |
-
)
|
565 |
-
|
566 |
-
# رسم المستطيل الداخلي
|
567 |
-
draw.rectangle(
|
568 |
-
[(width // 3, height // 2), (width * 2 // 3, height - padding * 2)],
|
569 |
-
fill=self.colors["white"]
|
570 |
-
)
|
571 |
-
|
572 |
-
def _draw_default_icon(self, draw, size, color):
|
573 |
-
"""رسم أيقونة افتراضية"""
|
574 |
-
width, height = size
|
575 |
-
padding = width // 8
|
576 |
-
|
577 |
-
# رسم دائرة
|
578 |
-
center_x = width // 2
|
579 |
-
center_y = height // 2
|
580 |
-
radius = min(width, height) // 2 - padding
|
581 |
-
|
582 |
-
draw.ellipse(
|
583 |
-
[(center_x - radius, center_y - radius), (center_x + radius, center_y + radius)],
|
584 |
-
fill=color
|
585 |
-
)
|
586 |
-
|
587 |
-
def generate_default_icons(self):
|
588 |
-
"""توليد الأيقونات الافتراضية"""
|
589 |
-
icons = [
|
590 |
-
"dashboard",
|
591 |
-
"projects",
|
592 |
-
"documents",
|
593 |
-
"pricing",
|
594 |
-
"resources",
|
595 |
-
"risk",
|
596 |
-
"reports",
|
597 |
-
"ai",
|
598 |
-
"settings",
|
599 |
-
"logout",
|
600 |
-
"search",
|
601 |
-
"add",
|
602 |
-
"upload",
|
603 |
-
"import",
|
604 |
-
"export",
|
605 |
-
"save"
|
606 |
-
]
|
607 |
-
|
608 |
-
for icon in icons:
|
609 |
-
self.generate_icon(icon)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
styling/theme.py
DELETED
@@ -1,541 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
ملف النمط لنظام إدارة المناقصات
|
3 |
-
"""
|
4 |
-
|
5 |
-
import os
|
6 |
-
import tkinter as tk
|
7 |
-
import customtkinter as ctk
|
8 |
-
from PIL import Image, ImageDraw
|
9 |
-
import matplotlib.pyplot as plt
|
10 |
-
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
11 |
-
|
12 |
-
class AppTheme:
|
13 |
-
"""فئة نمط التطبيق"""
|
14 |
-
|
15 |
-
# ألوان النمط الفاتح
|
16 |
-
LIGHT_BG_COLOR = "#F5F5F5"
|
17 |
-
LIGHT_FG_COLOR = "#333333"
|
18 |
-
LIGHT_CARD_BG_COLOR = "#FFFFFF"
|
19 |
-
LIGHT_SIDEBAR_BG_COLOR = "#2C3E50"
|
20 |
-
LIGHT_SIDEBAR_FG_COLOR = "#FFFFFF"
|
21 |
-
LIGHT_SIDEBAR_HOVER_COLOR = "#34495E"
|
22 |
-
LIGHT_SIDEBAR_ACTIVE_COLOR = "#1ABC9C"
|
23 |
-
LIGHT_BUTTON_BG_COLOR = "#2980B9"
|
24 |
-
LIGHT_BUTTON_HOVER_COLOR = "#3498DB"
|
25 |
-
LIGHT_BUTTON_ACTIVE_COLOR = "#1F618D"
|
26 |
-
LIGHT_INPUT_BG_COLOR = "#FFFFFF"
|
27 |
-
LIGHT_INPUT_FG_COLOR = "#333333"
|
28 |
-
LIGHT_BORDER_COLOR = "#E0E0E0"
|
29 |
-
|
30 |
-
# ألوان النمط الداكن
|
31 |
-
DARK_BG_COLOR = "#121212"
|
32 |
-
DARK_FG_COLOR = "#E0E0E0"
|
33 |
-
DARK_CARD_BG_COLOR = "#1E1E1E"
|
34 |
-
DARK_SIDEBAR_BG_COLOR = "#1A1A2E"
|
35 |
-
DARK_SIDEBAR_FG_COLOR = "#E0E0E0"
|
36 |
-
DARK_SIDEBAR_HOVER_COLOR = "#16213E"
|
37 |
-
DARK_SIDEBAR_ACTIVE_COLOR = "#0F3460"
|
38 |
-
DARK_BUTTON_BG_COLOR = "#0F3460"
|
39 |
-
DARK_BUTTON_HOVER_COLOR = "#16213E"
|
40 |
-
DARK_BUTTON_ACTIVE_COLOR = "#1A1A2E"
|
41 |
-
DARK_INPUT_BG_COLOR = "#2C2C2C"
|
42 |
-
DARK_INPUT_FG_COLOR = "#E0E0E0"
|
43 |
-
DARK_BORDER_COLOR = "#333333"
|
44 |
-
|
45 |
-
# ألوان الأساسية
|
46 |
-
PRIMARY_COLOR = {
|
47 |
-
"light": "#2980B9",
|
48 |
-
"dark": "#0F3460"
|
49 |
-
}
|
50 |
-
|
51 |
-
SECONDARY_COLOR = {
|
52 |
-
"light": "#1ABC9C",
|
53 |
-
"dark": "#16213E"
|
54 |
-
}
|
55 |
-
|
56 |
-
ACCENT_COLOR = {
|
57 |
-
"light": "#9B59B6",
|
58 |
-
"dark": "#533483"
|
59 |
-
}
|
60 |
-
|
61 |
-
WARNING_COLOR = {
|
62 |
-
"light": "#F39C12",
|
63 |
-
"dark": "#E58E26"
|
64 |
-
}
|
65 |
-
|
66 |
-
ERROR_COLOR = {
|
67 |
-
"light": "#E74C3C",
|
68 |
-
"dark": "#C0392B"
|
69 |
-
}
|
70 |
-
|
71 |
-
SUCCESS_COLOR = {
|
72 |
-
"light": "#2ECC71",
|
73 |
-
"dark": "#27AE60"
|
74 |
-
}
|
75 |
-
|
76 |
-
def __init__(self, config):
|
77 |
-
"""تهيئة النمط"""
|
78 |
-
self.config = config
|
79 |
-
self.current_theme = self.config.get_theme()
|
80 |
-
self.font_family = self.config.get_font()
|
81 |
-
self.font_size = self.config.get_font_size()
|
82 |
-
|
83 |
-
# تهيئة النمط
|
84 |
-
self._setup_theme()
|
85 |
-
|
86 |
-
def _setup_theme(self):
|
87 |
-
"""إعداد النمط"""
|
88 |
-
# تعيين نمط customtkinter
|
89 |
-
ctk.set_appearance_mode(self.current_theme)
|
90 |
-
ctk.set_default_color_theme("blue")
|
91 |
-
|
92 |
-
# تهيئة الخطوط
|
93 |
-
self.fonts = {
|
94 |
-
"title": (self.font_family, self.font_size + 8, "bold"),
|
95 |
-
"subtitle": (self.font_family, self.font_size + 4, "bold"),
|
96 |
-
"heading": (self.font_family, self.font_size + 2, "bold"),
|
97 |
-
"body": (self.font_family, self.font_size, "normal"),
|
98 |
-
"small": (self.font_family, self.font_size - 2, "normal")
|
99 |
-
}
|
100 |
-
|
101 |
-
def apply_theme_to_app(self, app):
|
102 |
-
"""تطبيق النمط على التطبيق"""
|
103 |
-
app.configure(fg_color=self.get_color("bg_color"))
|
104 |
-
|
105 |
-
def get_color(self, color_name):
|
106 |
-
"""الحصول على لون معين"""
|
107 |
-
if self.current_theme == "light":
|
108 |
-
colors = {
|
109 |
-
"bg_color": self.LIGHT_BG_COLOR,
|
110 |
-
"fg_color": self.LIGHT_FG_COLOR,
|
111 |
-
"card_bg_color": self.LIGHT_CARD_BG_COLOR,
|
112 |
-
"sidebar_bg_color": self.LIGHT_SIDEBAR_BG_COLOR,
|
113 |
-
"sidebar_fg_color": self.LIGHT_SIDEBAR_FG_COLOR,
|
114 |
-
"sidebar_hover_color": self.LIGHT_SIDEBAR_HOVER_COLOR,
|
115 |
-
"sidebar_active_color": self.LIGHT_SIDEBAR_ACTIVE_COLOR,
|
116 |
-
"button_bg_color": self.LIGHT_BUTTON_BG_COLOR,
|
117 |
-
"button_hover_color": self.LIGHT_BUTTON_HOVER_COLOR,
|
118 |
-
"button_active_color": self.LIGHT_BUTTON_ACTIVE_COLOR,
|
119 |
-
"input_bg_color": self.LIGHT_INPUT_BG_COLOR,
|
120 |
-
"input_fg_color": self.LIGHT_INPUT_FG_COLOR,
|
121 |
-
"border_color": self.LIGHT_BORDER_COLOR,
|
122 |
-
"primary_color": self.PRIMARY_COLOR["light"],
|
123 |
-
"secondary_color": self.SECONDARY_COLOR["light"],
|
124 |
-
"accent_color": self.ACCENT_COLOR["light"],
|
125 |
-
"warning_color": self.WARNING_COLOR["light"],
|
126 |
-
"error_color": self.ERROR_COLOR["light"],
|
127 |
-
"success_color": self.SUCCESS_COLOR["light"]
|
128 |
-
}
|
129 |
-
else:
|
130 |
-
colors = {
|
131 |
-
"bg_color": self.DARK_BG_COLOR,
|
132 |
-
"fg_color": self.DARK_FG_COLOR,
|
133 |
-
"card_bg_color": self.DARK_CARD_BG_COLOR,
|
134 |
-
"sidebar_bg_color": self.DARK_SIDEBAR_BG_COLOR,
|
135 |
-
"sidebar_fg_color": self.DARK_SIDEBAR_FG_COLOR,
|
136 |
-
"sidebar_hover_color": self.DARK_SIDEBAR_HOVER_COLOR,
|
137 |
-
"sidebar_active_color": self.DARK_SIDEBAR_ACTIVE_COLOR,
|
138 |
-
"button_bg_color": self.DARK_BUTTON_BG_COLOR,
|
139 |
-
"button_hover_color": self.DARK_BUTTON_HOVER_COLOR,
|
140 |
-
"button_active_color": self.DARK_BUTTON_ACTIVE_COLOR,
|
141 |
-
"input_bg_color": self.DARK_INPUT_BG_COLOR,
|
142 |
-
"input_fg_color": self.DARK_INPUT_FG_COLOR,
|
143 |
-
"border_color": self.DARK_BORDER_COLOR,
|
144 |
-
"primary_color": self.PRIMARY_COLOR["dark"],
|
145 |
-
"secondary_color": self.SECONDARY_COLOR["dark"],
|
146 |
-
"accent_color": self.ACCENT_COLOR["dark"],
|
147 |
-
"warning_color": self.WARNING_COLOR["dark"],
|
148 |
-
"error_color": self.ERROR_COLOR["dark"],
|
149 |
-
"success_color": self.SUCCESS_COLOR["dark"]
|
150 |
-
}
|
151 |
-
|
152 |
-
return colors.get(color_name, self.LIGHT_BG_COLOR)
|
153 |
-
|
154 |
-
def get_font(self, font_type):
|
155 |
-
"""الحصول على خط معين"""
|
156 |
-
return self.fonts.get(font_type, self.fonts["body"])
|
157 |
-
|
158 |
-
def toggle_theme(self):
|
159 |
-
"""تبديل النمط بين الفاتح والداكن"""
|
160 |
-
if self.current_theme == "light":
|
161 |
-
self.current_theme = "dark"
|
162 |
-
else:
|
163 |
-
self.current_theme = "light"
|
164 |
-
|
165 |
-
# تحديث النمط في الإعدادات
|
166 |
-
self.config.set_theme(self.current_theme)
|
167 |
-
|
168 |
-
# تحديث نمط customtkinter
|
169 |
-
ctk.set_appearance_mode(self.current_theme)
|
170 |
-
|
171 |
-
return self.current_theme
|
172 |
-
|
173 |
-
def create_styled_frame(self, parent, **kwargs):
|
174 |
-
"""إنشاء إطار منسق"""
|
175 |
-
default_kwargs = {
|
176 |
-
"fg_color": self.get_color("bg_color"),
|
177 |
-
"corner_radius": 10,
|
178 |
-
"border_width": 0
|
179 |
-
}
|
180 |
-
|
181 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
182 |
-
for key, value in kwargs.items():
|
183 |
-
default_kwargs[key] = value
|
184 |
-
|
185 |
-
return ctk.CTkFrame(parent, **default_kwargs)
|
186 |
-
|
187 |
-
def create_styled_scrollable_frame(self, parent, **kwargs):
|
188 |
-
"""إنشاء إطار قابل للتمرير منسق"""
|
189 |
-
default_kwargs = {
|
190 |
-
"fg_color": self.get_color("bg_color"),
|
191 |
-
"corner_radius": 10,
|
192 |
-
"border_width": 0
|
193 |
-
}
|
194 |
-
|
195 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
196 |
-
for key, value in kwargs.items():
|
197 |
-
default_kwargs[key] = value
|
198 |
-
|
199 |
-
return ctk.CTkScrollableFrame(parent, **default_kwargs)
|
200 |
-
|
201 |
-
def create_styled_label(self, parent, text, **kwargs):
|
202 |
-
"""إنشاء تسمية منسقة"""
|
203 |
-
default_kwargs = {
|
204 |
-
"text": text,
|
205 |
-
"font": self.get_font("body"),
|
206 |
-
"text_color": self.get_color("fg_color")
|
207 |
-
}
|
208 |
-
|
209 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
210 |
-
for key, value in kwargs.items():
|
211 |
-
default_kwargs[key] = value
|
212 |
-
|
213 |
-
return ctk.CTkLabel(parent, **default_kwargs)
|
214 |
-
|
215 |
-
def create_styled_button(self, parent, text, **kwargs):
|
216 |
-
"""إنشاء زر منسق"""
|
217 |
-
default_kwargs = {
|
218 |
-
"text": text,
|
219 |
-
"font": self.get_font("body"),
|
220 |
-
"fg_color": self.get_color("button_bg_color"),
|
221 |
-
"hover_color": self.get_color("button_hover_color"),
|
222 |
-
"text_color": "white",
|
223 |
-
"corner_radius": 8,
|
224 |
-
"border_width": 0,
|
225 |
-
"height": 36
|
226 |
-
}
|
227 |
-
|
228 |
-
# إضافة أيقونة إذا تم تحديدها
|
229 |
-
if "icon" in kwargs:
|
230 |
-
icon_name = kwargs.pop("icon")
|
231 |
-
# هنا يمكن إضافة منطق لتحميل الأيقونة
|
232 |
-
|
233 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
234 |
-
for key, value in kwargs.items():
|
235 |
-
default_kwargs[key] = value
|
236 |
-
|
237 |
-
return ctk.CTkButton(parent, **default_kwargs)
|
238 |
-
|
239 |
-
def create_styled_entry(self, parent, placeholder_text, **kwargs):
|
240 |
-
"""إنشاء حقل إدخال منسق"""
|
241 |
-
default_kwargs = {
|
242 |
-
"placeholder_text": placeholder_text,
|
243 |
-
"font": self.get_font("body"),
|
244 |
-
"fg_color": self.get_color("input_bg_color"),
|
245 |
-
"text_color": self.get_color("input_fg_color"),
|
246 |
-
"placeholder_text_color": self.get_color("border_color"),
|
247 |
-
"corner_radius": 8,
|
248 |
-
"border_width": 1,
|
249 |
-
"border_color": self.get_color("border_color"),
|
250 |
-
"height": 36
|
251 |
-
}
|
252 |
-
|
253 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
254 |
-
for key, value in kwargs.items():
|
255 |
-
default_kwargs[key] = value
|
256 |
-
|
257 |
-
return ctk.CTkEntry(parent, **default_kwargs)
|
258 |
-
|
259 |
-
def create_styled_textbox(self, parent, **kwargs):
|
260 |
-
"""إنشاء مربع نص منسق"""
|
261 |
-
default_kwargs = {
|
262 |
-
"font": self.get_font("body"),
|
263 |
-
"fg_color": self.get_color("input_bg_color"),
|
264 |
-
"text_color": self.get_color("input_fg_color"),
|
265 |
-
"corner_radius": 8,
|
266 |
-
"border_width": 1,
|
267 |
-
"border_color": self.get_color("border_color")
|
268 |
-
}
|
269 |
-
|
270 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
271 |
-
for key, value in kwargs.items():
|
272 |
-
default_kwargs[key] = value
|
273 |
-
|
274 |
-
return ctk.CTkTextbox(parent, **default_kwargs)
|
275 |
-
|
276 |
-
def create_styled_combobox(self, parent, values, **kwargs):
|
277 |
-
"""إنشاء قائمة منسدلة منسقة"""
|
278 |
-
default_kwargs = {
|
279 |
-
"values": values,
|
280 |
-
"font": self.get_font("body"),
|
281 |
-
"fg_color": self.get_color("input_bg_color"),
|
282 |
-
"text_color": self.get_color("input_fg_color"),
|
283 |
-
"border_color": self.get_color("border_color"),
|
284 |
-
"button_color": self.get_color("button_bg_color"),
|
285 |
-
"button_hover_color": self.get_color("button_hover_color"),
|
286 |
-
"dropdown_fg_color": self.get_color("card_bg_color"),
|
287 |
-
"dropdown_text_color": self.get_color("fg_color"),
|
288 |
-
"dropdown_hover_color": self.get_color("sidebar_hover_color"),
|
289 |
-
"corner_radius": 8,
|
290 |
-
"border_width": 1,
|
291 |
-
"dropdown_font": self.get_font("body")
|
292 |
-
}
|
293 |
-
|
294 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
295 |
-
for key, value in kwargs.items():
|
296 |
-
default_kwargs[key] = value
|
297 |
-
|
298 |
-
return ctk.CTkComboBox(parent, **default_kwargs)
|
299 |
-
|
300 |
-
def create_styled_switch(self, parent, text, **kwargs):
|
301 |
-
"""إنشاء مفتاح تبديل منسق"""
|
302 |
-
default_kwargs = {
|
303 |
-
"text": text,
|
304 |
-
"font": self.get_font("body"),
|
305 |
-
"fg_color": self.get_color("border_color"),
|
306 |
-
"progress_color": self.get_color("button_bg_color"),
|
307 |
-
"button_color": "white",
|
308 |
-
"button_hover_color": "white",
|
309 |
-
"text_color": self.get_color("fg_color")
|
310 |
-
}
|
311 |
-
|
312 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
313 |
-
for key, value in kwargs.items():
|
314 |
-
default_kwargs[key] = value
|
315 |
-
|
316 |
-
return ctk.CTkSwitch(parent, **default_kwargs)
|
317 |
-
|
318 |
-
def create_styled_radio_button(self, parent, text, variable, value, **kwargs):
|
319 |
-
"""إنشاء زر راديو منسق"""
|
320 |
-
default_kwargs = {
|
321 |
-
"text": text,
|
322 |
-
"font": self.get_font("body"),
|
323 |
-
"fg_color": self.get_color("button_bg_color"),
|
324 |
-
"border_color": self.get_color("border_color"),
|
325 |
-
"hover_color": self.get_color("button_hover_color"),
|
326 |
-
"text_color": self.get_color("fg_color"),
|
327 |
-
"variable": variable,
|
328 |
-
"value": value
|
329 |
-
}
|
330 |
-
|
331 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
332 |
-
for key, value in kwargs.items():
|
333 |
-
default_kwargs[key] = value
|
334 |
-
|
335 |
-
return ctk.CTkRadioButton(parent, **default_kwargs)
|
336 |
-
|
337 |
-
def create_styled_slider(self, parent, **kwargs):
|
338 |
-
"""إنشاء شريط تمرير منسق"""
|
339 |
-
default_kwargs = {
|
340 |
-
"fg_color": self.get_color("border_color"),
|
341 |
-
"progress_color": self.get_color("button_bg_color"),
|
342 |
-
"button_color": self.get_color("button_bg_color"),
|
343 |
-
"button_hover_color": self.get_color("button_hover_color")
|
344 |
-
}
|
345 |
-
|
346 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
347 |
-
for key, value in kwargs.items():
|
348 |
-
default_kwargs[key] = value
|
349 |
-
|
350 |
-
return ctk.CTkSlider(parent, **default_kwargs)
|
351 |
-
|
352 |
-
def create_styled_tabview(self, parent, **kwargs):
|
353 |
-
"""إنشاء عرض تبويب منسق"""
|
354 |
-
default_kwargs = {
|
355 |
-
"fg_color": self.get_color("card_bg_color"),
|
356 |
-
"segmented_button_fg_color": self.get_color("sidebar_bg_color"),
|
357 |
-
"segmented_button_selected_color": self.get_color("button_bg_color"),
|
358 |
-
"segmented_button_unselected_color": self.get_color("sidebar_bg_color"),
|
359 |
-
"segmented_button_selected_hover_color": self.get_color("button_hover_color"),
|
360 |
-
"segmented_button_unselected_hover_color": self.get_color("sidebar_hover_color"),
|
361 |
-
"segmented_button_text_color": self.get_color("sidebar_fg_color"),
|
362 |
-
"segmented_button_selected_text_color": "white",
|
363 |
-
"text_color": self.get_color("fg_color"),
|
364 |
-
"corner_radius": 10
|
365 |
-
}
|
366 |
-
|
367 |
-
# دمج الخصائص المخصصة مع الخصائص الافتراضية
|
368 |
-
for key, value in kwargs.items():
|
369 |
-
default_kwargs[key] = value
|
370 |
-
|
371 |
-
return ctk.CTkTabview(parent, **default_kwargs)
|
372 |
-
|
373 |
-
def create_styled_sidebar_button(self, parent, text, icon, command=None):
|
374 |
-
"""إنشاء زر الشريط الجانبي المنسق"""
|
375 |
-
# إنشاء إطار للزر
|
376 |
-
button_frame = ctk.CTkFrame(
|
377 |
-
parent,
|
378 |
-
fg_color="transparent",
|
379 |
-
corner_radius=0
|
380 |
-
)
|
381 |
-
|
382 |
-
# إنشاء الزر
|
383 |
-
button = ctk.CTkButton(
|
384 |
-
button_frame,
|
385 |
-
text=text,
|
386 |
-
font=self.get_font("body"),
|
387 |
-
fg_color="transparent",
|
388 |
-
hover_color=self.get_color("sidebar_hover_color"),
|
389 |
-
text_color=self.get_color("sidebar_fg_color"),
|
390 |
-
anchor="w",
|
391 |
-
corner_radius=0,
|
392 |
-
border_width=0,
|
393 |
-
height=40,
|
394 |
-
command=command
|
395 |
-
)
|
396 |
-
button.pack(fill="x", padx=0, pady=0)
|
397 |
-
|
398 |
-
return button_frame, button
|
399 |
-
|
400 |
-
def create_styled_card(self, parent, title):
|
401 |
-
"""إنشاء بطاقة منسقة"""
|
402 |
-
# إنشاء إطار البطاقة
|
403 |
-
card = self.create_styled_frame(
|
404 |
-
parent,
|
405 |
-
fg_color=self.get_color("card_bg_color")
|
406 |
-
)
|
407 |
-
|
408 |
-
# إنشاء عنوان البطاقة
|
409 |
-
title_label = self.create_styled_label(
|
410 |
-
card,
|
411 |
-
title,
|
412 |
-
font=self.get_font("heading")
|
413 |
-
)
|
414 |
-
title_label.pack(anchor="w", padx=15, pady=(15, 5))
|
415 |
-
|
416 |
-
# إنشاء خط فاصل
|
417 |
-
separator = ctk.CTkFrame(
|
418 |
-
card,
|
419 |
-
height=1,
|
420 |
-
fg_color=self.get_color("border_color")
|
421 |
-
)
|
422 |
-
separator.pack(fill="x", padx=15, pady=(5, 0))
|
423 |
-
|
424 |
-
# إنشاء إطار المحتوى
|
425 |
-
content_frame = self.create_styled_frame(
|
426 |
-
card,
|
427 |
-
fg_color="transparent"
|
428 |
-
)
|
429 |
-
content_frame.pack(fill="both", expand=True, padx=15, pady=15)
|
430 |
-
|
431 |
-
return card, content_frame
|
432 |
-
|
433 |
-
def create_styled_data_table(self, parent, columns, data):
|
434 |
-
"""إنشاء جدول بيانات منسق"""
|
435 |
-
# إنشاء إطار الجدول
|
436 |
-
table_frame = self.create_styled_frame(
|
437 |
-
parent,
|
438 |
-
fg_color="transparent"
|
439 |
-
)
|
440 |
-
|
441 |
-
# إنشاء إطار العناوين
|
442 |
-
header_frame = self.create_styled_frame(
|
443 |
-
table_frame,
|
444 |
-
fg_color=self.get_color("sidebar_bg_color")
|
445 |
-
)
|
446 |
-
header_frame.pack(fill="x", padx=0, pady=(0, 1))
|
447 |
-
|
448 |
-
# إنشاء عناوين الأعمدة
|
449 |
-
for i, column in enumerate(columns):
|
450 |
-
column_label = self.create_styled_label(
|
451 |
-
header_frame,
|
452 |
-
column,
|
453 |
-
font=self.get_font("heading"),
|
454 |
-
text_color=self.get_color("sidebar_fg_color")
|
455 |
-
)
|
456 |
-
column_label.grid(row=0, column=i, sticky="ew", padx=10, pady=10)
|
457 |
-
header_frame.grid_columnconfigure(i, weight=1)
|
458 |
-
|
459 |
-
# إنشاء إطار البيانات
|
460 |
-
data_frame = self.create_styled_scrollable_frame(
|
461 |
-
table_frame,
|
462 |
-
fg_color="transparent"
|
463 |
-
)
|
464 |
-
data_frame.pack(fill="both", expand=True, padx=0, pady=0)
|
465 |
-
|
466 |
-
# إنشاء صفوف البيانات
|
467 |
-
for i, row in enumerate(data):
|
468 |
-
row_frame = self.create_styled_frame(
|
469 |
-
data_frame,
|
470 |
-
fg_color=self.get_color("card_bg_color") if i % 2 == 0 else self.get_color("bg_color")
|
471 |
-
)
|
472 |
-
row_frame.pack(fill="x", padx=0, pady=(0, 1))
|
473 |
-
|
474 |
-
for j, cell in enumerate(row):
|
475 |
-
cell_label = self.create_styled_label(
|
476 |
-
row_frame,
|
477 |
-
cell,
|
478 |
-
font=self.get_font("body")
|
479 |
-
)
|
480 |
-
cell_label.grid(row=0, column=j, sticky="ew", padx=10, pady=10)
|
481 |
-
row_frame.grid_columnconfigure(j, weight=1)
|
482 |
-
|
483 |
-
return table_frame, data_frame
|
484 |
-
|
485 |
-
def create_styled_message_box(self, title, message, message_type="info"):
|
486 |
-
"""إنشاء مربع رسالة منسق"""
|
487 |
-
# تحديد لون الرسالة بناءً على النوع
|
488 |
-
if message_type == "error":
|
489 |
-
color = self.get_color("error_color")
|
490 |
-
elif message_type == "warning":
|
491 |
-
color = self.get_color("warning_color")
|
492 |
-
elif message_type == "success":
|
493 |
-
color = self.get_color("success_color")
|
494 |
-
else: # info
|
495 |
-
color = self.get_color("primary_color")
|
496 |
-
|
497 |
-
# إنشاء نافذة الرسالة
|
498 |
-
message_window = ctk.CTkToplevel()
|
499 |
-
message_window.title(title)
|
500 |
-
message_window.geometry("400x200")
|
501 |
-
message_window.resizable(False, False)
|
502 |
-
message_window.grab_set() # جعل النافذة المنبثقة مركز الاهتمام
|
503 |
-
|
504 |
-
# تطبيق النمط
|
505 |
-
message_window.configure(fg_color=self.get_color("bg_color"))
|
506 |
-
|
507 |
-
# إنشاء إطار الرسالة
|
508 |
-
message_frame = self.create_styled_frame(
|
509 |
-
message_window,
|
510 |
-
fg_color=self.get_color("card_bg_color")
|
511 |
-
)
|
512 |
-
message_frame.pack(fill="both", expand=True, padx=20, pady=20)
|
513 |
-
|
514 |
-
# إنشاء عنوان الرسالة
|
515 |
-
title_label = self.create_styled_label(
|
516 |
-
message_frame,
|
517 |
-
title,
|
518 |
-
font=self.get_font("heading"),
|
519 |
-
text_color=color
|
520 |
-
)
|
521 |
-
title_label.pack(padx=20, pady=(20, 10))
|
522 |
-
|
523 |
-
# إنشاء نص الرسالة
|
524 |
-
message_label = self.create_styled_label(
|
525 |
-
message_frame,
|
526 |
-
message,
|
527 |
-
font=self.get_font("body")
|
528 |
-
)
|
529 |
-
message_label.pack(padx=20, pady=(0, 20))
|
530 |
-
|
531 |
-
# إنشاء زر موافق
|
532 |
-
ok_button = self.create_styled_button(
|
533 |
-
message_frame,
|
534 |
-
"موافق",
|
535 |
-
fg_color=color,
|
536 |
-
hover_color=color,
|
537 |
-
command=message_window.destroy
|
538 |
-
)
|
539 |
-
ok_button.pack(pady=(0, 20))
|
540 |
-
|
541 |
-
return message_window
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|