0716更新

main
zcwBit 3 months ago
parent 1ff43e4347
commit a5ec3feadd

@ -14,7 +14,6 @@ from ctypes.wintypes import MSG
from win32 import win32api, win32gui
from win32.lib import win32con
from model.ProjectModel.VarManage import GlobalVarManager
from windoweffect import WindowEffect, MINMAXINFO, NCCALCSIZE_PARAMS
from UI.Main.MainLeft import MainLeft
from UI.Main.MainTop import MainTop
from ..ProjectManages.ProjectWidget import ProjectWidgets
@ -368,71 +367,7 @@ class MainWindow(QMainWindow):
event.accept()
def nativeEvent(self, eventType, message):
""" 接受windows发送的信息 """
msg = MSG.from_address(message.__int__())
if msg.message == win32con.WM_NCHITTEST:
# 解决 issue #2 and issue #7
r = self.devicePixelRatioF()
xPos = (win32api.LOWORD(msg.lParam) -
self.frameGeometry().x()*r) % 65536
yPos = win32api.HIWORD(msg.lParam) - self.frameGeometry().y()*r
w, h = self.width()*r, self.height()*r
lx = xPos < self.BORDER_WIDTH
rx = xPos + 9 > w - self.BORDER_WIDTH
ty = yPos < self.BORDER_WIDTH
by = yPos > h - self.BORDER_WIDTH
if lx and ty:
return True, win32con.HTTOPLEFT
elif rx and by:
return True, win32con.HTBOTTOMRIGHT
elif rx and ty:
return True, win32con.HTTOPRIGHT
elif lx and by:
return True, win32con.HTBOTTOMLEFT
elif ty:
return True, win32con.HTTOP
elif by:
return True, win32con.HTBOTTOM
elif lx:
return True, win32con.HTLEFT
elif rx:
return True, win32con.HTRIGHT
elif msg.message == win32con.WM_NCCALCSIZE:
if self.__isWindowMaximized(msg.hWnd):
self.__monitorNCCALCSIZE(msg)
return True, 0
elif msg.message == win32con.WM_GETMINMAXINFO:
if self.__isWindowMaximized(msg.hWnd):
window_rect = win32gui.GetWindowRect(msg.hWnd)
if not window_rect:
return False, 0
# get the monitor handle
monitor = win32api.MonitorFromRect(window_rect)
if not monitor:
return False, 0
# get the monitor info
__monitorInfo = win32api.GetMonitorInfo(monitor)
monitor_rect = __monitorInfo['Monitor']
work_area = __monitorInfo['Work']
# convert lParam to MINMAXINFO pointer
info = cast(msg.lParam, POINTER(MINMAXINFO)).contents
# adjust the size of window
info.ptMaxSize.x = work_area[2] - work_area[0]
info.ptMaxSize.y = work_area[3] - work_area[1]
info.ptMaxTrackSize.x = info.ptMaxSize.x
info.ptMaxTrackSize.y = info.ptMaxSize.y
# modify the upper left coordinate
info.ptMaxPosition.x = abs(window_rect[0] - monitor_rect[0])
info.ptMaxPosition.y = abs(window_rect[1] - monitor_rect[1])
return True, 1
return QWidget.nativeEvent(self, eventType, message)
def __isWindowMaximized(self, hWnd) -> bool:

@ -216,23 +216,36 @@ class ProjectButtonDelegate(QItemDelegate):
"请先保存工程",
QMessageBox.Yes)
return
self.loginWidget = LoginWidget()
self.loginWidget.show()
ProjectManage.switchProject(str(self.parent().model.datas[sender.index[0]][1]))
#初始化读取数据库数据
# 初始化读取数据库数据,添加进度条
progress = QtWidgets.QProgressDialog()
progress.setLabelText("正在加载工程数据...")
progress.setCancelButton(None)
progress.setMinimum(0)
progress.setMaximum(0)
progress.setWindowModality(QtCore.Qt.ApplicationModal)
progress.setMinimumDuration(0)
progress.show()
QtWidgets.QApplication.processEvents()
ProjectManage.switchProject(str(self.parent().model.datas[sender.index[0]][1]))
modelLists = ['ModbusTcpMasterTable', 'ModbusTcpSlaveTable', 'ModbusRtuMasterTable', \
'ModbusRtuSlaveTable', 'HartTable', 'TcRtdTable', 'AnalogTable', 'HartSimulateTable', 'userTable']
for l in modelLists:
Globals.getValue(l).model.initTable()
QtWidgets.QApplication.processEvents()
profibusTabWidgets = ['DP主站', 'DP从站', 'PA主站', 'PA从站']
for widget in profibusTabWidgets:
Globals.getValue(widget).switchProject()
Globals.getValue('HistoryWidget').exchangeProject()
Globals.getValue(widget).switchProject()
QtWidgets.QApplication.processEvents()
progress.close()
QtWidgets.QApplication.processEvents()
self.parent().proxy.invalidate()
self.loginWidget = LoginWidget()
self.loginWidget.show()
def edit_action(self):

