feat(app): 新增了模拟上位机的软件程序

1. 新增了模拟上位机软件,方便调试下位机
2. 编写了模拟上位机的使用说明文档
3. 修改了README的相关部分
This commit is contained in:
lyz 2023-06-20 18:54:42 +08:00
parent 6449ebeeac
commit 4f6a3953c8
58 changed files with 16771 additions and 4 deletions

7
.gitignore vendored
View File

@ -65,3 +65,10 @@
Network Trash Folder Network Trash Folder
Temporary Items Temporary Items
.apdisk .apdisk
__pycache__/
*.py[cod]
venv/
.idea/

View File

@ -25,6 +25,7 @@ IO扩展版提供了
- [develop_and_deploy.md](doc/develop_and_deploy.md)为开发和部署说明,首先看这个文档 - [develop_and_deploy.md](doc/develop_and_deploy.md)为开发和部署说明,首先看这个文档
- [hardware_description.md](doc/hardware_description.md)为PL端逻辑设计说明阐述了硬件工作的整体流程 - [hardware_description.md](doc/hardware_description.md)为PL端逻辑设计说明阐述了硬件工作的整体流程
- [pl_reference_mannual.md](doc/pl_reference_mannual.md)为PL端逻辑在AXI总线上映射的寄存器参考手册 - [pl_reference_mannual.md](doc/pl_reference_mannual.md)为PL端逻辑在AXI总线上映射的寄存器参考手册
- [sim_uppermachine_manual.md](doc/sim_uppermachine_manual.md)为模拟上位机运行的参考手册
- script为配置系统、安装环境、安装可执行文件、卸载可执行文件等的脚本 - script为配置系统、安装环境、安装可执行文件、卸载可执行文件等的脚本
@ -42,13 +43,14 @@ IO扩展版提供了
- pl_platform为PL端硬件设计 - pl_platform为PL端硬件设计
- xme0724ioextend为IO叠板的原理图和PCB - xme0724ioextend为IO叠板的原理图和PCB
- source为XME0724板子上运行的源程序 - source为XME0724板子上运行的源程序和模拟上位机程序
- liunx_app为Linux上运行的应用程序即业务逻辑 - liunx_app为Linux上运行的应用程序即业务逻辑
- linux_driver为Linux上的驱动用于控制自定义的PL端硬件其中drv_test结尾的目录为相应驱动模块的测试应用程序 - linux_driver为Linux上的驱动用于控制自定义的PL端硬件其中drv_test结尾的目录为相应驱动模块的测试应用程序
- petalinux_config为petalinux工具在编译u-boot、kernel、rootfs前进行的配置 - petalinux_config为petalinux工具在编译u-boot、kernel、rootfs前进行的配置
- petalinux_devicetree为本次自定义的Linux设备树文件部分其余设备树为自动生成的 - petalinux_devicetree为本次自定义的Linux设备树文件部分其余设备树为自动生成的
- petalinux_hwdescription为petalinux所使用的硬件描述文件包含了vivado工程中的比特流等信息 - petalinux_hwdescription为petalinux所使用的硬件描述文件包含了vivado工程中的比特流等信息
- sim_uppermachine_manul为基于pyside6所编写的软件用于模拟上位机发送指令方便调试
## 版本 ## 版本
@ -74,4 +76,4 @@ b分支编号-d文档版本-hPCB设计版本-lFPGA设计版本-p协议版本-s
## 作者 ## 作者
作者觉得还是不说明作者是谁比较好,免得毕业后有提着示波器的师弟师妹来问问题 作者觉得还是不说明是谁比较好,免得毕业后有提着示波器的师弟师妹来问问题

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@ -0,0 +1,11 @@
# 模拟上位机说明文档
## 模拟上位机连接下位机
模拟上位机使用python编写如下图所示实现显示界面并与下位机通信发送四个相机的分频值。运行程序后出现显示界面连接网线并选择Server IP为运行模拟上位机程序的本机系统中手动设定的IP地址Server Port端号为13452(默认)。选择完成后等待连接当界面下方出现本机与下位机的IP地址时即为连接成功。
## 模拟上位机发送指令
模拟上位机与下位机连接成功后可在MANUAL栏中发送通信协议中对应的命令在PRESET栏中填写四个相机触发所需的分频值在发送分频值前需要先点击停止按钮然后点击对应的A、B、C、D按钮即可发送各相机对应的分频值最后点击Start按钮即可将分频值发送给下位机。
![](sim_uppermachine_manual.assets/2.png)

View File

