duangsuse::Echo
不过在看过 pygame, tkinter, teek 里的 message loop 后我也终于理解 Android 里 os.Looper 和 Handler::post 的语义了…… 这么说 Handler 应该也有一个 pre(before) 方法, post 是 after 嘛 😂 pygame 和 tkinter 里的设计都蛮像的,不过前者不提供 .mainloop() 而是用 pygame.events.get_events() 要手动 while True: ,不过 tkinter 里用 tk.after(msec…
之前重写了一半但是部分有用的代码:
https://gist.github.com/duangsuse/6b9160c9a5ac7706e6165885fc07238d
https://gist.github.com/duangsuse/6b9160c9a5ac7706e6165885fc07238d
Gist
Teek command.py / structure.py rewrite (partially)
Teek command.py / structure.py rewrite (partially) - tkthr_update.py
还有一些常见视图控件可以添加: CollapsePane, TabWidget, TreeView
(Qt 里也会提供与 Model/View 对应的 Widget, 然后可以直接用 ->item(n) 这种形式访问数据而无需用 ->model() 和 modelIndex
看到Tk的对话框全都是 ApplicationModal 的,都是阻塞…… 🤔 不过 Qt 的 non-blocking Modal 看起来用处也不大
现在才想起来如果仅仅在 bind (connect) 的层面支持 queue 会引入一些鸡肋性……
如果你要发HTTP请求,用 Teek 可以在 init_threads 后随便在另一线程更新 text 什么的,但如果用 bindSafe 的话,不可能在主线去等待 http.get 的结果,只能在完成后塞到 queue 里去更新(打断成两份 异步回调 和 主线UI更新 ,头疼),换句话说 bindSafe 完全没有价值,因为创建它的目的就是为让异步UI修改更简单(但在主线执行、不允许阻塞就无意义,只有所有API都线程安全才可以只用 http.get callback 或手动新线程 http.getSync&w.text= 的同一块代码)
除非…… 除非 bindSafe 改名为 runAsync ,接受一个 Future 和一个依赖它结果的闭包,然后可以
不知道如果用 Python 的 await/async 或 generator 可不可以弄回 bindSafe 什么的…… 不过看起来用 future 的话更直观、库代码更少
所以 Teek 如果要支持任意线程修改界面,用那么多 call_threadsafe 是必须的……
打算了半天终于是回到 JavaScript 的老路了么…… 感觉好有趣啊呵呵
(Qt 里也会提供与 Model/View 对应的 Widget, 然后可以直接用 ->item(n) 这种形式访问数据而无需用 ->model() 和 modelIndex
看到Tk的对话框全都是 ApplicationModal 的,都是阻塞…… 🤔 不过 Qt 的 non-blocking Modal 看起来用处也不大
现在才想起来如果仅仅在 bind (connect) 的层面支持 queue 会引入一些鸡肋性……
如果你要发HTTP请求,用 Teek 可以在 init_threads 后随便在另一线程更新 text 什么的,但如果用 bindSafe 的话,不可能在主线去等待 http.get 的结果,只能在完成后塞到 queue 里去更新(打断成两份 异步回调 和 主线UI更新 ,头疼),换句话说 bindSafe 完全没有价值,因为创建它的目的就是为让异步UI修改更简单(但在主线执行、不允许阻塞就无意义,只有所有API都线程安全才可以只用 http.get callback 或手动新线程 http.getSync&w.text= 的同一块代码)
除非…… 除非 bindSafe 改名为 runAsync ,接受一个 Future 和一个依赖它结果的闭包,然后可以
runAsync(http.get(wtf), lambda res: self.txt.insert(0, res)) (其中 http.get 是一个 Thunk 函数,返回接受它回调的函数),这样在代码复用的层面它就又有意义了不知道如果用 Python 的 await/async 或 generator 可不可以弄回 bindSafe 什么的…… 不过看起来用 future 的话更直观、库代码更少
所以 Teek 如果要支持任意线程修改界面,用那么多 call_threadsafe 是必须的……
打算了半天终于是回到 JavaScript 的老路了么…… 感觉好有趣啊呵呵
duangsuse::Echo
喜闻 Mivik/kamet 编译器发布第一版,这个项目教会了我关于 Kotlin 里 receiver 和 first arg 的互换使用(难以想象草),居然可以这么写: interface ASTNode { fun codegen(ctx: Context) } class NameRefNode { fun Context.codegen() = ... } 因为这比较不常见, Mivik 起的名字是 ASTNode.codegenForThis 然后在 context 加了一个 ASTNode.codegen…
据大佬说推断算法已经可以导出上面
刚才又看见一个 Tk 封装是和 TkGUI 同样思路的 https://github.com/mtkennerly/tkup
不过虽然都是定义式思路,实际也有点差别,咱用的是返回收 parent 的函数,
不过这么做也有一个缺点: (1)要有一个 builder 作用域的"当前layout" (2)所有的控件都是看起来像构造器调用,其实是带副作用的 ("addXXX")
因为 tkup 的控件 DSL 是用副作用而咱的 TkGUI 是用闭包值(返回收 parent 的 widget_ctor) ,二者不兼容,本来还想也给 verticalLayout 什么的支持下 with 语法呢(当然也没必要,而且目前的做法也很能跨语言,不过 Kotlin 的 Anko 用的的确是副作用因为那样就是花括号了(而且也可以直接插入构造控件树),选择而已。)
应该说两种做法都值得学习。(是同类编程思想的通常实现) 顺便提一句,这个库的作者是个 Kotliner :
fun <T> extract 的类型参数了(虽然有一个 T 作为和参数实际类型 估计是数据模型设计不当),这真是太棒了(这样未来想实现的那门语言就不存在悬念了)刚才又看见一个 Tk 封装是和 TkGUI 同样思路的 https://github.com/mtkennerly/tkup
不过虽然都是定义式思路,实际也有点差别,咱用的是返回收 parent 的函数,
_.by(name, widget) 可以引用控件,而 tkup 用的是 __enter__ 和 __exit__ (with horizontalLayout():) 然后直接对 self 的属性赋值就可以引用了不过这么做也有一个缺点: (1)要有一个 builder 作用域的"当前layout" (2)所有的控件都是看起来像构造器调用,其实是带副作用的 ("addXXX")
因为 tkup 的控件 DSL 是用副作用而咱的 TkGUI 是用闭包值(返回收 parent 的 widget_ctor) ,二者不兼容,本来还想也给 verticalLayout 什么的支持下 with 语法呢(当然也没必要,而且目前的做法也很能跨语言,不过 Kotlin 的 Anko 用的的确是副作用因为那样就是花括号了(而且也可以直接插入构造控件树),选择而已。)
应该说两种做法都值得学习。(是同类编程思想的通常实现) 顺便提一句,这个库的作者是个 Kotliner :
Inspired by Kotlin's implicit creation of the it variable in lambdas, the GUI class provides an it method which returns the current master widget, and there is an additional convenience method called with_it which returns the GUI instance and its it method for quick assignment to variables
用 it() 可以像 Python 的 super() 一样,直接引用到隐含于上下文的变量。GitHub
mtkennerly/tkup
Hierarchical Tkinter wrapper for Python. Contribute to mtkennerly/tkup development by creating an account on GitHub.
super(HelloFrame, self) https://wxpython.org/pages/overview/#hello-world
欸 Python 还可以这样用...
(刚才发现其实 还有 tuple(
...) 和 "ABC" (abstract class) 这种名词emmm )对了,#Python 的参数列表可不止 vararg 和 kwarg , 看看这个
super(self, /, *args, **kwargs) ,这是什么鬼…… 还有 def op(a, *, named=b) 这样的特例简写当然了, Python 的 [] 和 {} 也都可以用 * 和 ** ...
说起来,对于GUI来说,除了维护控件更新顺序、styling、快速原型、定义用户的交互行为(click, doubleclick, longpress) 外,
更好的交互体验也是很重要的,像 Qt 除了它著名的 signal/slot 设计(GObject 都抄了 而且动词没改都是connect) 外, drag&drop 和 selection 也是一个很重要的特性
(在许多专业软件也用的到,例如 After Effects, FL Studio 都大量使用了 drag&drop ,DOM 也支持 ondrag 事件不过不知道 drop 能不能)
wxPython
Overview of wxPython
What is wxPython?
wxPython is a cross-platform GUI toolkit for
the Python programming language. It allows Python
programmers to create programs with a robust, highly functional graphical
user interfac
wxPython is a cross-platform GUI toolkit for
the Python programming language. It allows Python
programmers to create programs with a robust, highly functional graphical
user interfac
比起从 geeksforgeeks.com 抄那些常用的 Tk GUI 模式,我觉得开始着手提升这个模型的抽象度比较好…… 目前是简单支持了 Tk/T(hemed)Tk 两个widgets底层,我还打算支持GTK和WxWidgets
还有codegen…… (这样弄完了以后就可以给诸如C#,Java的语言生成初始化布局的代码 从树形结构前序遍历扁平化) 我觉得应该能行,但要花一些力气。
说起来PyDearGui也是挺不错的(ImGui 的主题系统和图表/绘制都很赞),不过它的作者因为是 C++ 实现,使用的定义式风格是
还有codegen…… (这样弄完了以后就可以给诸如C#,Java的语言生成初始化布局的代码 从树形结构前序遍历扁平化) 我觉得应该能行,但要花一些力气。
说起来PyDearGui也是挺不错的(ImGui 的主题系统和图表/绘制都很赞),不过它的作者因为是 C++ 实现,使用的定义式风格是
add_somewidget() 和 add_sameline() ,从语言角度看抽象层次更高但的确不如直接的vararg函数调用好看,不过目前 TkGUI 支持的是 verticalLayout/horizontalLayout (VBox&HBox) 所以应该也能用就是了Font,Color 之类的抽象也要弄…… 如果当真打算把这玩意做成框架的话
最重要的是,所有函数都要变成 class ,因为至少得支持 wx 和 GTK ,不可能滥用动态类型的,底层要改的也不少啊
而且貌似只有 Tk 这个框架需要 parent 变量,那这个玩意到底有没有用呢…… 要知道 parent 的 DSL (verticalLayout) 里是可以拿到 child 的,这么弄就没意思了,除非是在 Kotlin 里然后可以玩
如果你要专门为 Tk 弄一个不需要把 parent 给 partial apply 出来的 lazy constructor ,那又不可能(太麻烦)
反正这种冗余就是消除不掉,看看有 codegen 以后可以做什么弥补下吧……
最重要的是,所有函数都要变成 class ,因为至少得支持 wx 和 GTK ,不可能滥用动态类型的,底层要改的也不少啊
而且貌似只有 Tk 这个框架需要 parent 变量,那这个玩意到底有没有用呢…… 要知道 parent 的 DSL (verticalLayout) 里是可以拿到 child 的,这么弄就没意思了,除非是在 Kotlin 里然后可以玩
verticalLayout { add(wtfChild() ) } 这种弱智式的 parent 变量利用法……如果你要专门为 Tk 弄一个不需要把 parent 给 partial apply 出来的 lazy constructor ,那又不可能(太麻烦)
反正这种冗余就是消除不掉,看看有 codegen 以后可以做什么弥补下吧……
顺便给没接触过GUI的各位科普下目前我封装了的 tkinter widgets:
MenuItems: OpNamed(named), SubMenu, Sep(sep), CheckBox, RadioButton
Widgets(button/bar/line/box): button, radioButton, menuButton; scrollBar, progressBar; slider, text;
(string:)input, textarea, (number:)spinBox, (boolean:): checkBox, (listing:)listBox, comboBox;
menu, separator, canvas, treeWidget
Containers: HBox(horizontalLayout), VBox(verticalLayout); labeledBox, splitter, tabWidget, withFill, withScroll
其中控件里最常见的当属 text(文本), button(按钮), input(输入行), menu, scrollBar(滚动条), comboBox(多选框)
separator 是我自己写的(如果要移植到新底层,就得费好大力气调整类型...)(后来因为 TTk 里有又删了)
当然因为 Tk 做得比较纯,没有菜单栏和状态栏这回事,和 Gtk 的默认没有是一样的
容器里 labeledBox (分组盒) 和 tabWidget (Tk的Notebook ,就是多页视图) 很常见
现在很流行的布局方法是 grid ,但这里没有支持(其实很多Android应用用的grid也不多)
treeView 和 canvas 用处也蛮多,我想给接收 canvas 的操作们加一个 drawOn(widget, op_paint) ,就不用像 Java AWT 一样覆盖 onPaint 方法了,可惜要跨框架很麻烦
TreeView 的支持目前还不完全,因为它可以 multi-column 的,毕竟是一个 Model/View 的复杂数据视图,建模还需要时间
关于布局填充和顺序,如果要做一个全屏的 horizontalLayout(textarea,scrollbar) ,直接按顺序然后要 textarea fill 整个窗口是会盖住 scrollbar 的,所以只能把 scrollbar 放在前面然后用 _.fill(scroll, _.right, "y") (不过,scrollbar 本来就是 packSideFill, 只需修改 side 即可)
MenuItems: OpNamed(named), SubMenu, Sep(sep), CheckBox, RadioButton
Widgets(button/bar/line/box): button, radioButton, menuButton; scrollBar, progressBar; slider, text;
(string:)input, textarea, (number:)spinBox, (boolean:): checkBox, (listing:)listBox, comboBox;
menu, separator, canvas, treeWidget
Containers: HBox(horizontalLayout), VBox(verticalLayout); labeledBox, splitter, tabWidget, withFill, withScroll
其中控件里最常见的当属 text(文本), button(按钮), input(输入行), menu, scrollBar(滚动条), comboBox(多选框)
separator 是我自己写的(如果要移植到新底层,就得费好大力气调整类型...)(后来因为 TTk 里有又删了)
当然因为 Tk 做得比较纯,没有菜单栏和状态栏这回事,和 Gtk 的默认没有是一样的
容器里 labeledBox (分组盒) 和 tabWidget (Tk的Notebook ,就是多页视图) 很常见
现在很流行的布局方法是 grid ,但这里没有支持(其实很多Android应用用的grid也不多)
treeView 和 canvas 用处也蛮多,我想给接收 canvas 的操作们加一个 drawOn(widget, op_paint) ,就不用像 Java AWT 一样覆盖 onPaint 方法了,可惜要跨框架很麻烦
TreeView 的支持目前还不完全,因为它可以 multi-column 的,毕竟是一个 Model/View 的复杂数据视图,建模还需要时间
关于布局填充和顺序,如果要做一个全屏的 horizontalLayout(textarea,scrollbar) ,直接按顺序然后要 textarea fill 整个窗口是会盖住 scrollbar 的,所以只能把 scrollbar 放在前面然后用 _.fill(scroll, _.right, "y") (不过,scrollbar 本来就是 packSideFill, 只需修改 side 即可)
上面的窗口只有第一个是我自己写的,canvas/tree 还有那个异步拿 COVID-19 统计数字JSON的 都是网上抄的... 不过说实话他们的文章写得很好,但代码有时候真不敢恭维emm
duangsuse::Echo
相信我,这玩意拿来做预览也不错…… 不过还是要尝试下兼容 GTK 什么的,动态类型脏就脏了,反正只要开发者注意好 框架做基本支持 动态类型也没关系,何况还有 codegen 可以用呢。
codegen 已经完成了,还算顺利(并不)
为此我花了昨天下午到今天下午的所有时间,而且昨天熬夜到12点(第三天了)
使用的原理很简单,就是 value-variable substitution , 我利用 Python 的动态特性创建了 call, callNew, invoke 等方法,实现这些函数的语义并且在请求代码生成,就实现了复用
但就在刚刚遇到了一个怪事使我以为 Python 有 bug (看起来一个方法调用没成功执行,事后发现是头部的 if not: return 时序没注意到以及用print调试时看走眼了)
不过这件事也使我找到了 id_map/id_set (object id mapping) 的正确写法不是先 try: except TypeError: return _id.get(key) 而是把 key 直接映射到 id ,所以磨叽了一个半小时后,完成。
下一步可能是收尾这个特性(还要考虑拿它来自动为GTK/Wx忽略第一个 parent 参数,虽然我也不想这么干emmm) 然后发布
为此我花了昨天下午到今天下午的所有时间,而且昨天熬夜到12点(第三天了)
使用的原理很简单,就是 value-variable substitution , 我利用 Python 的动态特性创建了 call, callNew, invoke 等方法,实现这些函数的语义并且在请求代码生成,就实现了复用
但就在刚刚遇到了一个怪事使我以为 Python 有 bug (看起来一个方法调用没成功执行,事后发现是头部的 if not: return 时序没注意到以及用print调试时看走眼了)
不过这件事也使我找到了 id_map/id_set (object id mapping) 的正确写法不是先 try: except TypeError: return _id.get(key) 而是把 key 直接映射到 id ,所以磨叽了一个半小时后,完成。
下一步可能是收尾这个特性(还要考虑拿它来自动为GTK/Wx忽略第一个 parent 参数,虽然我也不想这么干emmm) 然后发布
这么一看我就是太弱智了才在codegen支持上花这么多时间,明明是一个毫无难度的东西,竟然浪费了我那么多看片的时间,罪过罪过…… 欲哭无泪
这个算法的名字就是 value-name substitution ,我一开始就明白,却想了半天才得出结论
对了,生成的代码貌似是按后序遍历排序的,尽管这个是基于副作用build str
这个算法的名字就是 value-name substitution ,我一开始就明白,却想了半天才得出结论
对了,生成的代码貌似是按后序遍历排序的,尽管这个是基于副作用build str
#Python 真是无法保证这样复杂的时序写对…… 在有了Kotlin以后我不会在区间开闭上出错 但时序变量更新还真不能保证。(据说bug还是因为我顺手简化"无用赋值"导致的) 我不想写下去了,花了好大精力还熬了三天夜……刚刚有点好转
总感觉可以优化,总感觉看着让人觉得要删掉,可按顺序看看倒也没什么,可是关键在于把它删掉看着也没什么…… 直觉要好啊
注:是指
注:是指
expr = get() ; if (expr == null) { expr = get1(); if (expr != null) bla() } 这种看起来怎么这么相似的