feat(app): 新增了模拟上位机的软件程序
1. 新增了模拟上位机软件,方便调试下位机 2. 编写了模拟上位机的使用说明文档 3. 修改了README的相关部分
9
.gitignore
vendored
@ -64,4 +64,11 @@
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
.apdisk
|
||||
|
||||
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
venv/
|
||||
.idea/
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ IO扩展版提供了
|
||||
- [develop_and_deploy.md](doc/develop_and_deploy.md)为开发和部署说明,首先看这个文档
|
||||
- [hardware_description.md](doc/hardware_description.md)为PL端逻辑设计说明,阐述了硬件工作的整体流程
|
||||
- [pl_reference_mannual.md](doc/pl_reference_mannual.md)为PL端逻辑在AXI总线上映射的寄存器参考手册
|
||||
- [sim_uppermachine_manual.md](doc/sim_uppermachine_manual.md)为模拟上位机运行的参考手册
|
||||
|
||||
- script为配置系统、安装环境、安装可执行文件、卸载可执行文件等的脚本
|
||||
|
||||
@ -42,13 +43,14 @@ IO扩展版提供了
|
||||
- pl_platform为PL端硬件设计
|
||||
- xme0724ioextend为IO叠板的原理图和PCB
|
||||
|
||||
- source为XME0724板子上运行的源程序
|
||||
- source为XME0724板子上运行的源程序和模拟上位机程序
|
||||
|
||||
- liunx_app为Linux上运行的应用程序,即业务逻辑
|
||||
- linux_driver为Linux上的驱动,用于控制自定义的PL端硬件,其中drv_test结尾的目录为相应驱动模块的测试应用程序
|
||||
- petalinux_config为petalinux工具在编译u-boot、kernel、rootfs前进行的配置
|
||||
- petalinux_devicetree为本次自定义的Linux设备树文件部分,其余设备树为自动生成的
|
||||
- petalinux_hwdescription为petalinux所使用的硬件描述文件,包含了vivado工程中的比特流等信息
|
||||
- sim_uppermachine_manul为基于pyside6所编写的软件,用于模拟上位机发送指令,方便调试
|
||||
|
||||
## 版本
|
||||
|
||||
@ -74,4 +76,4 @@ b分支编号-d文档版本-hPCB设计版本-lFPGA设计版本-p协议版本-s
|
||||
|
||||
## 作者
|
||||
|
||||
作者觉得还是不说明作者是谁比较好,免得毕业后有提着示波器的师弟师妹来问问题
|
||||
作者觉得还是不说明是谁比较好,免得毕业后有提着示波器的师弟师妹来问问题
|
||||
BIN
doc/sim_uppermachine_manual.assets/2.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
11
doc/sim_uppermachine_manual.md
Normal file
@ -0,0 +1,11 @@
|
||||
# 模拟上位机说明文档
|
||||
|
||||
## 模拟上位机连接下位机
|
||||
|
||||
模拟上位机使用python编写,如下图所示,实现显示界面并与下位机通信发送四个相机的分频值。运行程序后,出现显示界面,连接网线并选择Server IP为运行模拟上位机程序的本机系统中手动设定的IP地址,Server Port端号为13452(默认)。选择完成后等待连接,当界面下方出现本机与下位机的IP地址时即为连接成功。
|
||||
|
||||
## 模拟上位机发送指令
|
||||
|
||||
模拟上位机与下位机连接成功后,可在MANUAL栏中发送通信协议中对应的命令,在PRESET栏中填写四个相机触发所需的分频值,在发送分频值前,需要先点击停止按钮,然后点击对应的A、B、C、D按钮即可发送各相机对应的分频值,最后点击Start按钮即可将分频值发送给下位机。
|
||||
|
||||

