|
"""
|
|
ملف النمط لنظام إدارة المناقصات
|
|
"""
|
|
|
|
import os
|
|
import tkinter as tk
|
|
import customtkinter as ctk
|
|
from PIL import Image, ImageDraw
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
|
|
|
class AppTheme:
|
|
"""فئة نمط التطبيق"""
|
|
|
|
|
|
LIGHT_BG_COLOR = "#F5F5F5"
|
|
LIGHT_FG_COLOR = "#333333"
|
|
LIGHT_CARD_BG_COLOR = "#FFFFFF"
|
|
LIGHT_SIDEBAR_BG_COLOR = "#2C3E50"
|
|
LIGHT_SIDEBAR_FG_COLOR = "#FFFFFF"
|
|
LIGHT_SIDEBAR_HOVER_COLOR = "#34495E"
|
|
LIGHT_SIDEBAR_ACTIVE_COLOR = "#1ABC9C"
|
|
LIGHT_BUTTON_BG_COLOR = "#2980B9"
|
|
LIGHT_BUTTON_HOVER_COLOR = "#3498DB"
|
|
LIGHT_BUTTON_ACTIVE_COLOR = "#1F618D"
|
|
LIGHT_INPUT_BG_COLOR = "#FFFFFF"
|
|
LIGHT_INPUT_FG_COLOR = "#333333"
|
|
LIGHT_BORDER_COLOR = "#E0E0E0"
|
|
|
|
|
|
DARK_BG_COLOR = "#121212"
|
|
DARK_FG_COLOR = "#E0E0E0"
|
|
DARK_CARD_BG_COLOR = "#1E1E1E"
|
|
DARK_SIDEBAR_BG_COLOR = "#1A1A2E"
|
|
DARK_SIDEBAR_FG_COLOR = "#E0E0E0"
|
|
DARK_SIDEBAR_HOVER_COLOR = "#16213E"
|
|
DARK_SIDEBAR_ACTIVE_COLOR = "#0F3460"
|
|
DARK_BUTTON_BG_COLOR = "#0F3460"
|
|
DARK_BUTTON_HOVER_COLOR = "#16213E"
|
|
DARK_BUTTON_ACTIVE_COLOR = "#1A1A2E"
|
|
DARK_INPUT_BG_COLOR = "#2C2C2C"
|
|
DARK_INPUT_FG_COLOR = "#E0E0E0"
|
|
DARK_BORDER_COLOR = "#333333"
|
|
|
|
|
|
PRIMARY_COLOR = {
|
|
"light": "#2980B9",
|
|
"dark": "#0F3460"
|
|
}
|
|
|
|
SECONDARY_COLOR = {
|
|
"light": "#1ABC9C",
|
|
"dark": "#16213E"
|
|
}
|
|
|
|
ACCENT_COLOR = {
|
|
"light": "#9B59B6",
|
|
"dark": "#533483"
|
|
}
|
|
|
|
WARNING_COLOR = {
|
|
"light": "#F39C12",
|
|
"dark": "#E58E26"
|
|
}
|
|
|
|
ERROR_COLOR = {
|
|
"light": "#E74C3C",
|
|
"dark": "#C0392B"
|
|
}
|
|
|
|
SUCCESS_COLOR = {
|
|
"light": "#2ECC71",
|
|
"dark": "#27AE60"
|
|
}
|
|
|
|
def __init__(self, config):
|
|
"""تهيئة النمط"""
|
|
self.config = config
|
|
self.current_theme = self.config.get_theme()
|
|
self.font_family = self.config.get_font()
|
|
self.font_size = self.config.get_font_size()
|
|
|
|
|
|
self._setup_theme()
|
|
|
|
def _setup_theme(self):
|
|
"""إعداد النمط"""
|
|
|
|
ctk.set_appearance_mode(self.current_theme)
|
|
ctk.set_default_color_theme("blue")
|
|
|
|
|
|
self.fonts = {
|
|
"title": (self.font_family, self.font_size + 8, "bold"),
|
|
"subtitle": (self.font_family, self.font_size + 4, "bold"),
|
|
"heading": (self.font_family, self.font_size + 2, "bold"),
|
|
"body": (self.font_family, self.font_size, "normal"),
|
|
"small": (self.font_family, self.font_size - 2, "normal")
|
|
}
|
|
|
|
def apply_theme_to_app(self, app):
|
|
"""تطبيق النمط على التطبيق"""
|
|
app.configure(fg_color=self.get_color("bg_color"))
|
|
|
|
def get_color(self, color_name):
|
|
"""الحصول على لون معين"""
|
|
if self.current_theme == "light":
|
|
colors = {
|
|
"bg_color": self.LIGHT_BG_COLOR,
|
|
"fg_color": self.LIGHT_FG_COLOR,
|
|
"card_bg_color": self.LIGHT_CARD_BG_COLOR,
|
|
"sidebar_bg_color": self.LIGHT_SIDEBAR_BG_COLOR,
|
|
"sidebar_fg_color": self.LIGHT_SIDEBAR_FG_COLOR,
|
|
"sidebar_hover_color": self.LIGHT_SIDEBAR_HOVER_COLOR,
|
|
"sidebar_active_color": self.LIGHT_SIDEBAR_ACTIVE_COLOR,
|
|
"button_bg_color": self.LIGHT_BUTTON_BG_COLOR,
|
|
"button_hover_color": self.LIGHT_BUTTON_HOVER_COLOR,
|
|
"button_active_color": self.LIGHT_BUTTON_ACTIVE_COLOR,
|
|
"input_bg_color": self.LIGHT_INPUT_BG_COLOR,
|
|
"input_fg_color": self.LIGHT_INPUT_FG_COLOR,
|
|
"border_color": self.LIGHT_BORDER_COLOR,
|
|
"primary_color": self.PRIMARY_COLOR["light"],
|
|
"secondary_color": self.SECONDARY_COLOR["light"],
|
|
"accent_color": self.ACCENT_COLOR["light"],
|
|
"warning_color": self.WARNING_COLOR["light"],
|
|
"error_color": self.ERROR_COLOR["light"],
|
|
"success_color": self.SUCCESS_COLOR["light"]
|
|
}
|
|
else:
|
|
colors = {
|
|
"bg_color": self.DARK_BG_COLOR,
|
|
"fg_color": self.DARK_FG_COLOR,
|
|
"card_bg_color": self.DARK_CARD_BG_COLOR,
|
|
"sidebar_bg_color": self.DARK_SIDEBAR_BG_COLOR,
|
|
"sidebar_fg_color": self.DARK_SIDEBAR_FG_COLOR,
|
|
"sidebar_hover_color": self.DARK_SIDEBAR_HOVER_COLOR,
|
|
"sidebar_active_color": self.DARK_SIDEBAR_ACTIVE_COLOR,
|
|
"button_bg_color": self.DARK_BUTTON_BG_COLOR,
|
|
"button_hover_color": self.DARK_BUTTON_HOVER_COLOR,
|
|
"button_active_color": self.DARK_BUTTON_ACTIVE_COLOR,
|
|
"input_bg_color": self.DARK_INPUT_BG_COLOR,
|
|
"input_fg_color": self.DARK_INPUT_FG_COLOR,
|
|
"border_color": self.DARK_BORDER_COLOR,
|
|
"primary_color": self.PRIMARY_COLOR["dark"],
|
|
"secondary_color": self.SECONDARY_COLOR["dark"],
|
|
"accent_color": self.ACCENT_COLOR["dark"],
|
|
"warning_color": self.WARNING_COLOR["dark"],
|
|
"error_color": self.ERROR_COLOR["dark"],
|
|
"success_color": self.SUCCESS_COLOR["dark"]
|
|
}
|
|
|
|
return colors.get(color_name, self.LIGHT_BG_COLOR)
|
|
|
|
def get_font(self, font_type):
|
|
"""الحصول على خط معين"""
|
|
return self.fonts.get(font_type, self.fonts["body"])
|
|
|
|
def toggle_theme(self):
|
|
"""تبديل النمط بين الفاتح والداكن"""
|
|
if self.current_theme == "light":
|
|
self.current_theme = "dark"
|
|
else:
|
|
self.current_theme = "light"
|
|
|
|
|
|
self.config.set_theme(self.current_theme)
|
|
|
|
|
|
ctk.set_appearance_mode(self.current_theme)
|
|
|
|
return self.current_theme
|
|
|
|
def create_styled_frame(self, parent, **kwargs):
|
|
"""إنشاء إطار منسق"""
|
|
default_kwargs = {
|
|
"fg_color": self.get_color("bg_color"),
|
|
"corner_radius": 10,
|
|
"border_width": 0
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkFrame(parent, **default_kwargs)
|
|
|
|
def create_styled_scrollable_frame(self, parent, **kwargs):
|
|
"""إنشاء إطار قابل للتمرير منسق"""
|
|
default_kwargs = {
|
|
"fg_color": self.get_color("bg_color"),
|
|
"corner_radius": 10,
|
|
"border_width": 0
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkScrollableFrame(parent, **default_kwargs)
|
|
|
|
def create_styled_label(self, parent, text, **kwargs):
|
|
"""إنشاء تسمية منسقة"""
|
|
default_kwargs = {
|
|
"text": text,
|
|
"font": self.get_font("body"),
|
|
"text_color": self.get_color("fg_color")
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkLabel(parent, **default_kwargs)
|
|
|
|
def create_styled_button(self, parent, text, **kwargs):
|
|
"""إنشاء زر منسق"""
|
|
default_kwargs = {
|
|
"text": text,
|
|
"font": self.get_font("body"),
|
|
"fg_color": self.get_color("button_bg_color"),
|
|
"hover_color": self.get_color("button_hover_color"),
|
|
"text_color": "white",
|
|
"corner_radius": 8,
|
|
"border_width": 0,
|
|
"height": 36
|
|
}
|
|
|
|
|
|
if "icon" in kwargs:
|
|
icon_name = kwargs.pop("icon")
|
|
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkButton(parent, **default_kwargs)
|
|
|
|
def create_styled_entry(self, parent, placeholder_text, **kwargs):
|
|
"""إنشاء حقل إدخال منسق"""
|
|
default_kwargs = {
|
|
"placeholder_text": placeholder_text,
|
|
"font": self.get_font("body"),
|
|
"fg_color": self.get_color("input_bg_color"),
|
|
"text_color": self.get_color("input_fg_color"),
|
|
"placeholder_text_color": self.get_color("border_color"),
|
|
"corner_radius": 8,
|
|
"border_width": 1,
|
|
"border_color": self.get_color("border_color"),
|
|
"height": 36
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkEntry(parent, **default_kwargs)
|
|
|
|
def create_styled_textbox(self, parent, **kwargs):
|
|
"""إنشاء مربع نص منسق"""
|
|
default_kwargs = {
|
|
"font": self.get_font("body"),
|
|
"fg_color": self.get_color("input_bg_color"),
|
|
"text_color": self.get_color("input_fg_color"),
|
|
"corner_radius": 8,
|
|
"border_width": 1,
|
|
"border_color": self.get_color("border_color")
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkTextbox(parent, **default_kwargs)
|
|
|
|
def create_styled_combobox(self, parent, values, **kwargs):
|
|
"""إنشاء قائمة منسدلة منسقة"""
|
|
default_kwargs = {
|
|
"values": values,
|
|
"font": self.get_font("body"),
|
|
"fg_color": self.get_color("input_bg_color"),
|
|
"text_color": self.get_color("input_fg_color"),
|
|
"border_color": self.get_color("border_color"),
|
|
"button_color": self.get_color("button_bg_color"),
|
|
"button_hover_color": self.get_color("button_hover_color"),
|
|
"dropdown_fg_color": self.get_color("card_bg_color"),
|
|
"dropdown_text_color": self.get_color("fg_color"),
|
|
"dropdown_hover_color": self.get_color("sidebar_hover_color"),
|
|
"corner_radius": 8,
|
|
"border_width": 1,
|
|
"dropdown_font": self.get_font("body")
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkComboBox(parent, **default_kwargs)
|
|
|
|
def create_styled_switch(self, parent, text, **kwargs):
|
|
"""إنشاء مفتاح تبديل منسق"""
|
|
default_kwargs = {
|
|
"text": text,
|
|
"font": self.get_font("body"),
|
|
"fg_color": self.get_color("border_color"),
|
|
"progress_color": self.get_color("button_bg_color"),
|
|
"button_color": "white",
|
|
"button_hover_color": "white",
|
|
"text_color": self.get_color("fg_color")
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkSwitch(parent, **default_kwargs)
|
|
|
|
def create_styled_radio_button(self, parent, text, variable, value, **kwargs):
|
|
"""إنشاء زر راديو منسق"""
|
|
default_kwargs = {
|
|
"text": text,
|
|
"font": self.get_font("body"),
|
|
"fg_color": self.get_color("button_bg_color"),
|
|
"border_color": self.get_color("border_color"),
|
|
"hover_color": self.get_color("button_hover_color"),
|
|
"text_color": self.get_color("fg_color"),
|
|
"variable": variable,
|
|
"value": value
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkRadioButton(parent, **default_kwargs)
|
|
|
|
def create_styled_slider(self, parent, **kwargs):
|
|
"""إنشاء شريط تمرير منسق"""
|
|
default_kwargs = {
|
|
"fg_color": self.get_color("border_color"),
|
|
"progress_color": self.get_color("button_bg_color"),
|
|
"button_color": self.get_color("button_bg_color"),
|
|
"button_hover_color": self.get_color("button_hover_color")
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkSlider(parent, **default_kwargs)
|
|
|
|
def create_styled_tabview(self, parent, **kwargs):
|
|
"""إنشاء عرض تبويب منسق"""
|
|
default_kwargs = {
|
|
"fg_color": self.get_color("card_bg_color"),
|
|
"segmented_button_fg_color": self.get_color("sidebar_bg_color"),
|
|
"segmented_button_selected_color": self.get_color("button_bg_color"),
|
|
"segmented_button_unselected_color": self.get_color("sidebar_bg_color"),
|
|
"segmented_button_selected_hover_color": self.get_color("button_hover_color"),
|
|
"segmented_button_unselected_hover_color": self.get_color("sidebar_hover_color"),
|
|
"segmented_button_text_color": self.get_color("sidebar_fg_color"),
|
|
"segmented_button_selected_text_color": "white",
|
|
"text_color": self.get_color("fg_color"),
|
|
"corner_radius": 10
|
|
}
|
|
|
|
|
|
for key, value in kwargs.items():
|
|
default_kwargs[key] = value
|
|
|
|
return ctk.CTkTabview(parent, **default_kwargs)
|
|
|
|
def create_styled_sidebar_button(self, parent, text, icon, command=None):
|
|
"""إنشاء زر الشريط الجانبي المنسق"""
|
|
|
|
button_frame = ctk.CTkFrame(
|
|
parent,
|
|
fg_color="transparent",
|
|
corner_radius=0
|
|
)
|
|
|
|
|
|
button = ctk.CTkButton(
|
|
button_frame,
|
|
text=text,
|
|
font=self.get_font("body"),
|
|
fg_color="transparent",
|
|
hover_color=self.get_color("sidebar_hover_color"),
|
|
text_color=self.get_color("sidebar_fg_color"),
|
|
anchor="w",
|
|
corner_radius=0,
|
|
border_width=0,
|
|
height=40,
|
|
command=command
|
|
)
|
|
button.pack(fill="x", padx=0, pady=0)
|
|
|
|
return button_frame, button
|
|
|
|
def create_styled_card(self, parent, title):
|
|
"""إنشاء بطاقة منسقة"""
|
|
|
|
card = self.create_styled_frame(
|
|
parent,
|
|
fg_color=self.get_color("card_bg_color")
|
|
)
|
|
|
|
|
|
title_label = self.create_styled_label(
|
|
card,
|
|
title,
|
|
font=self.get_font("heading")
|
|
)
|
|
title_label.pack(anchor="w", padx=15, pady=(15, 5))
|
|
|
|
|
|
separator = ctk.CTkFrame(
|
|
card,
|
|
height=1,
|
|
fg_color=self.get_color("border_color")
|
|
)
|
|
separator.pack(fill="x", padx=15, pady=(5, 0))
|
|
|
|
|
|
content_frame = self.create_styled_frame(
|
|
card,
|
|
fg_color="transparent"
|
|
)
|
|
content_frame.pack(fill="both", expand=True, padx=15, pady=15)
|
|
|
|
return card, content_frame
|
|
|
|
def create_styled_data_table(self, parent, columns, data):
|
|
"""إنشاء جدول بيانات منسق"""
|
|
|
|
table_frame = self.create_styled_frame(
|
|
parent,
|
|
fg_color="transparent"
|
|
)
|
|
|
|
|
|
header_frame = self.create_styled_frame(
|
|
table_frame,
|
|
fg_color=self.get_color("sidebar_bg_color")
|
|
)
|
|
header_frame.pack(fill="x", padx=0, pady=(0, 1))
|
|
|
|
|
|
for i, column in enumerate(columns):
|
|
column_label = self.create_styled_label(
|
|
header_frame,
|
|
column,
|
|
font=self.get_font("heading"),
|
|
text_color=self.get_color("sidebar_fg_color")
|
|
)
|
|
column_label.grid(row=0, column=i, sticky="ew", padx=10, pady=10)
|
|
header_frame.grid_columnconfigure(i, weight=1)
|
|
|
|
|
|
data_frame = self.create_styled_scrollable_frame(
|
|
table_frame,
|
|
fg_color="transparent"
|
|
)
|
|
data_frame.pack(fill="both", expand=True, padx=0, pady=0)
|
|
|
|
|
|
for i, row in enumerate(data):
|
|
row_frame = self.create_styled_frame(
|
|
data_frame,
|
|
fg_color=self.get_color("card_bg_color") if i % 2 == 0 else self.get_color("bg_color")
|
|
)
|
|
row_frame.pack(fill="x", padx=0, pady=(0, 1))
|
|
|
|
for j, cell in enumerate(row):
|
|
cell_label = self.create_styled_label(
|
|
row_frame,
|
|
cell,
|
|
font=self.get_font("body")
|
|
)
|
|
cell_label.grid(row=0, column=j, sticky="ew", padx=10, pady=10)
|
|
row_frame.grid_columnconfigure(j, weight=1)
|
|
|
|
return table_frame, data_frame
|
|
|
|
def create_styled_message_box(self, title, message, message_type="info"):
|
|
"""إنشاء مربع رسالة منسق"""
|
|
|
|
if message_type == "error":
|
|
color = self.get_color("error_color")
|
|
elif message_type == "warning":
|
|
color = self.get_color("warning_color")
|
|
elif message_type == "success":
|
|
color = self.get_color("success_color")
|
|
else:
|
|
color = self.get_color("primary_color")
|
|
|
|
|
|
message_window = ctk.CTkToplevel()
|
|
message_window.title(title)
|
|
message_window.geometry("400x200")
|
|
message_window.resizable(False, False)
|
|
message_window.grab_set()
|
|
|
|
|
|
message_window.configure(fg_color=self.get_color("bg_color"))
|
|
|
|
|
|
message_frame = self.create_styled_frame(
|
|
message_window,
|
|
fg_color=self.get_color("card_bg_color")
|
|
)
|
|
message_frame.pack(fill="both", expand=True, padx=20, pady=20)
|
|
|
|
|
|
title_label = self.create_styled_label(
|
|
message_frame,
|
|
title,
|
|
font=self.get_font("heading"),
|
|
text_color=color
|
|
)
|
|
title_label.pack(padx=20, pady=(20, 10))
|
|
|
|
|
|
message_label = self.create_styled_label(
|
|
message_frame,
|
|
message,
|
|
font=self.get_font("body")
|
|
)
|
|
message_label.pack(padx=20, pady=(0, 20))
|
|
|
|
|
|
ok_button = self.create_styled_button(
|
|
message_frame,
|
|
"موافق",
|
|
fg_color=color,
|
|
hover_color=color,
|
|
command=message_window.destroy
|
|
)
|
|
ok_button.pack(pady=(0, 20))
|
|
|
|
return message_window
|
|
|