@ -1 +1 @@
1.6 1.7

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -0,0 +1,328 @@
"""
BY: Miaow, Wanderson M.Pimenta
PROJECT MADE WITH: Qt Designer and PySide6
V: 1.0.0
This project can be used freely for all uses, as long as they maintain the
respective credits only in the Python scripts, any information in the visual
interface (GUI) can be modified without any implication.
There are limitations on Qt licenses if you want to use your products
commercially, I recommend reading them on the official website:
https://doc.qt.io/qtforpython/licenses.html
"""
import re
import sys
import os
import time
from threading import Thread, Event
from binascii import unhexlify, hexlify
from typing import Optional
import json
import PySide6
from modules.list_model import PacketListModel
from modules.protocol import Protocol
from PySide6.QtCore import Signal
from modules import *
from PySide6.QtWidgets import QMainWindow, QApplication, QComboBox, QLineEdit, QSpinBox
from PySide6.QtGui import QIcon, QFontMetrics, QStandardItemModel, QStandardItem
from modules.ui_main import Ui_MainWindow
from modules.server import Server
# os.environ["QT_FONT_DPI"] = "96" # FIX Problem for High DPI and Scale above 100%
class MainWindow(QMainWindow):
connected_signal = Signal()
disconnected_signal = Signal()
send_signal = Signal(bytes)
receive_signal = Signal(bytes)
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.packet_list_model = QStandardItemModel()
with open("settings.json") as f:
self.settings = json.load(f)
UIFunctions.uiDefinitions(self) # set ui, e.g. title bar, grip & resize, min/max/close
self.total_send_bytes, self.total_send_packets = 0, 0
self.total_receive_bytes, self.total_receive_packets = 0, 0
# ===buttons on left menu bar===
self.ui.toggleButton.clicked.connect(lambda: UIFunctions.toggleMenu(self, True))
self.ui.btn_home.clicked.connect(self.menu_btn_clicked)
self.ui.btn_connection.clicked.connect(self.menu_btn_clicked)
self.ui.btn_divider.clicked.connect(self.menu_btn_clicked)
self.ui.btn_valvedata.clicked.connect(self.menu_btn_clicked)
# set custom theme
# UIFunctions.theme(self, "themes\py_dracula_light.qss", True)
# AppFunctions.setThemeHack(self)
# ===page_connection===
page_connection_settings = self.settings["page_connection"]
self.ui.sb_server_port.setValue(page_connection_settings["server_port"])
# ===page_divider===
page_divider_settings = self.settings["page_divider"]
[self.ui.__dict__[f"pb_send_{i}"].clicked.connect(self.pb_send_clicked) for i in range(1, 6)]
for t in ["start", "command", "data", "end"]:
for i in range(1, 6):
cb = self.ui.__dict__[f"cb_{t}_{i}"]
cb.currentTextChanged.connect(self.manual_current_text_changed)
cb.clear()
cb.addItems(page_divider_settings["manual"][f"cb_{t}_list"])
cb.setCurrentText(page_divider_settings["manual"]["data"][i][f"cb_{t}"])
[self.ui.__dict__[f"pb_send_camera_{t}"].clicked.connect(self.pb_send_camera_clicked) for t in "abcd"]
self.ui.le_preset_num.textChanged.connect(self.le_preset_num_text_changed)
self.ui.le_preset_num.setText(str(page_divider_settings["preset"]["le_preset_num"]))
self.ui.btn_preset_next.clicked.connect(self.btn_preset_next_clicked)
self.ui.btn_preset_previous.clicked.connect(self.btn_preset_previous_clicked)
self.ui.btn_preset_add.clicked.connect(self.btn_preset_add_clicked)
self.ui.pb_send_camera_all.clicked.connect(self.pb_send_camera_all_clicked)
self.ui.pb_send_start.clicked.connect(self.pb_send_start_clicked)
self.ui.pb_send_stop.clicked.connect(self.pb_send_stop_clicked)
self.ui.pb_divider_restore.clicked.connect(self.pb_divider_restore_clicked)
# ===show main window, start at page_home page with stopped status===
self.show()
self.ui.btn_home.click()
self.connected_signal.connect(self.slot_connected_signal)
self.disconnected_signal.connect(self.slot_disconnected_signal)
self.send_signal.connect(self.slot_send_signal)
self.receive_signal.connect(self.slot_receive_signal)
self.server: Optional[Server] = None
self.init_server()
def init_server(self):
if self.server is not None:
self.server.close()
self.server = Server()
self.server.set_connected_signal(self.connected_signal)
self.server.set_disconnected_signal(self.disconnected_signal)
self.server.set_send_signal(self.send_signal)
self.server.set_receive_signal(self.receive_signal)
last_selected = self.ui.cb_server_ip.currentText()
self.ui.cb_server_ip.clear()
ip_list = Server.get_server_ip_list()
self.ui.cb_server_ip.addItems(ip_list)
if last_selected is not None and last_selected.strip() != "" and last_selected in ip_list:
self.ui.cb_server_ip.setCurrentText(last_selected)
else:
self.ui.cb_server_ip.setCurrentIndex(0)
def slot_send_signal(self, data: bytes):
self.total_send_bytes += len(data)
self.total_send_packets += 1
self.ui.lbl_tx_count.setText(f"{self.total_send_bytes} bytes | {self.total_send_packets} packets")
data = hexlify(data, " ").decode("ascii").upper()
UIFunctions.set_elide_text(self.ui.lbl_datagram, data)
item = QStandardItem(data)
item.setIcon(QIcon(u":/icons/images/icons/cil-cloud-upload-small.png"))
self.packet_list_model.appendRow(item)
def slot_receive_signal(self, data: bytes):
self.total_receive_bytes += len(data)
self.total_receive_packets += 1
self.ui.lbl_rx_count.setText(f"{self.total_receive_bytes} bytes | {self.total_receive_packets} packets")
if data != b"\xaa\x00\x00\x00\x03hb\xff\xff\xff\xbb":
data = hexlify(data, " ").decode("ascii").upper()
UIFunctions.set_elide_text(self.ui.lbl_datagram, data)
item = QStandardItem(data)
item.setIcon(QIcon(u":/icons/images/icons/cil-cloud-download-small.png"))
self.packet_list_model.appendRow(item)
def slot_connected_signal(self):
self.ui.page_divider.setEnabled(True)
self.ui.lbl_server_addr.setText(f"{self.server.ip}:{self.server.port}")
self.ui.lbl_client_addr.setText(f"{self.server.client_ip}:{self.server.client_port}")
self.ui.lv_packets.setModel(self.packet_list_model)
def slot_disconnected_signal(self):
self.ui.page_divider.setEnabled(False)
self.ui.lbl_server_addr.setText(f"0.0.0.0:0")
self.ui.lbl_client_addr.setText(f"0.0.0.0:0")
def menu_btn_clicked(self):
btn = self.sender()
btnName = btn.objectName()
# show page_home page
if btnName == "btn_home":
self.ui.stackedWidget.setCurrentWidget(self.ui.page_home)
UIFunctions.resetStyle(self, btnName)
btn.setStyleSheet(UIFunctions.selectMenu(btn.styleSheet()))
# show settings page
if btnName == "btn_connection":
self.ui.stackedWidget.setCurrentWidget(self.ui.page_connection)
UIFunctions.resetStyle(self, btnName)
btn.setStyleSheet(UIFunctions.selectMenu(btn.styleSheet()))
# transmission page
if btnName == "btn_divider":
self.ui.stackedWidget.setCurrentWidget(self.ui.page_divider) # SET PAGE
UIFunctions.resetStyle(self, btnName) # RESET ANOTHERS BUTTONS SELECTED
btn.setStyleSheet(UIFunctions.selectMenu(btn.styleSheet())) # SELECT MENU
if self.server.ip is None or self.server.ip != self.ui.cb_server_ip.currentText() or self.server.port != self.ui.sb_server_port.value():
self.init_server()
self.server.start_listen_thread(self.ui.cb_server_ip.currentText(), self.ui.sb_server_port.value())
# show table page
if btnName == "btn_valvedata":
self.ui.stackedWidget.setCurrentWidget(self.ui.page_valvedata)
UIFunctions.resetStyle(self, btnName)
btn.setStyleSheet(UIFunctions.selectMenu(btn.styleSheet()))
def pb_send_clicked(self):
try:
index = int(self.sender().objectName()[-1])
cb_start: QComboBox = self.ui.__dict__[f"cb_start_{index}"]
cb_command: QComboBox = self.ui.__dict__[f"cb_command_{index}"]
cb_data: QComboBox = self.ui.__dict__[f"cb_data_{index}"]
cb_end: QComboBox = self.ui.__dict__[f"cb_end_{index}"]
start = unhexlify(cb_start.currentText().replace(" ", ""))
command = cb_command.currentText().replace(" ", "").encode("ascii").lower()
data = unhexlify(cb_data.currentText().replace(" ", ""))
end = unhexlify(cb_end.currentText().replace(" ", ""))
p = Protocol(start=start, cmd=command, data=data, end=end)
self.server.send_datagram(p.get_datagram())
except BaseException as e:
pass
def pb_divider_restore_clicked(self):
self.ui.le_preset_num.setText("1")
def pb_send_camera_clicked(self):
try:
abcd = self.sender().objectName()[-1]
sb_camera: QSpinBox = self.ui.__dict__[f"sb_camera_{abcd}"]
data = sb_camera.value()
p = Protocol(cmd=f"p{abcd}", data=f"{data:08d}".encode("ascii"))
self.server.send_datagram(p.get_datagram())
except BaseException as e:
pass
def manual_current_text_changed(self):
try:
index = int(self.sender().objectName()[-1])
cb_start: QComboBox = self.ui.__dict__[f"cb_start_{index}"]
cb_command: QComboBox = self.ui.__dict__[f"cb_command_{index}"]
cb_data: QComboBox = self.ui.__dict__[f"cb_data_{index}"]
cb_end: QComboBox = self.ui.__dict__[f"cb_end_{index}"]
start = unhexlify(cb_start.currentText().replace(" ", ""))
command = cb_command.currentText().replace(" ", "").encode("ascii").lower()
data = unhexlify(cb_data.currentText().replace(" ", ""))
end = unhexlify(cb_end.currentText().replace(" ", ""))
p = Protocol(start=start, cmd=command, data=data, end=end)
le_length: QLineEdit = self.ui.__dict__[f"le_length_{index}"]
le_check: QLineEdit = self.ui.__dict__[f"le_check_{index}"]
le_length.setText(str(p.get_length()))
le_check.setText(hexlify(p.get_check_bytes(), " ").decode("ascii"))
except BaseException as e:
le_length: QLineEdit = self.ui.__dict__[f"le_length_{index}"]
le_check: QLineEdit = self.ui.__dict__[f"le_check_{index}"]
le_length.setText("--")
le_check.setText("--")
def le_preset_num_text_changed(self):
index = int(self.ui.le_preset_num.text())
data = self.settings["page_divider"]["preset"]["data"][index - 1]
self.ui.le_preset_name.setText(data["le_preset_name"])
[self.ui.__dict__[f"sb_camera_{t}"].setValue(data[f"sb_camera_{t}"]) for t in "abcd"]
self.ui.sb_valve.setValue(data["sb_valve"])
self.ui.cb_from_camera.setCurrentText(data["cb_from_camera"])
self.ui.le_to_valve.setText(str(data["le_to_valve"]))
length = len(self.settings["page_divider"]["preset"]["data"])
self.ui.btn_preset_next.setEnabled(index != length)
self.ui.btn_preset_previous.setEnabled(index != 1)
def btn_preset_next_clicked(self):
last_index = int(self.ui.le_preset_num.text())
length = len(self.settings["page_divider"]["preset"]["data"])
if last_index < length:
last_index += 1
self.ui.le_preset_num.setText(str(last_index))
def btn_preset_previous_clicked(self):
last_index = int(self.ui.le_preset_num.text())
if last_index > 1:
last_index -= 1
self.ui.le_preset_num.setText(str(last_index))
def btn_preset_add_clicked(self):
data = self.settings["page_divider"]["preset"]["data"]
tmp_name = self.ui.le_preset_name.text()
tmp_name_adj = tmp_name.replace("(", r"\(").replace(")", r"\)")
num_list = []
for i in data:
l = re.findall(rf"^{tmp_name_adj}\s\((\d+)\)$", i["le_preset_name"])
if len(l) != 0:
num_list.append(int(l[-1]))
if len(num_list) != 0:
tmp_name += f" ({max(num_list) + 1})"
for i in data:
if tmp_name == i["le_preset_name"]:
tmp_name += " (1)"
break
new_dict = {
"le_preset_name": tmp_name,
"sb_camera_a": self.ui.sb_camera_a.value(),
"sb_camera_b": self.ui.sb_camera_b.value(),
"sb_camera_c": self.ui.sb_camera_c.value(),
"sb_camera_d": self.ui.sb_camera_d.value(),
"sb_valve": self.ui.sb_valve.value(),
"cb_from_camera": self.ui.cb_from_camera.currentText(),
"le_to_valve": int(self.ui.le_to_valve.text())
}
data.append(new_dict)
length = len(data)
self.ui.le_preset_name.setText(tmp_name)
self.ui.le_preset_num.setText(str(length))
def pb_send_camera_all_clicked(self):
try:
[self.server.send_datagram(Protocol(cmd=f"p{abcd}",
data=f"{self.ui.__dict__[f'sb_camera_{abcd}'].value():08d}".encode(
"ascii")).get_datagram()) for abcd in "abcd"]
except BaseException as e:
pass
def pb_send_start_clicked(self):
try:
p = Protocol(cmd=b"st", data=b"\xff")
self.server.send_datagram(p.get_datagram())
except BaseException as e:
pass
def pb_send_stop_clicked(self):
try:
p = Protocol(cmd=b"sp", data=b"\xff")
self.server.send_datagram(p.get_datagram())
except BaseException as e:
pass
def closeEvent(self, event: PySide6.QtGui.QCloseEvent) -> None:
super().closeEvent(event)
self.server.close()
with open(self.ui.le_profile_file.text(), "w") as f:
json.dump(self.settings, f)
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setWindowIcon(QIcon("icon.ico"))
window = MainWindow()
sys.exit(app.exec())

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
"""
BY: Miaow, Wanderson M.Pimenta
PROJECT MADE WITH: Qt Designer and PySide6
V: 1.0.0
This project can be used freely for all uses, as long as they maintain the
respective credits only in the Python scripts, any information in the visual
interface (GUI) can be modified without any implication.
There are limitations on Qt licenses if you want to use your products
commercially, I recommend reading them on the official website:
https://doc.qt.io/qtforpython/licenses.html
"""
# from PySide6.QtCore import *
# from PySide6.QtGui import *
# from PySide6.QtWidgets import *
# GUI FILE
from .ui_main import Ui_MainWindow
# APP SETTINGS
from .app_settings import Settings
# IMPORT FUNCTIONS
from .ui_functions import UIFunctions
# APP FUNCTIONS
from .app_functions import *

View File

