Tcl/Tk Notes (1)

Tcl/Tk Notes



Part I TCL Overview

1 Everything Is a String

In Tcl, evrything is a string, and you have to explicitly ask for evaluation of variables and nested commands.

2 Substitution

  • Variable
The use of dollar sign($) is one kind of substitution. Variables can be assigned values by set command.

  • Command Substitution
A nested command is delimited by square brackets, [ and ]. The Tcl takes everything between the brackets and evaluates it as a command. It Reqrites the outer command by replacing the square brackets and everything between them with the result of nested command.

  • Math Expression
The expr command is used to evaluate math expressions. The Tcl interpreter treats expr just like any other command, and it leaves the expression parsing up to the expr implementation.

  • Backslash Substitution
Backslash(/) is used to quote characters that have special meaning to the interpreter.
You can also specify characters that are hard to type directly by giving their octal or hexadecimal value.
Another common use of backslashes is to continue long commands on multiple lines. A backslash as the last character in a line is converted into a space.

3 Double Quotes vs. Curly Braces

Double quotes, like braces, are used to group words together. The difference between double quotes and curly braces is that quotes allow substitutions to occur in the group, while curly braces prevent substitutions.
File?id=dd6nw3mt_112fm5tm2gc_b
In practice, grouping with curly braces is used when substitutions on the argument need to be delayed until a later time( or never done at all). Examples include contrl flow statements and procedure declararions. Double quotes are usefulin simple cases like the puts command.
Another common use of quotes is with the format command that is similar to C printf function. The only way to effectively group special characters in format command into a single argument is with quotes.
e.g. puts [format "Item: %s/t%5.3f" $name $value]

4 Procedures

Tcl uses the proc command to define procedures. The syntax is :
proc name arglist body
The return command in Tcl procedure is optional because the interpreter will return the value of the last command in the body as the value of the procedure.

5 A While Loop Example

File?id=dd6nw3mt_113d4kxm7gt_b
The loop around boolean expression $i<=10 is crucial, because it delays substitution until while command implementation tests the expression. The following expression is an infinite loop:
set i; while $i<=10 {incr i}

The Tcl interpreter will substitute for $i before while is called, so while gets a constant expression 1<=10 that will always be ture.You can avoid this kind of errors by adopting a consistent coding style that always groups expressions and command bodies with curly braces.
The command in the example uses set with a single argument. When used in this way the set command returns the current value of the named variable.

6 Grouping And Command Substitution

Grouping decisions are made before substitutions are performed. This means that the values of variables or command results do not affect grouping.
A single round if substitutions is performed before command invocation. That is, the result of a substitution is not interpreted a second time. This rule is important if you have a variable value or a command result that contains special characters such as spaces, dollar-signs, square brackets or braces. Because only a single round of substitution is done, you don't have to worry about special characters in values causing extra substitutions.

7 Comments

Tcl uses # for comments. Unlike many languages, the # character must occur at the begining of a command. An easy trick to append a comment to the end of a command is to proceed the # with a semicolon in order to terminate the previous command.
A backslash(/) continues a comment line onto the next line of the script. In addition, a semi-colon inside a comment is not significant. only a newline terminates comments.

8 Reference

  • backslash Sequences
File?id=dd6nw3mt_114hkq332ct_b

  • Arithmetic Operators
File?id=dd6nw3mt_115cx6tf2gk_b
  • Built-in Math Function
File?id=dd6nw3mt_116gqq38zfc_b
  • Core Tcl Commands
File?id=dd6nw3mt_117fsn9v3dj_b
File?id=dd6nw3mt_118vb3288fz_b
File?id=dd6nw3mt_119vq4tt6fx_b
  • Predefined Variables
File?id=dd6nw3mt_120c9tpw3fv_b


