@ -65,172 +65,94 @@ class HistoryTrend(object):
class TrendWidgets(QWidget):
def __init__(self, parent=None):
super(TrendWidgets, self).__init__(parent)
self.memLsit = [] # 所有mem列表
self.setAttribute(Qt.WA_StyledBackground, True)
# self.setAttribute(Qt.WA_StyledBackground, True) # 移除无效属性
self.gridLayout = QtWidgets.QGridLayout(self)
self.gridLayout.setObjectName("gridLayout")
self.timeBox = QtWidgets.QComboBox()
self.timeBox.setObjectName("timeBox")
self.timeBox.activated.connect(self.refreshList)
self.listview = QListView()
self.listview.setObjectName("trendListView")
self.model = QStandardItemModel()
self.listview.setContextMenuPolicy(Qt.CustomContextMenu)
self.listview.customContextMenuRequested.connect(self.listContext)
self.model.itemChanged.connect(self.itemCheckstate)
# self.widget = QtWidgets.QWidget(self)
# 变量列表
self.varListWidget = QListWidget()
self.varListWidget.setObjectName("varListWidget")
self.varListWidget.itemDoubleClicked.connect(self.showVarTrend)
self.gridLayout.addWidget(self.varListWidget, 0, 0, 15, 4)
# 趋势图WebView
self.trendWebView = QWebEngineView()
self.trendWebView.setObjectName("trendWebView")
self.proxy = QtCore.QSortFilterProxyModel(self)
# self.listview.proxy = self.proxy
self.proxy.setSourceModel(self.model)
self.listview.setModel(self.proxy)
self.delBtn = QtWidgets.QPushButton(QIcon(':/static/delete.png'), '删除记录', self)
self.delBtn.setObjectName('delBtn')
self.delBtn.setIconSize(QSize(22, 22))
self.delBtn.clicked.connect(self.deleteMem)
self.gridLayout.addWidget(self.timeBox, 0, 0, 1, 3)
self.gridLayout.addWidget(self.delBtn, 0, 3, 1, 1)
self.gridLayout.addWidget(self.listview, 1, 0, 14, 4)
self.gridLayout.addWidget(self.trendWebView, 0, 4, 15, 22)
self.gridLayout.setSpacing(10)
self.gridLayout.setContentsMargins(20, 20, 20, 20)
Globals.setValue('HistoryWidget', self)
Globals.setValue('HistoryWidget', self)
def exchangeProject(self):
self.historyDB = Globals.getValue('historyDBManage')
self.refreshVarList()
def deleteMem(self):
if not self.memLsit:
return
self.historyDB.mem = self.memLsit[self.timeBox.currentIndex()]
self.historyDB.deleteMem()
InfluxMem.deleteMem(self.historyDB.mem)
self.getMems()
self.exchangeProject()
self.model.clear()
pass # 历史mem相关功能已废弃
def itemCheckstate(self):
# 勾选变量复选框事件
allItems = self.model.findItems('*', Qt.MatchWildcard)
itemCheckedList = []
for item in allItems:
if item.checkState() == Qt.Checked:
itemCheckedList.append(item.text())
if not itemCheckedList:
return
self.lineTrend = HistoryTrend(self.trendWebView)
self.createHtml(itemCheckedList)
pass # 历史mem相关功能已废弃
@QtCore.pyqtSlot(str)
def on_lineEdit_textChanged(self, text):
# 搜索框文字变化函数
search = QtCore.QRegExp(text,
QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.RegExp
)
self.proxy.setFilterKeyColumn(0)
self.proxy.setFilterRegExp(search)
pass # 历史mem相关功能已废弃
def listContext(self, position):
# 点击右键删除事件
menu = QtWidgets.QMenu()
listDel = QtWidgets.QAction('删除')
listDel.triggered.connect(self.delVarRecard)
menu.addAction(listDel)
menu.exec_(self.listview.mapToGlobal(position))
pass # 历史mem相关功能已废弃
def delVarRecard(self):
# 删除变量记录
# count = self.listWidget.count()
# cb_list = [self.listWidget.itemWidget(self.listWidget.item(i))
# for i in range(count)]
index = self.listview.currentIndex()
varName = self.model.item(index.row()).text()
self.model.removeRow(index.row())
# self.memName = self.timeBox.currentText()
self.historyDB.mem = self.memLsit[self.timeBox.currentIndex()]
self.historyDB.deleteFun(str(varName))
pass # 历史mem相关功能已废弃
def exchangeProject(self):
self.timeBox.clear()
self.memLsit = []
self.getMems()
self.refreshList(0)
self.trendWebView.setHtml('')
def getMems(self):
# 获取所有的趋势表
mems = InfluxMem.get_all()
self.proName = Globals.getValue('currentPro')
if not self.proName:
return
self.timeBox.clear()
if mems is 'Error':
return
for x in mems:
time = datetime.datetime.fromtimestamp(float(x.mem)).strftime("%Y-%m-%d %H:%M:%S")
if self.timeBox.findText(time) == -1:
self.timeBox.addItem(time)
self.memLsit.append(x.mem)
self.historyDB = HistoryDBManage(bucket = self.proName, isCelery = False)
# self.refreshList(self.timeBox.currentIndex())
pass # 历史mem相关功能已废弃
def refreshList(self, index):
# 更新变量列表
self.model.clear()
if self.memLsit == []:
return
self.historyDB.mem = self.memLsit[index]
# self.historyDB.startTime = int(float(self.memLsit[index]))
# print(self.memLsit[index])
tagSet = self.historyDB.queryVarName()
# if not tagSet:
# QMessageBox.information(self, '提示', '当前工程历史趋势已损坏')
# return
for tag in tagSet:
item = QStandardItem(str(tag))
item.setCheckable(True)
self.model.appendRow(item)
self.lineTrend = HistoryTrend(self.trendWebView)
pass # 历史mem相关功能已废弃
def createHtml(self, varNames):
# 创建趋势图网页
self.historyDB.mem = self.memLsit[self.timeBox.currentIndex()]
xAxisList = []
allData = []
for var in varNames:
varData, xAxis = self.historyDB.queryFun(str(var))
xAxisList.append(xAxis)
allData.append(varData)
xAxis = max(xAxisList, key=len)
self.lineTrend.addXAxis(xAxis)
[self.lineTrend.addYAxis(varNames[index], varData) for index, varData in enumerate(allData)]
# self.trendWebView.reload()
pass # 历史mem相关功能已废弃
def refreshVarList(self):
# 显示进度条
self.varListWidget.clear()
varNames = []
try:
sql = f'SELECT DISTINCT("varName") FROM "{self.historyDB.table}"'
df = self.historyDB.client.query(sql, mode="pandas")
import pandas as pd
if isinstance(df, pd.DataFrame) and 'varName' in df.columns:
varNames = df['varName'].tolist()
except Exception as e:
print(f"获取变量名失败: {e}")
varNames = []
for name in varNames:
item = QListWidgetItem(str(name))
self.varListWidget.addItem(item)
def showVarTrend(self, item):
varName = item.text()
# 查询该变量历史数据
data, timeList = self.historyDB.queryVarHistory(varName)
if not data or not timeList:
self.trendWebView.setHtml('<h3>无历史数据</h3>')
return
# 构建趋势图
line = Line(init_opts=opts.InitOpts(width='900px', height='500px'))
line.add_xaxis(timeList)
line.add_yaxis(varName, data)
line.set_global_opts(
title_opts=opts.TitleOpts(title=f"{varName} 历史趋势"),
tooltip_opts=opts.TooltipOpts(trigger="axis"),
xaxis_opts=opts.AxisOpts(type_="category", boundary_gap=False),
datazoom_opts=[opts.DataZoomOpts(xaxis_index=0, range_start=0, range_end=100)],
)
html = line.render_embed()
page = QWebEnginePage(self.trendWebView)
page.setHtml('<meta charset="utf-8">\n<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">\n' + self.lineTrend.html)
page.setHtml('<meta charset="utf-8">\n<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">\n' + html)
self.trendWebView.setPage(page)
self.trendWebView.reload()

