【问题标题】:Clojure: how to architect desktop UIClojure:如何构建桌面 UI
【发布时间】:2015-03-10 14:48:05
【问题描述】:

我正在尝试为原理图、布局和绘图设计一个桌面 UI。只是从实际的软件设计师那里寻求高水平的建议。

假设有一个内存中的“数据库”(所有用户数据的任意深度的 clojure 映射,可能还有另一个用于应用程序首选项的映射等),我正在研究如何在模型-视图-控制器上进行操作这些,其中数据可能由以下任何一项或多项呈现和修改

  1. 显示单个参数(例如框宽)的独立文本字段。
  2. 一种“检查器”类型的视图,显示选定对象的多个参数,例如框宽度、高度、颜色、复选框等。
  3. 一种表格/电子表格类型的视图,显示多个对象的多个参数,可能是整个数据库
  4. 整个事物的图形渲染,例如示意图和布局视图。

修改其中任何一个都应立即显示在其他所有活动视图中,包括文本和图形,而不是在单击“确定”之后...因此不允许使用模式框。如果由于某种原因表格视图、检查器视图和图形渲染都在视图中,则以图形方式拖动框的角应该立即显示在文本中,等等。

有问题的平台是 JavaFX,但我希望 UI 和其他所有内容之间有一个清晰的分离,所以我想避免 JFX 意义上的binding,因为这将我的设计数据与 JFX 属性紧密联系在一起,增加了模型的粒度,并迫使我在处理数据的标准 clojure 函数之外工作,和/或大量处理整个getValue/setValue 世界。

我仍然假设至少一些有状态/可变性,并使用内置的 Clojure 功能,例如在 atom/var/ref 上 add-watch 并让运行时信号相关函数。

平台特定的交互将与实际的 UI 紧密结合,例如具体化ActionListeners,以及处理ObservableValues 等,并将尽量减少对JavaFX Property 等实际应用程序的依赖数据。我不会为此而取悦 FRP。

我不介意扩展 JFX 接口或编写自己的协议以使用特定于应用程序的defrecords,但我更希望应用程序数据保持为直接 Clojure 数据,不受平台污染。

问题是如何在最接近不可变模型的情况下设置这一切。我看到了几个选项:

  1. 细粒度:每个参数值/基元(即 Long、Double、Boolean 或 String)都是一个原子,每个可以修改值的视图“到达”数据库中的任何需要更改的值价值。这可能很糟糕,因为可能有数千个单独的值(例如手绘曲线上的点),并且需要大量 (deref...) 垃圾。我相信这就是 JFX 想要做到这一点的方式,在叶节点等处有巨大的属性数组,感觉很臃肿。使用这种方法似乎并不比仅使用 Java/C++ 编码好多少。
  2. 中粒度:数据库中的每个对象/记录都是 Clojure 映射的一个原子。当其中任何一个值发生更改时,整个地图都会被替换。需要处理的原子总数更少,并且允许例如用于各种事物的长数组。但是,当数据库中的某些对象需要比其他对象更多的嵌套时,这会变得复杂。
  3. 粗粒度:只有一个原子:数据库。任何时候发生任何变化,整个数据库都会被替换,并且每个视图都需要重新渲染其特定部分。这感觉有点像用锤子拍打苍蝇,天真的实现需要一直重新渲染所有内容。但我仍然认为这是最好的权衡,因为任何原语都有从根节点开始的清晰访问路径,无论是在每个基元级别还是在每个记录级别访问。

我还需要一个数据模板多次实例化的能力。因此,例如,如果用户更改了在多个地方使用的符号或形状,则单个编辑将适用于所有地方。我相信这也需要某种类型的类似“指针”的行为。我想我可以将原子存储到模型中,然后根据需要进行实例化,它可以在上述任何一种颗粒模型中工作。

还有其他方法吗?试图用函数式语言做一个类似 GUI 编辑器的工具只是愚蠢的吗? 谢谢

