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