Files
ctbjrj/tools/ribbon_replace.py

425 lines
19 KiB
Python

# -*- coding: utf-8 -*-
"""把主窗口顶部 tabControl1(项目/报价/设置 三页签×24按钮)替换为 RibbonStrip 功能区(主功能区子类)。
改写 MainForm.Designer.cs / MainForm.cs / 主窗口.cs / MainForm.旧页签兼容.cs。
幂等:重复运行前先检查关键标记是否已存在。"""
import io
import re
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
# 24 个按钮: (原字段名, Text)
BTN_PROJECT = [("button1", "新建项目"), ("button2", "修改项目"), ("button3", "删除项目"),
("button4", "打开项目"), ("button5", "复制项目")]
BTN_PROJECT2 = [("button8", "导入项目"), ("button7", "导出项目")]
BTN_QUOTE1 = [("button9", "打开项目"), ("button10", "修改项目"), ("button17", "保存")]
BTN_QUOTE2 = [("button18", "导入报价"), ("button16", "甲方报表")]
BTN_QUOTE3 = [("button15", "调母排"), ("button13", "调系数"), ("button33", "元件排序"), ("button29", "模板费用")]
BTN_QUOTE4 = [("button11", "CAD扒图")]
BTN_SET1 = [("btn登录", "登录"), ("btn修改密码", "修改密码"), ("btn会员信息", "会员信息")]
BTN_SET2 = [("btn企业信息", "企业信息"), ("btn保存布局", "保存布局")]
BTN_SET3 = [("btn备份", "备份"), ("btn恢复", "恢复")]
ALL_BTNS = [b for grp in (BTN_PROJECT, BTN_PROJECT2, BTN_QUOTE1, BTN_QUOTE2, BTN_QUOTE3,
BTN_QUOTE4, BTN_SET1, BTN_SET2, BTN_SET3) for b in grp]
TAB_PAGES = ["tabPage项目", "tabPage报价", "tabPage设置"]
BTN_NAMES = set(n for n, _ in ALL_BTNS)
def read(p):
with io.open(p, "r", encoding="utf-8-sig") as f:
return f.read()
def write(p, s):
with io.open(p, "w", encoding="utf-8-sig") as f:
f.write(s)
def must_remove(line):
"""Designer.cs 里要删的行:24 按钮 + tabControl1 + 3 tabPage 的字段/new/suspend/resume/属性行。"""
s = line.strip()
# 按钮字段/new
m = re.match(r"private System\.Windows\.Forms\.Button (button\d+|btn[^;]+);$", s)
if m and m.group(1).strip() in BTN_NAMES:
return True
m = re.match(r"this\.(button\d+|btn[^ =]+) = new System\.Windows\.Forms\.Button\(\);$", s)
if m and m.group(1) in BTN_NAMES:
return True
# 按钮属性行(this.buttonN.xxx = ...)
m = re.match(r"this\.([A-Za-z0-9_]+)\.[A-Za-z0-9_]+ = ", s)
if m and m.group(1) in BTN_NAMES:
return True
# tabControl1/tabPage
if re.match(r"private System\.Windows\.Forms\.TabControl tabControl1;$", s):
return True
if s == "this.tabControl1 = new System.Windows.Forms.TabControl();":
return True
if s.startswith("this.tabControl1.") or s == "this.tabControl1.ResumeLayout(false);":
return True
for tp in TAB_PAGES:
if re.match(r"private System\.Windows\.Forms\.TabPage %s;$" % tp, s):
return True
if s == "this.%s = new System.Windows.Forms.TabPage();" % tp:
return True
if s.startswith("this.%s." % tp) or s == "this.%s.ResumeLayout(false);" % tp:
return True
return False
def ribbon_block():
"""生成 ribbonStrip1 的 Designer 区块(tab 缩进)。"""
t = "\t\t\t"
L = []
L.append(t + "// ")
L.append(t + "// ribbonStrip1")
L.append(t + "// ")
def tab(var, text):
L.append(t + "Smart.CustomComponents.RibbonTab %s = new Smart.CustomComponents.RibbonTab();" % var)
L.append(t + "%s.Text = \"%s\";" % (var, text))
def group(var, text):
L.append(t + "Smart.CustomComponents.RibbonGroup %s = new Smart.CustomComponents.RibbonGroup();" % var)
L.append(t + "%s.Text = \"%s\";" % (var, text))
def btn(field, text):
var = "rbtn_" + field
L.append(t + "Smart.CustomComponents.RibbonButton %s = new Smart.CustomComponents.RibbonButton();" % var)
L.append(t + "%s.Name = \"%s\";" % (var, field))
L.append(t + "%s.Text = \"%s\";" % (var, text))
def add_group(var):
L.append(t + "rtab1.Groups.Add(%s);" % var)
def add_tab():
L.append(t + "this.ribbonStrip1.Tabs.Add(rtab1);")
# ---- 页签1 项目 ----
tab("rtab1", "项目")
group("rgrpA", "项目文件")
for f, x in BTN_PROJECT:
btn(f, x)
L.append(t + "rgrpA.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpA")
group("rgrpB", "数据")
for f, x in BTN_PROJECT2:
btn(f, x)
L.append(t + "rgrpB.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpB")
add_tab()
# ---- 页签2 报价 ----
tab("rtab1", "报价")
group("rgrpA", "项目")
for f, x in BTN_QUOTE1:
btn(f, x)
L.append(t + "rgrpA.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpA")
group("rgrpB", "报价")
for f, x in BTN_QUOTE2:
btn(f, x)
L.append(t + "rgrpB.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpB")
group("rgrpC", "计算")
for f, x in BTN_QUOTE3:
btn(f, x)
L.append(t + "rgrpC.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpC")
group("rgrpD", "工具")
for f, x in BTN_QUOTE4:
btn(f, x)
L.append(t + "rgrpD.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpD")
add_tab()
# ---- 页签3 设置 ----
tab("rtab1", "设置")
group("rgrpA", "账号")
for f, x in BTN_SET1:
btn(f, x)
L.append(t + "rgrpA.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpA")
group("rgrpB", "系统")
for f, x in BTN_SET2:
btn(f, x)
L.append(t + "rgrpB.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpB")
group("rgrpC", "数据")
for f, x in BTN_SET3:
btn(f, x)
L.append(t + "rgrpC.Buttons.Add(rbtn_%s);" % f)
add_group("rgrpC")
add_tab()
L.append(t + "this.ribbonStrip1.Dock = System.Windows.Forms.DockStyle.Top;")
L.append(t + "this.ribbonStrip1.Location = new System.Drawing.Point(0, 0);")
L.append(t + "this.ribbonStrip1.Name = \"ribbonStrip1\";")
L.append(t + "this.ribbonStrip1.Size = new System.Drawing.Size(1884, 110);")
L.append(t + "this.ribbonStrip1.TabIndex = 3;")
return "\n".join(L)
def replace_designer(src):
lines = src.split("\n")
out = []
i = 0
n = len(lines)
while i < n:
line = lines[i]
s = line.strip()
# 1) 按钮区块整体跳过:遇到 "// buttonN" 或 "// btnXxx" 标题行,连标题三行一起跳到区块尾
m = re.match(r"// (button\d+|btn[^ ]+)$", s)
if m and m.group(1) in BTN_NAMES:
i += 3 # 跳过 // 、// buttonN、// 三行
while i < n and lines[i].strip().startswith("this." + m.group(1) + "."):
i += 1
continue
# 2) tabControl1 区块跳过(注释头三行 + this.tabControl1.* 行),替换为 ribbonStrip1 区块
if s == "// tabControl1":
i += 3
while i < n and lines[i].strip().startswith("this.tabControl1"):
i += 1
out.append("\t\t\t// ")
out.append("\t\t\t// ribbonStrip1")
out.append("\t\t\t// ")
out.append(ribbon_block())
# 跳过后续 3 个 tabPage 区块
for tp in TAB_PAGES:
while i < n and lines[i].strip() != "// " + tp:
i += 1
if i < n:
i += 3
while i < n and lines[i].strip().startswith("this." + tp + "."):
i += 1
continue
# 3) 其余单行删除(字段/new/suspend/resume/Controls.Add(tabControl1))
if must_remove(line):
i += 1
continue
if s == "this.Controls.Add(this.tabControl1);":
out.append(line.replace("tabControl1", "ribbonStrip1"))
i += 1
continue
out.append(line)
i += 1
txt = "\n".join(out)
# new 行与字段行插入
txt = txt.replace(
"\t\t\tthis.tabControl1 = new System.Windows.Forms.TabControl();",
"\t\t\tthis.ribbonStrip1 = new 主功能区();")
txt = txt.replace(
"\t\tprivate System.Windows.Forms.TabControl tabControl1;",
"\t\tprivate 主功能区 ribbonStrip1;")
return txt
def replace_mainform_cs(src):
# 1) 删 24 行 Click 挂接(button 系 12 行 + button15 lambda 段 + btn 系 5 行)
pat_click = re.compile(
r"[ \t]*this\.(button1|button3|button2|button5|button10|button16|button13|button18|button7|button8|button33|btn企业信息|btn备份|btn恢复|button11)\.Click \+= [^\n]*\n")
src = pat_click.sub("", src)
# button17 保存
src = re.sub(r"[ \t]*this\.button17\.Click \+= \(s, e\) => SaveAllToDatabase\(\);[^\n]*\n", "", src)
# button15 lambda 整段(从注释行到闭括号行)
src = re.sub(
r"[ \t]*// ===== 调母排\(button15\):普通非模态弹窗 =====\n"
r"[ \t]*// 不自动居中、不贴靠右侧、不跟随主窗口移动、不限制大小,直接弹出。\n"
r"[ \t]*调母排 _tiaomuForm = null;\n"
r"[ \t]*\n"
r"[ \t]*this\.button15\.Click \+= \(s, e\) =>\n"
r"[ \t]*\{.*?\n"
r"[ \t]*\};\n",
"", src, flags=re.S)
# 2) tabControl1.SelectedIndexChanged 联动段替换为功能区事件
src = re.sub(
r"[ \t]*// ===== 顶部选项卡:点击\"项目\"/\"报价\"时激活对应停靠文档窗口 =====\n"
r"[ \t]*this\.tabControl1\.SelectedIndexChanged \+= \(s, e\) =>\n"
r"[ \t]*\{.*?\n"
r"[ \t]*\};\n",
(" // ===== 功能区(ribbonStrip1):统一按钮点击路由 + 页签切换联动 =====\n"
" this.ribbonStrip1.ButtonClick += ribbonStrip1_ButtonClick;\n"
" this.ribbonStrip1.活动页已切换 += (s, e) =>\n"
" {\n"
" string tabName = (this.ribbonStrip1.ActiveTab != null) ? this.ribbonStrip1.ActiveTab.Text : \"\";\n"
" Log(\"[TabSync] 功能区页签切换: [\" + tabName + \"]\");\n"
" if (tabName == \"项目\" && this.w1 != null)\n"
" {\n"
" this.w1.Show(this.dockSurface1);\n"
" this.w1.Activate();\n"
" }\n"
" else if (tabName == \"报价\" && this.w2 != null)\n"
" {\n"
" this.w2.Show(this.dockSurface1);\n"
" this.w2.Activate();\n"
" }\n"
" // \"设置\"页只显示按钮面板,无停靠窗口\n"
" };\n\n"),
src, flags=re.S)
return src
def add_handlers(src):
"""在 MainForm 类尾部(canvas:找类的最后一个方法后)插入处理器与辅助方法。找 'private void Dgv_CellValuePushed' 所在文件不适用——直接加在构造器后第一个方法前。"""
handlers = '''
// ============================================================
// 功能区(ribbonStrip1)按钮路由与辅助
// ============================================================
/// <summary>功能区统一按钮点击:按 RibbonButton.Name(保留原按钮字段名)分发到原处理器。</summary>
private void ribbonStrip1_ButtonClick(object sender, Smart.CustomComponents.RibbonButtonEventArgs e)
{
if (e.Button == null || string.IsNullOrEmpty(e.Button.Name)) return;
switch (e.Button.Name)
{
case "button1": button1_Click(sender, e); break; // 新建项目
case "button2": button2_Click(sender, e); break; // 修改项目(项目页)
case "button3": button3_Click(sender, e); break; // 删除项目
case "button4": break; // 打开项目(项目页,原无处理器)
case "button5": button5_Click(sender, e); break; // 复制项目
case "button7": button7_Click(sender, e); break; // 导出项目
case "button8": button8_Click(sender, e); break; // 导入项目
case "button9": break; // 打开项目(报价页,原无处理器)
case "button10": button10_Click(sender, e); break; // 修改项目(报价页)
case "button11": button11_Click(sender, e); break; // CAD扒图
case "button13": button13_Click(sender, e); break; // 调系数
case "button15": 打开调母排窗口(); break; // 调母排
case "button16": button16_Click(sender, e); break; // 甲方报表
case "button17": SaveAllToDatabase(); break; // 保存
case "button18": button18_Click(sender, e); break; // 导入报价
case "button29": break; // 模板费用(原无处理器)
case "button33": button33_Click(sender, e); break; // 元件排序
case "btn登录": break; // 登录(原无处理器)
case "btn修改密码": break; // 修改密码(原无处理器)
case "btn企业信息": btn企业信息_Click(sender, e); break; // 企业信息
case "btn会员信息": break; // 会员信息(原无处理器)
case "btn保存布局": btn保存布局_Click(sender, e); break; // 保存布局
case "btn备份": btn备份_Click(sender, e); break; // 备份
case "btn恢复": btn恢复_Click(sender, e); break; // 恢复
}
}
/// <summary>按 Name 在功能区三层集合里找 RibbonButton(找不到返回 null)。</summary>
private Smart.CustomComponents.RibbonButton FindRibbonButton(string name)
{
if (this.ribbonStrip1 == null) return null;
foreach (Smart.CustomComponents.RibbonTab tab in this.ribbonStrip1.Tabs)
foreach (Smart.CustomComponents.RibbonGroup grp in tab.Groups)
foreach (Smart.CustomComponents.RibbonButton btn in grp.Buttons)
if (btn.Name == name) return btn;
return null;
}
/// <summary>调母排(button15):普通非模态弹窗(原构造器内 lambda 抽出)。
/// 不自动居中、不贴靠右侧、不跟随主窗口移动、不限制大小,直接弹出。</summary>
private 调母排 _调母排窗;
private void 打开调母排窗口()
{
if (_调母排窗 != null && !_调母排窗.IsDisposed)
{
_调母排窗.Activate();
return;
}
if (string.IsNullOrWhiteSpace(_currentProjectId))
{
MessageBox.Show(this, "请先在项目列表双击打开一个项目。", "调母排",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
_调母排窗 = new 调母排(this);
// 不设置 StartPosition/Location/Size/MaximumSize/MinimumSize,使用窗体自身默认值
_调母排窗.Show(this);
_调母排窗.FormClosed += (s2, e2) => { _调母排窗 = null; };
}
/// <summary>项目菜单页"修改项目"按钮(项目页 button2):无条件可用,补路由。</summary>
private void button2_Click(object sender, EventArgs e)
{
修改项目入口();
}
'''
# button2_Click 原本存在?若存在会重复定义——先检查
if "private void button2_Click" in src and "ribbonStrip1_ButtonClick" not in src:
# 已有 button2_Click,去掉补丁里的重复定义
handlers = handlers.replace('''
/// <summary>项目菜单页"修改项目"按钮(项目页 button2):无条件可用,补路由。</summary>
private void button2_Click(object sender, EventArgs e)
{
修改项目入口();
}
''', "")
# 插入点:构造器结束后(锚点在构造器内,往后找第一个方法级结束括号 \t\t})
anchor = " // ===== 监听主区域 tabControl2 切换"
idx = src.find(anchor)
if idx < 0:
raise SystemExit("MainForm.cs 找不到插入锚点")
m = re.search(r"\n\t\t\}\n", src[idx:])
if not m:
raise SystemExit("MainForm.cs 找不到构造器结束位置")
pos = idx + m.end()
return src[:pos] + handlers + "\n" + src[pos:]
def replace_zhuwindow(src):
# UpdateButton10State: button10 → FindRibbonButton
src = src.replace(
" this.button10.Enabled = onQuotePage && hasProject;",
" var rb10 = FindRibbonButton(\"button10\");\n"
" if (rb10 != null) rb10.Enabled = onQuotePage && hasProject;")
# EnterProjectCore: tabControl1.SelectedTab = tabPage2
src = src.replace(
" this.tabControl1.SelectedTab = this.tabPage2;\n"
" Log(\"[EnterProjectCore] 已设 tabControl1.SelectedTab = tabPage2\");",
" int 报价页idx = this.ribbonStrip1.页索引(\"报价\");\n"
" if (报价页idx >= 0) this.ribbonStrip1.设活动页(报价页idx);\n"
" Log(\"[EnterProjectCore] 已设功能区活动页=报价\");")
return src
def replace_compat(src):
# 删 tabPage1/2/8 三个兼容属性
src = re.sub(
r"[ \t]*// ---- 旧页签名 → 新控件别名(旧代码 this\.tabPageN 继续可用)----\n"
r".*?public DockContent tabPage3 \{ get \{ return this\.w1; \} \}",
" // ---- 旧页签名 → 新控件别名(旧代码 this.tabPageN 继续可用)----\n\n"
" /// <summary>旧左侧\"项目\"页 → 停靠窗口\"项目\"。</summary>\n"
" public DockContent tabPage3 { get { return this.w1; } }",
src, flags=re.S)
return src
def main():
import os
# 幂等保护
d = read(os.path.join(ROOT, "MainForm.Designer.cs"))
if "ribbonStrip1" in d:
print("Designer.cs 已含 ribbonStrip1,跳过")
else:
write(os.path.join(ROOT, "MainForm.Designer.cs"), replace_designer(d))
print("Designer.cs 改写完成")
m = read(os.path.join(ROOT, "MainForm.cs"))
if "ribbonStrip1_ButtonClick(object sender" not in m:
m2 = replace_mainform_cs(m)
m2 = add_handlers(m2)
write(os.path.join(ROOT, "MainForm.cs"), m2)
print("MainForm.cs 改写完成")
else:
print("MainForm.cs 已改,跳过")
z = read(os.path.join(ROOT, "主窗口.cs"))
if "FindRibbonButton(\"button10\")" not in z:
write(os.path.join(ROOT, "主窗口.cs"), replace_zhuwindow(z))
print("主窗口.cs 改写完成")
else:
print("主窗口.cs 已改,跳过")
c = read(os.path.join(ROOT, "MainForm.旧页签兼容.cs"))
if "tabPage2 { get" in c:
write(os.path.join(ROOT, "MainForm.旧页签兼容.cs"), replace_compat(c))
print("旧页签兼容.cs 改写完成")
else:
print("旧页签兼容.cs 已改,跳过")
if __name__ == "__main__":
main()