【问题讨论】:

  • 当您将 JavaFX 定位为 GUI 工具包时,也许研究 SceneBuilderKit(用于图形化编辑 JavaFX 场景的库框架)及其 source code 可能对您有所帮助。它的某些功能与您所描述的相似,但有所不同。不过,它绝对不是基于 clojure 类型的函数模型。
  • 是的,我的目标是 JavaFX。在工作、交通和尖叫的婴儿之间偶尔敲击键盘几个月后,我终于设法获得了一个匿名/本地ChangeListener 来更改原子并触发 Clojure 手表。尼托。我还没有见过 SceneBuilderKit(虽然我玩过他们的 SceneBuilder 桌面应用程序),但我会看看。另一个考虑因素是是否允许 JFX 使用自己的图元拥有整个场景图,或者是否以标准的“OnPaint”样式在画布上绘制我自己的形状,但这对于这个 SO 问题来说是题外话。
  • 是的,索尼克有很多选择。这是一个很好的问题,但很难回答。我的猜测是,如果您不使用场景图和一些预构建的控件(例如 [ControlsFX PropertySheet]),工作量会更大。可以给clojurefx 开发者发邮件,听听他对你的问题的看法。
  • 忘记 MVC 模式,转而采用带有 core.async 通道作为事件总线的 Flux 架构怎么样?只是我的 2 美分。
  • 谢谢,我尽量不依赖“模式”,但我使用了这个术语,以便读者立即知道我在做什么。在最低级别,用户交互程序的状态是可变的,UI 平台有需要注册的回调。如何使用 Flux、React、core.async、compojure、datomic、node.js……啊……东西太多了!我仍然不在clojurescript火车上。我只剩下几个神经元了,所以我想尽量减少过载。

标签: clojure model javafx cad


【解决方案1】:

我不认为使用函数式语言来做一个类似于 GUI 编辑器的工具是愚蠢的。但我不能声称对你的问题有答案。以下是一些可能对您的旅程有所帮助的链接:

  1. 斯图尔特塞拉 - Components Just Enough Structure
  2. Chris Granger - Light Table:解释 Light Table (source) 的结构。
  3. Chris Granger - The IDE as a Value:与上述视频相关的博文
  4. Conal Elliott - Tangible Functional Programming:使用 Functional Reactive Programming 创建可组合的 UI,但他的代码在 Haskell 中。
  5. Nathan Herzing 和 Chris Shea - Helping voters with Pedestal, Datomic, Om and core.async
  6. David Nolen - Comparative Literate Programming:展示所有使用 core.async 来简化 ClojureScript 中的 UI 编程。此处的想法可用于桌面 UI。
  7. Rich Hickey - The Language of the System:Clojure 的创建者关于系统编程的精彩演讲。

Erik Meijer 对函数式代码和命令式代码有一个很好的引用:

...无论是 Haskell、C# Java、F#、Scala、Python 还是 PHP 都想一想拥有大量与外部世界交互的命令式代码的想法,并且在其中编写的纯代码孤岛你的功能以纯粹的方式。 但是你必须决定岛屿有多大,海有多大。但答案绝不是只有岛屿或只有大海。一个好的程序员完全知道正确的平衡。

【讨论】:

  • 谢谢,这些都是很好的链接。我以前见过一两个,但大多数对我来说都是新的。只需要消化并找到我需要的适当抽象......我想了解何时/如何做 core.async 的东西,何时使用承诺、期货、代理、原始线程等,而不是留下东西单线程,以及如何将低级 GUI 调用焊接到功能/数据流/异步/frp/whatever 抽象。我想这将需要多次迭代并找到添加更多线程会有所帮助的阻塞/慢速部分。
  • 又添加了一篇演讲,我不敢相信我忘记了那篇精彩的演讲。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-05
  • 1970-01-01
  • 2013-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多