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