Fyne ( go跨平台GUI )中文文档- 扩展Fyne (七)

news2024/12/23 2:34:51

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法

go代码展示为Go 1.16 及更高版本, idegoland2021.2

 这是一个系列文章:

Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客

Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客

Fyne ( go跨平台GUI )中文文档-绘图和动画(三)-CSDN博客

Fyne ( go跨平台GUI )中文文档-容器和布局 (四)-CSDN博客

Fyne ( go跨平台GUI )中文文档-小部件 (五)-CSDN博客

Fyne ( go跨平台GUI )中文文档- 数据绑定 (六)-CSDN博客

Fyne ( go跨平台GUI )中文文档- 扩展Fyne (七)-CSDN博客

Fyne ( go跨平台GUI )中文文档- 架构 (八)完结-CSDN博客

7. 扩展Fyne

7.1 构建自定义布局

Fyne 应用程序中,每个都Container使用简单的布局算法来排列它的子元素。Fynefyne.io/fyne/v2/layout包中定义了许多可用的布局。如果您查看代码,您会发现它们都实现了该Layout接口。

type Layout interface {
    Layout([]CanvasObject, Size)
    MinSize(objects []CanvasObject) Size
}

任何应用程序都可以提供自定义布局来以非标准方式排列小部件。为此,您需要在自己的代码中实现上述接口。为了说明这一点,我们将创建一个新布局,将元素排列在对角线上,并排列在其容器的左下角
首先,我们将定义一个新类型 diagonal并定义它的最小尺寸。要计算这一点,我们只需将所有子元素的宽度和高度(指定[]fyne.CanvasObjectMinSize.

import "fyne.io/fyne/v2"
​
type diagonal struct {
}
​
func (d *diagonal) MinSize(objects []fyne.CanvasObject) fyne.Size {
    w, h := float32(0), float32(0)
    for _, o := range objects {
        childSize := o.MinSize()
​
        w += childSize.Width
        h += childSize.Height
    }
    return fyne.NewSize(w, h)
}

对于这种类型,我们添加一个Layout()函数,该函数应该将所有指定的对象移动到fyne.Size第二个参数中指定的对象中。
在我们的实现中,我们计算小部件的左上角(这是x参数,y位置是容器的高度减去所有子项高度的总和)。从顶部位置开始,我们只需将每个项目位置推进前一个子项的大小。

func (d *diagonal) Layout(objects []fyne.CanvasObject, containerSize fyne.Size) {
    pos := fyne.NewPos(0, containerSize.Height - d.MinSize(objects).Height)
    for _, o := range objects {
        size := o.MinSize()
        o.Resize(size)
        o.Move(pos)
​
        pos = pos.Add(fyne.NewPos(size.Width, size.Height))
    }
}

这就是创建自定义布局的全部内容。现在代码都写好了,我们可以将它layout用作container.New. 下面的代码设置了3Label个小部件,并将它们放置在具有我们新布局的容器中。如果您运行此应用程序,您将看到对角线小部件排列,并且在调整窗口大小时,它们将与可用空间的左下角对齐。

package main
​
import (
    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/app"
    "fyne.io/fyne/v2/container"
    "fyne.io/fyne/v2/widget"
)
​
func main() {
    a := app.New()
    w := a.NewWindow("Diagonal")
​
    text1 := widget.NewLabel("topleft")
    text2 := widget.NewLabel("Middle Label")
    text3 := widget.NewLabel("bottomright")
​
    w.SetContent(container.New(&diagonal{}, text1, text2, text3))
    w.ShowAndRun()
}

7.2 自定义小部件

小部件分为两个区域 - 每个区域都实现一个标准接口 -fyne.Widget和fyne.WidgetRenderer. 小部件定义行为和状态,渲染器用于定义如何将其绘制到屏幕

fyne.Widget

Fyne 中的小部件只是一个有状态的画布对象,其渲染定义与主逻辑分离。正如您从fyne.Widget 界面中看到的那样,没有太多必须实现的东西。

type Widget interface {
    CanvasObject
​
    CreateRenderer() WidgetRenderer
}

由于小部件需要像我们从同一界面继承的任何其他画布项一样使用。为了节省编写所需的所有函数,我们可以使用widget.BaseWidget处理基础的类型。
每个小部件定义将包含比界面所需的更多内容。Fyne小部件中的标准是导出定义行为的字段(就像canvas包中定义的原语一样)。
例如,查看widget.Button类型:

type Button struct {
    BaseWidget
    Text  string
    Style ButtonStyle
    Icon  fyne.Resource
​
    OnTapped func()
}

您可以看到这些项目中的每一个如何存储有关小部件行为的状态,但看不到它是如何呈现的。

fyne.WidgetRenderer

小部件渲染器负责管理fyne.CanvasObject组合在一起以创建小部件设计的原语列表。它很像fyne.Container一个自定义布局和一些额外的主题处理。
每个小部件都必须提供一个渲染器,但完全有可能从另一个小部件重用渲染器 - 特别是如果您的小部件是另一个标准控件的轻量级包装器。

type WidgetRenderer interface {
    Layout(Size)
    MinSize() Size
​
    Refresh()
    Objects() []CanvasObject
    Destroy()
}

如您所见,Layout(Size)MinSize()功能类似于 fyne.Layout界面,没有[]fyne.CanvasObject参数 - 这是因为确实需要布局小部件,但它控制将包含哪些对象。

Refresh()当此渲染器绘制的小部件已更改或主题已更改时,将触发该方法。在任何一种情况下,我们都可能需要对其外观进行调整。最后,Destroy()当不再需要此渲染器时调用该方法,因此它应该清除任何可能泄漏的资源。

再次与按钮小部件进行比较 - 它的fyne.WidgetRenderer实现基于以下类型:

type buttonRenderer struct {
    icon   *canvas.Image
    label  *canvas.Text
    shadow *fyne.CanvasObject
​
    objects []fyne.CanvasObject
    button  *Button
}

如您所见,它具有缓存实际图像、文本和阴影画布对象以进行绘图的字段。fyne.WidgetRenderer跟踪所需的对象切片。
最后,它保留widget.Button对所有状态信息的引用。在该Refresh()方法中,它将根据基础widget.Button类型的任何更改来更新图形状态。

把它放在一起

基本小部件将扩展widget.BaseWidget类型并声明小部件所拥有的任何状态。该CreateRenderer()函数必须存在并返回一个新fyne.WidgetRenderer实例。Fyne 中的小部件和驱动程序代码将确保对其进行相应的缓存 - 此方法可能会被多次调用(例如,如果小部件被隐藏然后显示)。如果CreateRenderer()再次调用,您应该返回一个新的渲染器实例,因为旧的渲染器实例可能已被破坏。
注意不要在渲染器中保留任何重要状态 - 动画代码非常适合该位置,但用户状态不适合。隐藏的小部件可能会破坏其渲染器,如果再次显示,新的渲染器必须能够反映相同的小部件状态。

7.3 捆绑资源

基于 Go 的应用程序通常构建为单个二进制可执行文件,对于Fyne应用程序也是如此。单个文件可以更轻松地分发安装我们的软件。不幸的是,GUI 应用程序通常需要额外的资源来呈现用户界面。为了应对这一挑战,Go 应用程序可以将资源捆绑到二进制文件本身中。Fyne工具包更喜欢使用“fyne bundle”,因为它具有我们将在下面探讨的各种好处。

捆绑资源的基本方法是执行“fyne bundle”命令。这个工具有各种参数来自定义输出,但在它的基本形式中,要捆绑的文件将被转换为可以构建到您的应用程序中的Go源代码。

$ ls
image.png   main.go
$ fyne bundle image.png >> bundled.go
$ ls
bundled.go  image.png   main.go
$ 

bundled.go的内容将是我们可以在代码中访问的资源变量列表。例如,上面的代码将生成一个包含以下内容的文件:

var resourceImagePng = &fyne.StaticResource{
    StaticName: "image.png",
    StaticContent: []byte{
...
    }}

如您所见,默认命名为“resource.”。该文件中使用的名称和包可以在命令参数中自定义。然后,我们可以使用这个名称,例如,在画布上加载图像:

img := canvas.NewImageFromResource(resourceImagePng)

fyne资源只是具有唯一名称的字节集合,因此它可以是字体、声音文件或您希望加载的任何其他数据。-append您还可以使用该参数将许多资源捆绑到一个文件中。如果您将捆绑许多文件,建议将命令保存在 shell 脚本中,例如此文件gen.sh

#!/bin/bash
fyne bundle image1.png > bundled.go
fyne bundle -append image2.png >> bundled.go

如果您随后更改任何资源或添加新资源,则可以更新此文件并运行一次以更新您的bundled.go文件。然后您应该添加bundled.go到版本控制,以便其他人可以构建您的应用程序而无需运行“fyne bundle”。添加也是一个好主意,gen.sh以便其他人可以在需要时重新生成捆绑的资源。

7.4 自定义主题

应用程序能够加载自定义主题,这些主题可以对标准主题应用微调或重写覆盖。一个主题需要实现接口的功能,fyne.Theme接口定义如下:

type Theme interface {
    Color(ThemeColorName, ThemeVariant) color.Color
    Font(TextStyle) Resource
    Icon(ThemeIconName) Resource
    Size(ThemeSizeName) float32
}

为了应用我们的主题更改,我们将首先定义一个实现此接口的新类型。

定义你的主题

我们首先定义一个新类型作为我们的主题,一个简单的空结构就可以了:

type myTheme struct {}

我们实现了一个接口以使编译错误更接近定义类型是一个好主意。

var _ fyne.Theme = (*myTheme)(nil)

此时您可能会看到编译错误,因为我们仍然需要实现方法,我们从颜色开始。

自定义颜色

接口中定义的Color函数Theme要求我们定义一个命名颜色,并为用户想要的变体提供提示(例如theme.VariantLighttheme.VariantDark)。在我们的主题中,我们将返回自定义背景颜色 - 使用不同的明暗值。

func (m myTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
    if name == theme.ColorNameBackground {
        if variant == theme.VariantLight {
            return color.White
        }
        return color.Black
    }
​
    return theme.DefaultTheme().Color(name, variant)
}

您将看到此处的最后一行引用了theme.DefaultTheme()查找标准值。这允许我们提供自定义值,但在我们不想提供自己的值时回退到标准主题。
当然颜色比资源简单,我们看看自定义图标。

覆盖默认图标

图标(和字体)用fyne.Resource 替换简单的类型,如intcolor.Color。我们可以使用fyne.NewStaticResource 构建我们自己的资源,或者您可以传入使用[资源嵌入]创建的值。

func (m myTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
    if name == theme.IconNameHome {
        fyne.NewStaticResource("myHome", homeBytes)
    }

    return theme.DefaultTheme().Icon(name)
}

如上所述,如果我们不想提供特定的覆盖,我们将返回默认主题图标。

加载主题

在我们加载主题之前,您还需要实现Size和Font方法。如果您愿意使用默认值,则可以只使用这些空实现。

func (m myTheme) Font(style fyne.TextStyle) fyne.Resource {
    return theme.DefaultTheme().Font(style)
}
​
func (m myTheme) Size(name fyne.ThemeSizeName) float32 {
    return theme.DefaultTheme().Size(name)
}

要为您的应用设置主题,您需要添加以下代码行:

app.Settings().SetTheme(&myTheme{})

通过这些更改,您可以应用自己的样式、进行小调整或提供完全自定义的应用程序!

7.5 扩展小部件

标准的Fyne小部件提供最少的功能和自定义来支持大多数用例。在某些时候可能需要具有更高级的功能。与其让开发人员构建自己的小部件,不如扩展现有的小部件。
例如,我们将扩展图标小部件以支持被点击。为此,我们声明了一个嵌入widget.Icon类型的新结构。我们创建一个调用重要函数的构造ExtendBaseWidget函数。

import (
    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/widget"
)
​
type tappableIcon struct {
    widget.Icon
}
​
func newTappableIcon(res fyne.Resource) *tappableIcon {
    icon := &tappableIcon{}
    icon.ExtendBaseWidget(icon)
    icon.SetResource(res)
​
    return icon
}

注意: 像这样的小部件构造函数widget.NewIcon可能不会用于扩展,因为它已经调用了ExtendBaseWidget.

然后我们添加新函数来实现fyne.Tappable界面,添加这些函数后,Tapped每次用户点击我们的新图标时都会调用新函数。所需的接口有两个功能, Tapped(*PointEvent)TappedSecondary(*PointEvent),所以我们将两者都添加。

import "log"
​
func (t *tappableIcon) Tapped(_ *fyne.PointEvent) {
    log.Println("I have been tapped")
}
​
func (t *tappableIcon) TappedSecondary(_ *fyne.PointEvent) {
}

我们可以使用一个简单的应用程序来测试这个新的小部件,如下所示。

import (
    "fyne.io/fyne/v2/app"
    "fyne.io/fyne/v2/theme"
)
​
func main() {
    a := app.New()
    w := a.NewWindow("Tappable")
    w.SetContent(newTappableIcon(theme.FyneLogo()))
    w.ShowAndRun()
}
7.6 数字输入框

在传统意义上,GUI 程序使用回调来自定义小部件的操作。Fyne没有公开插入自定义回调来捕获小部件上的事件,但它不需要这样做。Go 语言具有足够的可扩展性来完成这项工作。
相反,我们可以简单地使用 Type Embedding并扩展小部件,使其只能输入数值。
首先创建一个新的类型结构体,我们将其命名为numericalEntry

type numericalEntry struct {
    widget.Entry
}

如[扩展现有小部件]中所述,我们遵循良好实践并创建一个构造函数来扩展BaseWidget.

func newNumericalEntry() *numericalEntry {
    entry := &numericalEntry{}
    entry.ExtendBaseWidget(entry)
    return entry
}

现在我们需要让条目只接受数字。这可以通过覆盖fyne.Focusable接口中 TypedRune(rune)方法来完成。这将允许我们拦截从按键接收到的符文的标准处理,并且只通过我们想要的符文。在这个方法中,我们将使用条件来检查符文是否匹配0到 9之间的任何数字。如果是数字,我们将其委托给TypedRune(rune)嵌入条目的标准方法。如果不是数字,我们就忽略输入。此实现仅允许输入整数,但如果需要,可以轻松扩展以在将来检查其他键。

func (e *numericalEntry) TypedRune(r rune) {
    switch r {
    case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
        e.Entry.TypedRune(r)
    }
}

如果我们想更新实现以允许十进制数字,我们可以简单地将. 到允许的符文列表中。

func (e *numericalEntry) TypedRune(r rune) {
    switch r {
    case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
        e.Entry.TypedRune(r)
    }
}

有了这个,该条目现在只允许用户在按键时输入数值。但是,粘贴快捷方式仍允许输入文本。为了解决这个问题,我们可以覆盖fyne.Focusable 接口中 TypedRune(rune)方法来完成。首先我们需要做一个类型断言来检查给定的快捷方式是否属于type *fyne.ShortcutPaste。如果不是,我们可以将快捷方式委托回嵌入条目。如果是,我们检查剪贴板内容是否为数字,通过使用strconv.ParseFloat()(如果您只想允许整数,strconv.Atoi()就可以了),然后如果剪贴板内容可以被解析而没有错误,则将快捷方式委托回嵌入条目。

func (e *numericalEntry) TypedShortcut(shortcut fyne.Shortcut) {
    paste, ok := shortcut.(*fyne.ShortcutPaste)
    if !ok {
        e.Entry.TypedShortcut(shortcut)
        return
    }
​
    content := paste.Clipboard.Content()
    if _, err := strconv.ParseFloat(content, 64); err == nil {
        e.Entry.TypedShortcut(shortcut)
    }
}

我们还可以确保移动操作系统打开数字键盘而不是默认键盘。这可以通过首先导入fyne.io/fyne/v2/driver/mobile包并覆盖m̀obile.Keyboardable中的Keyboard() mobile.KeyboardType方法来完成。在函数内部,我们然后简单地返回mobile.NumberKeyboard类型。

func (e *numericalEntry) Keyboard() mobile.KeyboardType {
    return mobile.NumberKeyboard
}

最后,生成的代码可能如下所示:

package main
​
import (
    "strconv"
​
    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/app"
    "fyne.io/fyne/v2/driver/mobile"
    "fyne.io/fyne/v2/widget"
)
​
type numericalEntry struct {
    widget.Entry
}
​
func newNumericalEntry() *numericalEntry {
    entry := &numericalEntry{}
    entry.ExtendBaseWidget(entry)
    return entry
}
​
func (e *numericalEntry) TypedRune(r rune) {
    switch r {
    case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ',':
        e.Entry.TypedRune(r)
    }
}
​
func (e *numericalEntry) TypedShortcut(shortcut fyne.Shortcut) {
    paste, ok := shortcut.(*fyne.ShortcutPaste)
    if !ok {
        e.Entry.TypedShortcut(shortcut)
        return
    }
​
    content := paste.Clipboard.Content()
    if _, err := strconv.ParseFloat(content, 64); err == nil {
        e.Entry.TypedShortcut(shortcut)
    }
}
​
func (e *numericalEntry) Keyboard() mobile.KeyboardType {
    return mobile.NumberKeyboard
}
​
func main() {
    a := app.New()
    w := a.NewWindow("Numerical")
​
    entry := newNumericalEntry()
​
    w.SetContent(entry)
    w.ShowAndRun()
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2157616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像处理软件,常用于照片编辑和修饰

一、简介 1、一款功能强大的图像处理软件,常用于照片编辑和修饰。它提供多种工具和特效,允许用户调整照片的亮度、对比度、色彩、锐化等 二、下载 1、文末有下载链接,不明白可以私聊我哈(麻烦咚咚咚,动动小手给个关注收藏小三连&a…

【掘金量化使用技巧】用日线合成长周期k线

掘金API中的接口最长的周期是‘1d’的,因此周线/月线/年线等数据需要自己进行合成。 基本思路 用日线合成长周期的k线只需要确定好合成的周期以及需要的数据即可。 周期: 一般行情软件上提供年k、月k、周k,我也选择年、月、周再加一个季度频率。 数据:…

图纸加密防泄密软件 | 从设计到交付,2024年值得关注的图纸加密软件大盘点!

图纸者,匠心之凝聚,智慧之结晶。然,信息之海浩瀚无垠,暗流涌动,图纸之安全,实乃企业之头等大事。 故,择一良器,以密护图纸,实为当务之急。 以下,七款图纸加密…

Linux之实战命令01:xargs应用实例(三十五)

简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【…

高刷显示器哪个好?540Hz才有资格称高刷

高刷显示器哪个好?说实话,540Hz这些才能成为高刷显示器,什么200,240的,都不够高,什么是从容,有我不用才叫从容。下面我们一起来看看540Hz的高刷显示器都有哪些吧! 1.高刷显示器哪个好 - 蚂蚁电…

2024风湿免疫科常用评估量表汇总,附操作步骤与评定标准!

常笑医学整理了5个风湿免疫科常用的评估量表,包括类风湿关节炎患者病情评价(DAS28)、系统性狼疮活动性测定(SLAM)等。这些量表在常笑医学网均支持在线评估、下载和创建项目使用。 01 类风湿关节炎患者病情评价 &#x…

实践中如何选择o1或sonnet3-5?

简述 AI更新太快导致我们不知选择什么使用更好?本文对比了新模型o1系列和Claude-3.5-sonnet的一些特点,针对不同开发场景提供了选择建议,希望能为你提供一些模型选择的参考。 模型对比 o1系列: 优势: 推理能力非常强&#xff0…

【动态规划】两个数组的 dp 问题二

两个数组的 dp 问题 1.正则表达式匹配2.交错字符串3.两个字符串的最小ASCII删除和4.最长重复子数组 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃&#x1…

高德地图自定义点标记

const markerContent <div class"custom-content-marker"> <span>摄像机<span> <img src"//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png"> </div> marker.value new AMap.Marker({position:…

Ubuntu搭建java开发环境

一&#xff1a;Ubuntu安装 1、下载Ubuntu 24.04.1 LTS 官网下载地址&#xff1a;https://releases.ubuntu.com/24.04.1/ubuntu-24.04.1-desktop-amd64.iso 可以直接点击这里下载 2、使用VMware安装 新建虚拟机 之后一直下一步&#xff0c;到如下界面&#xff0c;选择 刚刚…

【MYSQL】聚合查询、分组查询、联合查询

目录 聚合查询聚合函数count()sum()avg()max()和min()总结 分组查询group by 子句having 子句 联合查询笛卡尔积内连接外连接自连接子查询单行子查询多行子查询from子句使用子查询 合并查询 聚合查询 聚合查询就是针对表中行与行之间的查询。 聚合函数 count() count(列名)&a…

战神5/战神:诸神黄昏/God of War Ragnarok(容量175GB)百度网盘下载

版本介绍 v1.0.612.4312|容量175GB|官方简体中文|支持键盘.鼠标.手柄|赠单板学习补丁 配置要求 战神5/战神&#xff1a;诸神黄昏/God of War Ragnarok 游戏介绍 不灭的北欧传奇 由Santa Monica Studio出品、Jetpack Interactive负责PC移植的佳作《God of War Ragnark》将带您…

python实现语音唤醒

1. 环境 python版本&#xff1a;3.11.9 2.完整代码 import sqlite3 import timefrom funasr import AutoModel import sounddevice as sd import numpy as np from pypinyin import lazy_pinyin# 模型参数设置 chunk_size [0, 10, 5] encoder_chunk_look_back 7 decoder_c…

气膜体育馆:低成本、高效益的体育空间解决方案—轻空间

随着全民健身和健康生活理念的兴起&#xff0c;各类体育场馆需求日益增加。在这样的市场背景下&#xff0c;气膜体育馆凭借其低成本、快速建造以及灵活多变的空间设计&#xff0c;成为现代体育场馆建设的新趋势。气膜技术为体育场馆提供了一种全新的解决方案&#xff0c;让运营…

Tomcat 漏洞复现

1、CVE-2017-12615 1、环境开启 2、首页抓包&#xff0c;修改为 PUT 方式提交 Tomcat允许适用put方法上传任意文件类型&#xff0c;但不允许isp后缀文件上传&#xff0c;因此需要配合 windows的解析漏洞 3、访问上传的jsp文件 4、使用工具进行连接 2、后台弱⼝令部署war包 1…

新建flask项目,配置入口文件,启动项目

pycharm新建flask项目时&#xff0c;会提供一个创建flask项目的导向&#xff0c;自动设置虚拟环境&#xff0c;并且安装flask及其依赖而vscode新建flask项目时&#xff0c;需要手动设置虚拟环境并安装flask&#xff0c;需要在终端使用pip install flask命令来安装flask及其依赖…

一文了解什么是大模型?到底大模型有什么用呢?

党中央、国务院面向未来准确把握时代大势&#xff0c;已于十三五期间部署推进数字中国建设&#xff0c;《国民经济和社会发展第十四个五年规划和2035年远景目标纲要》更是将“加快数字化发展&#xff0c;建设数字中国”单列成篇&#xff0c;要求“提高数字政府建设水平”&#…

Kettle的实战练习指南:从数据导入到ETL自动化

在数据集成和数据仓库建设中&#xff0c;Kettle作为一个强大的开源ETL工具&#xff0c;提供了灵活的数据抽取、转换和加载功能。本文将通过实战案例&#xff0c;详细介绍Kettle在数据导入、ETL流程设计、自动化任务调度等方面的应用。 一、数据导入 1. SQL语句导入 导入sql语…

机器人速度雅可比矩阵求解(2自由度平面关节机器人)

关节速度和末端速度空间的映射需要计算雅可比矩阵的逆矩阵,在博途PLC里如何计算一个方阵的逆矩阵,大家可以参考下面这篇文章: 博途PLC矩阵求逆 矩阵求逆 博图SCL_博图矩阵运算-CSDN博客文章浏览阅读839次。本文介绍如何用C语言实现矩阵求逆的过程,详细解析了相关代码,适…

Openai gym environment for multi-agent games

题意&#xff1a;用于多智能体游戏的 OpenAI Gym 环境 问题背景&#xff1a; Is it possible to use openais gym environments for multi-agent games? Specifically, I would like to model a card game with four players (agents). The player scoring a turn starts the…