From bcb0392af14d2d9ccb463b4cc741e96130331c05 Mon Sep 17 00:00:00 2001 From: 3mora2 <66757189+3mora2@users.noreply.github.com> Date: Sun, 10 Sep 2023 04:31:22 +0300 Subject: [PATCH] accept all var type create class Var accept all var type --- .gitignore | 1 + modules/CodeEditor/CodeEditor/editor.py | 2 +- requirements.txt | 6 +- src/qss_template/qsst.py | 64 ++++++++++--- src/ui/mainwin.py | 117 +++++++++++++++--------- 5 files changed, 130 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index d1ff4dd..a7caa14 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,4 @@ venv.bak/ # mypy .mypy_cache/ +src/config/config.toml diff --git a/modules/CodeEditor/CodeEditor/editor.py b/modules/CodeEditor/CodeEditor/editor.py index 355efa9..aac32d5 100644 --- a/modules/CodeEditor/CodeEditor/editor.py +++ b/modules/CodeEditor/CodeEditor/editor.py @@ -360,7 +360,7 @@ def load(self, filename): def save(self, filename): """Save the editor contents to the given filename.""" - with open(filename, 'w', newline='') as outfile: + with open(filename, 'w', newline='', encoding="utf-8") as outfile: # 不指定newline,则换行符为各系统默认的换行符(\n, \r, or \r\n, ) # newline=''表示不转换 outfile.write(self.text()) diff --git a/requirements.txt b/requirements.txt index 25e8653..f082aee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ requests>=2.0 preimport>=1.1.0 toml>=0.10.0 tomlconfig>=1.2.1 -PyQt5>=5.10.1,<5.14 -PyQt5-sip>=12.7,<12.7.1 -QScintilla==2.11.4 +PyQt5 +PyQt5-sip +QScintilla CodeEditor>=1.1.0 \ No newline at end of file diff --git a/src/qss_template/qsst.py b/src/qss_template/qsst.py index a8d160b..7e6a17a 100644 --- a/src/qss_template/qsst.py +++ b/src/qss_template/qsst.py @@ -5,10 +5,41 @@ """ import re +from dataclasses import dataclass from PyQt5.QtGui import QColor + +@dataclass +class Var: + value: str + key: str + _type: str = None + + def __repr__(self): + return self.value + + @property + def var(self): + return self.value.strip() + + @property + def type(self): + if self.value in QColor.colorNames(): + return "color" + elif self.var.startswith("#") or "rgb" in self.var or "rgba" in self.var: + return "color" + + elif "px" in self.var: + return "px" + elif self.var.replace('.', '', 1).isdigit(): + return "digit" + + return "text" + + class Qsst(): """qss template""" + def __init__(self, qssFile=None): if qssFile is None: self.srctext = '' # qss template,qss with vars @@ -40,20 +71,21 @@ def loadVars(self, qssStr=None): for var, val in varsDefined: if not valerr: if val in QColor.colorNames(): - self.varDict[var] = val + self.varDict[var] = Var(val, var) else: - valerrind = re.match( - r'#[0-9A-Fa-f]{1,8}|rgb\(\s*[0-9]*\s*(,\s*[0-9]*\s*){2}\)|rgba\(\s*[0-9]*\s*(,\s*[0-9]*\s*){3}\)|[\w]*px', - val) - if not valerrind: + # valerrind = re.match( + # r'#[0-9A-Fa-f]{1,8}|rgb\(\s*[0-9]*\s*(,\s*[0-9]*\s*){2}\)|rgba\(\s*[0-9]*\s*(,\s*[0-9]*\s*){3}\)|[\w]*px', + # val) + v = Var(val, var) + if not v.type: valerr = True else: - self.varDict[var] = val + self.varDict[var] = v self.varUndefined = [] for varused in self.varUsed: if varused not in self.varDict.keys(): - self.varDict[varused] = '' + self.varDict[varused] = Var('', varused) self.varUndefined.append(varused) return not valerr # 如果有变量的值格式不正确返回false @@ -75,7 +107,7 @@ def addColorOpacity(self, color: str, opacity: str): """ if '#' not in color and len(color) not in [4, 7]: return color - + if len(color) == 4: color = ''.join(item + item for item in color)[1:] @@ -86,7 +118,7 @@ def addColorOpacity(self, color: str, opacity: str): opacity = 255 return '#' + hex(opacity).replace('0x', '') + color[1:] - + def convertQss(self): """根据varDict中变量的值,把模板文件中引用的变量用值替换,转换为qss文件。 """ @@ -102,9 +134,11 @@ def convertQss(self): if v in varDict.keys(): # opacity qssStr = re.sub(r'[$](\w+)[%](\w+)([\s;]*)', - lambda m: '{}{}'.format(self.addColorOpacity(varDict[m.group(1)], m.group(2)), m.group(3)), qssStr) + lambda m: '{}{}'.format(self.addColorOpacity(varDict[m.group(1)].value, m.group(2)), + m.group(3)), qssStr) # qssStr = qssStr.replace("$" + v, varDict[v]) - qssStr = re.sub(r'[$](\w+)([\s;]*)', lambda m: '{}{}'.format(varDict[m.group(1)], m.group(2)), qssStr) + qssStr = re.sub(r'[$](\w+)([\s;]*)', lambda m: '{}{}'.format(varDict[m.group(1)].value, m.group(2)), + qssStr) else: self.varUndefined.append(v) # qssStr = qssStr.replace("$" + v, ' ') @@ -127,19 +161,20 @@ def writeVars(self): self.loadVars() if self.varDict: # 如果文件中变量不为空,更新变量值 self.srctext = re.sub(r'[$](\w+)\s*=[ \t]*([#(),.\w]*)[\t ]*[;]?', - lambda m: '${} = {};'.format(m.group(1), varDictNew.get(m.group(1), "")), + lambda m: '${} = {};'.format(m.group(1), + varDictNew.get(m.group(1), Var("", "")).value), self.srctext) if self.varUndefined: # 在第一的变量处插入多出来的变量,引用比定义的变量多的时候回出现这种情况 s = '' for var, val in varDictNew.items(): if var in self.varUndefined: - s += "$" + var + " = " + val + ";\n" + s += "$" + var + " = " + val.value + ";\n" self.srctext = re.sub(r'[$](\w+)\s*=[ \t]*([#(),.\w]*)[\t ]*[;]?', r'{}$\1 = \2;\n'.format(s), self.srctext, 1) else: s = '' for var, val in varDictNew.items(): - s += "$" + var + " = " + val + ";\n" + s += "$" + var + " = " + val.value + ";\n" s += '\n' self.srctext = s + self.srctext self.loadVars() @@ -147,6 +182,7 @@ def writeVars(self): if __name__ == "__main__": import os + os.chdir(os.path.dirname(__file__)) qssT = Qsst('default.qsst') qssT.loadVars() diff --git a/src/ui/mainwin.py b/src/ui/mainwin.py index 3ed1018..d77e46d 100644 --- a/src/ui/mainwin.py +++ b/src/ui/mainwin.py @@ -6,13 +6,14 @@ import sys from PyQt5.QtWidgets import (qApp, QWidget, QLabel, QPushButton, QColorDialog, QFileDialog, QMessageBox, QFormLayout, - QVBoxLayout, QHBoxLayout) + QVBoxLayout, QHBoxLayout, QLineEdit) from PyQt5.QtGui import QIcon, QColor, qGray, QFont from PyQt5.QtCore import Qt, QSize, QTimer # import sip from config import Config, ConfDialog from qss_template import Qsst +from qss_template.qsst import Var from .mainwinbase import MainWinBase from .palettedialog import PaletteDialog from .recent import Recent @@ -166,7 +167,7 @@ def uncommentcode(self): lineto = linefrom linetext = self.editor.text(linefrom) linebytes = linetext.encode() - if len(linetext.encode()) < 4 or colcursor<2 or colcursor >len(linetext.encode())-2: + if len(linetext.encode()) < 4 or colcursor < 2 or colcursor > len(linetext.encode()) - 2: return for i in range(colcursor - 1, -1, -1): if linebytes[i] == ord("*") and linebytes[i - 1] == ord("/"): @@ -174,67 +175,67 @@ def uncommentcode(self): break for i in range(colcursor, len(linebytes) - 1): if linebytes[i] == ord("*") and linebytes[i + 1] == ord("/"): - print(i, linebytes[i], linebytes[i+1]) + print(i, linebytes[i], linebytes[i + 1]) end = i break else: linestart = self.editor.text(linefrom).encode() - colend = len(linestart) if lineto>linefrom else colto + colend = len(linestart) if lineto > linefrom else colto for i in range(colfrom, colend): - if linestart[i] == ord('/') and linestart[i+1] == ord('*'): + if linestart[i] == ord('/') and linestart[i + 1] == ord('*'): start = i break if start == -1: for i in range(colfrom, 0, -1): - if linestart[i] == ord('/') and linestart[i-1] == ord('*'): + if linestart[i] == ord('/') and linestart[i - 1] == ord('*'): start = -2 break - elif linestart[i] == ord('*') and linestart[i-1] == ord('/'): - start = i-1 + elif linestart[i] == ord('*') and linestart[i - 1] == ord('/'): + start = i - 1 break - if start ==-1 and linefrom > 0: + if start == -1 and linefrom > 0: while linefrom > 0: - linefrom = linefrom -1 + linefrom = linefrom - 1 if self.editor.text(linefrom).strip() != "": break linestart = self.editor.text(linefrom).encode() - for i in range(len(linestart)-1, 0, -1): - if linestart[i] == ord('/') and linestart[i-1] == ord('*'): + for i in range(len(linestart) - 1, 0, -1): + if linestart[i] == ord('/') and linestart[i - 1] == ord('*'): break - elif linestart[i] == ord('*') and linestart[i-1] == ord('/'): - start = i-1 + elif linestart[i] == ord('*') and linestart[i - 1] == ord('/'): + start = i - 1 break if start > 0: lineend = self.editor.text(lineto).encode() - colstart = 0 if lineto>linefrom else colfrom + colstart = 0 if lineto > linefrom else colfrom for i in range(colto, colstart, -1): if lineend[i] == ord('/') and lineend[i - 1] == ord('*'): end = i break if end == -1: - for i in range(colto, len(lineend)-1): + for i in range(colto, len(lineend) - 1): if lineend[i] == ord('/') and lineend[i + 1] == ord('*'): end = -2 break elif lineend[i] == ord('*') and lineend[i + 1] == ord('/'): end = i break - if end < 0 and lineto < self.editor.lines()-1: - while lineto < self.editor.lines()-1: + if end < 0 and lineto < self.editor.lines() - 1: + while lineto < self.editor.lines() - 1: lineto = lineto + 1 if self.editor.text(lineto).strip() != "": break print(lineto) lineend = self.editor.text(lineto).encode() - for i in range(len(lineend)-1): + for i in range(len(lineend) - 1): if lineend[i] == ord('/') and lineend[i + 1] == ord('*'): break elif lineend[i] == ord('*') and lineend[i + 1] == ord('/'): end = i - print(linefrom,start, lineto, end) + print(linefrom, start, lineto, end) - if start>=0 and end>=0: + if start >= 0 and end >= 0: # try: # self.editor.positionFromLineIndex(linefrom, lineto) self.editor.setSelection(lineto, end, lineto, end + 2) @@ -290,7 +291,7 @@ def renderStyle(self): # self.statusbar.showMessage("")#不起作用 # except Exception: # self.statusbar.showMessage("qss parse failed") - cstr = self.qsst.varDict.get("background", "") + cstr = self.qsst.varDict.get("background", Var("", "background")).value if cstr: try: c = QColor() @@ -338,31 +339,51 @@ def loadColorPanel(self): # self.colorGridLayout.update() # 不起作用 # a,b=list(self.clrBtnDict.keys()),list(self.qsst.varDict.keys());a.sort();b.sort() - if sorted(list(self.clrBtnDict.keys())) != sorted(list(self.qsst.varDict.keys())): + if sorted(list(self.clrBtnDict.items())) != sorted(list(self.qsst.varDict.items())): while self.colorPanelLayout.count() > 0: self.colorPanelLayout.removeItem(self.colorPanelLayout.itemAt(0)) self.clrBtnDict = {} + self.varLinDict = {} labels = {} widLabel = 0 widBtn = 0 + widLin = 0 for varName, clrStr in self.qsst.varDict.items(): - label = QLabel(varName) # , contianerWidget) - btn = QPushButton(clrStr) # , contianerWidget) - if sys.platform.startswith("win"): - font1 = QFont("Arial", 10, QFont.Medium) - font2 = QFont("sans-serif", 9, QFont.Medium) - label.setFont(font1) - btn.setFont(font2) - self.clrBtnDict[varName] = btn - labels[varName] = label - label.adjustSize() - widLabel = label.width() if label.width() > widLabel else widLabel - btn.adjustSize() - widBtn = btn.width() if btn.width() > widBtn else widBtn - # label.move(5, 5) - # btn.move(100, 5) - btn.clicked.connect(lambda x, var=varName: self.chclr(var)) - for name, btn in self.clrBtnDict.items(): + if clrStr.type in ["color"]: + label = QLabel(varName) # , contianerWidget) + btn = QPushButton(clrStr.value) # , contianerWidget) + if sys.platform.startswith("win"): + font1 = QFont("Arial", 10, QFont.Medium) + font2 = QFont("sans-serif", 9, QFont.Medium) + label.setFont(font1) + btn.setFont(font2) + self.clrBtnDict[varName] = btn + labels[varName] = label + label.adjustSize() + widLabel = label.width() if label.width() > widLabel else widLabel + btn.adjustSize() + widBtn = btn.width() if btn.width() > widBtn else widBtn + # label.move(5, 5) + # btn.move(100, 5) + btn.clicked.connect(lambda x, var=varName: self.chclr(var)) + elif clrStr.type: + label = QLabel(varName) # , contianerWidget) + line = QLineEdit(clrStr.value) + if sys.platform.startswith("win"): + font1 = QFont("Arial", 10, QFont.Medium) + font2 = QFont("sans-serif", 9, QFont.Medium) + label.setFont(font1) + line.setFont(font2) + + self.varLinDict[varName] = line + labels[varName] = label + label.adjustSize() + widLabel = label.width() if label.width() > widLabel else widLabel + line.adjustSize() + widLin = line.width() if line.width() > widLin else widLin + line.textChanged.connect(lambda x, var=varName: self.chLine(var)) + + for name, btn in [*self.clrBtnDict.items(), *self.varLinDict.items()]: contianerWidget = QWidget() lay = QHBoxLayout() labels[name].setFixedWidth(widLabel) @@ -374,7 +395,7 @@ def loadColorPanel(self): self.colorPanelLayout.addWidget(contianerWidget) for varName, btn in self.clrBtnDict.items(): - clrStr = self.qsst.varDict[varName] + clrStr = self.qsst.varDict[varName].value btn.setText(clrStr) if "rgb" in clrStr: t = clrStr.strip(r" rgba()") @@ -400,6 +421,18 @@ def loadColorPanel(self): btn.setStyleSheet(s + "background:" + btn.text()) + def chLine(self, var): + val = self.sender().text() + self.qsst.varDict[var] = Var(val, var) + self.qsst.writeVars() + # 用setText之后undo redo堆栈全部消失,所以暂时用这种方法 + pos = self.editor.verticalScrollBar().sliderPosition() + self.editor.selectAll() + self.editor.replaceSelectedText(self.qsst.srctext) # setText(self.qsst.srctext) + # self.CodeEditor.setCursorPosition(xp,yp) + self.editor.verticalScrollBar().setSliderPosition(pos) + self.renderStyle() + def chclr(self, var): c = QColor() cstr = self.sender().text() @@ -421,7 +454,7 @@ def chclr(self, var): s += 'color:white;' self.clrBtnDict[var].setText(clrstr) self.clrBtnDict[var].setStyleSheet(s + "background:" + clrstr) - self.qsst.varDict[var] = clrstr + self.qsst.varDict[var] = Var(clrstr, var) self.qsst.writeVars() # 用setText之后undo redo堆栈全部消失,所以暂时用这种方法 pos = self.editor.verticalScrollBar().sliderPosition()