SUBDIR git-gui SUBDIR gitk-git SUBDIR templates install -d -m 755 '/usr/local/bin' install -d -m 755 '/usr/local/libexec/git-core' install git-daemon git-http-backend git-imap-send git-sh-i18n--envsubst git-shell git-http-fetch git-http-push git-remote-http git-remote-https git-remote-ftp git-remote-ftps git-bisect git-difftool--helper git-filter-branch git-merge-octopus git-merge-one-file git-merge-resolve git-mergetool git-quiltimport git-request-pull git-submodule git-web--browse git-add--interactive git-archimport git-cvsexportcommit git-cvsimport git-cvsserver git-send-email git-svn git-p4 git-instaweb '/usr/local/libexec/git-core' install -m 644 git-mergetool--lib git-rebase--preserve-merges git-sh-i18n git-sh-setup '/usr/local/libexec/git-core' install git git-receive-pack git-shell git-upload-archive git-upload-pack git-cvsserver '/usr/local/bin' make -C templates DESTDIR='' install make[1]: Entering directory `/usr/local/src/git-2.30.0/templates' install -d -m 755 '/usr/local/share/git-core/templates' (cd blt && tar cf - .) | \ (cd '/usr/local/share/git-core/templates' && umask 022 && tar xof -) make[1]: Leaving directory `/usr/local/src/git-2.30.0/templates' install -d -m 755 '/usr/local/libexec/git-core/mergetools' install -m 644 mergetools/* '/usr/local/libexec/git-core/mergetools' install -d -m 755 '/usr/local/share/locale' (cd po/build/locale && tar cf - .) | \ (cd '/usr/local/share/locale' && umask 022 && tar xof -) install -d -m 755 '/usr/local/share/perl5' (cd perl/build/lib && tar cf - .) | \ (cd '/usr/local/share/perl5' && umask 022 && tar xof -) make -C gitweb install make[1]: Entering directory `/usr/local/src/git-2.30.0/gitweb' make[2]: Entering directory `/usr/local/src/git-2.30.0' make[2]: `GIT-VERSION-FILE' is up to date. make[2]: Leaving directory `/usr/local/src/git-2.30.0' install -d -m 755 '/usr/local/share/gitweb' install -m 755 gitweb.cgi '/usr/local/share/gitweb' install -d -m 755 '/usr/local/share/gitweb/static' install -m 644 static/gitweb.js static/gitweb.css static/git-logo.png static/git-favicon.png '/usr/local/share/gitweb/static' make[1]: Leaving directory `/usr/local/src/git-2.30.0/gitweb' make -C gitk-git install make[1]: Entering directory `/usr/local/src/git-2.30.0/gitk-git' install -d -m 755 '/usr/local/bin' install -m 755 gitk-wish '/usr/local/bin'/gitk install -d -m 755 '/usr/local/share/gitk/lib/msgs' install -m 644 po/pt_br.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/bg.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/zh_cn.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/ja.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/ca.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/sv.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/it.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/de.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/pt_pt.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/fr.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/ru.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/vi.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/hu.msg '/usr/local/share/gitk/lib/msgs' && install -m 644 po/es.msg '/usr/local/share/gitk/lib/msgs' && true make[1]: Leaving directory `/usr/local/src/git-2.30.0/gitk-git' make -C git-gui gitexecdir='/usr/local/libexec/git-core' install make[1]: Entering directory `/usr/local/src/git-2.30.0/git-gui' DEST /usr/local/libexec/git-core INSTALL 755 git-gui INSTALL 755 git-gui--askpass LINK git-citool -> git-gui DEST /usr/local/share/git-gui/lib INSTALL 644 tclIndex INSTALL 644 themed.tcl INSTALL 644 spellcheck.tcl INSTALL 644 branch_create.tcl INSTALL 644 line.tcl INSTALL 644 console.tcl INSTALL 644 checkout_op.tcl INSTALL 644 remote_add.tcl INSTALL 644 browser.tcl INSTALL 644 option.tcl INSTALL 644 merge.tcl INSTALL 644 index.tcl INSTALL 644 branch_checkout.tcl INSTALL 644 branch.tcl INSTALL 644 chord.tcl INSTALL 644 diff.tcl INSTALL 644 remote.tcl INSTALL 644 sshkey.tcl INSTALL 644 logo.tcl INSTALL 644 choose_font.tcl INSTALL 644 transport.tcl INSTALL 644 encoding.tcl INSTALL 644 mergetool.tcl INSTALL 644 tools.tcl INSTALL 644 tools_dlg.tcl INSTALL 644 status_bar.tcl INSTALL 644 search.tcl INSTALL 644 shortcut.tcl INSTALL 644 branch_rename.tcl INSTALL 644 class.tcl INSTALL 644 remote_branch_delete.tcl INSTALL 644 choose_repository.tcl INSTALL 644 about.tcl INSTALL 644 blame.tcl INSTALL 644 win32.tcl INSTALL 644 choose_rev.tcl INSTALL 644 commit.tcl INSTALL 644 branch_delete.tcl INSTALL 644 date.tcl INSTALL 644 database.tcl INSTALL 644 error.tcl INSTALL 644 git-gui.ico INSTALL 644 win32_shortcut.js DEST /usr/local/share/git-gui/lib/msgs INSTALL 644 nb.msg INSTALL 644 pt_br.msg INSTALL 644 bg.msg INSTALL 644 zh_cn.msg INSTALL 644 ja.msg INSTALL 644 it.msg INSTALL 644 de.msg INSTALL 644 pt_pt.msg INSTALL 644 fr.msg INSTALL 644 ru.msg INSTALL 644 el.msg INSTALL 644 hu.msg INSTALL 644 vi.msg INSTALL 644 sv.msg make[1]: Leaving directory `/usr/local/src/git-2.30.0/git-gui' bindir=$(cd '/usr/local/bin' && pwd) && \ execdir=$(cd '/usr/local/libexec/git-core' && pwd) && \ destdir_from_execdir_SQ=$(echo 'libexec/git-core' | sed -e 's|[^/][^/]*|..|g') && \ { test "$bindir/" = "$execdir/" || \ for p in git git-shell git-cvsserver; do \ rm -f "$execdir/$p" && \ test -n "" && \ ln -s "$destdir_from_execdir_SQ/bin/$p" "$execdir/$p" || \ { test -z "" && \ ln "$bindir/$p" "$execdir/$p" 2>/dev/null || \ cp "$bindir/$p" "$execdir/$p" || exit; } \ done; \ } && \ for p in git-receive-pack git-upload-archive git-upload-pack; do \ rm -f "$bindir/$p" && \ test -n "" && \ ln -s "git" "$bindir/$p" || \ { test -z "" && \ ln "$bindir/git" "$bindir/$p" 2>/dev/null || \ ln -s "git" "$bindir/$p" 2>/dev/null || \ cp "$bindir/git" "$bindir/$p" || exit; }; \ done && \ for p in git-add git-am git-annotate git-apply git-archive git-bisect--helper git-blame git-branch git-bugreport git-bundle git-cat-file git-check-attr git-check-ignore git-check-mailmap git-check-ref-format git-checkout-index git-checkout git-clean git-clone git-column git-commit-graph git-commit-tree git-commit git-config git-count-objects git-credential-cache--daemon git-credential-cache git-credential-store git-credential git-describe git-diff-files git-diff-index git-diff-tree git-diff git-difftool git-env--helper git-fast-export git-fast-import git-fetch-pack git-fetch git-fmt-merge-msg git-for-each-ref git-for-each-repo git-fsck git-gc git-get-tar-commit-id git-grep git-hash-object git-help git-index-pack git-init-db git-interpret-trailers git-log git-ls-files git-ls-remote git-ls-tree git-mailinfo git-mailsplit git-merge-base git-merge-file git-merge-index git-merge-ours git-merge-recursive git-merge-tree git-merge git-mktag git-mktree git-multi-pack-index git-mv git-name-rev git-notes git-pack-objects git-pack-redundant git-pack-refs git-patch-id git-prune-packed git-prune git-pull git-push git-range-diff git-read-tree git-rebase git-receive-pack git-reflog git-remote-ext git-remote-fd git-remote git-repack git-replace git-rerere git-reset git-rev-list git-rev-parse git-revert git-rm git-send-pack git-shortlog git-show-branch git-show-index git-show-ref git-sparse-checkout git-stash git-stripspace git-submodule--helper git-symbolic-ref git-tag git-unpack-file git-unpack-objects git-update-index git-update-ref git-update-server-info git-upload-archive git-upload-pack git-var git-verify-commit git-verify-pack git-verify-tag git-worktree git-write-tree git-cherry git-cherry-pick git-format-patch git-fsck-objects git-init git-maintenance git-merge-subtree git-restore git-show git-stage git-status git-switch git-whatchanged; do \ rm -f "$execdir/$p" && \ if test -z ""; \ then \ test -n "" && \ ln -s "$destdir_from_execdir_SQ/bin/git" "$execdir/$p" || \ { test -z "" && \ ln "$execdir/git" "$execdir/$p" 2>/dev/null || \ ln -s "git" "$execdir/$p" 2>/dev/null || \ cp "$execdir/git" "$execdir/$p" || exit; }; \ fi \ done && \ remote_curl_aliases="git-remote-https git-remote-ftp git-remote-ftps" && \ for p in $remote_curl_aliases; do \ rm -f "$execdir/$p" && \ test -n "" && \ ln -s "git-remote-http" "$execdir/$p" || \ { test -z "" && \ ln "$execdir/git-remote-http" "$execdir/$p" 2>/dev/null || \ ln -s "git-remote-http" "$execdir/$p" 2>/dev/null || \ cp "$execdir/git-remote-http" "$execdir/$p" || exit; } \ done && \ ./check_bindir "z$bindir" "z$execdir" "$bindir/git-add"
07-15
import tkinter as tk from tkinter import messagebox, Menu, filedialog import mido import os # 音符映射(MIDI音符到简谱) note_map = { 24: '1...', 26: '2...', 28: '3...', 29: '4...', 31: '5...', 33: '6...', 35: '7...', 36: '1..', 38: '2..', 40: '3..', 41: '4..', 43: '5..', 45: '6..', 47: '7..', 48: '1.', 50: '2.', 52: '3.', 53: '4.', 55: '5.', 57: '6.', 59: '7.', 60: '1', 62: '2', 64: '3', 65: '4', 67: '5', 69: '6', 71: '7', 72: '1*', 74: '2*', 76: '3*', 77: '4*', 79: '5*', 81: '6*', 83: '7*', 84: '1**', 86: '2**', 88: '3**', 89: '4**', 91: '5**', 93: '6**', 95: '7**', 96: '1***', 98: '2***', 100: '3***', 101: '4***', 103: '5***', 105: '6***', 107: '7***' } def convert_midi_to_txt(): try: # 打开文件对话框选择MIDI文件 midi_file = filedialog.askopenfilename( title="选择MIDI文件", filetypes=[("MIDI文件", "*.mid;*.midi"), ("所有文件", "*.*")] ) if not midi_file: return # 创建保存文件对话框 txt_file = filedialog.asksaveasfilename( title="保存简谱文件", defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")] ) if not txt_file: return # 读取MIDI文件 mid = mido.MidiFile(midi_file) # 初始化变量 ticks_per_beat = mid.ticks_per_beat current_tempo = 500000 # 默认tempo(120 BPM) active_notes = {} note_events = [] time_signature = "4/4" # 默认拍号 key_signature = "C" # 默认调号 # 解析所有轨道 for i, track in enumerate(mid.tracks): abs_time = 0 # 绝对时间(tick) for msg in track: abs_time += msg.time # 处理元信息 if msg.is_meta: if msg.type == 'set_tempo': current_tempo = msg.tempo elif msg.type == 'time_signature': time_signature = f"{msg.numerator}/{msg.denominator}" elif msg.type == 'key_signature': key_signature = msg.key continue # 处理音符事件 if msg.type == 'note_on' and msg.velocity > 0: # 记录音符开始 active_notes[msg.note] = abs_time elif msg.type == 'note_off' or (msg.type == 'note_on' and msg.velocity == 0): # 记录音符结束 if msg.note in active_notes: start_time = active_notes[msg.note] duration = abs_time - start_time note_events.append((start_time, duration, msg.note)) del active_notes[msg.note] # 如果没有找到音符事件 if not note_events: messagebox.showwarning("警告", "MIDI文件中未找到音符事件") return # 按开始时间排序 note_events.sort(key=lambda x: x[0]) # 计算最小时间单位(基于16分音符) min_duration = ticks_per_beat // 4 # 生成简谱 score = "" last_end_time = 0 for start_time, duration, note in note_events: # 获取简谱表示 note_str = note_map.get(note, '?') # 计算音符时值(以最小时间单位计) beats = max(1, round(duration / min_duration)) # 添加音符和时值 score += note_str if beats > 1: score += '-' * (beats - 1) last_end_time = start_time + duration # 添加文件头信息 bpm = int(mido.tempo2bpm(current_tempo)) header = ( f"// 从 {os.path.basename(midi_file)} 转换的简谱\n" f"// 速度: {bpm} BPM\n" f"// 拍号: {time_signature}\n" f"// 调号: {key_signature}\n" f"// 说明: 使用'-'表示音符时值\n\n" ) # 写入文件 with open(txt_file, 'w', encoding='utf-8') as f: f.write(header) # 格式化输出(每40个字符换行) line_length = 40 for i in range(0, len(score), line_length): line = score[i:i+line_length] f.write(line + '\n') messagebox.showinfo("成功", f"简谱文件已保存至:\n{txt_file}") except Exception as e: messagebox.showerror("错误", f"转换失败: {str(e)}") def create_textbox_menu(textbox): """创建文本框右键菜单""" menu = Menu(textbox, tearoff=0) def select_all(event=None): textbox.tag_add(tk.SEL, "1.0", tk.END) textbox.icursor(tk.END) return "break" def copy_text(event=None): textbox.event_generate("<<Copy>>") return "break" def paste_text(event=None): textbox.event_generate("<<Paste>>") return "break" def show_menu(event): try: menu.tk_popup(event.x_root, event.y_root) finally: menu.grab_release() menu.add_command(label="全选", command=select_all) menu.add_separator() menu.add_command(label="复制", command=copy_text) menu.add_command(label="粘贴", command=paste_text) textbox.bind("<Button-3>", show_menu) textbox.bind("<Control-a>", select_all) textbox.bind("<Control-c>", copy_text) textbox.bind("<Control-v>", paste_text) def create_ui(): """创建用户界面""" root = tk.Tk() root.title("MIDI转简谱工具(使用-表示时值)") root.geometry("600x500") root.resizable(True, True) # 设置应用图标 try: root.iconbitmap("midi_icon.ico") except: pass # 创建主框架 main_frame = tk.Frame(root, padx=20, pady=20) main_frame.pack(fill=tk.BOTH, expand=True) # 标题 title_label = tk.Label(main_frame, text="MIDI转简谱工具", font=("Arial", 16, "bold")) title_label.pack(pady=(0, 15)) # 说明文本 description = ( "此工具可将MIDI文件转换为简谱文本文件。\n" "简谱使用数字表示音符,点(.)表示低音,星号(*)表示高音,\n" "使用'-'表示音符时值(不表示休止符)。\n" "例如:'5---'表示一个四分音符的5和三个延长符" ) desc_label = tk.Label(main_frame, text=description, justify=tk.LEFT) desc_label.pack(pady=(0, 20)) # 转换按钮 convert_btn = tk.Button( main_frame, text="选择MIDI文件并转换", command=convert_midi_to_txt, bg="#4CAF50", fg="white", font=("Arial", 12), padx=20, pady=10 ) convert_btn.pack(pady=20) # 简谱示例 example_frame = tk.LabelFrame(main_frame, text="简谱示例", padx=10, pady=10) example_frame.pack(fill=tk.BOTH, expand=True, pady=(20, 0)) example_text = ( "5---3-5-1*---7-6-1*--5----5-1-2-3-2-1-2---\n" "5---3-5-1*---7-6-1*--5----5-2-3-4-7-.1---\n" "6---1*--1*---7---6-7-1*---6-7-1*--6-6-5-3-1-2---" ) example_label = tk.Label(example_frame, text=example_text, font=("Courier", 12), justify=tk.LEFT) example_label.pack(padx=10, pady=10) # 状态栏 status_bar = tk.Label(root, text="就绪", bd=1, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) # 版权信息 copyright_label = tk.Label(root, text="© 2023 MIDI转简谱工具", fg="gray") copyright_label.pack(side=tk.BOTTOM, pady=5) return root if __name__ == "__main__": app = create_ui() app.mainloop()
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值