@ -0,0 +1,37 @@
"""
BY: Miaow, Wanderson M.Pimenta
PROJECT MADE WITH: Qt Designer and PySide6
V: 1.0.0
This project can be used freely for all uses, as long as they maintain the
respective credits only in the Python scripts, any information in the visual
interface (GUI) can be modified without any implication.
There are limitations on Qt licenses if you want to use your products
commercially, I recommend reading them on the official website:
https://doc.qt.io/qtforpython/licenses.html
"""
from main import MainWindow
from modules.app_settings import Settings
from PySide6.QtCore import QAbstractTableModel, Qt
class AppFunctions(MainWindow):
def set_theme_hack(self):
Settings.BTN_LEFT_BOX_COLOR = "background-color: #495474;"
Settings.BTN_RIGHT_BOX_COLOR = "background-color: #495474;"
Settings.MENU_SELECTED_STYLESHEET = MENU_SELECTED_STYLESHEET = """
border-left: 22px solid qlineargradient(spread:pad, x1:0.034, y1:0, x2:0.216, y2:0, stop:0.499 rgba(255, 121, 198, 255), stop:0.5 rgba(85, 170, 255, 0));
background-color: #566388;
"""
# SET MANUAL STYLES
# self.ui.lineEdit.setStyleSheet("background-color: #6272a4;")
# self.ui.pushButton.setStyleSheet("background-color: #6272a4;")
# self.ui.plainTextEdit.setStyleSheet("background-color: #6272a4;")
# self.ui.tableWidget.setStyleSheet("QScrollBar:vertical { background: #6272a4; } QScrollBar:horizontal { background: #6272a4; }")
# self.ui.scrollArea.setStyleSheet("QScrollBar:vertical { background: #6272a4; } QScrollBar:horizontal { background: #6272a4; }")
# self.ui.comboBox.setStyleSheet("background-color: #6272a4;")
# self.ui.horizontalScrollBar.setStyleSheet("background-color: #6272a4;")
# self.ui.verticalScrollBar.setStyleSheet("background-color: #6272a4;")
# self.ui.commandLinkButton.setStyleSheet("color: #ff79c6;")

View File

@ -0,0 +1,31 @@
"""
BY: Wanderson M.Pimenta
PROJECT MADE WITH: Qt Designer and PySide6
V: 1.0.0
This project can be used freely for all uses, as long as they maintain the
respective credits only in the Python scripts, any information in the visual
interface (GUI) can be modified without any implication.
There are limitations on Qt licenses if you want to use your products
commercially, I recommend reading them on the official website:
https://doc.qt.io/qtforpython/licenses.html
"""
class Settings():
# APP SETTINGS
# ///////////////////////////////////////////////////////////////
ENABLE_CUSTOM_TITLE_BAR = True
MENU_WIDTH = 180
LEFT_BOX_WIDTH = 240
RIGHT_BOX_WIDTH = 240
TIME_ANIMATION = 500
# BTNS LEFT AND RIGHT BOX COLORS
BTN_LEFT_BOX_COLOR = "background-color: rgb(44, 49, 58);"
BTN_RIGHT_BOX_COLOR = "background-color: #ff79c6;"
# MENU SELECTED STYLESHEET
MENU_SELECTED_STYLESHEET = """
border-left: 22px solid qlineargradient(spread:pad, x1:0.034, y1:0, x2:0.216, y2:0, stop:0.499 rgba(255, 121, 198, 255), stop:0.5 rgba(85, 170, 255, 0));
background-color: rgb(40, 44, 52);
"""

View File

