鱼C论坛

 找回密码
 立即注册
查看: 24|回复: 1

[作品展示] python内置库做的html编译器

[复制链接]
发表于 昨天 21:04 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
代码如下(我等级太低了,不能发图片):
  1. from tkinter import *
  2. from tkinter import scrolledtext, filedialog, messagebox
  3. import re
  4. import tempfile
  5. import webbrowser
  6. import os
  7. import xml.etree.ElementTree as ET

  8. class HTMLCompiler:
  9.     def __init__(self, root):
  10.         self.root = root
  11.         self.root.title("HTML 编译器 - 未命名")
  12.         self.root.configure(bg='black')
  13.         self.current_file = None
  14.         self.last_error = None
  15.         
  16.         # 创建工具栏
  17.         self.toolbar = Frame(root, bg='#333')
  18.         
  19.         self.new_btn = Button(self.toolbar, text="新建", command=self.new_file, bg='#333', fg='white')
  20.         self.open_btn = Button(self.toolbar, text="打开", command=self.open_file, bg='#333', fg='white')
  21.         self.save_btn = Button(self.toolbar, text="保存", command=self.save_file, bg='#333', fg='white')
  22.         self.run_btn = Button(self.toolbar, text="运行", command=self.run_html, bg='#333', fg='white')
  23.         self.help_btn = Button(self.toolbar, text='帮助', command=self.help_file, bg='#333', fg='white')
  24.         
  25.         self.new_btn.pack(side=LEFT, padx=2, pady=2)
  26.         self.open_btn.pack(side=LEFT, padx=2, pady=2)
  27.         self.save_btn.pack(side=LEFT, padx=2, pady=2)
  28.         self.run_btn.pack(side=LEFT, padx=2, pady=2)
  29.         self.help_btn.pack(side=LEFT, padx=2, pady=2)
  30.         
  31.         self.toolbar.pack(fill=X)
  32.         
  33.         # 创建文本编辑器
  34.         self.text_area = scrolledtext.ScrolledText(
  35.             root, wrap=WORD, bg='black', fg='white',
  36.             insertbackground='white', font=('Consolas', 12)
  37.         )
  38.         self.text_area.pack(fill=BOTH, expand=True)
  39.         
  40.         # 绑定事件
  41.         self.text_area.bind('<KeyRelease>', self.on_key_release)
  42.         self.text_area.bind('<Tab>', self.handle_tab)
  43.         self.text_area.bind('<Return>', self.handle_return)
  44.         
  45.         # 绑定快捷键
  46.         self.root.bind('<Control-n>', lambda event: self.new_file())
  47.         self.root.bind('<Control-o>', lambda event: self.open_file())
  48.         self.root.bind('<Control-s>', lambda event: self.save_file())
  49.         self.root.bind('<Control-r>', lambda event: self.run_html())
  50.         
  51.         # 语法高亮颜色配置
  52.         self.tag_colors = {
  53.             'tag': '#569CD6',
  54.             'attribute': '#9CDCFE',
  55.             'value': '#CE9178',
  56.             'comment': '#6A9955',
  57.             'doctype': '#569CD6',
  58.             'string': '#CE9178',
  59.             'symbol': '#D4D4D4'
  60.         }
  61.         
  62.         # 配置标签样式
  63.         for tag, color in self.tag_colors.items():
  64.             self.text_area.tag_config(tag, foreground=color)
  65.         
  66.         # 状态栏
  67.         self.status_bar = Frame(root, bg='black')
  68.         self.charn = Label(self.status_bar, text='字数:0', fg='white', bg='black')
  69.         self.place = Label(self.status_bar, text='行:0;列:0', fg='white', bg='black')
  70.         self.html_bool = Button(self.status_bar, text='&#10003;', bg='black', fg='green', command=self.print_error)
  71.         
  72.         self.charn.pack(side=LEFT, padx=5)
  73.         self.place.pack(side=LEFT, padx=5)
  74.         self.html_bool.pack(side=LEFT, padx=5)
  75.         self.status_bar.pack(fill=X, side=BOTTOM)
  76.         
  77.         # 初始内容
  78.         self.text_area.insert(1.0, '''<!DOCTYPE html>
  79. <html>
  80. <head>
  81.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  82.     <meta charset="UTF-8">
  83. </head>
  84. <body>
  85.     <!--文档的内容......-->
  86. </body>
  87. </html>''')
  88.         
  89.         # 初始更新
  90.         self.highlight()
  91.         self.update_status()
  92.         self.auto_update()

  93.     def auto_update(self):
  94.         self.update_status()
  95.         self.root.after(100, self.auto_update)
  96.         
  97.     def update_status(self):
  98.         self.len_char()
  99.         self.label_place()
  100.         self.test(self.text_area.get(1.0, END))
  101.         
  102.     def new_file(self, event=None):
  103.         self.text_area.delete('1.0', END)
  104.         self.current_file = None
  105.         self.root.title("HTML 编译器 - 未命名")
  106.    
  107.     def open_file(self, event=None):
  108.         file_path = filedialog.askopenfilename(
  109.             filetypes=[("HTML 文件", "*.html"), ("所有文件", "*.*")]
  110.         )
  111.         if file_path:
  112.             try:
  113.                 with open(file_path, 'r', encoding='utf-8') as file:
  114.                     content = file.read()
  115.                     self.text_area.delete('1.0', END)
  116.                     self.text_area.insert('1.0', content)
  117.                     self.current_file = file_path
  118.                     self.root.title(f"HTML 编译器 - {os.path.basename(file_path)}")
  119.                     self.highlight()
  120.             except Exception as e:
  121.                 messagebox.showerror("错误", f"无法打开文件:\n{str(e)}")
  122.    
  123.     def save_file(self, event=None):
  124.         if self.current_file:
  125.             try:
  126.                 with open(self.current_file, 'w', encoding='utf-8') as file:
  127.                     file.write(self.text_area.get('1.0', END))
  128.                 messagebox.showinfo("保存", "文件保存成功!")
  129.             except Exception as e:
  130.                 messagebox.showerror("错误", f"无法保存文件:\n{str(e)}")
  131.         else:
  132.             self.save_as_file()
  133.    
  134.     def save_as_file(self):
  135.         file_path = filedialog.asksaveasfilename(
  136.             defaultextension=".html",
  137.             filetypes=[("HTML 文件", "*.html"), ("所有文件", "*.*")]
  138.         )
  139.         if file_path:
  140.             try:
  141.                 with open(file_path, 'w', encoding='utf-8') as file:
  142.                     file.write(self.text_area.get('1.0', END))
  143.                 self.current_file = file_path
  144.                 self.root.title(f"HTML 编译器 - {os.path.basename(file_path)}")
  145.                 messagebox.showinfo("保存", "文件保存成功!")
  146.             except Exception as e:
  147.                 messagebox.showerror("错误", f"无法保存文件:\n{str(e)}")
  148.    
  149.     def run_html(self, event=None):
  150.         html_code = self.text_area.get("1.0", END)
  151.         
  152.         if not html_code.strip():
  153.             messagebox.showwarning("警告", "没有内容可运行!")
  154.             return
  155.         
  156.         try:
  157.             # 使用临时目录
  158.             temp_dir = tempfile.mkdtemp()
  159.             temp_path = os.path.join(temp_dir, 'preview.html')
  160.             
  161.             with open(temp_path, 'w', encoding='utf-8') as f:
  162.                 f.write(html_code)

  163.             # 尝试多种浏览器
  164.             browsers = ['google-chrome', 'chrome', 'msedge', 'firefox']
  165.             opened = False
  166.             for browser in browsers:
  167.                 try:
  168.                     webb = webbrowser.get(browser)
  169.                     webb.open('file://' + os.path.abspath(temp_path))
  170.                     opened = True
  171.                     break
  172.                 except webbrowser.Error:
  173.                     continue
  174.             
  175.             if not opened:
  176.                 webbrowser.open('file://' + os.path.abspath(temp_path))
  177.                
  178.         except Exception as e:
  179.             messagebox.showerror("错误", f"无法运行HTML:\n{str(e)}")

  180.     def help_file(self):
  181.         messagebox.showinfo('提示','''快捷键:
  182. Ctrl+N 新建文件
  183. Ctrl+O 打开文件
  184. Ctrl+S 保存文件
  185. Ctrl+R 运行HTML''')
  186.    
  187.     def on_key_release(self, event=None):
  188.         self.highlight()
  189.    
  190.     def handle_tab(self, event):
  191.         current_pos = self.text_area.index(INSERT)
  192.         line, col = map(int, current_pos.split('.'))
  193.         
  194.         space_count = 4 - (col % 4)
  195.         self.text_area.insert(INSERT, ' ' * space_count)
  196.         return 'break'
  197.    
  198.     def handle_return(self, event):
  199.         current_pos = self.text_area.index(INSERT)
  200.         line, col = map(int, current_pos.split('.'))
  201.         line_text = self.text_area.get(f'{line}.0', f'{line}.end')
  202.         
  203.         indent = 0
  204.         while indent < len(line_text) and line_text[indent] == ' ':
  205.             indent += 1
  206.         
  207.         self.text_area.insert(INSERT, '\n' + ' ' * indent)
  208.         
  209.         if line_text.strip().startswith('<') and not line_text.strip().startswith('</'):
  210.             match = re.match(r'^\s*<([a-zA-Z]+)[^>]*>', line_text)
  211.             if match and not line_text.strip().endswith('/>'):
  212.                 tag = match.group(1)
  213.                 self.text_area.insert(INSERT, f'\n{" " * indent}</{tag}>')
  214.                 self.text_area.mark_set(INSERT, f'{line+1}.{indent+1}')
  215.         
  216.         return 'break'
  217.    
  218.     def highlight(self):
  219.         for tag in self.tag_colors.keys():
  220.             self.text_area.tag_remove(tag, '1.0', END)
  221.         
  222.         text = self.text_area.get('1.0', END)
  223.         
  224.         # 高亮注释
  225.         self.highlight_pattern(r'<!--.*?-->', 'comment')
  226.         
  227.         # 高亮DOCTYPE
  228.         self.highlight_pattern(r'<!DOCTYPE.*?>', 'doctype')
  229.         
  230.         # 高亮标签
  231.         self.highlight_pattern(r'<\/?[a-zA-Z]+', 'tag')
  232.         self.highlight_pattern(r'<\/[a-zA-Z]+>', 'tag')
  233.         
  234.         # 高亮属性
  235.         self.highlight_pattern(r'\s[a-zA-Z-]+=', 'attribute')
  236.         
  237.         # 高亮属性值
  238.         self.highlight_pattern(r'"[^"]*"', 'value')
  239.         self.highlight_pattern(r"'[^']*'", 'value')
  240.         
  241.         # 高亮符号
  242.         self.highlight_pattern(r'[<>\/=]', 'symbol')
  243.    
  244.     def highlight_pattern(self, pattern, tag):
  245.         start = '1.0'
  246.         end = END
  247.         
  248.         self.text_area.mark_set('matchStart', start)
  249.         self.text_area.mark_set('matchEnd', start)
  250.         
  251.         count = IntVar()
  252.         while True:
  253.             index = self.text_area.search(
  254.                 pattern, 'matchEnd', end,
  255.                 count=count, regexp=True
  256.             )
  257.             if index == '': break
  258.             
  259.             self.text_area.mark_set('matchStart', index)
  260.             self.text_area.mark_set('matchEnd', f'{index}+{count.get()}c')
  261.             
  262.             self.text_area.tag_add(tag, 'matchStart', 'matchEnd')
  263.             
  264.     def len_char(self, event=None):
  265.         self.char = len(self.text_area.get(1.0, END)) - 1
  266.         self.charn.config(text=f'字数: {self.char}')
  267.         
  268.     def label_place(self):
  269.         self.cursor_pos = self.text_area.index(INSERT)
  270.         self.line, self.column = self.cursor_pos.split('.')
  271.         self.place.config(text=f"行: {self.line}, 列: {self.column}")

  272.     def test(self, code):
  273.         try:
  274.             # 包装成根节点,避免「文档多个根元素」报错
  275.             wrapped_code = f"<root>{code}</root>"
  276.             ET.fromstring(wrapped_code)
  277.             self.html_bool.config(text='&#10003;', fg='green')
  278.             self.last_error = None
  279.         except ET.ParseError as e:
  280.             self.last_error = f"HTML 语法错误(需符合 XML 规范):\n{str(e)}"
  281.             self.html_bool.config(text='&#10007;',fg='red')

  282.     def print_error(self):
  283.         if self.last_error:
  284.             messagebox.showerror('错误', self.last_error)
  285.         else:
  286.             messagebox.showinfo('提示', 'HTML 代码合法(符合 XML 标准)')
  287.             

  288.             

  289. if __name__ == '__main__':
  290.     root = Tk()
  291.     root.geometry('800x600')
  292.     app = HTMLCompiler(root)
  293.     root.mainloop()
复制代码

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 昨天 21:29 | 显示全部楼层
迭代这么快
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-5 02:25

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表