duangsuse::Echo
709 subscribers
4.23K photos
127 videos
583 files
6.43K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
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…
据大佬说推断算法已经可以导出上面 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() 一样,直接引用到隐含于上下文的变量。
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 能不能)
比起从 geeksforgeeks.com 抄那些常用的 Tk GUI 模式,我觉得开始着手提升这个模型的抽象度比较好…… 目前是简单支持了 Tk/T(hemed)Tk 两个widgets底层,我还打算支持GTK和WxWidgets
还有codegen…… (这样弄完了以后就可以给诸如C#,Java的语言生成初始化布局的代码 从树形结构前序遍历扁平化) 我觉得应该能行,但要花一些力气。

说起来PyDearGui也是挺不错的(ImGui 的主题系统和图表/绘制都很赞),不过它的作者因为是 C++ 实现,使用的定义式风格是 add_somewidget()add_sameline() ,从语言角度看抽象层次更高但的确不如直接的vararg函数调用好看,不过目前 TkGUI 支持的是 verticalLayout/horizontalLayout (VBox&HBox) 所以应该也能用就是了
This media is not supported in your browser
VIEW IN TELEGRAM
Font,Color 之类的抽象也要弄…… 如果当真打算把这玩意做成框架的话
最重要的是,所有函数都要变成 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 即可)
相信我,这玩意拿来做预览也不错…… 不过还是要尝试下兼容 GTK 什么的,动态类型脏就脏了,反正只要开发者注意好 框架做基本支持 动态类型也没关系,何况还有 codegen 可以用呢。
This media is not supported in your browser
VIEW IN TELEGRAM
上面的窗口只有第一个是我自己写的,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) 然后发布
老实说(无需重复代码逻辑但可以同时实现codegen的)实现方法虽然对我来说比较新,但总感觉也就这样…… 只有 _names/_exprs/_autoNamed 三个变量, 不是很复杂 ,不过我也算是知道为什么解释器里有 "call-by-name" 这个名词了,因为我开始也把 _names和_exprs 混在一起放,我相信设计好时序那样也能用,不过这也太过分了,昨天晚上也想了半天才明白该怎么写。哈哈。
This media is not supported in your browser
VIEW IN TELEGRAM
这么一看我就是太弱智了才在codegen支持上花这么多时间,明明是一个毫无难度的东西,竟然浪费了我那么多看片的时间,罪过罪过…… 欲哭无泪

这个算法的名字就是 value-name substitution ,我一开始就明白,却想了半天才得出结论

对了,生成的代码貌似是按后序遍历排序的,尽管这个是基于副作用build str
呃……看起来应该还能用 (patchy 吧) 我有点不想写 GTK 支持了
#Python 真是无法保证这样复杂的时序写对…… 在有了Kotlin以后我不会在区间开闭上出错 但时序变量更新还真不能保证。(据说bug还是因为我顺手简化"无用赋值"导致的) 我不想写下去了,花了好大精力还熬了三天夜……刚刚有点好转
This media is not supported in your browser
VIEW IN TELEGRAM
总感觉可以优化,总感觉看着让人觉得要删掉,可按顺序看看倒也没什么,可是关键在于把它删掉看着也没什么…… 直觉要好啊

注:是指 expr = get() ; if (expr == null) { expr = get1(); if (expr != null) bla() } 这种看起来怎么这么相似的
This media is not supported in your browser
VIEW IN TELEGRAM
第一个从生成的代码创建出的Tk frame
This media is not supported in your browser
VIEW IN TELEGRAM
太赞了!我居然做出了这种框架…… 等等,这样又如何呢emm