Untitled
Anonymous
plain_text
02/28/2026 5:51 AM
17.6 KB
12
Indexable
import sys
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QComboBox, QPushButton, QGridLayout, QScrollArea,
QFrame, QMessageBox, QSpacerItem, QSizePolicy
)
from PyQt6.QtCore import Qt, QSize
from PyQt6.QtGui import QFont, QIcon
import pymysql
from datetime import datetime
DB_CONFIG = {
'host': 'localhost',
'user': 'root',
'password': '',
'database': 'coworking_db',
'cursorclass': pymysql.cursors.DictCursor
}
class WorkplaceCard(QFrame):
def __init__(self, data):
super().__init__()
self.setFrameShape(QFrame.Shape.StyledPanel)
self.setLineWidth(1)
self.setFixedSize(220, 180)
self.setObjectName("WorkplaceCard")
self.layout = QVBoxLayout(self)
self.id_label = QLabel(data['workplace_id'])
self.id_label.setObjectName("CardId")
self.id_label.setFont(QFont("Arial", 16, QFont.Weight.Bold))
self.zone_label = QLabel(data['zone_name'])
self.zone_label.setObjectName("CardZone")
self.zone_label.setFont(QFont("Arial", 10))
self.type_label = QLabel(data['workplace_type'])
self.type_label.setObjectName("CardType")
self.type_label.setFont(QFont("Arial", 9))
self.type_label.setStyleSheet("color: #555;")
self.status_label = QLabel()
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.status_label.setFixedSize(200, 30)
self.status_label.setObjectName("CardStatus")
self.status_label.setFont(QFont("Arial", 10, QFont.Weight.Bold))
if data['end_time']:
end_time_str = data['end_time'].strftime('%d.%m %H:%M')
self.status_label.setText(f"Занято до {end_time_str}")
self.status_label.setStyleSheet("background-color: #fce8b2; color: #a67d03; border-radius: 5px;")
else:
self.status_label.setText("Свободно")
self.status_label.setStyleSheet("background-color: #c8e6c9; color: #2e7d32; border-radius: 5px;")
self.cost_label = QLabel(f"{int(data['hourly_cost'])} ₽/час")
self.cost_label.setObjectName("CardCost")
self.cost_label.setFont(QFont("Arial", 12, QFont.Weight.Bold))
self.cost_label.setAlignment(Qt.AlignmentFlag.AlignRight)
self.layout.addWidget(self.id_label)
self.layout.addWidget(self.zone_label)
self.layout.addWidget(self.type_label)
self.layout.addStretch(1)
self.layout.addWidget(self.status_label)
self.layout.addWidget(self.cost_label)
self.setLayout(self.layout)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.db_connection = None
self.current_page = 1
self.items_per_page = 8
self.total_items = 0
self.setWindowTitle("Бронирование рабочих мест")
self.setGeometry(100, 100, 1200, 800)
self.setStyleSheet("""
QMainWindow { background-color: #f0f2f5; }
#WorkplaceCard { border: 1px solid #e0e0e0; border-radius: 8px; background-color: white; }
#FilterPanel { background-color: white; border-radius: 8px; padding: 10px; }
QPushButton {
background-color: #007bff; color: white; border-radius: 5px;
padding: 8px 16px; font-size: 14px; border: none;
}
QPushButton:hover { background-color: #0056b3; }
QLineEdit, QComboBox {
border: 1px solid #ced4da; border-radius: 5px;
padding: 8px; font-size: 14px;
}
QLabel { font-size: 14px; }
#HeaderLabel { font-size: 16px; color: #333; }
#CardId { font-size: 18px; font-weight: bold; color: #333; }
#CardZone { font-size: 14px; color: #444; }
#CardType { font-size: 12px; color: #666; }
#CardCost { font-size: 16px; font-weight: bold; color: #007bff; }
#PageLabel { font-size: 14px; color: #555; }
#NavButton { background-color: #6c757d; }
#NavButton:hover { background-color: #5a6268; }
#NavButton:disabled { background-color: #e9ecef; color: #adb5bd; }
""")
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.main_layout = QHBoxLayout(self.central_widget)
self.setup_navigation_panel()
self.setup_content_panel()
try:
self.connect_to_db()
self.populate_filters()
self.load_workplaces()
except pymysql.Error as e:
QMessageBox.critical(self, "Ошибка базы данных", f"Не удалось подключиться к базе данных или загрузить данные: {e}")
sys.exit(1)
def connect_to_db(self):
self.db_connection = pymysql.connect(**DB_CONFIG)
def setup_navigation_panel(self):
nav_panel = QFrame()
nav_panel.setFrameShape(QFrame.Shape.StyledPanel)
nav_panel.setFixedWidth(200)
nav_layout = QVBoxLayout(nav_panel)
nav_layout.addWidget(QLabel("Бронирование\nрабочих мест", font=QFont("Arial", 16, QFont.Weight.Bold)))
nav_layout.addSpacerItem(QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding))
self.main_layout.addWidget(nav_panel)
def setup_content_panel(self):
content_panel = QWidget()
content_layout = QVBoxLayout(content_panel)
header_layout = QHBoxLayout()
welcome_label = QLabel("Добро пожаловать, Пользователь")
welcome_label.setObjectName("HeaderLabel")
exit_button = QPushButton("Выйти")
exit_button.clicked.connect(self.close)
exit_button.setFixedWidth(100)
header_layout.addWidget(welcome_label)
header_layout.addStretch()
header_layout.addWidget(exit_button)
content_layout.addLayout(header_layout)
filter_panel = QWidget()
filter_panel.setObjectName("FilterPanel")
filter_layout = QHBoxLayout(filter_panel)
self.zone_search = QLineEdit()
self.zone_search.setPlaceholderText("Поиск зоны...")
self.type_combo = QComboBox()
self.status_combo = QComboBox()
filter_button = QPushButton("Фильтровать")
filter_button.clicked.connect(self.apply_filters)
filter_layout.addWidget(self.zone_search)
filter_layout.addWidget(QLabel("Тип:"))
filter_layout.addWidget(self.type_combo)
filter_layout.addWidget(QLabel("Статус:"))
filter_layout.addWidget(self.status_combo)
filter_layout.addWidget(filter_button)
content_layout.addWidget(filter_panel)
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
self.grid_container = QWidget()
self.grid_layout = QGridLayout(self.grid_container)
self.grid_layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft)
scroll_area.setWidget(self.grid_container)
content_layout.addWidget(scroll_area)
pagination_layout = QHBoxLayout()
self.prev_button = QPushButton("<")
self.prev_button.setObjectName("NavButton")
self.prev_button.setFixedSize(40, 40)
self.prev_button.clicked.connect(self.prev_page)
self.page_label = QLabel("1-8 из 18")
self.page_label.setObjectName("PageLabel")
self.next_button = QPushButton(">")
self.next_button.setObjectName("NavButton")
self.next_button.setFixedSize(40, 40)
self.next_button.clicked.connect(self.next_page)
pagination_layout.addStretch()
pagination_layout.addWidget(self.prev_button)
pagination_layout.addWidget(self.page_label)
pagination_layout.addWidget(self.next_button)
pagination_layout.addStretch()
content_layout.addLayout(pagination_layout)
self.main_layout.addWidget(content_panel)
def populate_filters(self):
self.type_combo.addItem("Все типы", userData=None)
self.status_combo.addItem("Все статусы", userData=None)
self.status_combo.addItem("Свободно", userData="available")
self.status_combo.addItem("Занято", userData="occupied")
try:
with self.db_connection.cursor() as cursor:
cursor.execute("SELECT workplace_type_id, type_name FROM WorkplaceTypes ORDER BY type_name")
types = cursor.fetchall()
for t in types:
self.type_combo.addItem(t['type_name'], userData=t['workplace_type_id'])
except pymysql.Error as e:
QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить типы рабочих мест: {e}")
def apply_filters(self):
self.current_page = 1
self.load_workplaces()
def load_workplaces(self):
try:
with self.db_connection.cursor() as cursor:
base_query = """
SELECT SQL_CALC_FOUND_ROWS
w.workplace_id, z.zone_name, wt.type_name AS workplace_type, w.hourly_cost, b.end_time
FROM Workplaces AS w
JOIN Zones AS z ON w.zone_id = z.zone_id
JOIN WorkplaceTypes AS wt ON w.workplace_type_id = wt.workplace_type_id
LEFT JOIN (
SELECT workplace_id, end_time
FROM Bookings
WHERE NOW() BETWEEN start_time AND end_time
) AS b ON w.workplace_id = b.workplace_id
"""
conditions = []
params = []
if self.zone_search.text():
conditions.append("z.zone_name LIKE %s")
params.append(f"%{self.zone_search.text()}%")
if self.type_combo.currentData():
conditions.append("w.workplace_type_id = %s")
params.append(self.type_combo.currentData())
status_filter = self.status_combo.currentData()
if status_filter == "available":
conditions.append("b.end_time IS NULL")
elif status_filter == "occupied":
conditions.append("b.end_time IS NOT NULL")
if conditions:
base_query += " WHERE " + " AND ".join(conditions)
offset = (self.current_page - 1) * self.items_per_page
base_query += f" ORDER BY w.workplace_id LIMIT {self.items_per_page} OFFSET {offset}"
cursor.execute(base_query, tuple(params))
workplaces = cursor.fetchall()
cursor.execute("SELECT FOUND_ROWS()")
self.total_items = cursor.fetchone()['FOUND_ROWS()']
self.display_workplaces(workplaces)
self.update_pagination()
except pymysql.Error as e:
QMessageBox.critical(self, "Ошибка загрузки", f"Не удалось загрузить данные о рабочих местах: {e}")
def display_workplaces(self, workplaces):
for i in reversed(range(self.grid_layout.count())):
self.grid_layout.itemAt(i).widget().setParent(None)
if not workplaces:
self.grid_layout.addWidget(QLabel("Рабочие места не найдены."), 0, 0, 1, 4)
return
row, col = 0, 0
for workplace in workplaces:
card = WorkplaceCard(workplace)
self.grid_layout.addWidget(card, row, col)
col += 1
if col > 3:
col = 0
row += 1
def update_pagination(self):
start_item = (self.current_page - 1) * self.items_per_page + 1
end_item = min(self.current_page * self.items_per_page, self.total_items)
if self.total_items == 0:
self.page_label.setText("0 из 0")
else:
self.page_label.setText(f"{start_item}-{end_item} из {self.total_items}")
self.prev_button.setEnabled(self.current_page > 1)
self.next_button.setEnabled(end_item < self.total_items)
def prev_page(self):
if self.current_page > 1:
self.current_page -= 1
self.load_workplaces()
def next_page(self):
if (self.current_page * self.items_per_page) < self.total_items:
self.current_page += 1
self.load_workplaces()
def closeEvent(self, event):
if self.db_connection:
self.db_connection.close()
super().closeEvent(event)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec()) Editor is loading...
Leave a Comment