@ -0,0 +1,22 @@
from binascii import hexlify
from typing import List
from PySide6.QtCore import QAbstractListModel, Qt
class PacketListModel(QAbstractListModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data = []
def rowCount(self, parent):
return len(self._data)
def append(self, obj):
self._data.append(obj)
def data(self, index, role):
if role == Qt.DisplayRole:
return self._data[index.row()]
else:
return None

View File

@ -0,0 +1,83 @@
from typing import Optional, Union
from crcmod import mkCrcFun
import struct
class Protocol(object):
calc_crc16 = mkCrcFun(0x18005, rev=True, initCrc=0xffff, xorOut=0x0000)
def __init__(self, cmd_data_bytes: Optional[bytes] = None, cmd: Union[bytes, str, None] = None,
data: Optional[bytes] = None, start: Union[bytes, str] = b'\xaa',
end: Union[bytes, str] = b'\xbb'):
assert (cmd_data_bytes is not None and cmd is None and data is None) or (
cmd_data_bytes is None and cmd is not None and data is not None), AssertionError(
"choose cmd_data_bytes or cmd/data")
if cmd is not None:
self.cmd_bytes = cmd.encode("ascii") if isinstance(cmd, str) else cmd
self.data_bytes = data
else:
self.cmd_bytes = cmd_data_bytes[:2]
self.data_bytes = cmd_data_bytes[2:]
self.start_bytes = start
self.end_bytes = end
self._calc_datagram()
def _calc_datagram(self) -> bytes:
self.length_bytes = struct.pack(">L", len(self.cmd_bytes) + len(self.data_bytes))
length_cmd_data_bytes = self.length_bytes + self.cmd_bytes + self.data_bytes
self.check_bytes = struct.pack(">H", Protocol.calc_crc16(length_cmd_data_bytes))
self.datagram = self.start_bytes + length_cmd_data_bytes + self.check_bytes + self.end_bytes
def get_datagram(self):
return self.datagram
def get_length_bytes(self) -> bytes:
return self.length_bytes
def get_start_bytes(self) -> bytes:
return self.start_bytes
def get_end_bytes(self) -> bytes:
return self.end_bytes
def get_command_bytes(self) -> bytes:
return self.cmd_bytes
def get_check_bytes(self) -> bytes:
return self.check_bytes
def get_data_bytes(self) -> bytes:
return self.data_bytes
def get_length(self) -> int:
return len(self.cmd_bytes) + len(self.data_bytes)
def __len__(self):
return len(self.datagram)
def get_data(self) -> int:
try:
return int(self.data_bytes.decode("ascii"))
except BaseException as e:
return -1
def get_command(self) -> str:
return self.cmd_bytes.decode("ascii")
def print_summary(self):
print(str(self.get_datagram()) + "\n")
print("total bytes:", len(p))
print("start:", self.get_start_bytes())
print("length:", self.get_length())
print("command:", self.get_command())
print("data:", p.get_data())
print("check:", p.get_check_bytes())
print("end:", p.get_end_bytes())
if __name__ == '__main__':
p = Protocol(cmd_data_bytes=b'st\xff')
p.print_summary()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,192 @@
import socket
from threading import Thread, Event
from typing import Optional
from time import sleep, time
from queue import Queue
import re
class Server(object):
def __init__(self):
self._s = None
self._listen_th, self._receive_th = None, None
self.ip, self.port = None, None
self.client_ip, self.client_port, self.client_s = None, None, None
self._last_heartbeat = None
self._closing_callback = None
self._closing_signal = None
self._connected_callback = None
self._connected_signal = None
self._disconnected_callback = None
self._disconnected_signal = None
self._send_callback = None
self._send_signal = None
self._receive_callback = None
self._receive_signal = None
self._receive_queue = Queue()
self._timeout = 10.0
self._connection_status, self._need_recv_exit, self._need_listen_exit = Event(), Event(), Event()
def set_ip_port(self, ip: Optional[str] = None, port: Optional[int] = None):
if ip is not None:
self.ip = ip
if port is not None:
self.port = port
def set_heartbeat_timeout(self, timeout: float):
self._timeout = timeout
def set_closing_callback(self, callback):
self._closing_callback = callback
def set_closing_signal(self, signal):
self._closing_signal = signal
def set_connected_callback(self, callback):
self._connected_callback = callback
def set_connected_signal(self, signal):
self._connected_signal = signal
def set_disconnected_callback(self, callback):
self._disconnected_callback = callback
def set_disconnected_signal(self, signal):
self._disconnected_signal = signal
def set_send_callback(self, callback):
self._send_callback = callback
def set_send_signal(self, signal):
self._send_signal = signal
def set_receive_callback(self, callback):
self._receive_callback = callback
def set_receive_signal(self, signal):
self._receive_signal = signal
def _listen_thread(self, ip: str, port: int):
while not self._need_listen_exit.isSet():
if self._connection_status.isSet():
sleep(2)
continue
try:
self._s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._s.bind((ip, port))
self._s.listen(1)
# print("connecting...")
self.client_s, (self.client_ip, self.client_port) = self._s.accept()
# print("connected")
self._connection_status.set()
self._need_recv_exit.clear()
self._receive_th = Thread(target=self._receive_thread)
self._receive_th.start()
if self._connected_callback is not None:
self._connected_callback()
if self._connected_signal is not None:
self._connected_signal.emit()
# print("receiving...")
except BaseException as e:
pass
self._close()
def _close(self) -> None:
try:
self.client_s.shutdown(socket.SHUT_RDWR)
self.client_s.close()
except BaseException as e:
pass
self.client_s, self.client_ip, self.client_port = None, None, None
self._last_heartbeat = None
self._connection_status.clear()
if self._disconnected_callback is not None:
self._disconnected_callback()
if self._disconnected_signal is not None:
self._disconnected_signal.emit()
self._need_recv_exit.set()
def close(self) -> None:
try:
self._s.close()
except BaseException as e:
pass
if self._closing_callback is not None:
self._closing_callback()
if self._closing_signal is not None:
self._closing_signal.emit()
self._close()
self._need_recv_exit.set()
self._need_listen_exit.set()
def is_connected(self) -> bool:
return self._connection_status.isSet()
def start_listen_thread(self, ip: Optional[str] = None, port: Optional[int] = None):
if ip is not None and port is not None:
self.ip, self.port = ip, port
self._listen_th = Thread(target=self._listen_thread, args=(self.ip, self.port))
self._listen_th.start()
def _receive_thread(self):
while not self._need_recv_exit.isSet():
data = None
if not self._connection_status.isSet():
sleep(2)
continue
try:
self.client_s.settimeout(2)
data = self.client_s.recv(1024)
except BaseException as e:
pass
if self._last_heartbeat is None:
self._last_heartbeat = time()
if time() - self._last_heartbeat > self._timeout:
# print(time() - self._last_heartbeat)
# print("timeout")
self._close()
if data is not None and len(data) != 0:
if self._receive_callback is not None:
self._receive_callback()
if self._receive_signal is not None:
self._receive_signal.emit(data)
# if data != b"\xaa\x00\x00\x00\x03hb\xff\xff\xff\xbb":
# self._receive_queue.put(data)
# else:
# pass
self._last_heartbeat = time()
def send_datagram(self, datagram: bytes):
try:
self.client_s.settimeout(2)
self.client_s.sendall(datagram)
if self._send_callback is not None:
self._send_callback(datagram)
if self._send_signal is not None:
self._send_signal.emit(datagram)
except BaseException as e:
# print(e)
self._close()
@classmethod
def get_server_ip_list(cls) -> list:
addrs = socket.getaddrinfo(socket.gethostname(), None)
addrs = [i[4][0] for i in addrs]
ip4_list = [i for i in addrs if
re.match(r"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
i) is not None]
ip6_list = [i for i in addrs if
re.match(
r"^([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,7}:$|^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$|^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$|^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$|^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})$|^:((:[0-9a-fA-F]{1,4}){1,7}|:)$",
i) is not None]
ip4_list_192 = [i for i in ip4_list if re.match(r"^192\.168\.\S*$", i) is not None]
return ip4_list_192 + list(set(ip4_list) - set(ip4_list_192)) + ip6_list
if __name__ == '__main__':
# s = Server()
# s.start_listen_thread("192.168.2.125", 13452)
Server.get_server_ip_list()

View File

@ -0,0 +1,262 @@
"""
BY: Miaow, Wanderson M.Pimenta
PROJECT MADE WITH: Qt Designer and PySide6
V: 1.0.0
This project can be used freely for all uses, as long as they maintain the
respective credits only in the Python scripts, any information in the visual
interface (GUI) can be modified without any implication.
There are limitations on Qt licenses if you want to use your products
commercially, I recommend reading them on the official website:
https://doc.qt.io/qtforpython/licenses.html
"""
from typing import List
from main import MainWindow
from PySide6.QtCore import (Qt, QPropertyAnimation, QEasingCurve, QParallelAnimationGroup,
QEvent, QTimer)
from PySide6.QtGui import QIcon, QColor, QFontMetrics
from PySide6.QtWidgets import QGraphicsDropShadowEffect, QSizeGrip, QPushButton, QComboBox, QWidget
from modules.app_settings import Settings
from widgets.custom_grips.custom_grips import CustomGrip
GLOBAL_STATE = False
GLOBAL_TITLE_BAR = True
class UIFunctions(MainWindow):
def maximize_restore(self):
"""
MAXIMIZE/RESTORE
"""
global GLOBAL_STATE
status = GLOBAL_STATE
if status == False:
self.showMaximized()
GLOBAL_STATE = True
self.ui.appMargins.setContentsMargins(0, 0, 0, 0)
self.ui.maximizeRestoreAppBtn.setToolTip("Restore")
self.ui.maximizeRestoreAppBtn.setIcon(QIcon(u":/icons/images/icons/icon_restore.png"))
self.ui.frame_size_grip.hide()
self.left_grip.hide()
self.right_grip.hide()
self.top_grip.hide()
self.bottom_grip.hide()
else:
GLOBAL_STATE = False
self.showNormal()
self.resize(self.width() + 1, self.height() + 1)
self.ui.appMargins.setContentsMargins(10, 10, 10, 10)
self.ui.maximizeRestoreAppBtn.setToolTip("Maximize")
self.ui.maximizeRestoreAppBtn.setIcon(QIcon(u":/icons/images/icons/icon_maximize.png"))
self.ui.frame_size_grip.show()
self.left_grip.show()
self.right_grip.show()
self.top_grip.show()
self.bottom_grip.show()
def returStatus(self):
"""
RETURN STATUS
"""
return GLOBAL_STATE
def setStatus(self, status):
"""
SET STATUS
"""
global GLOBAL_STATE
GLOBAL_STATE = status
def toggleMenu(self, enable):
"""
TOGGLE MENU
"""
if enable:
# GET WIDTH
width = self.ui.leftMenuBg.width()
maxExtend = Settings.MENU_WIDTH
standard = 60
# SET MAX WIDTH
if width == 60:
widthExtended = maxExtend
else:
widthExtended = standard
# ANIMATION
self.animation = QPropertyAnimation(self.ui.leftMenuBg, b"minimumWidth")
self.animation.setDuration(Settings.TIME_ANIMATION)
self.animation.setStartValue(width)
self.animation.setEndValue(widthExtended)
self.animation.setEasingCurve(QEasingCurve.InOutQuart)
self.animation.start()
def start_box_animation(self, left_box_width, right_box_width, direction):
right_width = 0
left_width = 0
# Check values
if left_box_width == 0 and direction == "left":
left_width = 240
else:
left_width = 0
# Check values
if right_box_width == 0 and direction == "right":
right_width = 240
else:
right_width = 0
# ANIMATION RIGHT BOX
self.right_box = QPropertyAnimation(self.ui.extraRightBox, b"minimumWidth")
self.right_box.setDuration(Settings.TIME_ANIMATION)
self.right_box.setStartValue(right_box_width)
self.right_box.setEndValue(right_width)
self.right_box.setEasingCurve(QEasingCurve.InOutQuart)
# GROUP ANIMATION
self.group = QParallelAnimationGroup()
# self.group.addAnimation(self.left_box)
self.group.addAnimation(self.right_box)
self.group.start()
def selectMenu(getStyle):
"""
SELECT/DESELECT MENU
"""
select = getStyle + Settings.MENU_SELECTED_STYLESHEET
return select
def deselectMenu(getStyle):
"""
SELECT/DESELECT MENU
:return:
"""
deselect = getStyle.replace(Settings.MENU_SELECTED_STYLESHEET, "")
return deselect
def selectStandardMenu(self, widget):
"""
START SELECTION
"""
for w in self.ui.topMenu.findChildren(QPushButton):
if w.objectName() == widget:
w.setStyleSheet(UIFunctions.selectMenu(w.styleSheet()))
def resetStyle(self, widget):
"""
RESET SELECTION
"""
for w in self.ui.topMenu.findChildren(QPushButton):
if w.objectName() != widget:
w.setStyleSheet(UIFunctions.deselectMenu(w.styleSheet()))
def theme(self, file, useCustomTheme):
"""
IMPORT THEMES FILES QSS/CSS
"""
if useCustomTheme:
str = open(file, 'r').read()
self.ui.styleSheet.setStyleSheet(str)
def uiDefinitions(self):
"""
GUI DEFINITIONS
"""
def dobleClickMaximizeRestore(event):
# IF DOUBLE CLICK CHANGE STATUS
if event.type() == QEvent.MouseButtonDblClick:
QTimer.singleShot(250, lambda: UIFunctions.maximize_restore(self))
self.ui.titleRightInfo.mouseDoubleClickEvent = dobleClickMaximizeRestore
if Settings.ENABLE_CUSTOM_TITLE_BAR:
# STANDARD TITLE BAR
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
# MOVE WINDOW / MAXIMIZE / RESTORE
def moveWindow(event):
# IF MAXIMIZED CHANGE TO NORMAL
if UIFunctions.returStatus(self):
UIFunctions.maximize_restore(self)
# MOVE WINDOW
if event.buttons() == Qt.LeftButton:
self.move(self.pos() + event.globalPosition().toPoint() - self.drag_pos)
self.drag_pos = event.globalPosition().toPoint()
event.accept()
def mousePressEvent(event):
self.drag_pos = event.globalPosition().toPoint()
self.ui.titleRightInfo.mouseMoveEvent = moveWindow
self.ui.leftMenuBg.mouseMoveEvent = moveWindow
self.mousePressEvent = mousePressEvent
# CUSTOM GRIPS
self.left_grip = CustomGrip(self, Qt.LeftEdge, True)
self.right_grip = CustomGrip(self, Qt.RightEdge, True)
self.top_grip = CustomGrip(self, Qt.TopEdge, True)
self.bottom_grip = CustomGrip(self, Qt.BottomEdge, True)
else:
self.ui.appMargins.setContentsMargins(0, 0, 0, 0)
self.ui.minimizeAppBtn.hide()
self.ui.maximizeRestoreAppBtn.hide()
self.ui.closeAppBtn.hide()
self.ui.frame_size_grip.hide()
# DROP SHADOW
self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(17)
self.shadow.setXOffset(0)
self.shadow.setYOffset(0)
self.shadow.setColor(QColor(0, 0, 0, 150))
self.ui.bgApp.setGraphicsEffect(self.shadow)
# RESIZE WINDOW
def resize_grips(event):
if Settings.ENABLE_CUSTOM_TITLE_BAR:
self.left_grip.setGeometry(0, 10, 10, self.height())
self.right_grip.setGeometry(self.width() - 10, 10, 10, self.height())
self.top_grip.setGeometry(0, 0, self.width(), 10)
self.bottom_grip.setGeometry(0, self.height() - 10, self.width(), 10)
self.sizegrip = QSizeGrip(self.ui.frame_size_grip)
self.sizegrip.setStyleSheet("width: 20px; height: 20px; margin 0px; padding: 0px;")
self.resizeEvent = resize_grips
# MINIMIZE
self.ui.minimizeAppBtn.clicked.connect(lambda: self.showMinimized())
# MAXIMIZE/RESTORE
self.ui.maximizeRestoreAppBtn.clicked.connect(lambda: UIFunctions.maximize_restore(self))
# CLOSE APPLICATION
self.ui.closeAppBtn.clicked.connect(self.close)
self.ui.btn_exit.clicked.connect(self.close)
# COMBOBOX STYLE HACK
box_list: List[QComboBox] = self.ui.styleSheet.findChildren(QComboBox, options=Qt.FindChildrenRecursively)
for box in box_list:
w = box.view().window()
w.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
w.setAttribute(Qt.WA_TranslucentBackground)
w.setStyleSheet(f"QComboBox QAbstractItemView:item{{height:{w.height()};}}")
self.ui.cb_start_list = [self.ui.__dict__[f"cb_start_{i}"] for i in range(1, 6)]
self.ui.le_length_list = [self.ui.__dict__[f"le_length_{i}"] for i in range(1, 6)]
self.ui.cb_command_list = [self.ui.__dict__[f"cb_command_{i}"] for i in range(1, 6)]
self.ui.cb_data_list = [self.ui.__dict__[f"cb_data_{i}"] for i in range(1, 6)]
self.ui.le_check_list = [self.ui.__dict__[f"le_check_{i}"] for i in range(1, 6)]
self.ui.cb_end_list = [self.ui.__dict__[f"cb_end_{i}"] for i in range(1, 6)]
self.ui.pb_preset_list = [self.ui.__dict__[f"pb_preset_{i}"] for i in range(1, 6)]
self.ui.pb_send_list = [self.ui.__dict__[f"pb_send_{i}"] for i in range(1, 6)]
@classmethod
def set_elide_text(cls, widget: QWidget, text: str):
elided_text = widget.fontMetrics().elidedText(text, Qt.ElideMiddle, widget.width() - 20)
widget.setText(elided_text)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
<RCC>
<qresource prefix="images">
<file>images/images/PyDracula.png</file>
<file>images/images/PyDracula_vertical.png</file>
</qresource>
<qresource prefix="icons">
<file>images/icons/chevron-miniup.png</file>
<file>images/icons/chevron-down.png</file>
<file>images/icons/chevron-minidown.png</file>
<file>images/icons/chevron-statusbarup.png</file>
<file>images/icons/chevron-statusbardown.png</file>
<file>images/icons/save.png</file>
<file>images/icons/log-out.png</file>
<file>images/icons/image.png</file>
<file>images/icons/aperture.png</file>
<file>images/icons/sliders.png</file>
<file>images/icons/home.png</file>
<file>images/icons/close.png</file>
<file>images/icons/maximize.png</file>
<file>images/icons/menu.png</file>
<file>images/icons/minimize.png</file>
<file>images/icons/restore.png</file>
<file>images/icons/camera-and-down.png</file>
<file>images/icons/play.png</file>
<file>images/icons/30-height-sep.png</file>
<file>images/icons/chevron-right.png</file>
<file>images/icons/chevron-left.png</file>
<file>images/icons/plus.png</file>
<file>images/icons/arrow-right.png</file>
<file>images/icons/camera.png</file>
<file>images/icons/wind.png</file>
<file>images/icons/link-2.png</file>
<file>images/icons/link.png</file>
<file>images/icons/square.png</file>
<file>images/icons/shuffle.png</file>
<file>images/icons/rotate-ccw.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1 @@
{"page_connection": {"server_port": 13452}, "page_divider": {"manual": {"cb_start_list": ["AA"], "cb_command_list": ["st", "sp", "te", "pa", "pb", "pc", "pd"], "cb_data_list": ["FF"], "cb_end_list": ["BB"], "data": [{"index": 0, "cb_start": "AA", "cb_command": "st", "cb_data": "FF", "cb_end": "BB"}, {"index": 1, "cb_start": "AA", "cb_command": "sp", "cb_data": "FF", "cb_end": "BB"}, {"index": 2, "cb_start": "AA", "cb_command": "te", "cb_data": "30 30 30 30 30 32 33 31", "cb_end": "BB"}, {"index": 3, "cb_start": "AA", "cb_command": "pa", "cb_data": "30 30 30 30 30 32 33 31", "cb_end": "BB"}, {"index": 4, "cb_start": "AA", "cb_command": "pb", "cb_data": "30 30 30 30 30 32 33 31", "cb_end": "BB"}, {"index": 5, "cb_start": "AA", "cb_command": "pc", "cb_data": "30 30 30 30 30 32 33 31", "cb_end": "BB"}]}, "preset": {"le_preset_num": 1, "data": [{"le_preset_name": "Default", "sb_camera_a": 100, "sb_camera_b": 100, "sb_camera_c": 100, "sb_camera_d": 100, "sb_valve": 100, "cb_from_camera": "A", "le_to_valve": 1000}, {"le_preset_name": "my", "sb_camera_a": 123, "sb_camera_b": 123, "sb_camera_c": 123, "sb_camera_d": 123, "sb_valve": 123, "cb_from_camera": "B", "le_to_valve": 123}]}}, "page": {}}

View File

@ -0,0 +1,519 @@
/* /////////////////////////////////////////////////////////////////////////////////////////////////
SET APP STYLESHEET - FULL STYLES HERE
DARK THEME - DRACULA COLOR BASED
# BY: WANDERSON M.PIMENTA
# PROJECT MADE WITH: Qt Designer and PySide6
# V: 1.0.0
#
# This project can be used freely for all uses, as long as they maintain the
# respective credits only in the Python scripts, any information in the visual
# interface (GUI) can be modified without any implication.
#
# There are limitations on Qt licenses if you want to use your products
# commercially, I recommend reading them on the official website:
# https://doc.qt.io/qtforpython/licenses.html
///////////////////////////////////////////////////////////////////////////////////////////////// */
QWidget{
color: rgb(221, 221, 221);
font: 10pt "Segoe UI";
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Tooltip */
QToolTip {
color: #ffffff;
background-color: rgba(33, 37, 43, 180);
border: 1px solid rgb(44, 49, 58);
background-image: none;
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 2px solid rgb(255, 121, 198);
text-align: left;
padding-left: 8px;
margin: 0px;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Bg App */
#bgApp {
background-color: rgb(40, 44, 52);
border: 1px solid rgb(44, 49, 58);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Left Menu */
#leftMenuBg {
background-color: rgb(33, 37, 43);
}
#topLogo {
background-color: rgb(33, 37, 43);
background-image: url(:/images/images/images/PyDracula.png);
background-position: centered;
background-repeat: no-repeat;
}
#titleLeftApp { font: 63 12pt "Segoe UI Semibold"; }
/* #titleLeftDescription { font: 8pt "Segoe UI"; color: rgb(189, 147, 249); } */
#titleLeftDescription { font: 8pt "Segoe UI"; color: #ff79c6; }
/* MENUS */
#topMenu .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 22px solid transparent;
background-color: transparent;
text-align: left;
padding-left: 44px;
}
#topMenu .QPushButton:hover {
background-color: rgb(40, 44, 52);
}
#topMenu .QPushButton:pressed {
background-color: rgb(189, 147, 249);
color: rgb(255, 255, 255);
}
#bottomMenu .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 20px solid transparent;
background-color:transparent;
text-align: left;
padding-left: 44px;
}
#bottomMenu .QPushButton:hover {
background-color: rgb(40, 44, 52);
}
#bottomMenu .QPushButton:pressed {
background-color: rgb(189, 147, 249);
color: rgb(255, 255, 255);
}
#leftMenuFrame{
border-top: 3px solid rgb(44, 49, 58);
}
/* Toggle Button */
#toggleButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 20px solid transparent;
background-color: rgb(37, 41, 48);
text-align: left;
padding-left: 44px;
color: rgb(113, 126, 149);
}
#toggleButton:hover {
background-color: rgb(40, 44, 52);
}
#toggleButton:pressed {
background-color: rgb(189, 147, 249);
}
/* Title Menu */
#titleRightInfo { padding-left: 10px; }
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Extra Tab */
#extraLeftBox {
background-color: rgb(44, 49, 58);
}
#extraTopBg{
background-color: rgb(189, 147, 249)
}
/* Icon */
#extraIcon {
background-position: center;
background-repeat: no-repeat;
background-image: url(:/icons/images/icons/icon_settings.png);
}
/* Label */
#extraLabel { color: rgb(255, 255, 255); }
/* Btn Close */
#extraCloseColumnBtn { background-color: rgba(255, 255, 255, 0); border: none; border-radius: 5px; }
#extraCloseColumnBtn:hover { background-color: rgb(196, 161, 249); border-style: solid; border-radius: 4px; }
#extraCloseColumnBtn:pressed { background-color: rgb(180, 141, 238); border-style: solid; border-radius: 4px; }
/* Extra Content */
#extraContent{
border-top: 3px solid rgb(40, 44, 52);
}
/* Extra Top Menus */
#extraTopMenu .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 22px solid transparent;
background-color:transparent;
text-align: left;
padding-left: 44px;
}
#extraTopMenu .QPushButton:hover {
background-color: rgb(40, 44, 52);
}
#extraTopMenu .QPushButton:pressed {
background-color: rgb(189, 147, 249);
color: rgb(255, 255, 255);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Content App */
#contentTopBg{
background-color: rgb(33, 37, 43);
}
#contentBottom{
border-top: 3px solid rgb(44, 49, 58);
}
/* Top Buttons */
#rightButtons .QPushButton { background-color: rgba(255, 255, 255, 0); border: none; border-radius: 5px; }
#rightButtons .QPushButton:hover { background-color: rgb(44, 49, 57); border-style: solid; border-radius: 4px; }
#rightButtons .QPushButton:pressed { background-color: rgb(23, 26, 30); border-style: solid; border-radius: 4px; }
/* Theme Settings */
#extraRightBox { background-color: rgb(44, 49, 58); }
#themeSettingsTopDetail { background-color: rgb(189, 147, 249); }
/* Bottom Bar */
#bottomBar { background-color: rgb(44, 49, 58); }
#bottomBar QLabel { font-size: 11px; color: rgb(113, 126, 149); padding-left: 10px; padding-right: 10px; padding-bottom: 2px; }
/* CONTENT SETTINGS */
/* MENUS */
#contentSettings .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 22px solid transparent;
background-color:transparent;
text-align: left;
padding-left: 44px;
}
#contentSettings .QPushButton:hover {
background-color: rgb(40, 44, 52);
}
#contentSettings .QPushButton:pressed {
background-color: rgb(189, 147, 249);
color: rgb(255, 255, 255);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
QTableWidget */
QTableWidget {
background-color: transparent;
padding: 10px;
border-radius: 5px;
gridline-color: rgb(44, 49, 58);
border-bottom: 1px solid rgb(44, 49, 60);
}
QTableWidget::item{
border-color: rgb(44, 49, 60);
padding-left: 5px;
padding-right: 5px;
gridline-color: rgb(44, 49, 60);
}
QTableWidget::item:selected{
background-color: rgb(189, 147, 249);
}
QHeaderView::section{
background-color: rgb(33, 37, 43);
max-width: 30px;
border: 1px solid rgb(44, 49, 58);
border-style: none;
border-bottom: 1px solid rgb(44, 49, 60);
border-right: 1px solid rgb(44, 49, 60);
}
QTableWidget::horizontalHeader {
background-color: rgb(33, 37, 43);
}
QHeaderView::section:horizontal
{
border: 1px solid rgb(33, 37, 43);
background-color: rgb(33, 37, 43);
padding: 3px;
border-top-left-radius: 7px;
border-top-right-radius: 7px;
}
QHeaderView::section:vertical
{
border: 1px solid rgb(44, 49, 60);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
LineEdit */
QLineEdit {
background-color: rgb(33, 37, 43);
border-radius: 5px;
border: 2px solid rgb(33, 37, 43);
padding-left: 10px;
selection-color: rgb(255, 255, 255);
selection-background-color: rgb(255, 121, 198);
}
QLineEdit:hover {
border: 2px solid rgb(64, 71, 88);
}
QLineEdit:focus {
border: 2px solid rgb(91, 101, 124);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
PlainTextEdit */
QPlainTextEdit {
background-color: rgb(27, 29, 35);
border-radius: 5px;
padding: 10px;
selection-color: rgb(255, 255, 255);
selection-background-color: rgb(255, 121, 198);
}
QPlainTextEdit QScrollBar:vertical {
width: 8px;
}
QPlainTextEdit QScrollBar:horizontal {
height: 8px;
}
QPlainTextEdit:hover {
border: 2px solid rgb(64, 71, 88);
}
QPlainTextEdit:focus {
border: 2px solid rgb(91, 101, 124);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
ScrollBars */
QScrollBar:horizontal {
border: none;
background: rgb(52, 59, 72);
height: 8px;
margin: 0px 21px 0 21px;
border-radius: 0px;
}
QScrollBar::handle:horizontal {
background: rgb(189, 147, 249);
min-width: 25px;
border-radius: 4px
}
QScrollBar::add-line:horizontal {
border: none;
background: rgb(55, 63, 77);
width: 20px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
subcontrol-position: right;
subcontrol-origin: margin;
}
QScrollBar::sub-line:horizontal {
border: none;
background: rgb(55, 63, 77);
width: 20px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
subcontrol-position: left;
subcontrol-origin: margin;
}
QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal
{
background: none;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
{
background: none;
}
QScrollBar:vertical {
border: none;
background: rgb(52, 59, 72);
width: 8px;
margin: 21px 0 21px 0;
border-radius: 0px;
}
QScrollBar::handle:vertical {
background: rgb(189, 147, 249);
min-height: 25px;
border-radius: 4px
}
QScrollBar::add-line:vertical {
border: none;
background: rgb(55, 63, 77);
height: 20px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
border: none;
background: rgb(55, 63, 77);
height: 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
background: none;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
CheckBox */
QCheckBox::indicator {
border: 3px solid rgb(52, 59, 72);
width: 15px;
height: 15px;
border-radius: 10px;
background: rgb(44, 49, 60);
}
QCheckBox::indicator:hover {
border: 3px solid rgb(58, 66, 81);
}
QCheckBox::indicator:checked {
background: 3px solid rgb(52, 59, 72);
border: 3px solid rgb(52, 59, 72);
background-image: url(:/icons/images/icons/cil-check-alt.png);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
RadioButton */
QRadioButton::indicator {
border: 3px solid rgb(52, 59, 72);
width: 15px;
height: 15px;
border-radius: 10px;
background: rgb(44, 49, 60);
}
QRadioButton::indicator:hover {
border: 3px solid rgb(58, 66, 81);
}
QRadioButton::indicator:checked {
background: 3px solid rgb(94, 106, 130);
border: 3px solid rgb(52, 59, 72);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
ComboBox */
QComboBox{
background-color: rgb(27, 29, 35);
border-radius: 5px;
border: 2px solid rgb(33, 37, 43);
padding: 5px;
padding-left: 10px;
}
QComboBox:hover{
border: 2px solid rgb(64, 71, 88);
}
QComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
width: 25px;
border-left-width: 3px;
border-left-color: rgba(39, 44, 54, 150);
border-left-style: solid;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
background-image: url(:/icons/images/icons/cil-arrow-bottom.png);
background-position: center;
background-repeat: no-reperat;
}
QComboBox QAbstractItemView {
color: rgb(255, 121, 198);
background-color: rgb(33, 37, 43);
padding: 10px;
selection-background-color: rgb(39, 44, 54);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Sliders */
QSlider::groove:horizontal {
border-radius: 5px;
height: 10px;
margin: 0px;
background-color: rgb(52, 59, 72);
}
QSlider::groove:horizontal:hover {
background-color: rgb(55, 62, 76);
}
QSlider::handle:horizontal {
background-color: rgb(189, 147, 249);
border: none;
height: 10px;
width: 10px;
margin: 0px;
border-radius: 5px;
}
QSlider::handle:horizontal:hover {
background-color: rgb(195, 155, 255);
}
QSlider::handle:horizontal:pressed {
background-color: rgb(255, 121, 198);
}
QSlider::groove:vertical {
border-radius: 5px;
width: 10px;
margin: 0px;
background-color: rgb(52, 59, 72);
}
QSlider::groove:vertical:hover {
background-color: rgb(55, 62, 76);
}
QSlider::handle:vertical {
background-color: rgb(189, 147, 249);
border: none;
height: 10px;
width: 10px;
margin: 0px;
border-radius: 5px;
}
QSlider::handle:vertical:hover {
background-color: rgb(195, 155, 255);
}
QSlider::handle:vertical:pressed {
background-color: rgb(255, 121, 198);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
CommandLinkButton */
QCommandLinkButton {
color: rgb(255, 121, 198);
border-radius: 5px;
padding: 5px;
}
QCommandLinkButton:hover {
color: rgb(255, 170, 255);
background-color: rgb(44, 49, 60);
}
QCommandLinkButton:pressed {
color: rgb(189, 147, 249);
background-color: rgb(52, 58, 71);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Button */
#pagesContainer QPushButton {
border: 2px solid rgb(52, 59, 72);
border-radius: 5px;
background-color: rgb(52, 59, 72);
}
#pagesContainer QPushButton:hover {
background-color: rgb(57, 65, 80);
border: 2px solid rgb(61, 70, 86);
}
#pagesContainer QPushButton:pressed {
background-color: rgb(35, 40, 49);
border: 2px solid rgb(43, 50, 61);
}

View File

@ -0,0 +1,532 @@
/* /////////////////////////////////////////////////////////////////////////////////////////////////
SET APP STYLESHEET - FULL STYLES HERE
DARK THEME - DRACULA COLOR BASED
# BY: WANDERSON M.PIMENTA
# PROJECT MADE WITH: Qt Designer and PySide6
# V: 1.0.0
#
# This project can be used freely for all uses, as long as they maintain the
# respective credits only in the Python scripts, any information in the visual
# interface (GUI) can be modified without any implication.
#
# There are limitations on Qt licenses if you want to use your products
# commercially, I recommend reading them on the official website:
# https://doc.qt.io/qtforpython/licenses.html
///////////////////////////////////////////////////////////////////////////////////////////////// */
QWidget{
color: #333;
font: 10pt "Segoe UI";
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Tooltip */
QToolTip {
color: #333;
background-color: #f8f8f2;
border: 1px solid #CCC;
background-image: none;
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 2px solid rgb(255, 121, 198);
text-align: left;
padding-left: 8px;
margin: 0px;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Bg App */
#bgApp {
background-color: #f8f8f2;
border: 1px solid #CCC;
color: #44475a;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Left Menu */
#leftMenuBg {
background-color: #6272a4;
}
#topLogo {
background-color: #6272a4;
background-image: url(:/images/images/images/PyDracula.png);
background-position: centered;
background-repeat: no-repeat;
}
#titleLeftApp { font: 63 12pt "Segoe UI Semibold"; color: #f8f8f2; }
#titleLeftDescription { font: 8pt "Segoe UI"; color: #bd93f9; }
/* MENUS */
#topMenu .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 22px solid transparent;
background-color: transparent;
text-align: left;
padding-left: 44px;
color: #f8f8f2;
}
#topMenu .QPushButton:hover {
background-color: #bd93f9;
}
#topMenu .QPushButton:pressed {
background-color: #ff79c6;
color: rgb(255, 255, 255);
}
#bottomMenu .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 20px solid transparent;
background-color:transparent;
text-align: left;
padding-left: 44px;
color: #f8f8f2;
}
#bottomMenu .QPushButton:hover {
background-color: #bd93f9;
}
#bottomMenu .QPushButton:pressed {
background-color: #ff79c6;
color: rgb(255, 255, 255);
}
#leftMenuFrame{
border-top: 3px solid #6a7cb1;
}
/* Toggle Button */
#toggleButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 20px solid transparent;
background-color: #5b6996;
text-align: left;
padding-left: 44px;
color: #f8f8f2;
}
#toggleButton:hover {
background-color: #bd93f9;
}
#toggleButton:pressed {
background-color: #ff79c6;
color: rgb(255, 255, 255);
}
/* Title Menu */
#titleRightInfo { padding-left: 10px; }
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Extra Tab */
#extraLeftBox {
background-color: #495474;
color: #f8f8f2;
}
#extraTopBg{
background-color: rgb(189, 147, 249)
}
/* Icon */
#extraIcon {
background-position: center;
background-repeat: no-repeat;
background-image: url(:/icons/images/icons/icon_settings.png);
}
/* Label */
#extraLabel { color: rgb(255, 255, 255); }
/* Btn Close */
#extraCloseColumnBtn { background-color: rgba(255, 255, 255, 0); border: none; border-radius: 5px; }
#extraCloseColumnBtn:hover { background-color: rgb(196, 161, 249); border-style: solid; border-radius: 4px; }
#extraCloseColumnBtn:pressed { background-color: rgb(180, 141, 238); border-style: solid; border-radius: 4px; }
/* Extra Content */
#extraContent{
border-top: 3px solid #6272a4;
}
/* Extra Top Menus */
#extraTopMenu .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 22px solid transparent;
background-color:transparent;
text-align: left;
padding-left: 44px;
color: #f8f8f2;
}
#extraTopMenu .QPushButton:hover {
background-color: #5d6c99;
}
#extraTopMenu .QPushButton:pressed {
background-color: rgb(189, 147, 249);
color: rgb(255, 255, 255);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Content App */
#contentTopBg{
background-color: #6272a4;
}
#contentBottom{
border-top: 3px solid #bd93f9;
}
#titleRightInfo{
color: #f8f8f2;
}
/* Top Buttons */
#rightButtons .QPushButton { background-color: rgba(255, 255, 255, 0); border: none; border-radius: 5px; }
#rightButtons .QPushButton:hover { background-color: #bd93f9; border-style: solid; border-radius: 4px; }
#rightButtons .QPushButton:pressed { background-color: #ff79c6; border-style: solid; border-radius: 4px; }
/* Theme Settings */
#extraRightBox { background-color: #495474; }
#themeSettingsTopDetail { background-color: #6272a4; }
/* Bottom Bar */
#bottomBar { background-color: #495474 }
#bottomBar QLabel { font-size: 11px; color: #f8f8f2; padding-left: 10px; padding-right: 10px; padding-bottom: 2px; }
/* CONTENT SETTINGS */
/* MENUS */
#contentSettings .QPushButton {
background-position: left center;
background-repeat: no-repeat;
border: none;
border-left: 22px solid transparent;
background-color:transparent;
text-align: left;
padding-left: 44px;
color: #f8f8f2;
}
#contentSettings .QPushButton:hover {
background-color: #5d6c99;
}
#contentSettings .QPushButton:pressed {
background-color: rgb(189, 147, 249);
color: rgb(255, 255, 255);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
QTableWidget */
QTableWidget {
background-color: transparent;
padding: 10px;
border-radius: 5px;
gridline-color: #9faeda;
outline: none;
}
QTableWidget::item{
border-color: #9faeda;
padding-left: 5px;
padding-right: 5px;
gridline-color: #9faeda;
}
QTableWidget::item:selected{
background-color: rgb(189, 147, 249);
color: #f8f8f2;
}
QHeaderView::section{
background-color: #6272a4;
max-width: 30px;
border: none;
border-style: none;
}
QTableWidget::horizontalHeader {
background-color: #6272a4;
}
QHeaderView::section:horizontal
{
border: 1px solid #6272a4;
background-color: #6272a4;
padding: 3px;
border-top-left-radius: 7px;
border-top-right-radius: 7px;
color: #f8f8f2;
}
QHeaderView::section:vertical
{
border: 1px solid #6272a4;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
LineEdit */
QLineEdit {
background-color: #6272a4;
border-radius: 5px;
border: 2px solid #6272a4;
padding-left: 10px;
selection-color: rgb(255, 255, 255);
selection-background-color: rgb(255, 121, 198);
color: #f8f8f2;
}
QLineEdit:hover {
border: 2px solid rgb(64, 71, 88);
}
QLineEdit:focus {
border: 2px solid #ff79c6;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
PlainTextEdit */
QPlainTextEdit {
background-color: #6272a4;
border-radius: 5px;
padding: 10px;
selection-color: rgb(255, 255, 255);
selection-background-color: rgb(255, 121, 198);
color: #f8f8f2;
}
QPlainTextEdit QScrollBar:vertical {
width: 8px;
}
QPlainTextEdit QScrollBar:horizontal {
height: 8px;
}
QPlainTextEdit:hover {
border: 2px solid rgb(64, 71, 88);
}
QPlainTextEdit:focus {
border: 2px solid #ff79c6;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
ScrollBars */
QScrollBar:horizontal {
border: none;
background: #6272a4;
height: 8px;
margin: 0px 21px 0 21px;
border-radius: 0px;
}
QScrollBar::handle:horizontal {
background: rgb(189, 147, 249);
min-width: 25px;
border-radius: 4px
}
QScrollBar::add-line:horizontal {
border: none;
background: #6272a4;
width: 20px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
subcontrol-position: right;
subcontrol-origin: margin;
}
QScrollBar::sub-line:horizontal {
border: none;
background: #6272a4;
width: 20px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
subcontrol-position: left;
subcontrol-origin: margin;
}
QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal
{
background: none;
}
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
{
background: none;
}
QScrollBar:vertical {
border: none;
background-color: #6272a4;
width: 8px;
margin: 21px 0 21px 0;
border-radius: 0px;
}
QScrollBar::handle:vertical {
background: rgb(189, 147, 249);
min-height: 25px;
border-radius: 4px
}
QScrollBar::add-line:vertical {
border: none;
background: #6272a4;
height: 20px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
border: none;
background: #6272a4;
height: 20px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
background: none;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
CheckBox */
QCheckBox::indicator {
border: 3px solid #6272a4;
width: 15px;
height: 15px;
border-radius: 10px;
background: #6272a4;
}
QCheckBox::indicator:hover {
border: 3px solid rgb(119, 136, 187);
}
QCheckBox::indicator:checked {
background: 3px solid #bd93f9;
border: 3px solid #bd93f9;
background-image: url(:/icons/images/icons/cil-check-alt.png);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
RadioButton */
QRadioButton::indicator {
border: 3px solid #6272a4;
width: 15px;
height: 15px;
border-radius: 10px;
background: #6272a4;
}
QRadioButton::indicator:hover {
border: 3px solid rgb(119, 136, 187);
}
QRadioButton::indicator:checked {
background: 3px solid #bd93f9;
border: 3px solid #bd93f9;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
ComboBox */
QComboBox{
background-color: #6272a4;
border-radius: 5px;
border: 2px solid #6272a4;
padding: 5px;
padding-left: 10px;
color: #f8f8f2;
}
QComboBox:hover{
border: 2px solid #7284b9;
}
QComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
width: 25px;
border-left-width: 3px;
border-left-color: #6272a4;
border-left-style: solid;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
background-image: url(:/icons/images/icons/cil-arrow-bottom.png);
background-position: center;
background-repeat: no-reperat;
}
QComboBox QAbstractItemView {
color: rgb(255, 121, 198);
background-color: #6272a4;
padding: 10px;
selection-background-color: #6272a4;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Sliders */
QSlider::groove:horizontal {
border-radius: 5px;
height: 10px;
margin: 0px;
background-color: #6272a4;
}
QSlider::groove:horizontal:hover {
background-color: #6272a4;
}
QSlider::handle:horizontal {
background-color: rgb(189, 147, 249);
border: none;
height: 10px;
width: 10px;
margin: 0px;
border-radius: 5px;
}
QSlider::handle:horizontal:hover {
background-color: rgb(195, 155, 255);
}
QSlider::handle:horizontal:pressed {
background-color: rgb(255, 121, 198);
}
QSlider::groove:vertical {
border-radius: 5px;
width: 10px;
margin: 0px;
background-color: #6272a4;
}
QSlider::groove:vertical:hover {
background-color: #6272a4;
}
QSlider::handle:vertical {
background-color: rgb(189, 147, 249);
border: none;
height: 10px;
width: 10px;
margin: 0px;
border-radius: 5px;
}
QSlider::handle:vertical:hover {
background-color: rgb(195, 155, 255);
}
QSlider::handle:vertical:pressed {
background-color: rgb(255, 121, 198);
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
CommandLinkButton */
#pagesContainer QCommandLinkButton {
color: rgb(255, 121, 198);
border-radius: 5px;
padding: 5px;
border: 2px solid #ff79c6;
color: #ff79c6;
}
#pagesContainer QCommandLinkButton:hover {
color: rgb(255, 170, 255);
background-color: #6272a4;
}
#pagesContainer QCommandLinkButton:pressed {
color: rgb(189, 147, 249);
background-color: #586796;
}
/* /////////////////////////////////////////////////////////////////////////////////////////////////
Button */
#pagesContainer QPushButton {
border: 2px solid #6272a4;
border-radius: 5px;
background-color: #6272a4;
color: #f8f8f2;
}
#pagesContainer QPushButton:hover {
background-color: #7082b6;
border: 2px solid #7082b6;
}
#pagesContainer QPushButton:pressed {
background-color: #546391;
border: 2px solid #ff79c6;
}

View File

@ -0,0 +1 @@
1.0

View File

@ -0,0 +1,17 @@
# ///////////////////////////////////////////////////////////////
#
# BY: WANDERSON M.PIMENTA
# PROJECT MADE WITH: Qt Designer and PySide6
# V: 1.0.0
#
# This project can be used freely for all uses, as long as they maintain the
# respective credits only in the Python scripts, any information in the visual
# interface (GUI) can be modified without any implication.
#
# There are limitations on Qt licenses if you want to use your products
# commercially, I recommend reading them on the official website:
# https://doc.qt.io/qtforpython/licenses.html
#
# ///////////////////////////////////////////////////////////////
from . custom_grips import CustomGrip

View File

@ -0,0 +1,17 @@
# ///////////////////////////////////////////////////////////////
#
# BY: WANDERSON M.PIMENTA
# PROJECT MADE WITH: Qt Designer and PySide6
# V: 1.0.0
#
# This project can be used freely for all uses, as long as they maintain the
# respective credits only in the Python scripts, any information in the visual
# interface (GUI) can be modified without any implication.
#
# There are limitations on Qt licenses if you want to use your products
# commercially, I recommend reading them on the official website:
# https://doc.qt.io/qtforpython/licenses.html
#
# ///////////////////////////////////////////////////////////////
from . custom_grips import CustomGrip

View File

@ -0,0 +1,243 @@
# ///////////////////////////////////////////////////////////////
#
# BY: WANDERSON M.PIMENTA
# PROJECT MADE WITH: Qt Designer and PySide6
# V: 1.0.0
#
# This project can be used freely for all uses, as long as they maintain the
# respective credits only in the Python scripts, any information in the visual
# interface (GUI) can be modified without any implication.
#
# There are limitations on Qt licenses if you want to use your products
# commercially, I recommend reading them on the official website:
# https://doc.qt.io/qtforpython/licenses.html
#
# ///////////////////////////////////////////////////////////////
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
class CustomGrip(QWidget):
def __init__(self, parent, position, disable_color=False):
# SETUP UI
QWidget.__init__(self)
self.parent = parent
self.setParent(parent)
self.wi = Widgets()
# SHOW TOP GRIP
if position == Qt.TopEdge:
self.wi.top(self)
self.setGeometry(0, 0, self.parent.width(), 10)
self.setMaximumHeight(10)
# GRIPS
top_left = QSizeGrip(self.wi.top_left)
top_right = QSizeGrip(self.wi.top_right)
# RESIZE TOP
def resize_top(event):
delta = event.pos()
height = max(self.parent.minimumHeight(), self.parent.height() - delta.y())
geo = self.parent.geometry()
geo.setTop(geo.bottom() - height)
self.parent.setGeometry(geo)
event.accept()
self.wi.top.mouseMoveEvent = resize_top
# ENABLE COLOR
if disable_color:
self.wi.top_left.setStyleSheet("background: transparent")
self.wi.top_right.setStyleSheet("background: transparent")
self.wi.top.setStyleSheet("background: transparent")
# SHOW BOTTOM GRIP
elif position == Qt.BottomEdge:
self.wi.bottom(self)
self.setGeometry(0, self.parent.height() - 10, self.parent.width(), 10)
self.setMaximumHeight(10)
# GRIPS
self.bottom_left = QSizeGrip(self.wi.bottom_left)
self.bottom_right = QSizeGrip(self.wi.bottom_right)
# RESIZE BOTTOM
def resize_bottom(event):
delta = event.pos()
height = max(self.parent.minimumHeight(), self.parent.height() + delta.y())
self.parent.resize(self.parent.width(), height)
event.accept()
self.wi.bottom.mouseMoveEvent = resize_bottom
# ENABLE COLOR
if disable_color:
self.wi.bottom_left.setStyleSheet("background: transparent")
self.wi.bottom_right.setStyleSheet("background: transparent")
self.wi.bottom.setStyleSheet("background: transparent")
# SHOW LEFT GRIP
elif position == Qt.LeftEdge:
self.wi.left(self)
self.setGeometry(0, 10, 10, self.parent.height())
self.setMaximumWidth(10)
# RESIZE LEFT
def resize_left(event):
delta = event.pos()
width = max(self.parent.minimumWidth(), self.parent.width() - delta.x())
geo = self.parent.geometry()
geo.setLeft(geo.right() - width)
self.parent.setGeometry(geo)
event.accept()
self.wi.leftgrip.mouseMoveEvent = resize_left
# ENABLE COLOR
if disable_color:
self.wi.leftgrip.setStyleSheet("background: transparent")
# RESIZE RIGHT
elif position == Qt.RightEdge:
self.wi.right(self)
self.setGeometry(self.parent.width() - 10, 10, 10, self.parent.height())
self.setMaximumWidth(10)
def resize_right(event):
delta = event.pos()
width = max(self.parent.minimumWidth(), self.parent.width() + delta.x())
self.parent.resize(width, self.parent.height())
event.accept()
self.wi.rightgrip.mouseMoveEvent = resize_right
# ENABLE COLOR
if disable_color:
self.wi.rightgrip.setStyleSheet("background: transparent")
def mouseReleaseEvent(self, event):
self.mousePos = None
def resizeEvent(self, event):
if hasattr(self.wi, 'container_top'):
self.wi.container_top.setGeometry(0, 0, self.width(), 10)
elif hasattr(self.wi, 'container_bottom'):
self.wi.container_bottom.setGeometry(0, 0, self.width(), 10)
elif hasattr(self.wi, 'leftgrip'):
self.wi.leftgrip.setGeometry(0, 0, 10, self.height() - 20)
elif hasattr(self.wi, 'rightgrip'):
self.wi.rightgrip.setGeometry(0, 0, 10, self.height() - 20)
class Widgets(object):
def top(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
self.container_top = QFrame(Form)
self.container_top.setObjectName(u"container_top")
self.container_top.setGeometry(QRect(0, 0, 500, 10))
self.container_top.setMinimumSize(QSize(0, 10))
self.container_top.setMaximumSize(QSize(16777215, 10))
self.container_top.setFrameShape(QFrame.NoFrame)
self.container_top.setFrameShadow(QFrame.Raised)
self.top_layout = QHBoxLayout(self.container_top)
self.top_layout.setSpacing(0)
self.top_layout.setObjectName(u"top_layout")
self.top_layout.setContentsMargins(0, 0, 0, 0)
self.top_left = QFrame(self.container_top)
self.top_left.setObjectName(u"top_left")
self.top_left.setMinimumSize(QSize(10, 10))
self.top_left.setMaximumSize(QSize(10, 10))
self.top_left.setCursor(QCursor(Qt.SizeFDiagCursor))
self.top_left.setStyleSheet(u"background-color: rgb(33, 37, 43);")
self.top_left.setFrameShape(QFrame.NoFrame)
self.top_left.setFrameShadow(QFrame.Raised)
self.top_layout.addWidget(self.top_left)
self.top = QFrame(self.container_top)
self.top.setObjectName(u"top")
self.top.setCursor(QCursor(Qt.SizeVerCursor))
self.top.setStyleSheet(u"background-color: rgb(85, 255, 255);")
self.top.setFrameShape(QFrame.NoFrame)
self.top.setFrameShadow(QFrame.Raised)
self.top_layout.addWidget(self.top)
self.top_right = QFrame(self.container_top)
self.top_right.setObjectName(u"top_right")
self.top_right.setMinimumSize(QSize(10, 10))
self.top_right.setMaximumSize(QSize(10, 10))
self.top_right.setCursor(QCursor(Qt.SizeBDiagCursor))
self.top_right.setStyleSheet(u"background-color: rgb(33, 37, 43);")
self.top_right.setFrameShape(QFrame.NoFrame)
self.top_right.setFrameShadow(QFrame.Raised)
self.top_layout.addWidget(self.top_right)
def bottom(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
self.container_bottom = QFrame(Form)
self.container_bottom.setObjectName(u"container_bottom")
self.container_bottom.setGeometry(QRect(0, 0, 500, 10))
self.container_bottom.setMinimumSize(QSize(0, 10))
self.container_bottom.setMaximumSize(QSize(16777215, 10))
self.container_bottom.setFrameShape(QFrame.NoFrame)
self.container_bottom.setFrameShadow(QFrame.Raised)
self.bottom_layout = QHBoxLayout(self.container_bottom)
self.bottom_layout.setSpacing(0)
self.bottom_layout.setObjectName(u"bottom_layout")
self.bottom_layout.setContentsMargins(0, 0, 0, 0)
self.bottom_left = QFrame(self.container_bottom)
self.bottom_left.setObjectName(u"bottom_left")
self.bottom_left.setMinimumSize(QSize(10, 10))
self.bottom_left.setMaximumSize(QSize(10, 10))
self.bottom_left.setCursor(QCursor(Qt.SizeBDiagCursor))
self.bottom_left.setStyleSheet(u"background-color: rgb(33, 37, 43);")
self.bottom_left.setFrameShape(QFrame.NoFrame)
self.bottom_left.setFrameShadow(QFrame.Raised)
self.bottom_layout.addWidget(self.bottom_left)
self.bottom = QFrame(self.container_bottom)
self.bottom.setObjectName(u"bottom")
self.bottom.setCursor(QCursor(Qt.SizeVerCursor))
self.bottom.setStyleSheet(u"background-color: rgb(85, 170, 0);")
self.bottom.setFrameShape(QFrame.NoFrame)
self.bottom.setFrameShadow(QFrame.Raised)
self.bottom_layout.addWidget(self.bottom)
self.bottom_right = QFrame(self.container_bottom)
self.bottom_right.setObjectName(u"bottom_right")
self.bottom_right.setMinimumSize(QSize(10, 10))
self.bottom_right.setMaximumSize(QSize(10, 10))
self.bottom_right.setCursor(QCursor(Qt.SizeFDiagCursor))
self.bottom_right.setStyleSheet(u"background-color: rgb(33, 37, 43);")
self.bottom_right.setFrameShape(QFrame.NoFrame)
self.bottom_right.setFrameShadow(QFrame.Raised)
self.bottom_layout.addWidget(self.bottom_right)
def left(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
self.leftgrip = QFrame(Form)
self.leftgrip.setObjectName(u"left")
self.leftgrip.setGeometry(QRect(0, 10, 10, 480))
self.leftgrip.setMinimumSize(QSize(10, 0))
self.leftgrip.setCursor(QCursor(Qt.SizeHorCursor))
self.leftgrip.setStyleSheet(u"background-color: rgb(255, 121, 198);")
self.leftgrip.setFrameShape(QFrame.NoFrame)
self.leftgrip.setFrameShadow(QFrame.Raised)
def right(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
Form.resize(500, 500)
self.rightgrip = QFrame(Form)
self.rightgrip.setObjectName(u"right")
self.rightgrip.setGeometry(QRect(0, 0, 10, 500))
self.rightgrip.setMinimumSize(QSize(10, 0))
self.rightgrip.setCursor(QCursor(Qt.SizeHorCursor))
self.rightgrip.setStyleSheet(u"background-color: rgb(255, 0, 127);")
self.rightgrip.setFrameShape(QFrame.NoFrame)
self.rightgrip.setFrameShadow(QFrame.Raised)