@ -47,6 +47,8 @@ class Client(object):
from model.ProjectModel.ProjectManage import ProjectManage
popen = Globals.getValue('popen')
popenBeat = Globals.getValue('beatPopen')
histroyMan = Globals.getValue('historyDBManage')
histroyMan.close()
if popen:
popen.kill()
if popenBeat:

@ -1,13 +1,14 @@
import os
import time
from datetime import datetime
from datetime import datetime, timezone, timedelta
from influxdb_client import InfluxDBClient, Point, WritePrecision, BucketsApi
from influxdb_client.client.write_api import SYNCHRONOUS
from influxdb_client.client.util import date_utils
from influxdb_client.client.util.date_utils import DateHelper
from influxdb_client_3 import InfluxDBClient3, Point, WriteOptions, write_client_options
import dateutil.parser
from dateutil import tz
import pandas as pd
import requests
import queue
import threading
@ -17,106 +18,146 @@ def parseDate(date_string: str):
return dateutil.parser.parse(date_string).astimezone(tz.gettz('ETC/GMT-8'))
class HistoryDBManage():
org = "DCS"
# bucket = "history"
url = "http://localhost:8086"
def __init__(self, bucket, mem = None, isCelery = True):
self.getToken(isCelery)
self.client = InfluxDBClient(url = self.url, token = self.token, org = self.org)
self.writeApi = self.client.write_api(write_options = SYNCHRONOUS)
self.deleteApi = self.client.delete_api()
self.queryApi = self.client.query_api()
self.bucketApi = self.client.buckets_api()
self.mem = mem
self.bucket = bucket
date_utils.date_helper = DateHelper()
date_utils.date_helper.parse_date = parseDate
# if self.mem:
self.startTime = 1680534
def __del__(self):
self.client.close()
def getToken(self, isCelery):
if not isCelery:
with open('Static/InfluxDB.api', 'r', encoding='utf-8') as f:
self.token = f.read()
else:
with open('../../../Static/InfluxDB.api', 'r', encoding='utf-8') as f:
self.token = f.read()
def writeFun(self, varName, value):
try:
value = int(value)
point = Point(self.mem).tag("varName", varName).field("value", value).time(datetime.utcnow(), WritePrecision.NS)
self.writeApi.write(self.bucket, self.org, point)
except Exception as e:
print(e)
BucketsApi(self.client).create_bucket(bucket_name = self.bucket, org = self.org)
def queryFun(self, varName):
# startTime = time.mktime(time.strptime("%Y-%m-%d %H:%M:%S", mem))
data = []
timeList = []
query = ' from(bucket:"{}")\
|> range(start: {})\
|> filter(fn:(r) => r._measurement == "{}")\
|> filter(fn:(r) => r.varName == "{}")\
|> filter(fn:(r) => r._field == "value" )'.format(self.bucket, self.startTime, self.mem, varName)
results = self.queryApi.query(query, org = self.org)
for result in results:
for record in result.records:
data.append(record['_value'])
timeList.append(record['_time'].strftime("%H:%M:%S:%f")[:-3])
return data, timeList
def deleteMem(self):
self.deleteApi.delete(start = '1970-01-01T00:00:00Z', stop = '2099-01-01T00:00:00Z', predicate = '_measurement={}'.format(self.mem), bucket = self.bucket, org = self.org, )
def deleteFun(self, varName):
self.deleteApi.delete(start = '1970-01-01T00:00:00Z', stop = '2099-01-01T00:00:00Z', predicate = '_measurement={} AND varName={}'.format(self.mem, varName), bucket = self.bucket, org = self.org, )
def deleteBucket(self):
bucket = self.bucketApi.find_bucket_by_name(self.bucket)
self.bucketApi.delete_bucket(bucket)
def queryMem(self):
query = 'from(bucket: "{}") |> range(start: 0) |> keys(column: " _measurement")'.format(self.bucket)
result = self.queryApi.query(query)
return result
def queryVarName(self):
tagSet = set()
query = ' from(bucket:"{}")\
|> range(start: {})\
|> filter(fn:(r) => r._measurement == "{}")\
|> filter(fn:(r) => r._field == "value")'.format(self.bucket, self.startTime, self.mem)
try:
results = self.queryApi.query(query, org = self.org)
except Exception as e:
BucketsApi(self.client).create_bucket(bucket_name = self.bucket, org = self.org)
return tagSet
for result in results:
for record in result.records:
tagSet.add(record['varName'])
return tagSet
class HistoryDBManage:
def __init__(self, database='dcs', table='history', host="http://localhost:8181", token="", org="dcs"):
token = self.getAPIToken() if not token else token
self.database = database
self.table = table
self.host = host
self.token = token
self.org = org
# 写入回调
def onSuccess(conf, data, exception=None):
now_ms = datetime.now(timezone(timedelta(hours=8)))
# 统一转为字符串
if isinstance(data, bytes):
data = data.decode(errors="ignore")
elif not isinstance(data, str):
data = str(data)
for line in data.split('\n'):
# print(line)
# m = re.search(r'enqueue_time_ms=([0-9]+)', line)
m = line.split(' ')[-1][:-3]
if m:
enqueue_time_ms = int(m)
# 转为datetime对象
# print(enqueue_time_ms / 1000)
enqueue_time_dt = datetime.fromtimestamp(enqueue_time_ms /1000000, tz=timezone(timedelta(hours=8)))
# delay = now_ms - enqueue_time_ms
print(f"写入延迟: {1} ms (enqueue_time={enqueue_time_dt}, now={now_ms})")
def onError(conf, data, exception=None):
print("InfluxDB写入失败")
def onRetry(conf, data, exception=None):
print("InfluxDB写入重试")
# 官方推荐批量写入配置
self.client = InfluxDBClient3(
host=host,
token=token,
org=org,
database=database,
write_client_options=write_client_options(
write_options=WriteOptions(batch_size=5000, flush_interval=2000), # flush_interval单位ms
# success_callback=onSuccess,
error_callback=onError,
retry_callback=onRetry
)
)
self.writeQueue = queue.Queue()
self._stopWriteThread = threading.Event()
self.writeThread = threading.Thread(target=self._writeWorker, daemon=True)
self.writeThread.start()
@classmethod
def getAPIToken(cls):
try:
with open('Static/InfluxDB.api', 'r', encoding='utf-8') as f:
token = f.read()
except Exception as e:
print(f"读取token文件失败: {e}")
token = ""
return token
def createDatabaseIfNotExists(self):
try:
sql = f'SELECT 1 FROM "{self.table}" LIMIT 1'
self.client.query(sql)
except Exception:
pass
def writeVarValue(self, varName, value):
# 入队时记录当前时间
enqueue_time = datetime.now(timezone(timedelta(hours=8)))
# print(enqueue_time)
self.writeQueue.put((varName, value, enqueue_time))
def _writeWorker(self):
while not self._stopWriteThread.is_set():
try:
varName, value, enqueue_time = self.writeQueue.get()
# print(self.writeQueue.qsize())
except queue.Empty:
# print(1111)
continue
# 用Point对象构建数据点时间用入队时间
point = Point(self.table).tag("varName", varName).field("value", float(value)).time(enqueue_time)
self.client.write(point)
def queryVarHistory(self, varName, startTime=None, endTime=None, limit=1000):
where = f'"varName" = \'{varName}\''
if startTime:
where += f" AND time >= '{startTime}'"
if endTime:
where += f" AND time <= '{endTime}'"
sql = f'SELECT time, value FROM "{self.table}" WHERE {where} ORDER BY time LIMIT {limit}'
try:
df = self.client.query(sql, mode="pandas")
import pandas as pd
if isinstance(df, pd.DataFrame):
data = df["value"].tolist() if "value" in df.columns else []
timeList = df["time"].tolist() if "time" in df.columns else []
else:
data, timeList = [], []
except Exception as e:
print(f"查询结果处理失败: {e}")
data, timeList = [], []
return data, timeList
@classmethod
def deleteTable(cls, table):
token = cls.getAPIToken()
host = 'http://localhost:8181'
url = f"{host.rstrip('/')}/api/v3/configure/table?db={'dcs'}&table={table}"
headers = {
'Authorization': f'Bearer {token}'
}
response = requests.delete(url, headers=headers)
if response.status_code != 200:
print(f"删除失败: {response.status_code} {response.text}")
else:
print(f"已删除表 {table} 的所有历史数据。")
def stopWriteThread(self):
self._stopWriteThread.set()
self.writeThread.join(timeout=1)
def close(self):
self.stopWriteThread()
self.client.close()
if __name__ == '__main__':
t = time.time()
# t = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t))
print(t)
h = HistoryDBManage(mem = t, bucket = 'history')
h.writeFun('test1', 1)
h.writeFun('test1', 2)
h.writeFun('test1', 3)
h.queryFun('test1')
h.queryMem()
h.deleteMem()
# h.deleteBucket()
db = HistoryDBManage(
database="dcs",
table="p1",
host="http://localhost:8181",
token="apiv3_ynlNTgq_OX164srSzjYXetWZJGOpgokFJbp_JaToWYlzwIPAZboPxKt4ss6vD1_4jj90QOIDnRDodQSJ66m3_g",
org="dcs"
)
data, times = db.queryVarHistory("有源/无源4-20mA输入通道1")
print(data, times)
db.close()