|
||||
@ -1 +1 @@
|
||||
1.6
|
||||
1.7
|
||||
BIN
source/sim_uppermachine/images/icons/30-height-sep.png
Normal file
|
After Width: | Height: | Size: 107 B |
BIN
source/sim_uppermachine/images/icons/aperture.png
Normal file
|
After Width: | Height: | Size: 373 B |
BIN
source/sim_uppermachine/images/icons/arrow-right.png
Normal file
|
After Width: | Height: | Size: 195 B |
BIN
source/sim_uppermachine/images/icons/camera-and-down.png
Normal file
|
After Width: | Height: | Size: 447 B |
BIN
source/sim_uppermachine/images/icons/camera.png
Normal file
|
After Width: | Height: | Size: 373 B |
BIN
source/sim_uppermachine/images/icons/chevron-down.png
Normal file
|
After Width: | Height: | Size: 135 B |
BIN
source/sim_uppermachine/images/icons/chevron-left.png
Normal file
|
After Width: | Height: | Size: 170 B |
BIN
source/sim_uppermachine/images/icons/chevron-minidown.png
Normal file
|
After Width: | Height: | Size: 153 B |
BIN
source/sim_uppermachine/images/icons/chevron-miniup.png
Normal file
|
After Width: | Height: | Size: 154 B |
BIN
source/sim_uppermachine/images/icons/chevron-right.png
Normal file
|
After Width: | Height: | Size: 167 B |
BIN
source/sim_uppermachine/images/icons/chevron-statusbardown.png
Normal file
|
After Width: | Height: | Size: 191 B |
BIN
source/sim_uppermachine/images/icons/chevron-statusbarup.png
Normal file
|
After Width: | Height: | Size: 192 B |
BIN
source/sim_uppermachine/images/icons/close.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
source/sim_uppermachine/images/icons/home.png
Normal file
|
After Width: | Height: | Size: 322 B |
BIN
source/sim_uppermachine/images/icons/image.png
Normal file
|
After Width: | Height: | Size: 291 B |
BIN
source/sim_uppermachine/images/icons/link-2.png
Normal file
|
After Width: | Height: | Size: 266 B |
BIN
source/sim_uppermachine/images/icons/link.png
Normal file
|
After Width: | Height: | Size: 274 B |
BIN
source/sim_uppermachine/images/icons/log-out.png
Normal file
|
After Width: | Height: | Size: 256 B |
BIN
source/sim_uppermachine/images/icons/maximize.png
Normal file
|
After Width: | Height: | Size: 248 B |
BIN
source/sim_uppermachine/images/icons/menu.png
Normal file
|
After Width: | Height: | Size: 248 B |
BIN
source/sim_uppermachine/images/icons/minimize.png
Normal file
|
After Width: | Height: | Size: 172 B |
BIN
source/sim_uppermachine/images/icons/play.png
Normal file
|
After Width: | Height: | Size: 284 B |
BIN
source/sim_uppermachine/images/icons/plus.png
Normal file
|
After Width: | Height: | Size: 198 B |
BIN
source/sim_uppermachine/images/icons/restore.png
Normal file
|
After Width: | Height: | Size: 322 B |
BIN
source/sim_uppermachine/images/icons/rotate-ccw.png
Normal file
|
After Width: | Height: | Size: 359 B |
BIN
source/sim_uppermachine/images/icons/save.png
Normal file
|
After Width: | Height: | Size: 301 B |
BIN
source/sim_uppermachine/images/icons/shuffle.png
Normal file
|
After Width: | Height: | Size: 335 B |
BIN
source/sim_uppermachine/images/icons/sliders.png
Normal file
|
After Width: | Height: | Size: 254 B |
BIN
source/sim_uppermachine/images/icons/square.png
Normal file
|
After Width: | Height: | Size: 222 B |
BIN
source/sim_uppermachine/images/icons/wind.png
Normal file
|
After Width: | Height: | Size: 292 B |
BIN
source/sim_uppermachine/images/images/PyDracula.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
source/sim_uppermachine/images/images/PyDracula.psd
Normal file
BIN
source/sim_uppermachine/images/images/PyDracula_horizontal.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
source/sim_uppermachine/images/images/PyDracula_vertical.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
328
source/sim_uppermachine/main.py
Normal 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())
|
||||
5484
source/sim_uppermachine/main.ui
Normal file
29
source/sim_uppermachine/modules/__init__.py
Normal 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 *
|
||||
37
source/sim_uppermachine/modules/app_functions.py
Normal 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;")
|
||||
|
||||
31
source/sim_uppermachine/modules/app_settings.py
Normal 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);
|
||||
"""
|
||||
22
source/sim_uppermachine/modules/list_model.py
Normal 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
|
||||
83
source/sim_uppermachine/modules/protocol.py
Normal 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()
|
||||
5953
source/sim_uppermachine/modules/resources_rc.py
Normal file
192
source/sim_uppermachine/modules/server.py
Normal 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()
|
||||
262
source/sim_uppermachine/modules/ui_functions.py
Normal 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)
|
||||
2957
source/sim_uppermachine/modules/ui_main.py
Normal file
39
source/sim_uppermachine/resources.qrc
Normal 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>
|
||||
1
source/sim_uppermachine/settings.json
Normal 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": {}}
|
||||
519
source/sim_uppermachine/themes/py_dracula_dark.qss
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
532
source/sim_uppermachine/themes/py_dracula_light.qss
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
1
source/sim_uppermachine/version
Normal file
@ -0,0 +1 @@
|
||||
1.0
|
||||
17
source/sim_uppermachine/widgets/__init__.py
Normal 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
|
||||
17
source/sim_uppermachine/widgets/custom_grips/__init__.py
Normal 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
|
||||
243
source/sim_uppermachine/widgets/custom_grips/custom_grips.py
Normal 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)
|
||||