diff --git a/files/style.qss b/files/style.qss new file mode 100644 index 0000000..0cfe6e1 --- /dev/null +++ b/files/style.qss @@ -0,0 +1,92 @@ +/* Стилизация для Windows 10/11 в PyMASL */ + +QWidget { + font-family: "Segoe UI", "San Francisco", "Helvetica Neue", sans-serif; + font-size: 14px; + color: #212121; +} + +QMainWindow, QDialog { + background-color: #F3F3F3; +} + +QPushButton { + background-color: #E1E1E1; + border: 1px solid #ADADAD; + border-radius: 4px; + padding: 6px 16px; + font-weight: 500; +} + +QPushButton:hover { + background-color: #E5F1FB; + border: 1px solid #0078D4; +} + +QPushButton:pressed { + background-color: #CCE4F7; + border: 1px solid #005499; +} + +QPushButton:disabled { + background-color: #F0F0F0; + color: #A0A0A0; + border: 1px solid #D0D0D0; +} + +QLineEdit, QComboBox, QDateEdit { + background-color: #FFFFFF; + border: 1px solid #CCCCCC; + border-radius: 4px; + padding: 4px 8px; + selection-background-color: #0078D4; + selection-color: white; +} + +QLineEdit:hover, QComboBox:hover, QDateEdit:hover { + border: 1px solid #0078D4; +} + +QLineEdit:focus, QComboBox:focus, QDateEdit:focus { + border: 2px solid #0078D4; + padding: 3px 7px; /* Компенсируем увеличение border */ +} + +QTableWidget { + background-color: #FFFFFF; + alternate-background-color: #F9F9F9; + border: 1px solid #CCCCCC; + gridline-color: #E0E0E0; + selection-background-color: #0078D4; + selection-color: white; +} + +QHeaderView::section { + background-color: #F3F3F3; + padding: 4px; + border: none; + border-right: 1px solid #E0E0E0; + border-bottom: 1px solid #E0E0E0; + font-weight: bold; +} + +QScrollBar:vertical { + border: none; + background-color: #F3F3F3; + width: 12px; + margin: 0px 0px 0px 0px; +} + +QScrollBar::handle:vertical { + background-color: #CDCDCD; + min-height: 20px; + border-radius: 6px; +} + +QScrollBar::handle:vertical:hover { + background-color: #A6A6A6; +} + +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + height: 0px; +} diff --git a/main.py b/main.py index c7f54d0..1fb1cce 100644 --- a/main.py +++ b/main.py @@ -222,7 +222,8 @@ def show_main_window(): f"PyMASL ver. {system.software_version}. Пользователь: {user_full_name}. БД: {system.database}" ) window.main_button_all_sales() - window.exec_() + # Сохраняем ссылку на окно, чтобы сборщик мусора его не удалил + QtWidgets.QApplication.instance().main_window = window @@ -429,7 +430,7 @@ def edit_client_in_sale(self) -> None: # Сохраняем параметры данных об уже существующем клиенте system.client_update = 1 logger.info(f"Обновляем инф. клиента {system.client_id}") - client.exec_() + client.exec() def adding_new_client_to_sale(self) -> None: """ @@ -2344,7 +2345,7 @@ def open_pay_form(self, txt): # Передаем текст в форму PayForm pay.setText(txt) - res: int = pay.exec_() + res: int = pay.exec() # если пользователь нажал крестик или кнопку Escape, # то по-умолчанию возвращается QDialog.Rejected if res == QDialog.Rejected: @@ -2523,7 +2524,7 @@ def get_slip(self) -> None: slip.ui.lineEdit.setText(rrn_value) slip.ui.textEdit.setText(load_slip) slip.show() - slip.exec_() + slip.exec() class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): @@ -2835,7 +2836,7 @@ def main_edit_client(self) -> None: # Сохраняем параметры данных об уже существующем клиенте system.client_update = 1 logger.info(f"Обновляем информацию о клиенте") - client.exec_() + client.exec() def main_filter_clear(self) -> None: """ @@ -3130,7 +3131,7 @@ def main_search_selected_sale(self) -> None: system.sale_id = int(sale_number) system.sale_tickets = client_in_sale logger.debug(f"Билеты сохраненной продажи: {system.sale_tickets}") - sale.exec_() + sale.exec() @logger_wraps(entry=True, exit=True, level="DEBUG") def main_button_all_sales(self) -> None: @@ -3316,7 +3317,7 @@ def main_open_sale(self) -> None: system.sale_id = None system.sale_status = 0 # Т.е. новая продажа system.sale_tickets = None - sale.exec_() + sale.exec() def main_get_statistic(self) -> None: """ @@ -3556,6 +3557,19 @@ class Payment: if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) app.setStyle("Fusion") + + # Динамическая загрузка файла QSS + try: + qss_path = Path(__file__).parent / "files" / "style.qss" + if qss_path.exists(): + with open(qss_path, "r", encoding="utf-8") as style_file: + app.setStyleSheet(style_file.read()) + logger.info("Стили QSS успешно загружены.") + else: + logger.warning(f"Файл стилей не найден: {qss_path}") + except Exception as e: + logger.error(f"Ошибка при загрузке стилей QSS: {e}") + auth = AuthForm() auth.show() auth.ui.label_9.setText(system.database) diff --git a/modules/windows.py b/modules/windows.py index bef3e4d..74c4a80 100644 --- a/modules/windows.py +++ b/modules/windows.py @@ -26,7 +26,7 @@ def info_dialog_window(title, text): buttonN.setText("Нет") # Отображаем окно поверх всех box.setWindowFlags(box.windowFlags() | Qt.WindowStaysOnTopHint) - box.exec_() + box.exec() if box.clickedButton() == buttonY: return 1 diff --git a/tests/test_windows.py b/tests/test_windows.py index b9f29fa..0244a4b 100644 --- a/tests/test_windows.py +++ b/tests/test_windows.py @@ -38,7 +38,7 @@ def test_info_dialog_window_yes(self, mock_msgbox_class): mock_msgbox_inst.setWindowTitle.assert_called_with("Title") mock_msgbox_inst.setText.assert_called_with("Text") - self.assertTrue(mock_msgbox_inst.exec_.called) + self.assertTrue(mock_msgbox_inst.exec.called) self.assertEqual(result, 1) @patch('modules.windows.QMessageBox')