@ -113,6 +113,8 @@ class ProjectManage(object):
except OSError as e:
print(e)
Project.deleteProject(name = name)
histroyTableName = 'p' + name
HistoryDBManage.deleteTable(table=histroyTableName)
@classmethod
def switchProject(self, name):
@ -140,7 +142,9 @@ class ProjectManage(object):
Globals.setValue('currentProDB', projectDB)
Globals.setValue('currentProType', 1)#切换工程
protocolManage = ProtocolManage()
histroyTableName = 'p' + name
historyDBManage = HistoryDBManage(table=histroyTableName)
Globals.setValue('historyDBManage', historyDBManage)
# protocolManage.writeVariableValue('无源4-20mA输出通道1', 150)
# protocolManage.writeVariableValue('有源24V数字输出通道12', 1)
# protocolManage.writeVariableValue('热偶输出2', 40)
@ -148,7 +152,10 @@ class ProjectManage(object):
# print(protocolManage.readVariableValue('有源/无源4-20mA输入通道2'))
# print(protocolManage.readVariableValue('无源24V数字输入通道2'))
# print(a)
# print(Globals.getValue('historyDBManage'))
protocolManage = ProtocolManage()
Globals.setValue('protocolManage', protocolManage)
Globals.getValue('HistoryWidget').exchangeProject()
# if Globals.getValue('FFThread').isRunning():
# Globals.getValue('FFThread').reStart()

@ -9,9 +9,21 @@ import openpyxl
from utils.DBModels.ProtocolModel import ModbusTcpMasterVar, ModbusRtuMasterVar, ModbusRtuSlaveVar,\
ModbusTcpSlaveVar, HartVar, TcRtdVar, AnalogVar, HartSimulateVar
from PyQt5.Qt import QFileDialog, QMessageBox
from protocol.ProtocolManage import ProtocolManage
# ID 从站地址 变量名 变量描述 变量类型 寄存器地址 工程量下限 工程量上限
# 获取ProtocolManage单例或全局对象假设为全局唯一实际项目可根据实际情况调整
def getProtocolManager():
protocolManagerInstance = Globals.getValue('protocolManage')
if protocolManagerInstance is None:
protocolManagerInstance = ProtocolManage()
return protocolManagerInstance
class ModbusVarManage(object):
ModBusVarClass = {
'ModbusTcpMaster': ModbusTcpMasterVar,
@ -42,6 +54,8 @@ class ModbusVarManage(object):
modbusVarType =self.getVarClass(modbusType)()
modbusVarType.createVar(varName = varName, varType = varType, des = des, address = address,
slaveID = slaveID, min = min, max = max, order = order, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
# def importModbusVar(self, path):
@ -147,6 +161,8 @@ class ModbusVarManage(object):
modbusVarType =self.getVarClass(modbusType)()
modbusVarType.createVar(varName = varName, varType = varType, des = des, address = address,
slaveID = slaveID, min = min, max = max, order = order, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
@ -155,6 +171,8 @@ class ModbusVarManage(object):
name = str(name)
# print(name)
self.getVarClass(modbusType).deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
@ -182,18 +200,24 @@ class ModbusVarManage(object):
else:
self.getVarClass(modbusType).update(varName = Nname, description = des, varType = varType, address = address, slaveID = slaveID,
min = min, max = max, order = order, varModel = varModel).where(self.getVarClass(modbusType).varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def editOrder(self, name, order, modbusType):
name = str(name)
order = str(order)
self.getVarClass(modbusType).update(order = order).where(self.getVarClass(modbusType).varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def editVarModel(self, name, varModel, modbusType):
name = str(name)
varModel = str(varModel)
self.getVarClass(modbusType).update(varModel = varModel).where(self.getVarClass(modbusType).varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getAllVar(self, modbusType):
@ -230,6 +254,8 @@ class HartVarManage(object):
else:
hartVarType = HartVar()
hartVarType.createVar(varName = varName, des = des, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getAllVar(self):
@ -249,6 +275,8 @@ class HartVarManage(object):
name = str(name)
# print(name)
HartVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
@ -269,12 +297,16 @@ class HartVarManage(object):
return
else:
HartVar.update(varName = Nname, description = des, varModel = varModel).where(HartVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def editVarModel(self, name, varModel):
name = str(name)
print('修改变量模型',name)
HartVar.update(varModel = str(varModel)).where(HartVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
@ -299,6 +331,8 @@ class HartVarManage(object):
else:
hartVarType = HartVar()
hartVarType.createVar(varName = varName, des = des, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
class TcRtdManage(object):
@ -317,6 +351,8 @@ class TcRtdManage(object):
else:
tcRtdVarType = TcRtdVar()
tcRtdVarType.createVar(varName = varName, channelNumber=channelNumber, des = des, varType = varType, min = min, max = max, compensationVar = compensationVar, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getAllVar(self):
@ -336,6 +372,8 @@ class TcRtdManage(object):
name = str(name)
# print(name)
TcRtdVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
@ -360,6 +398,8 @@ class TcRtdManage(object):
return
else:
TcRtdVar.update(varName=Nname, channelNumber = channelNumber, description=des, varType=varType, min=min, max=max, compensationVar = compensationVar).where(TcRtdVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getByName(self, name):
@ -384,11 +424,15 @@ class TcRtdManage(object):
def editvarType(self, name, varType):
name = str(name)
TcRtdVar.update(varType = str(varType)).where(TcRtdVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def editVarModel(self, name, varModel):
name = str(name)
TcRtdVar.update(varModel = str(varModel)).where(TcRtdVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def importVarForm(self, varName, channelNumber, varType, des, min, max, compensationVar, varModel):
@ -398,6 +442,8 @@ class TcRtdManage(object):
else:
tcRtdVarType = TcRtdVar()
tcRtdVarType.createVar(varName = varName, channelNumber=channelNumber, des = des, varType = varType, min = min, max = max, compensationVar = compensationVar, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
class AnalogManage(object):
def __init__(self):
@ -415,6 +461,8 @@ class AnalogManage(object):
# else:
analogVarType = AnalogVar()
analogVarType.createVar(varName = varName, channelNumber = channelNumber, des = des, varType = varType, min = min, max = max, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getAllVar(self):
@ -434,6 +482,8 @@ class AnalogManage(object):
name = str(name)
# print(name)
AnalogVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
@ -456,11 +506,15 @@ class AnalogManage(object):
return
else:
AnalogVar.update(varName=Nname, channelNumber =channelNumber, description=des, varType=varType, min=min, max=max).where(AnalogVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def editVarModel(self, name, varModel):
name = str(name)
AnalogVar.update(varModel = str(varModel)).where(AnalogVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getByName(self, name):
# 查询指定变量信息
@ -510,6 +564,8 @@ class AnalogManage(object):
else:
analogVarType = AnalogVar()
analogVarType.createVar(varName = varName, channelNumber = channelNumber, des = des, varType = varType, min = min, max = max, varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@ -528,6 +584,8 @@ class HartSimulateVarManage(object):
else:
hartSimulateVarType = HartSimulateVar()
hartSimulateVarType.createVar(varName = varName, des = des,varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getAllVar(self):
@ -547,6 +605,8 @@ class HartSimulateVarManage(object):
name = str(name)
# print(name)
HartSimulateVar.deleteVar(name = name)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
@ -566,11 +626,15 @@ class HartSimulateVarManage(object):
return
else:
HartSimulateVar.update(varName = Nname, description = des, varModel = varModel).where(HartSimulateVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def editVarModel(self, name, varModel):
name = str(name)
HartSimulateVar.update(varModel = str(varModel)).where(HartSimulateVar.varName == name).execute()
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
@classmethod
def getByName(self, name):
# 查询指定变量信息
@ -593,6 +657,8 @@ class HartSimulateVarManage(object):
else:
hartSimulateVarType = HartSimulateVar()
hartSimulateVarType.createVar(varName = varName, des = des,varModel = varModel)
# 操作后刷新缓存
getProtocolManager().refreshVarCache()
class GlobalVarManager(object):

@ -7,6 +7,9 @@ from protocol.TCP.TCPVarManage import *
from protocol.TCP.TemToMv import temToMv
from protocol.RPC.RpcClient import RpcClient
from protocol.RPC.RpcServer import RpcServer
from utils import Globals
import threading
import time
class ProtocolManage(object):
"""通讯变量查找类,用于根据变量名在数据库模型中检索变量信息"""
@ -25,31 +28,61 @@ class ProtocolManage(object):
self.writeRTD = [0] * 8
self.RpcClient = None
self.RpcServer = None
self.varInfoCache = {} # 保持驼峰命名
self.historyDBManage = Globals.getValue('historyDBManage')
self.variableValueCache = {} # {varName: value}
self.refreshVarCache()
self.cacheLock = threading.Lock()
self.readThreadStop = threading.Event()
self.readThread = threading.Thread(target=self._backgroundReadAllVariables, daemon=True)
self.readThread.start()
def clearVarCache(self):
"""清空变量信息缓存"""
self.varInfoCache.clear()
@classmethod
def lookupVariable(cls, variableName):
def refreshVarCache(self):
"""重新加载所有变量信息到缓存(可选实现)"""
self.varInfoCache.clear()
for modelClass in self.MODEL_CLASSES:
try:
for varInstance in modelClass.select():
varName = getattr(varInstance, 'varName', None)
if varName:
varData = {}
for field in varInstance._meta.sorted_fields:
fieldName = field.name
varData[fieldName] = getattr(varInstance, fieldName)
self.varInfoCache[varName] = {
'modelType': modelClass.__name__,
'variableData': varData
}
except Exception as e:
print(f"刷新缓存时出错: {modelClass.__name__}: {e}")
def lookupVariable(self, variableName):
"""
根据变量名检索变量信息
根据变量名检索变量信息优先查缓存
:param variableName: 要查询的变量名
:return: 包含变量信息和模型类型的字典如果未找到返回None
"""
for modelClass in cls.MODEL_CLASSES:
if variableName in self.varInfoCache:
# print(111)
return self.varInfoCache[variableName]
for modelClass in self.MODEL_CLASSES:
varInstance = modelClass.getByName(variableName)
if varInstance:
# 获取变量字段信息
varData = {}
for field in varInstance._meta.sorted_fields:
fieldName = field.name
varData[fieldName] = getattr(varInstance, fieldName)
return {
'model_type': modelClass.__name__,
'variable_data': varData
result = {
'modelType': modelClass.__name__,
'variableData': varData
}
self.varInfoCache[variableName] = result # 写入缓存
return result
return None
def setClentMode(self, clentName, rabbitmqHost='localhost'):
@ -105,8 +138,8 @@ class ProtocolManage(object):
return False
return False
modelType = varInfo['model_type']
info = varInfo['variable_data']
modelType = varInfo['modelType']
info = varInfo['variableData']
# print(info)
@ -181,51 +214,46 @@ class ProtocolManage(object):
print(f"写入变量值失败: {str(e)}")
return False
# 添加读取函数
def readVariableValue(self, variableName):
"""
根据变量名读取变量值根据变量类型进行不同处理留空
:param variableName: 变量名
:return: 读取的值或None失败时
"""
#
def _backgroundReadAllVariables(self, interval=0.5):
while not self.readThreadStop.is_set():
allVarNames = list(self.getAllVariableNames())
for varName in allVarNames:
value = self._readVariableValueOriginal(varName)
with self.cacheLock:
self.variableValueCache[varName] = value
time.sleep(interval)
def getAllVariableNames(self):
# 直接从缓存获取所有变量名,降低与数据库模型的耦合
return list(self.varInfoCache.keys())
def _readVariableValueOriginal(self, variableName):
# 完全保留原有读取逻辑
varInfo = self.lookupVariable(variableName)
value = None
if not varInfo:
if self.RpcServer:
print(variableName, 1111111111111111111)
existsVar, clientNames = self.RpcServer.existsVar(variableName)
if existsVar:
# print(clientNames, 1111111111111111)
value =float(self.RpcServer.getVarValue(clientNames[0], variableName)['value'])
return value
value = float(self.RpcServer.getVarValue(clientNames[0], variableName)['value'])
# return value
else:
return None
return None
modelType = varInfo['model_type']
info = varInfo['variable_data']
modelType = varInfo['modelType']
info = varInfo['variableData']
try:
# 拆分为独立的协议条件判断
if modelType == 'ModbusTcpMasterVar':
# 读取操作(留空)
pass
elif modelType == 'ModbusTcpSlaveVar':
pass
elif modelType == 'ModbusRtuMasterVar':
pass
elif modelType == 'ModbusRtuSlaveVar':
pass
# HART协议变量处理
elif modelType == 'HartVar':
pass
# 温度/RTD变量处理
elif modelType == 'TcRtdVar':
channel = int(info['channelNumber']) - 1
varType = info['varType']
@ -236,44 +264,47 @@ class ProtocolManage(object):
value = self.tcpVarManager.simRTDData[channel]
elif varType in ['R', 'S', 'B', 'J', 'T', 'E', 'K', 'N', 'C', 'A']:
value = self.tcpVarManager.simTCData[channel]
if self.RpcClient:
self.RpcClient.setVarContent(variableName, value, info['min'], info['max'], info['varType'])
return value
# if self.RpcClient:
# self.RpcClient.setVarContent(variableName, value, info['min'], info['max'], info['varType'])
# return value
else:
if varType == 'PT100':
value = self.writeRTD[channel]
elif varType in ['R', 'S', 'B', 'J', 'T', 'E', 'K', 'N', 'C', 'A']:
value = self.writeTC[channel]
if self.RpcClient:
self.RpcClient.setVarContent(variableName, value, info['min'], info['max'], info['varType'])
return value
# 模拟量变量处理
# if self.RpcClient:
# self.RpcClient.setVarContent(variableName, value, info['min'], info['max'], info['varType'])
# return value
elif modelType == 'AnalogVar':
channel = int(info['channelNumber']) - 1
varType = info['varType']
# print(varType, channel)
varModel = info['varModel']
model = self.getModelType(varModel)
value = self.tcpVarManager.readValue(varType, channel, model=model)
if varType in ['AI','AO']:
# print(value)
value = self.getRealAI(value, info['max'], info['min'])
# return value
elif modelType == 'HartSimulateVar':
pass
if value is not None:
if self.RpcClient:
self.RpcClient.setVarContent(variableName, value, info['min'], info['max'], info['varType'])
self.historyDBManage.writeVarValue(variableName, value)
# print('sucess')
return value
# print(1)
# HART模拟变量处理
elif modelType == 'HartSimulateVar':
pass
# print(1111111111111111)
return None # 暂时返回None
else:
return None
except Exception as e:
print(f"读取变量值失败: {str(e)}")
return str(e)
return None
def readVariableValue(self, variableName):
# 优先从缓存读取,未命中则走原有逻辑
with self.cacheLock:
if variableName in self.variableValueCache:
return self.variableValueCache[variableName]
return self._readVariableValueOriginal(variableName)
# def sendTrigger(self, variableName, value, timeoutMS):
@ -284,6 +315,7 @@ class ProtocolManage(object):
def shutdown(self):
self.tcpVarManager.shutdown()
def getRealAO(self, value,highValue, lowValue):

@ -45,6 +45,8 @@ class TCPVarManager:
"""
try:
result = self.communicator.readAIDI()
self.AIDATA = result[0]
self.DIDATA = result[1]
# print(result)
if result is None:
logging.warning("Failed to read AI/DI data")
@ -61,12 +63,12 @@ class TCPVarManager:
:param callback: 数据回调函数
"""
self.stop_event = threading.Event()
self.readThread = threading.Thread(
target=self._read_worker,
args=(interval, callback),
daemon=True
)
self.readThread.start()
# self.readThread = threading.Thread(
# target=self._read_worker,
# args=(interval, callback),
# daemon=True
# )
# self.readThread.start()
logging.info(f"Started periodic read every {interval*1000}ms")
def _read_worker(self, interval, callback):
@ -225,6 +227,7 @@ class TCPVarManager:
def readValue(self, variableType, channel, model = localModel):
# channel = channel
self.readAiDi()
if variableType == "AI":
# print(self.AIDATA)
if model == SimModel:

@ -22,7 +22,7 @@ def _init():#初始化
_globalDict['AnalogThread'] = 0
_globalDict['FFSimulateThread'] = 0
_globalDict['HartSimulateThread'] = 0
_globalDict['HistoryWidget'] = 0
_globalDict['HistoryWidget'] = None
_globalDict['projectNumber'] = None
_globalDict['username'] = None
_globalDict['MainWindow'] = None
@ -32,6 +32,7 @@ def _init():#初始化
_globalDict['blockManage'] = None
_globalDict['protocolManage'] = None
_globalDict['historyDBManage'] = None
# 确保初始化
_init()

@ -0,0 +1,2 @@
from .window_effect import WindowEffect
from .c_structures import *

@ -0,0 +1,135 @@
# coding:utf-8
from ctypes import POINTER, Structure, c_int
from ctypes.wintypes import DWORD, HWND, ULONG, POINT, RECT, UINT
from enum import Enum
class WINDOWCOMPOSITIONATTRIB(Enum):
WCA_UNDEFINED = 0
WCA_NCRENDERING_ENABLED = 1
WCA_NCRENDERING_POLICY = 2
WCA_TRANSITIONS_FORCEDISABLED = 3
WCA_ALLOW_NCPAINT = 4
WCA_CAPTION_BUTTON_BOUNDS = 5
WCA_NONCLIENT_RTL_LAYOUT = 6
WCA_FORCE_ICONIC_REPRESENTATION = 7
WCA_EXTENDED_FRAME_BOUNDS = 8
WCA_HAS_ICONIC_BITMAP = 9
WCA_THEME_ATTRIBUTES = 10
WCA_NCRENDERING_EXILED = 11
WCA_NCADORNMENTINFO = 12
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13
WCA_VIDEO_OVERLAY_ACTIVE = 14
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15
WCA_DISALLOW_PEEK = 16
WCA_CLOAK = 17
WCA_CLOAKED = 18
WCA_ACCENT_POLICY = 19
WCA_FREEZE_REPRESENTATION = 20
WCA_EVER_UNCLOAKED = 21
WCA_VISUAL_OWNER = 22
WCA_LAST = 23
class ACCENT_STATE(Enum):
""" Client area status enumeration class """
ACCENT_DISABLED = 0
ACCENT_ENABLE_GRADIENT = 1
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2
ACCENT_ENABLE_BLURBEHIND = 3 # Aero effect
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4 # Acrylic effect
ACCENT_ENABLE_HOSTBACKDROP = 5 # Mica effect
ACCENT_INVALID_STATE = 6
class ACCENT_POLICY(Structure):
""" Specific attributes of client area """
_fields_ = [
("AccentState", DWORD),
("AccentFlags", DWORD),
("GradientColor", DWORD),
("AnimationId", DWORD),
]
class WINDOWCOMPOSITIONATTRIBDATA(Structure):
_fields_ = [
("Attribute", DWORD),
# Pointer() receives any ctypes type and returns a pointer type
("Data", POINTER(ACCENT_POLICY)),
("SizeOfData", ULONG),
]
class DWMNCRENDERINGPOLICY(Enum):
DWMNCRP_USEWINDOWSTYLE = 0
DWMNCRP_DISABLED = 1
DWMNCRP_ENABLED = 2
DWMNCRP_LAS = 3
class DWMWINDOWATTRIBUTE(Enum):
DWMWA_NCRENDERING_ENABLED = 1
DWMWA_NCRENDERING_POLICY = 2
DWMWA_TRANSITIONS_FORCEDISABLED = 3
DWMWA_ALLOW_NCPAINT = 4
DWMWA_CAPTION_BUTTON_BOUNDS = 5
DWMWA_NONCLIENT_RTL_LAYOUT = 6
DWMWA_FORCE_ICONIC_REPRESENTATION = 7
DWMWA_FLIP3D_POLICY = 8
DWMWA_EXTENDED_FRAME_BOUNDS = 9
DWMWA_HAS_ICONIC_BITMAP = 10
DWMWA_DISALLOW_PEEK = 11
DWMWA_EXCLUDED_FROM_PEEK = 12
DWMWA_CLOAK = 13
DWMWA_CLOAKED = 14
DWMWA_FREEZE_REPRESENTATION = 15
DWMWA_PASSIVE_UPDATE_MODE = 16
DWMWA_USE_HOSTBACKDROPBRUSH = 17
DWMWA_USE_IMMERSIVE_DARK_MODE = 18
DWMWA_WINDOW_CORNER_PREFERENCE = 19
DWMWA_BORDER_COLOR = 20
DWMWA_CAPTION_COLOR = 21
DWMWA_TEXT_COLOR = 22
DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 23
DWMWA_LAST = 24
class MARGINS(Structure):
_fields_ = [
("cxLeftWidth", c_int),
("cxRightWidth", c_int),
("cyTopHeight", c_int),
("cyBottomHeight", c_int),
]
class MINMAXINFO(Structure):
_fields_ = [
("ptReserved", POINT),
("ptMaxSize", POINT),
("ptMaxPosition", POINT),
("ptMinTrackSize", POINT),
("ptMaxTrackSize", POINT),
]
class PWINDOWPOS(Structure):
_fields_ = [
('hWnd', HWND),
('hwndInsertAfter', HWND),
('x', c_int),
('y', c_int),
('cx', c_int),
('cy', c_int),
('flags', UINT)
]
class NCCALCSIZE_PARAMS(Structure):
_fields_ = [
('rgrc', RECT*3),
('lppos', POINTER(PWINDOWPOS))
]

@ -0,0 +1,222 @@
# coding:utf-8
import sys
from ctypes import POINTER, c_bool, c_int, pointer, sizeof, WinDLL, byref
from ctypes.wintypes import DWORD, LONG, LPCVOID
from win32 import win32api, win32gui
from win32.lib import win32con
from .c_structures import (
ACCENT_POLICY,
ACCENT_STATE,
MARGINS,
DWMNCRENDERINGPOLICY,
DWMWINDOWATTRIBUTE,
WINDOWCOMPOSITIONATTRIB,
WINDOWCOMPOSITIONATTRIBDATA,
)
class WindowEffect:
""" A class that calls Windows API to realize window effect """
def __init__(self):
# Declare the function signature of the API
self.user32 = WinDLL("user32")
self.dwmapi = WinDLL("dwmapi")
self.SetWindowCompositionAttribute = self.user32.SetWindowCompositionAttribute
self.DwmExtendFrameIntoClientArea = self.dwmapi.DwmExtendFrameIntoClientArea
self.DwmSetWindowAttribute = self.dwmapi.DwmSetWindowAttribute
self.SetWindowCompositionAttribute.restype = c_bool
self.DwmExtendFrameIntoClientArea.restype = LONG
self.DwmSetWindowAttribute.restype = LONG
self.SetWindowCompositionAttribute.argtypes = [
c_int,
POINTER(WINDOWCOMPOSITIONATTRIBDATA),
]
self.DwmSetWindowAttribute.argtypes = [c_int, DWORD, LPCVOID, DWORD]
self.DwmExtendFrameIntoClientArea.argtypes = [c_int, POINTER(MARGINS)]
# Initialize structure
self.accentPolicy = ACCENT_POLICY()
self.winCompAttrData = WINDOWCOMPOSITIONATTRIBDATA()
self.winCompAttrData.Attribute = WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY.value
self.winCompAttrData.SizeOfData = sizeof(self.accentPolicy)
self.winCompAttrData.Data = pointer(self.accentPolicy)
def setAcrylicEffect(self, hWnd, gradientColor: str = "F2F2F299", isEnableShadow: bool = True, animationId: int = 0):
""" Add the acrylic effect to the window
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
gradientColor: str
Hexadecimal acrylic mixed color, corresponding to four RGBA channels
isEnableShadow: bool
Enable window shadows
animationId: int
Turn on matte animation
"""
hWnd = int(hWnd)
# Acrylic mixed color
gradientColor = (
gradientColor[6:]
+ gradientColor[4:6]
+ gradientColor[2:4]
+ gradientColor[:2]
)
gradientColor = DWORD(int(gradientColor, base=16))
# matte animation
animationId = DWORD(animationId)
# window shadow
accentFlags = DWORD(0x20 | 0x40 | 0x80 |
0x100) if isEnableShadow else DWORD(0)
self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_ENABLE_ACRYLICBLURBEHIND.value
self.accentPolicy.GradientColor = gradientColor
self.accentPolicy.AccentFlags = accentFlags
self.accentPolicy.AnimationId = animationId
# enable acrylic effect
self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData))
def setMicaEffect(self, hWnd):
""" Add the mica effect to the window (Win11 only)
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
if sys.getwindowsversion().build < 22000:
raise Exception("The mica effect is only available on Win11")
hWnd = int(hWnd)
margins = MARGINS(-1, -1, -1, -1)
self.DwmExtendFrameIntoClientArea(hWnd, byref(margins))
self.DwmSetWindowAttribute(hWnd, 1029, byref(c_int(1)), 4)
self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_ENABLE_HOSTBACKDROP.value
self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData))
def setAeroEffect(self, hWnd):
""" Add the aero effect to the window
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_ENABLE_BLURBEHIND.value
self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData))
def removeBackgroundEffect(self, hWnd):
""" Remove background effect
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
self.accentPolicy.AccentState = ACCENT_STATE.ACCENT_DISABLED.value
self.SetWindowCompositionAttribute(hWnd, pointer(self.winCompAttrData))
@staticmethod
def moveWindow(hWnd):
""" Move the window
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
win32gui.ReleaseCapture()
win32api.SendMessage(
hWnd, win32con.WM_SYSCOMMAND, win32con.SC_MOVE + win32con.HTCAPTION, 0
)
def addShadowEffect(self, hWnd):
""" Add DWM shadow to window
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
margins = MARGINS(-1, -1, -1, -1)
self.DwmExtendFrameIntoClientArea(hWnd, byref(margins))
def addMenuShadowEffect(self, hWnd):
""" Add DWM shadow to menu
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
self.DwmSetWindowAttribute(
hWnd,
DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY.value,
byref(c_int(DWMNCRENDERINGPOLICY.DWMNCRP_ENABLED.value)),
4,
)
margins = MARGINS(-1, -1, -1, -1)
self.DwmExtendFrameIntoClientArea(hWnd, byref(margins))
def removeShadowEffect(self, hWnd):
""" Remove DWM shadow from the window
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
self.DwmSetWindowAttribute(
hWnd,
DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY.value,
byref(c_int(DWMNCRENDERINGPOLICY.DWMNCRP_DISABLED.value)),
4,
)
@staticmethod
def removeMenuShadowEffect(hWnd):
""" Remove shadow from pop-up menu
Parameters
----------
hWnd: int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
style = win32gui.GetClassLong(hWnd, win32con.GCL_STYLE)
style &= ~0x00020000 # CS_DROPSHADOW
win32api.SetClassLong(hWnd, win32con.GCL_STYLE, style)
@staticmethod
def addWindowAnimation(hWnd):
""" Enables the maximize and minimize animation of the window
Parameters
----------
hWnd : int or `sip.voidptr`
Window handle
"""
hWnd = int(hWnd)
style = win32gui.GetWindowLong(hWnd, win32con.GWL_STYLE)
win32gui.SetWindowLong(
hWnd,
win32con.GWL_STYLE,
style
| win32con.WS_MAXIMIZEBOX
| win32con.WS_CAPTION
| win32con.CS_DBLCLKS
| win32con.WS_THICKFRAME,
)
Loading…
Cancel
Save