Textual:为 Python 增加漂亮的文本用户界面(TUI)

快速入门使用 Textual

Python 在 Linux 上有像 TkInter 这样的优秀 GUI(图形用户界面)开发库,但如果你不能运行图形应用程序怎么办?

文本终端,并非只在 Linux 上有,而且 BSD 和其它的出色的类 Unix 操作系统上也有。如果你的代码是用 Python 编写的,你应该使用 Textual 来帮助你编写 TUI(文本用户界面)。在这个快速介绍中,我将向你展示两个你可以用 Textual 做的示例,并且介绍它未来可能的应用方向。

所以 Textual 是什么?

Textual 是一个为 Python 构建的快速应用程序开发框架,由 Textualize.io 构建。它可以让你用简单的 Python API 构建复杂的用户界面,并运行在终端或网络浏览器上!

你需要的跟进这个教程的工具

你需要有以下条件:

  1. 具备基础的编程经验,最好熟练使用 Python。
  2. 理解基础的面向对象概念,比如类和继承。
  3. 一台安装了 Linux 与 Python 3.9+ 的机器
  4. 一款好的编辑器(Vim 或者 PyCharm 是不错的选择)

我尽可能简单化代码,以便你能轻松理解。此外,我强烈建议你下载代码,或至少按照接下来的说明安装相关程序。

安装步骤

首先创建一个虚拟环境:

python3 -m venv ~/virtualenv/Textualize

现在,你可以克隆 Git 仓库并创建一个可以编辑的发布版本:

. ~/virtualenv/Textualize/bin/activate
pip install --upgrade pip
pip install --upgrade wheel
pip install --upgrade build
pip install --editable .

或者直接从 Pypi.org 安装:

. ~/virtualenv/Textualize/bin/activate
pip install --upgrade KodegeekTextualize

我们的首个程序:日志浏览器

这个 日志浏览器 就是一款简单的应用,能执行用户 PATH 路径上的一系列 UNIX 命令,并在任务执行完毕后捕获输出。

以下是该应用的代码:

import shutil
from textual import on
from textual.app import ComposeResult, App
from textual.widgets import Footer, Header, Button, SelectionList
from textual.widgets.selection_list import Selection
from textual.screen import ModalScreen
# Operating system commands are hardcoded
OS_COMMANDS = {
    "LSHW": ["lshw", "-json", "-sanitize", "-notime", "-quiet"],
    "LSCPU": ["lscpu", "--all", "--extended", "--json"],
    "LSMEM": ["lsmem", "--json", "--all", "--output-all"],
    "NUMASTAT": ["numastat", "-z"]
}

class LogScreen(ModalScreen):
    # ... Code of the full separate screen omitted, will be explained next
    def __init__(self, name = None, ident = None, classes = None, selections = None):
        super().__init__(name, ident, classes)
        pass

class OsApp(App):
    BINDINGS = [
        ("q", "quit_app", "Quit"),
    ]
    CSS_PATH = "os_app.tcss"
    ENABLE_COMMAND_PALETTE = False  # Do not need the command palette

    def action_quit_app(self):
        self.exit(0)

    def compose(self) -> ComposeResult:
        # Create a list of commands, valid commands are assumed to be on the PATH variable.
        selections = [Selection(name.title(), ' '.join(cmd), True) for name, cmd in OS_COMMANDS.items() if shutil.which(cmd[0].strip())]
        yield Header(show_clock=False)
        sel_list = SelectionList(*selections, id='cmds')
        sel_list.tooltip = "Select one more more command to execute"
        yield sel_list
        yield Button(f"Execute {len(selections)} commands", id="exec", variant="primary")
        yield Footer()

    @on(SelectionList.SelectedChanged)
    def on_selection(self, event: SelectionList.SelectedChanged) -> None:
        button = self.query_one("#exec", Button)
        selections = len(event.selection_list.selected)
        if selections:
            button.disabled = False
        else:
            button.disabled = True
        button.label = f"Execute {selections} commands"

    @on(Button.Pressed)
    def on_button_click(self):
        selection_list = self.query_one('#cmds', SelectionList)
        selections = selection_list.selected
        log_screen = LogScreen(selections=selections)
        self.push_screen(log_screen)

def main():
    app = OsApp()
    app.title = f"Output of multiple well known UNIX commands".title()
    app.sub_title = f"{len(OS_COMMANDS)} commands available"
    app.run()

if __name__ == "__main__":
    main()

现在我们逐条梳理一下程序的代码:

  1. 每个应用都扩展自 App 类。其中最重要的有 composemount 等方法。但在当前应用中,我们只实现了 compose
  2. compose 方法中,你会返回一系列 组件 Widget ,并按顺序添加到主屏幕中。每一个组件都有定制自身外观的选项。
  3. 你可以设定单字母的 绑定 binding ,比如此处我们设定了 q 键来退出应用(参见 action_quit_app 函数和 BINDINGS 列表)。
  4. 利用 SelectionList 组件,我们展示了待运行的命令列表。然后,你可以通过 @on(SelectionList.SelectedChanged) 注解以及 on_selection 方法告知应用获取所选的内容。
  5. 对于无选定元素的应对很重要,我们会根据运行的命令数量来决定是否禁用 “exec” 按钮。
  6. 我们使用类似的监听器( @on(Button.Pressed) )来执行命令。我们做的就是将我们的选择送到一个新的屏幕,该屏幕会负责执行命令并收集结果。

你注意到 CSS_PATH = "os_app.tcss" 这个变量了吗?Textual 允许你使用 CSS 来控制单个或多个组件的外观(色彩、位置、尺寸):

Screen {
        layout: vertical;
}

Header {
        dock: top;
}

Footer {
        dock: bottom;
}

SelectionList {
        padding: 1;
        border: solid $accent;
        width: 1fr;
        height: 80%;
}

Button {
        width: 1fr
}

引自 Textual 官方网站:

Textual 中使用的 CSS 是互联网上常见 CSS 的简化版本,容易上手。

这真是太棒了,只需要用一哥独立的 样式表,就可以轻松调整应用的样式。

好,我们现在来看看如何在新屏幕上展示结果。

在新屏幕上展示结果

以下是在新屏幕上处理输出的代码:

import asyncio
from typing import List
from textual import on, work
from textual.reactive import reactive
from textual.screen import ModalScreen
from textual.widgets import Button, Label, Log
from textual.worker import Worker
from textual.app import ComposeResult

class LogScreen(ModalScreen):
    count = reactive(0)
    MAX_LINES = 10_000
    ENABLE_COMMAND_PALETTE = False
    CSS_PATH = "log_screen.tcss"

    def __init__(
            self,
            name: str | None = None,
            ident: str | None = None,
            classes: str | None = None,
            selections: List = None
    ):
        super().__init__(name, ident, classes)
        self.selections = selections

    def compose(self) -> ComposeResult:
        yield Label(f"Running {len(self.selections)} commands")
        event_log = Log(
            id='event_log',
            max_lines=LogScreen.MAX_LINES,
            highlight=True
        )
        event_log.loading = True
        yield event_log
        button = Button("Close", id="close", variant="success")
        button.disabled = True
        yield button

    async def on_mount(self) -> None:
        event_log = self.query_one('#event_log', Log)
        event_log.loading = False
        event_log.clear()
        lst = '\n'.join(self.selections)
        event_log.write(f"Preparing:\n{lst}")
        event_log.write("\n")

        for command in self.selections:
            self.count += 1
            self.run_process(cmd=command)

    def on_worker_state_changed(self, event: Worker.StateChanged) -> None:
        if self.count == 0:
            button = self.query_one('#close', Button)
            button.disabled = False
        self.log(event)

    @work(exclusive=False)
    async def run_process(self, cmd: str) -> None:
        event_log = self.query_one('#event_log', Log)
        event_log.write_line(f"Running: {cmd}")
        # Combine STDOUT and STDERR output
        proc = await asyncio.create_subprocess_shell(
            cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT
        )
        stdout, _ = await proc.communicate()
        if proc.returncode != 0:
            raise ValueError(f"'{cmd}' finished with errors ({proc.returncode})")
        stdout = stdout.decode(encoding='utf-8', errors='replace')
        if stdout:
            event_log.write(f'\nOutput of "{cmd}":\n')
            event_log.write(stdout)
        self.count -= 1

    @on(Button.Pressed, "#close")
    def on_button_pressed(self, _) -> None:
        self.app.pop_screen()

你会注意到:

  1. LogScreen 类扩展自 ModalScreen 类, 该类负责处理模态模式的屏幕。
  2. 这个屏幕同样有一个 compose 方法,我们在这里添加了组件以展示 Unix 命令的内容。
  3. 我们创建了一个叫做 mount 的新方法。一旦你用 compose 编排好组件,你就可以运行代码来获取数据,并再进一步定制它们的外观。
  4. 我们使用 asyncio 运行命令,这样我们就能让 TUI 主工作线程在每个命令的结果出来时就及时更新内容。
  5. 对于“工作线程”,请注意 run_process 方法上的 @work(exclusive=False) 注解,该方法用于运行命令并捕获 STDOUT + STDERR 输出。使用 工作线程 来管理并发并不复杂,尽管它们在手册中确实有专门的章节。这主要是因为运行的外部命令可能会执行很长时间。
  6. run_process 中,我们通过调用 write 以命令的输出内容来更新 event_log
  7. 最后,on_button_pressed 把我们带回到前一屏幕(从堆栈中移除屏幕)。

这个小应用向你展示了如何一份不到 200 行的代码来编写一个简单的前端,用来运行非 Python 代码。

现在我们来看一个更复杂的例子,这个例子用到了我们还未探索过的 Textual 的新特性。

示例二:展示赛事成绩的表格

通过 Textual 创建的表格应用

本示例将展示如何使用 DataTable 组件在表格中展示赛事成绩。你能通过这个应用实现:

  • 通过列来排序表格
  • 选择表格中的行,完整窗口展示赛事细节,我们将使用我们在日志浏览器中看到的 “推送屏幕” 技巧。
  • 能进行表格搜索,查看选手详情,或执行其他操作如退出应用。

下面,我们来看看应用代码:

#!/usr/bin/env python
"""
Author: Jose Vicente Nunez
"""
from typing import Any, List

from rich.style import Style
from textual import on
from textual.app import ComposeResult, App
from textual.command import Provider
from textual.screen import ModalScreen, Screen
from textual.widgets import DataTable, Footer, Header

MY_DATA = [
    ("level", "name", "gender", "country", "age"),
    ("Green", "Wai", "M", "MYS", 22),
    ("Red", "Ryoji", "M", "JPN", 30),
    ("Purple", "Fabio", "M", "ITA", 99),
    ("Blue", "Manuela", "F", "VEN", 25)
]

class DetailScreen(ModalScreen):
    ENABLE_COMMAND_PALETTE = False
    CSS_PATH = "details_screen.tcss"

    def __init__(
            self,
            name: str | None = None,
            ident: str | None = None,
            classes: str | None = None,
            row: List[Any] | None = None,
    ):
        super().__init__(name, ident, classes)
        # Rest of screen code will be show later

class CustomCommand(Provider):

    def __init__(self, screen: Screen[Any], match_style: Style | None = None):
        super().__init__(screen, match_style)
        self.table = None
        # Rest of provider code will be show later

class CompetitorsApp(App):
    BINDINGS = [
        ("q", "quit_app", "Quit"),
    ]
    CSS_PATH = "competitors_app.tcss"
    # Enable the command palette, to add our custom filter commands
    ENABLE_COMMAND_PALETTE = True
    # Add the default commands and the TablePopulateProvider to get a row directly by name
    COMMANDS = App.COMMANDS | {CustomCommand}

    def action_quit_app(self):
        self.exit(0)

    def compose(self) -> ComposeResult:
        yield Header(show_clock=True)

        table = DataTable(id=f'competitors_table')
        table.cursor_type = 'row'
        table.zebra_stripes = True
        table.loading = True
        yield table
        yield Footer()

    def on_mount(self) -> None:
        table = self.get_widget_by_id(f'competitors_table', expect_type=DataTable)
        columns = [x.title() for x in MY_DATA[0]]
        table.add_columns(*columns)
        table.add_rows(MY_DATA[1:])
        table.loading = False
        table.tooltip = "Select a row to get more details"

    @on(DataTable.HeaderSelected)
    def on_header_clicked(self, event: DataTable.HeaderSelected):
        table = event.data_table
        table.sort(event.column_key)

    @on(DataTable.RowSelected)
    def on_row_clicked(self, event: DataTable.RowSelected) -> None:
        table = event.data_table
        row = table.get_row(event.row_key)
        runner_detail = DetailScreen(row=row)
        self.show_detail(runner_detail)

    def show_detail(self, detailScreen: DetailScreen):
        self.push_screen(detailScreen)

def main():
    app = CompetitorsApp()
    app.title = f"Summary".title()
    app.sub_title = f"{len(MY_DATA)} users"
    app.run()

if __name__ == "__main__":
    main()

有哪些部分值得我们关注呢?

  1. compose 方法中添加了 表头,“命令面板” 就位于此处,我们的表格(DataTable)也在这里。表格数据在 mount 方法中填充。
  2. 我们设定了预期的绑定(BINDINGS),并指定了外部的 CSS 文件来设置样式(CSS_PATH)。
  3. 默认情况下,我们无需任何设置便能使用 命令面板,但在此我们显式启用了它(ENABLE_COMMAND_PALETTE = True)。
  4. 我们的应用有一个自定义表格搜索功能。当用户输入一名选手的名字后,应用会显示可能的匹配项,用户可以点击匹配项查看该选手的详细信息。这需要告诉应用我们有一个定制的命令提供者(COMMANDS = App.COMMANDS | {CustomCo_ mmand}),即类 CustomCommand(Provider)
  5. 如果用户点击了表头,表格内容会按照该列进行排序。这是通过 on_header_clicked 方法实现的,该方法上具有 @on(DataTable.HeaderSelected) 注解。
  6. 类似地,当选中表格中的一行时, on_row_clicked 方法会被调用,这得益于它拥有 @on(DataTable.RowSelected) 注解。当方法接受选中的行后,它会推送一个新的屏幕,显示选中行的详细信息(class DetailScreen(ModalScreen))。

现在,我们详细地探讨一下如何显示选手的详细信息。

利用多屏展示复杂视图

当用户选择表格中的一行,on_row_clicked 方法就会被调用。它收到的是一个 DataTable.RowSelected 类型的事件。从这里我们会用选中的行的内容构建一个 DetailScreen(ModalScreen) 类的实例:

from typing import Any, List
from textual import on
from textual.app import ComposeResult
from textual.screen import ModalScreen
from textual.widgets import Button, MarkdownViewer

MY_DATA = [
    ("level", "name", "gender", "country", "age"),
    ("Green", "Wai", "M", "MYS", 22),
    ("Red", "Ryoji", "M", "JPN", 30),
    ("Purple", "Fabio", "M", "ITA", 99),
    ("Blue", "Manuela", "F", "VEN", 25)
]

class DetailScreen(ModalScreen):
    ENABLE_COMMAND_PALETTE = False
    CSS_PATH = "details_screen.tcss"

    def __init__(
            self,
            name: str | None = None,
            ident: str | None = None,
            classes: str | None = None,
            row: List[Any] | None = None,
    ):
        super().__init__(name, ident, classes)
        self.row: List[Any] = row

    def compose(self) -> ComposeResult:
        self.log.info(f"Details: {self.row}")
        columns = MY_DATA[0]
        row_markdown = "\n"
        for i in range(0, len(columns)):
            row_markdown += f"* **{columns[i].title()}:** {self.row[i]}\n"
        yield MarkdownViewer(f"""## User details:
        {row_markdown}
        """)
        button = Button("Close", variant="primary", id="close")
        button.tooltip = "Go back to main screen"
        yield button

    @on(Button.Pressed, "#close")
    def on_button_pressed(self, _) -> None:
        self.app.pop_screen()

这个类的职责很直接:

  1. compose 方法取得此行数据,并利用一个 支持 Markdown 渲染的组件 来展示内容。它的便利之处在于,它会为我们自动生成一个内容目录。
  2. 当用户点击 “close” 后,方法 on_button_pressed 会引导应用回到原始屏幕。注解 @on(Button.Pressed, "#close") 用来接收按键被点击的事件。

最后,我们来详细讲解一下那个多功能的搜索栏(也叫做命令面板)。

命令面板的搜索功能

任何使用了表头的 Textual 应用都默认开启了 命令面板。有意思的是,你可以在 CompetitorsApp 类中添加自定义的命令,这会增加到默认命令集之上:

COMMANDS = App.COMMANDS | {CustomCommand}

然后是执行大部分任务的 CustomCommand(Provider) 类:

from functools import partial
from typing import Any, List
from rich.style import Style
from textual.command import Provider, Hit
from textual.screen import ModalScreen, Screen
from textual.widgets import DataTable
from textual.app import App

class CustomCommand(Provider):

    def __init__(self, screen: Screen[Any], match_style: Style | None = None):
        super().__init__(screen, match_style)
        self.table = None

    async def startup(self) -> None:
        my_app = self.app
        my_app.log.info(f"Loaded provider: CustomCommand")
        self.table = my_app.query(DataTable).first()

    async def search(self, query: str) -> Hit:
        matcher = self.matcher(query)

        my_app = self.screen.app
        assert isinstance(my_app, CompetitorsApp)

        my_app.log.info(f"Got query: {query}")
        for row_key in self.table.rows:
            row = self.table.get_row(row_key)
            my_app.log.info(f"Searching {row}")
            searchable = row[1]
            score = matcher.match(searchable)
            if score > 0:
                runner_detail = DetailScreen(row=row)
                yield Hit(
                    score,
                    matcher.highlight(f"{searchable}"),
                    partial(my_app.show_detail, runner_detail),
                    help=f"Show details about {searchable}"
                )

class DetailScreen(ModalScreen):
        def __init__(
            self,
            name: str | None = None,
            ident: str | None = None,
            classes: str | None = None,
            row: List[Any] | None = None,
    ):
        super().__init__(name, ident, classes)
        # Code of this class explained on the previous section

class CompetitorsApp(App):
    # Add the default commands and the TablePopulateProvider to get a row directly by name
    COMMANDS = App.COMMANDS | {CustomCommand}
    # Most of the code shown before, only displaying relevant code
    def show_detail(self, detailScreen: DetailScreen):
        self.push_screen(detailScreen)
  1. 所有继承自 Provider 的类需实现 search 方法。在我们的例子中,我们还覆盖了 startup 方法,为了获取到我们应用表格(和其内容)的引用,这里使用到了 App.query(DataTable).first()。在类的生命周期中, startup 方法只会被调用一次。
  2. search 方法内,我们使用 Provider.matcher 对每个表格行的第二列(即名字)进行模糊搜索,以与用户在 TUI 中输入的词条进行比较。matcher.match(searchable) 返回一个整型的评分,大于零说明匹配成功。
  3. search 方法中,如果评分大于零,则返回一个 Hit 对象,以告知命令面板搜索查询是否成功。
  4. 每个 Hit 都有以下信息:评分(用于在命令面板中对匹配项排序)、高亮显示的搜索词、一个可调用对象的引用(在我们的案例中,它是一个可以将表格行推送到新屏幕的函数)。
  5. Provider 类的所有方法都是异步的。这使你能释放主线程,只有当响应准备好后才返回结果,这个过程不会冻结用户界面。

理解了这些信息,我们就可以现在展示赛手的详细信息了。

尽管这个架构的追踪功能相对直观,但是组件间传递的消息复杂性不可忽视。幸运的是,Textual 提供了有效的调试工具帮助我们理解背后的工作原理。

Textual 应用的问题排查

对于 Python 的 Textual 应用进行 调试 相较而言更具挑战性。这是因为其中有一些操作可能是异步的,而在解决组件问题时设置断点可能颇为复杂。

根据具体情况,你可以使用一些工具。但首先,确保你已经安装了 textual 的开发工具:

pip install textual-dev==1.3.0
确保你能捕捉到正确的按键

不确定 Textual 应用是否能捕捉到你的按键操作?运行 keys 应用:

textual keys

这让你能够验证一下你的按键组合,并确认在 Textual 中产生了哪些事件。

图片比千言万语更直观

如果说你在布局设计上遇到了问题,想向他人展示你当前的困境,Textual 为你的运行应用提供了截图功能:

textual run --screenshot 5 ./kodegeek_textualize/log_scroller.py

就像你所看到的,我是通过这种方式为这篇教程创建了插图。

捕获事件并输出定制消息

在 Textual 中,每一个应用实例都有一个日志记录器,可以使用如下方式访问:

my_app = self.screen.app
my_app.log.info(f"Loaded provider: CustomCommand")

想要查看这些消息,首先需要开启一个控制台:

. ~/virtualenv/Textualize/bin/activate
textual console

然后在另一个终端运行你的应用程序:

. ~/virtualenv/Textualize/bin/activate
textual run --dev ./kodegeek_textualize/log_scroller.py

在运行控制台的终端中,你可以看到实时的事件和消息输出:

Textual Development Console v0.46.0
Run a Textual app with textual run --dev my_app.py to connect.
Press Ctrl+C to quit.
─────────────────────────────────────────────────────────────────────────────── Client '127.0.0.1' connected ────────────────────────────────────────────────────────────────────────────────
[20:29:43] SYSTEM                                                                                                                                                                 app.py:2188
Connected to devtools ( ws://127.0.0.1:8081 )
[20:29:43] SYSTEM                                                                                                                                                                 app.py:2192
---
[20:29:43] SYSTEM                                                                                                                                                                 app.py:2194
driver=<class 'textual.drivers.linux_driver.LinuxDriver'>
[20:29:43] SYSTEM                                                                                                                                                                 app.py:2195
loop=<_UnixSelectorEventLoop running=True closed=False debug=False>
[20:29:43] SYSTEM                                                                                                                                                                 app.py:2196
features=frozenset({'debug', 'devtools'})
[20:29:43] SYSTEM                                                                                                                                                                 app.py:2228
STARTED FileMonitor({PosixPath('/home/josevnz/TextualizeTutorial/docs/Textualize/kodegeek_textualize/os_app.tcss')})
[20:29:43] EVENT

此外,以开发者模式运行的另一大好处是,如果你更改了 CSS,应用会尝试重新渲染,而无需重启程序。

如何编写单元测试

为你全新开发的 Textual 应用编写 单元测试,应该如何操作呢?

官方文档 展示了几种用于测试我们应用的方式。

我将采用 unittest 进行测试。为了处理异步例程,我们会需要特别的类 unittest.IsolatedAsyncioTestCase

import unittest
from textual.widgets import Log, Button
from kodegeek_textualize.log_scroller import OsApp

class LogScrollerTestCase(unittest.IsolatedAsyncioTestCase):
    async def test_log_scroller(self):
        app = OsApp()
        self.assertIsNotNone(app)
        async with app.run_test() as pilot:
            # Execute the default commands
            await pilot.click(Button)
            await pilot.pause()
            event_log = app.screen.query(Log).first()  # We pushed the screen, query nodes from there
            self.assertTrue(event_log.lines)
            await pilot.click("#close")  # Close the new screen, pop the original one
            await pilot.press("q")  # Quit the app by pressing q


if __name__ == '__main__':
    unittest.main()

现在让我们详细看看 test_log_scroller 方法中的操作步骤:

  1. 通过 app.run_test() 获取一个 Pilot 实例。然后点击主按钮,运行包含默认指令的查询,随后等待所有事件的处理。
  2. 从我们新推送出的屏幕中获取 Log,确保我们已获得几行返回的内容,即它并非空的。
  3. 关闭新屏幕并重新呈现旧屏幕。
  4. 最后,按下 q,退出应用。

可以测试表格吗?

import unittest
from textual.widgets import DataTable, MarkdownViewer
from kodegeek_textualize.table_with_detail_screen import CompetitorsApp


class TableWithDetailTestCase(unittest.IsolatedAsyncioTestCase):
    async def test_app(self):
        app = CompetitorsApp()
        self.assertIsNotNone(app)
        async with app.run_test() as pilot:

            """
            Test the command palette
            """
            await pilot.press("ctrl+\\")
            for char in "manuela".split():
                await pilot.press(char)
            await pilot.press("enter")
            markdown_viewer = app.screen.query(MarkdownViewer).first()
            self.assertTrue(markdown_viewer.document)
            await pilot.click("#close")  # Close the new screen, pop the original one

            """
            Test the table
            """
            table = app.screen.query(DataTable).first()
            coordinate = table.cursor_coordinate
            self.assertTrue(table.is_valid_coordinate(coordinate))
            await pilot.press("enter")
            await pilot.pause()
            markdown_viewer = app.screen.query(MarkdownViewer).first()
            self.assertTrue(markdown_viewer)
            # Quit the app by pressing q
            await pilot.press("q")


if __name__ == '__main__':
    unittest.main()

如果你运行所有的测试,你将看到如下类似的输出:

(Textualize) [josevnz@dmaf5 Textualize]$ python -m unittest tests/*.py
..
----------------------------------------------------------------------
Ran 2 tests in 2.065s

OK

这是测试 TUI 的一个不错的方式,对吧?

打包 Textual 应用

打包 Textual 应用与打包常规 Python 应用并没有太大区别。你需要记住,需要包含那些控制应用外观的 CSS 文件:

. ~/virtualenv/Textualize/bin/activate
python -m build
pip install dist/KodegeekTextualize-*-py3-none-any.whl

这个教程的 pyproject.toml 文件是一个打包应用的良好起点,告诉你需要做什么。

[build-system]
requires = [
    "setuptools >= 67.8.0",
    "wheel>=0.42.0",
    "build>=1.0.3",
    "twine>=4.0.2",
    "textual-dev>=1.2.1"
]
build-backend = "setuptools.build_meta"

[project]
name = "KodegeekTextualize"
version = "0.0.3"
authors = [
    {name = "Jose Vicente Nunez", email = "kodegeek.com@protonmail.com"},
]
description = "Collection of scripts that show how to use several features of textualize"
readme = "README.md"
requires-python = ">=3.9"
keywords = ["running", "race"]
classifiers = [
    "Environment :: Console",
    "Development Status :: 4 - Beta",
    "Programming Language :: Python :: 3",
    "Intended Audience :: End Users/Desktop",
    "Topic :: Utilities"
]
dynamic = ["dependencies"]

[project.scripts]
log_scroller = "kodegeek_textualize.log_scroller:main"
table_detail = "kodegeek_textualize.table_with_detail_screen:main"

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
where = ["."]
exclude = ["test*"]

[tool.setuptools.package-data]
kodegeek_textualize = ["*.txt", "*.tcss", "*.csv"]
img = ["*.svg"]

[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}

未来计划

这个简短的教程只覆盖了 Textual 的部分方面。还有很多需要探索和学习的内容:

  • 强烈建议你查看 官方教程。有大量的示例和指向参考 API 的链接。
  • Textual 可以使用来自 Rich 项目的组件,这个项目是一切的起源。我认为其中一些甚至可能所有这些组件在某些时候都会合并到 Textual 中。Textual 框架对于使用高级 API 的复杂应用更能胜任,但 Rich 也有很多漂亮的功能。
  • 创建你自己的组件!同样,在设计 TUI 时,拿一张纸,画出你希望这些组件如何布局的,这会为你后期省去很多时间和麻烦。
  • 调试 Python 应用可能会有点复杂。有时你可能需要 混合使用不同的工具 来找出应用的问题所在。
  • 异步 IO 是一个复杂的话题,你应该 阅读开发者文档 来了解更多可能的选择。
  • Textual 被其他项目所使用。其中一个非常易于使用的项目是 Trogon它会让你的 CLI 可以自我发现
  • Textual-web 是个很有前景的项目,能让你在浏览器上运行 Textual 应用。尽管它不如 Textual 成熟,但它的进化速度非常快。
  • 最后,查看这些外部项目。在项目组合中有许多有用的开源应用。

(题图:DA/f11b0eb0-8e16-4cbe-986a-0fe978f6732a)


via: https://fedoramagazine.org/crash-course-on-using-textual/

作者:Jose Nunez 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

硬核观察 #1246 OpenWrt 项目准备推出开放路由器

#1 OpenWrt 项目准备推出开放路由器

OpenWrt 计划与香蕉派合作开发开放路由器 OpenWrt One,售价将低于 100 美元。香蕉派负责制造、销售和售后,获利将部分捐赠给 OpenWrt。这台路由器属于开放硬件,其电路图也将会开源。

(插图:DA/54c35b6c-b3ef-47e3-9572-e6f3b5645518)

消息来源:OpenWrt

老王点评:开源的软件加上开源的硬件,必须支持一下。

#2 Linux 设备正遭受前所未有的矿工蠕虫攻击

一种前所未见的自我复制恶意软件正在全球范围内感染 Linux 设备,它使用特殊的隐藏方法安装加密矿工恶意软件。该蠕虫是 Mirai 僵尸网络恶意软件的变种。Mirai 于 2016 年首次出现,通过入侵易受攻击的设备发起了创纪录的分布式拒绝服务攻击,但这个最新变种安装了加密矿工软件,使攻击者可以利用受害者的计算资源、电力和带宽生成加密货币。另外,它的目标不是 telnet 弱密码,而是 SSH 弱密码。

(插图:DA/af1212dd-be5f-4f07-a9f3-edc31e9d9c68)

消息来源:Ars Technica

老王点评:Linux 再安全也防不住弱密码啊。

#3 高通称引领科技需要 “在中国做大生意”

高通公司 CEO 克里斯蒂安诺·阿蒙 Cristiano Amon 在 2024 年消费电子展上接受采访时,对高通公司在中国的业务充满信心。中国是高通公司收入最大的市场。他说:“如果你拥有领先的技术,你就会在中国有很大的业务。”根据半导体行业协会的数据,中国仍然是全球最大的半导体市场,其销售额占全球市场的三分之一。

(插图:DA/7f2b3977-c4f3-41a9-b824-3fd7c41a56df)

消息来源:Yahoo

老王点评:要是只卖低端货,中国也不需要。

回音

在 Linux 上提升游戏体验的 7 个有效建议及工具

下面是一些极好的建议来让你的 Linux 系统游戏体验更上一层楼。

多亏了各种新工具的诞生和 Linux 发行版在用户体验上的改进,目前已有成千上万的游戏可以在 Linux 系统中运行。

无论你是使用主流的 Linux 发行版还是 专为游戏设计的 Linux 发行版,你都能在 Linux 中享受游戏的乐趣。

然而,为了得到流畅的游戏体验,有一些你需要遵循的工具,技巧和方法。在这篇文章中,我会为你详细介绍一些。

1、选择合适的电子游戏

最首要的一步就是找到一款你热爱,且在 Linux 上运行流畅的游戏。

假设你购买或下载了一款最初为 Windows 系统制作的游戏,可能不论你使用何种工具,都无法在 Linux 系统中成功运行。而你最终只会挫败地想 “Linux 不适合游戏” ?

那么,该如何避免这类问题呢?

首先,你需要查阅你想要购买或下载的游戏可以在哪些平台上运行。如果游戏支持 Linux 或 Steam OS 系统,那就没问题了。

你还可以查看该游戏是否已经被添加到 Steam Deck 验证列表 中。

无论哪种情况,你都可以逛一逛 ProtonDB,在那儿搜索你所关注的游戏,看看其他用户对其的评价和打分。如果大部分评论听起来都令人信服,或者说这款游戏很值得尝试,那你就可以放心购买或下载了。

2、不要选择知名度较低的发行版

如果你希望得到社区或者使用 Linux 的朋友们的支持与帮助,请使用那些并非某某人业余项目的 Linux 发行版。

我建议你从 最佳 Linux 发行版 中作出选择。

最好的是,选择一款 长期支持版 以确保稳定的使用体验。

3、别为 Linux 游戏搭建顶级配置 PC

我知道听起来有点令人失望。但为了确保最大的兼容性以及无忧的游戏体验,最理想的状况是选用前一代的硬件而不是最新的。

利用这种方式,你还可以享受到大额的折扣,省下大笔钱!别忘了,在决定购买前,还可参阅其他 PC 硬件狂热爱好者的评论。

这个建议尤其适用于新硬件,那些刚刚发布没几个月的。

当然,你可以冒险尝试最新的硬件组件。但游戏可能会崩溃,或以其他方式出问题。而且,你也可能无法获取到关于这些硬件组件的可靠评论,从而做出明智的选择。

4、开启 Steam Play

如果你一直都是 Linux 原生游戏的粉丝,你可能想要 安装 Steam 游戏商店 并开启 Proton 兼容层,在 Linux 上运行那些仅供 Windows 系统的游戏。

你可以参考我前面提到的 ProtonDB 或者 Steam Deck 的验证列表,来确认该游戏是否能在 Linux 上流畅运行。

在安装了 Steam 以后,如果你想 开启 Steam Play,可以参照我们的指南操作。

5、没有 Steam?不必担忧!

尽管 Steam 提供了跨平台的卓越顺畅的游戏体验。

但如果你不想仅限于 Steam,还想要能访问像 Epic 游戏商店这样的商店中的游戏,你可以考虑安装 Lutris 这样的工具。

我们为你准备了一份详尽的指南,来帮助你 在 Linux 下使用 Epic 游戏商店,并学习使用此工具(和其他替代工具)。

不用担心,如果你偏爱 GOG 的无数字版权管理(DRM)游戏,我们同样为你准备了指南,指导你使用 Lutris 这样的工具在 Linux 下玩 GOG 游戏

6、使用 MangoHud 跟踪游戏性能

你是否希望在游戏中同时监控你的硬件性能和游戏性能?

借助 MangoHud,你可以得到一个信息层,显示 FPS、CPU/GPU 的温度、显存使用情况等信息,这与在 Windows 下利用英伟达 GeForce Experience 或者 MSI Afterburner 的效果相似。

安装十分简单,对于像 Fedora、Debian 和 Arch 这样的发行版,提供了 Flatpak 和其他形式的二进制文件。你可以在它的 GitHub 页面 上了解更多关于它的使用方法。

7、已经买好游戏硬件了吗?马上进行配置!

可惜的是,并不是所有的发烧友级游戏硬件都能在 Linux 下进行详细调整,至少,不能和在 Windows 下一样。

例如,你可以使用图形界面工具 piper 进行游戏鼠标的配置

同理,如果你想要控制或者 调整 Razer 设备上的灯光,你可以在这里找到我们的快速教程进行学习。

不只是设备,需要监控你的 PC 中的一体式水冷(AIO)或者其他散热硬件吗?你可以试试 CoolerControl(以前称为 Coolero)。

监控组件的温度对于获得流畅的游戏体验至关重要。因此,你可以选择工具如 CoolerControl 和 MangoHud,或者手动监控你的系统资源。

结束语

除了上述的所有建议,你还应该微调游戏内的设置以在你的系统上获得沉浸式体验。当然,这依赖于个人用户的偏好,所以没有通用的解决方案。

别忘了,如果你是新手,那么不妨读一读我们的 Linux 游戏指南

? 你最喜欢用什么工具来提升你的 Linux 游戏体验?你有想要添加到这个列表的建议吗?请随时在评论中让我们知道你的想法。

(题图:DA/68d71173-992a-423a-bd95-a6e2f64bf254)


via: https://itsfoss.com/linux-gaming-tips/

作者:Ankush Das 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

硬核观察 #1245 OpenAI 悄然删除禁止将 ChatGPT 用于 “军事和战争” 的禁令

#1 OpenAI 悄然删除禁止将 ChatGPT 用于 “军事和战争” 的禁令

在 1 月 10 日之前,OpenAI 的《使用政策》页面一直禁止“具有高身体伤害风险的活动”,包括 “武器开发” 及 “军事和战争”。而新政策保留了不得 “利用我们的服务伤害自己或他人” 的禁令,并以 “开发或使用武器” 来举例,但对 “军事和战争” 用途的全面禁止已不复存在。该公司表示,此次重写旨在使文件 “更清晰”、“更易读”,其中还包括许多其他实质性的语言和格式改动。

(插图:DA/1eb6f110-3d94-4c0c-84af-34e5ec2e1b64)

消息来源:Slashdot

老王点评:唉,AI 武器化似乎是不可避免的发展趋势,科技往往第一时间用在武器和战争上。

#2 博通抛弃 VMware 云服务提供商

在收购了 VMware 之后,博通对 VMware 进行了一系列“手术”。不但将 VMware 的许可变为 订阅制,而且还对其合作伙伴关系进行了一系列大动作。博通准备终止 VMware 的渠道计划,只有一些被邀请的解决方案提供商/经销商会过渡到博通的渠道计划。并且,同时博通还通知 VMware 的云服务提供商,告知将于 4 月底终止他们销售基于 VMware 的云服务的伙伴计划。只有一些服务提供商将被邀请加入博通的合作伙伴计划,而没有被邀请加入的则陷入了恐慌,不知道其客户该怎么办。

(插图:DA/e82f9ab3-baf2-412a-b208-f0b9f7f45884)

消息来源:The Register

老王点评:被卖的公司往往被吃干吞净。

#3 Linux 4.14 LTS 在六年后迎来生命终结

Linux 4.14 于 2017 年底首次亮相,但随着 Linux 4.14.336 的发布,它成为该系列的最后一个点版本。此版本中,只进行了少量的错误修复。LTS 内核维护者 Greg 说,“现在它已正式报废。请不要再使用这个版本的内核。”Linux 上游仍在维护的 LTS 内核有 Linux 4.19、5.4、5.10、5.15、6.1,以及最近作为 2023 LTS 内核的 6.6。Linux 4.19 将于今年年底到期,5.4 将于 2025 年到期,其余内核将于 2026 年底到期,只有两年支持期。

(插图:DA/20d496b2-c29c-433f-8a7e-3b2c7ddf2828)

消息来源:Phoronix

老王点评:随着这些支持六年的内核逐渐落幕,以后的内核也就支持两年。

13 款最佳开源 ChatGPT 替代品

正在寻找开源的 ChatGPT 替代方案?我们已经为你挑选了最优选项,供你一览。

ChatGPT 是 OpenAI 推出的一个强大的生成式 AI 工具。你只需要以对话形式键入文本提示,它就会给出详细的回应。

虽然它并非无懈可击,但无可否认,有时它表现得就是那么得力。然而,无论你如何利用它,不可更改的事实是,它并非开源方案。

作为一种专用选项,它并非在所有方面都有优势。那么,何处可以寻找 ChatGPT 的开源替代品呢?让我为你揭晓自然开源的优秀 ChatGPT 替代方案。

? 并非所有 ChatGPT 的替代品都有统一的运作方式。有些是为开发者设计的,让他们在其上打造属于自己的聊天机器人。还有一些则提供了供你测试使用的机器人或演示。

搜寻开源 ChatGPT 替代品的理由

依赖任何单一的服务对消费者来说都不是好事。对于 ChatGPT 来说也是如此。

除此之外,以下是我们需要寻找开源 ChatGPT 替代品的原因:

  • 掌握我们的数据处理方式,实现透明化。
  • 可以选择自己构建解决方案,节省了成本。
  • 按需定制 ChatGPT 替代方案。
  • 你不再受制于某个公司的规定。开源解决方案可以根据您的需求灵活变化。
  • 对开源项目的贡献会对全球每个人产生影响。

我并不是在此表明 ChatGPT 的功能不足,或者你应当放弃使用。然而,以“我们”用户的角度来看,长期而言,更应该从开源的替代品中获得更多收益。

现在,就让我们开始探寻最佳开源的 ChatGPT 方案吧。

❌ 并非所有的选项都可用于商业用途。你需要特别留意使用某些机器人时的相关规定。

1、OpenChatKit

OpenChatKit 是由 Together 开发的全功能 ChatGPT 替代品。

该公司起初与 LAION(主导了 稳定扩散 Stable Diffusion )等研究机构共同合作,构建出一个训练数据集。在撰写本文时,它搭载了 RedPajama 模型,这是目前最大的开源 AI 模型之一。

你可以试验看看它是否适合你的使用需求,并在其 GitHub 页面 进一步探索更多技术细节。

2、ChatRWKV

ChatRWKV 是由 循环神经网络 Recurrent Neural Network (RNN) 语言模型驱动的开源替代品。

你可以在 Huggingface 发现它的演示。 GitHub 页面 列出了关于其代码库、技术细节,以及最新版本预览的所有信息。

开发者和商业公司都可以利用 ChatRWKV 构建他们的聊天机器人。

3、ColossalChat

Colossal AI 是一个开源项目,目标是帮助你克隆 AI 模型,并打造出满足你需求的 ChatGPT 类似的平台。

ColossalChat 是以此项目为基础打造的聊天机器人。然而遗憾的是,在本文撰写之时,它的演示暂未上线。

你可以在 GitHub 上探索其源代码。

4、KoboldAI

KoboldAI 是一个类似于 ChatGPT 的 AI,主要以浏览器前端的形式为写作提供辅助。虽然它具备聊天机器人模式,但它首要的设计目标是作为专为小说设计的 AI 写作助手。

提供了各种各样的模型,你可以在 Google Colab 上轻松运行它。

它支持各种模式来优化你的写作,让你有更大的发挥空间。你可以在其 GitHub 页面 中详细了解它的所有信息。

5、GPT4ALL

GPT4ALL 是一个令人感兴趣的开源项目,旨在为你提供可在任何地方运行的聊天机器人。

没错,你可以在自己的 CPU 上本地运行它,而且几乎所有其他型号的 GPU 都能支持。

你需要做的就是安装它的桌面应用程序(聊天客户端),然后就能开始使用了。如果对其制作过程感兴趣,可以查看其 GitHub 页面

6、HuggingChat

听起来熟悉吗?嗯,Huggingface 的平台被其他 AI 模型和聊天机器人用作展示他们的演示。

HuggingChat 正是 Huggingface 打造的开源的 ChatGPT 替代品,使用了社区中最优秀的 AI 模型来实现聊天机器人的功能。

你可以 尝试一下,并在 源代码 中深入探讨更多细节。如果有特别的需求,可以选择搭配不同的后端来使用聊天应用。

7、Koala

EasyLM 的研发成果 Koala 是一款可尝试本地运行的聊天机器人,整个架构基于 LLaMA 数据集完成。

你可以参阅其 官方博客 来深入了解细节。

目前,演示版本还未上线,但是,你可以参考其 文档 在本地部署,进行试运行并测试。

8、Vicuna

Vicuna 是另一款开源聊天机器人,训练基础依然是 LLaMA。根据开发者的说法,使用 GPT-4 作为评价标准,得出的结论是 Vicuna 的聊天体验接近 ChatGPT。

这是一个你可能想要验证的有趣声明。你可以选择 Vicuna 作为语言模型,并在 Chatbot Arena 进行尝试。

想要了解更多关于它的信息,你可以查阅其 官方博文

9、Alpaca-LoRA

Alpaca-LoRA 雄心勃勃,旨在使用低秩适应技术提供一种可在树莓派中运行的模型。

利用单个 RTX 4090 GPU,整个模型可在几小时之内完成训练。

此时,演示版本仍然未开放,但你可以在其 GitHub 页面 上寻找更多详情。

10、Dolly

Dolly 是另一个在 Databricks 机器学习平台上训练的语言模型,并已获得商业使用许可。

你可以在 GitHub 上查阅源代码,并在 Huggingface 上探究模型详情。

11、H2oGPT

H2oGPT 是专门为查询定制的,可以帮助你总结文档。在保证隐私的前提下,它允许你通过用户界面上传和查看文档。

同样,你可以像使用 ChatGPT 那样开始对话。对于 Windows 和 macOS,该项目提供了易于安装的程序。对于 Linux 系统,你可能需要通过 Docker 进行些许设置。

可以尝试该聊天机器人的 在线演示 来了解其运行情况,并在 GitHub 页面 查阅源代码。

12、Cerebras-GPT

Cerebras-GPT 呈现了通过大量参数训练的开源 GPT 类似模型。

它并没有提供聊天机器人服务。它的目标旨在提供精准且高效的开源模型供你使用。

获取模型详情,你可以访问 Hugging Face

13、OpenAssistant

OpenAssistant 的目标,是让每一个人都能访问类似于 ChatGPT 的聊天机器人。

有一段时间,他们通过演示版本收集了用户数据。现在,由于项目已由创作者标记为完成,演示版本不再开放。

但你可以利用该项目的成果,及其 源代码,在此基础上进行扩展。

总结

面向用户和开发者,ChatGPT 的开源替代品提供了各种各样的优点。你可以选择运用一个开源的聊天机器人,或者借助开源语言模型构建一个属于自己的聊天机器人。

无论你选择哪一个,只要遵守可用语言模型的政策,你都可以自由地修改和使用它以满足你的需求。

你最爱哪个开源的 ChatGPT 替代品呢?是否还有其他优秀的开源型 ChatGPT 项目,我们错过了但你特别喜欢的?请在下方评论区告诉我们你的想法。

(题图:DA/b757432f-56a4-4493-a109-1eca6c57c1fc)


via: https://itsfoss.com/open-source-chatgpt-alternatives/

作者:Ankush Das 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

硬核观察 #1244 美国讨论限制中国获取 RISC-V 技术

#1 美国讨论限制中国获取 RISC-V 技术

美国政府过去几个月一直在讨论限制中国获取 RISC-V 技术,认为中国利用 RISC-V 绕过了美国对华芯片出口管制。美国众议院建议成立一个跨部门政府委员会,研究 RISC-V 的潜在风险,讨论是否以及如何限制这项技术。据知情人士称,英国的 Arm 控股公司也在游说美国政府限制 RISC-V。但由于 RISC-V 架构是开源免专利的,限制中国使用 RISC-V 技术就如同类似限制中国使用开源的 Linux,基本上是不可能的。而负责 RISC-V 技术的非盈利组织的总部设在欧洲的瑞士。

(插图:DA/97b6e341-9416-4956-9b7b-56194a047ea3)

消息来源:《纽约时报》

老王点评:感谢开源,感谢开源精神。

#2 谷歌取消迁出其云服务的费用

长期以来,云计算服务提供商之间的转换成本一直备受抱怨,这些服务被讥讽为 “蟑螂旅馆”,让企业只能入住而不能退房。现在,谷歌正在采取措施改变这种状况。从即日起,该公司将取消对希望离开其云服务转而使用竞争对手服务的客户收取的费用。这一政策转变可能会迫使竞争对手亚马逊和微软也这样做。据分析,该公司希望监管机构转而关注它认为更大的问题:微软的限制措施使客户在某些情况下更难选择谷歌云。

(插图:DA/b40ef8e3-b652-424a-a6af-e14aa66e751c)

消息来源:彭博社

老王点评:现在的公有云并没有那么“公有”。

#3 Linux 内核 6.8 遭遇“可怕的性能回归”

Linus Torvalds 指出开发中的 Linux 6.8 “让我的空内核构建从 22 秒变成了 44 秒,也让完整内核的构建速度大大降低。”虽然 Linux 内核缺乏常见的、强大的持续集成,但对于代码编译速度减半这样的性能回归,还是相当令人惊讶,尤其是代码已经通过了 linux-next 等版本的测试。目前,看起来性能回归似乎是 CPUFreq 调度器调速器回归造成的。Linus 已经还原了一系列补丁,除非问题很快得到解决,这些补丁也将在主分支上被还原。

(插图:DA/f1a12349-d36d-4402-b33d-c4399d1990b0)

消息来源:Phoronix

老王点评:我觉得靠“眼睛多”来发现问题,不如用 CI 这样的程序更可靠。

2024 开年,LLUG 和你相约在武汉

Hi,Linuxer,2024 新年伊始,不知道你是否已经准备好迎接新的一年~ 2024 年,Linux 爱好者沙龙重新起航,这次,我们从黄鹤楼畔,大美武汉开始我们新的一年线下相见!

2024 年 1 月 20 日,我们在武汉未来科技城,一起相约在统信软件武汉分公司,聊聊新的一年,你对于 Linux 的期待、对于开源、开发等一切问题的想法和探索。

本次活动由 Linux 中国、龙蜥社区(OpenAnolis)、deepin(深度)社区、WHLUG、华中科技大学网安学院开源俱乐部联合主办,统信软件技术有限公司提供场地支持。

龙蜥社区(OpenAnolis)是国内的顶尖 Linux 发行版社区,我们希望在普及 Linux 知识的同时,也能让中国的 Linux 发行版,为更多人知晓,推动国产发行版的发展和进步。

议题 分享简介 分享内容 分享者
14:00~14:20 签到
14:20~14:30 《玲珑-容器技术在桌面程序分发中的应用》 简单介绍 Linux 内核提供的容器相关特性的用户态接口使用方法、非 root 用户使用 Linux 容器的限制,以及其他有趣的 Linux 容器技术细节,并以玲珑为例介绍容器技术在桌面应用程序分发和治理等方面的实际应用。 陈麟轩 / 统信软件技术有限公司高级研发工程师
14:30~15:00 《华科内核贡献团队开源实践分享》 在本次演讲中将分享如何带领开放华科原子开源俱乐部中的内核贡献团队,挖掘并修复 Linux 内核漏洞,并通过内部审核机制保障内核补丁正确性。 慕冬亮 / 华科开放原子开源俱乐部
15:00~15:45 《开源之旅:从学生到核心贡献者》 一名学生从零开始参与开源社区,到成为核心贡献者的经历与心得,探讨开源的意义,还有给开源新人的一些小建议 朱俊星 / 华中科技大学学生,开源爱好者,RustSBI、KCL Maintainer
15:45~16:20 《Anolis OS 优化 Virtio 协议增强网络性能实践分享》 作为云计算重要组成部分的虚拟网卡正面临着性能和功能的双重挑战。针对实际业务需求,我们制定了新的 virtio 标准,包括 Inner Hash、Virtio Checksum修复、NetDIM、Device Stats 等技术,在阿里云软硬件融合的智能网卡上进行了实践,将来会为用户提供更好的虚拟网卡使用体验。 衡琪 / 阿里云计算有限公司研发工程师,龙蜥社区高性能网络 SIG 成员
16:20~17:00 闪电演讲(短分享)
17:00~18:00 线下交流

主题演讲:《玲珑-容器技术在桌面程序分发中的应用》

陈麟轩 /统信软件技术有限公司高级研发工程师

简单介绍 Linux 内核提供的容器相关特性的用户态接口使用方法、非 root 用户使用 Linux 容器的限制,以及其他有趣的 Linux 容器技术细节,并以玲珑为例介绍容器技术在桌面应用程序分发和治理等方面的实际应用。

主题演讲:《华科内核贡献团队开源实践分享》

慕冬亮 / 华科开放原子开源俱乐部

在本次演讲中,将分享如何带领开放华科原子开源俱乐部中的内核贡献团队,挖掘并修复 Linux 内核漏洞,并通过内部审核机制保障内核补丁正确性。

主题演讲:《开源之旅:从学生到核心贡献者》

朱俊星 / 华中科技大学学生,开源爱好者,RustSBI、KCL Maintainer

一名学生从零开始参与开源社区,到成为核心贡献者的经历与心得,探讨开源的意义,还有给开源新人的一些小建议。

主题演讲:《Anolis OS 优化 Virtio 协议增强网络性能实践分享》

衡琪 / 阿里云计算有限公司研发工程师,龙蜥社区高性能网络 SIG 成员

作为云计算重要组成部分的虚拟网卡正面临着性能和功能的双重挑战。针对实际业务需求,我们制定了新的 virtio 标准,包括 Inner Hash、Virtio Checksum修复、NetDIM、Device Stats 等技术,在阿里云软硬件融合的智能网卡上进行了实践,将来会为用户提供更好的虚拟网卡使用体验。

闪电演讲

本次线下活动依旧保留闪电演讲环节,作为最受欢迎的线下活动,本次活动依旧继续举办闪电演讲。每位演讲者有 5 分钟时间参与现场活动,可以提前报名,也可即兴上台演讲。时间一满,马上结束~强制大家控制自己的分享时间,用最短的时间,向大家发出你的声音~

李伟光现场介绍 neovim 的使用

李伟光现场介绍 neovim 的使用

丰雷同学为大家分享了他对开源、编程方面的感悟

活动地点及到达信息

活动地点:武汉市江夏区高新大道999号未来科技城B3栋9楼-A901(琴台大剧院会议厅)

抵达方式:

  • 自驾:导航到「武汉市江夏区高新大道999号未来科技城 B3 栋」,按指引停车入园即可。
  • 公共交通:地铁:乘坐轨道交通 11 号线,「光谷七路」D口出站,步行 600 米到。

如果你因为有事,没办法来到线下,那也没问题,我们的活动也会在 Linux 中国视频号、Linux 中国 B 站、龙蜥 B 站、龙蜥钉钉群等开启同步直播。

当然,我们更希望你能亲自来到线下,和我们一起聊聊开源,聊聊技术~

活动报名地址:

Scribus 1.6.0 发布:一次包含新功能的大规模升级

Scribus 1.6.0 更新是新年伊始的良好开端!

Scribus 是一种非常流行的开源桌面出版(DTP)软件,用于制作从小册子、新闻简报、海报到广告牌等各种插图。

最近,Scribus 1.6.0 正式发布,成为我们在 2024 年报道的第一个开源工具更新。

请允许我向你展示它所提供的内容。

? Scribus 1.6.0:有什么新变化?

作为一个重要的稳定版本,Scribus 1.6.0 已经开发了相当长一段时间了,据开发人员称,它包含数千个全面的增强功能和修复

我们将看一下此版本的关键亮点

首先是用户界面,该界面经过全面改造,采用了新图标,支持明/暗模式,并增加了新的搜索功能,允许你搜索 Scribus 的特定功能。

此外,新的“ 焊接 Weld ”功能可以让你组合对象,然后在不分组的情况下移动这些对象。 符号 Symbol ”/克隆功能与 Adobe Illustrator 中的功能类似,可让你保持主对象(符号)与所有副本(克隆)的进度同步。

在图形方面,有一个名为“ 图片浏览器 Picture Browser ”的新插件,可以使所有图形文件的资源管理变得简单。它通过允许你标记它们并将它们放入图形集合中以便于访问来实现这一点。

此外,文件的导入/导出也得到了改进,Scribus 现在具有许多新的和改进的导入过滤器、支持 Krita 的 KRA 文件格式、改进了 PDF 导入等等。

关于文本,Scribus 1.6.0 配备了一些社区要求最多的功能,其中包括支持页脚和页尾注释文本变量文本垂直对齐窗体/窗口控制交叉引用

?️ 其他更改和改进

最后,你还应该了解一些其他值得注意的变化:

  • 渲染帧功能中添加了对 XeLaTeX 的支持。
  • 你现在可以使用新的基于 PDF 的输出预览
  • 现在可以以原生文件格式存储位图图像
  • 用于处理在线资源(例如字典)的资源管理器
  • 在 Linux 上,Scribus 首选项目录已移动到新位置,使其符合 XDG 标准

Scribus 1.6 适用于 Linux、BSDWindowsmacOS。你可以浏览 发行说明 来深入了解这个扩展版本。

? 下载 Scribus 1.6.0

获取最新 Scribus 的最直接方法是从其 SourceForge 页面 获取 Linux 版的 AppImage 文件。

但是,如果你正在寻找其他安装方法或获取源代码,你可以访问 官方网站

还有一个 Ubuntu 的 PPA,但是,在撰写本文时,那里还没有最新版本。

Scribus 1.6.0(SourceForge)

如果你需要使用 AppImage 文件的帮助,我们的 如何在 Linux 中使用 AppImage 指南应该会有很大帮助。

? 你对本次更新有何看法? 你是否期望添加到其中的所有改进?


via: https://news.itsfoss.com/scribus-1-6-release/

作者:Sourav Rudra 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

硬核观察 #1243 Linux 内核开发者再次讨论从 C 语言转换为现代 C++ 语言

#1 Linux 内核开发者再次讨论从 C 语言转换为现代 C++ 语言

Linux 内核主要由 C 代码和汇编代码构成,但 Linux 内核邮件列表已重启讨论,探讨未来将 Linux 内核的 C 代码转换为 C++ 的可能性。早在 2018 年,红帽工程师 大卫·豪威尔斯 David Howells 就提出了一组 45 个补丁,将内核转换为 C++。这将允许主线内核使用内联模板函数、内联重载函数、类继承以及其他目前 Linux 内核的 C 代码不支持的功能。但该讨论没有进行下去,最终这些补丁在 Linux 内核邮件列表上停留了六年。昨天,长期从事 Linux 开发的 彼得·安文 H. Peter Anvin 用一篇长文重启了这个讨论。他认为,“C++ 已经有了长足的发展。……C++ 终于 ‘长大’ 了,对于操作系统内核所体现的嵌入式编程而言,它是一种更好的 C 语言。……我们最近提出的许多针对 gcc 扩展的要求,其实在标准 C++ 中很容易实现。”Linux 内核转向 C++ 的阻力之一是 Linus Torvalds 过去一直积极反对 C++。

(插图:DA/3ebf6893-7ebf-4901-8509-16c64ce7982d)

消息来源:Phoronix

老王点评:相关各方的讨论非常激烈,但无论如何,任何决定都应该通过广泛的讨论和不断的修正来进行。

#2 新设备允许用户用舌头滚动屏幕

一种名为 MouthPad^ 的新设备可以让用户只用舌头就能滚动浏览智能手机,从而使触摸屏不再需要双手。它是安装在口腔顶部的类似于固定器的触控板,可以感知舌头的运动,让用户可以用舌头轻扫或点击来滚动、打字、打电话甚至下棋 —— “它就是你嘴里的鼠标”。该设备的目的是为残疾人士,尤其是手部残疾或瘫痪人士提供帮助。

(插图:DA/ec5efb61-7b22-41ed-97e3-b7d743a78798)

消息来源:NBC News

老王点评:目的是很好,但总感觉有点奇怪。

#3 Brave Search 现在可以提供 AI 代码搜索

Brave 推出了 CodeLLM,这是一款集成到其搜索引擎中的人工智能工具,可提供编程查询结果,包括代码片段、逐步解释和引用。CodeLLM 是免费的,现在已集成到 Brave Search 中,用户无需切换应用即可访问。CodeLLM 建立在使用文本提示生成代码的 LLM Mixtral 之上。

(插图:DA/2cacb37f-574e-41ea-9d4e-2a260149f039)

消息来源:Tech Crunch

老王点评:以后程序员都不需要在 SO 上搜索代码了。

现在你可以在 Wayland 上运行 Shutter 了!

你无需在 Wayland 上放弃使用 Shutter!

哎,我一直在担心有一天我得切换到只支持 Wayland 的 Linux 发行版,结果发现 Shutter 不工作。

它是 Linux 中用于捕捉和编辑 屏幕截图的最佳工具 之一,我每天都会使用它来为这里和其他地方的工作捕捉各种截图。

我知道 GNOME 的屏幕截图工具已经有了很大的进步,但是就像人们所说的,“旧习难改”。所以,当我发现有一种方法可以在所有的 Wayland 上运行 Shutter 时,我自然是相当兴奋。

请跟随我一起了解这个精彩的开源项目。这个项目由一位热心的 Shutter 爱好者发起,他的目标就是让 Shutter 能在配备 Wayland 的发行版上顺利运行。

在 Wayland 上使用 Shutter:有何期待?

这个项目由来自意大利的 IT 开发者 Maurizio 发起,他热衷于 Linux,因为他不能接受 Shutter 无法在他的 Ubuntu 系统上正常运行,因此这个项目应运而生。

因此,他复刻了 Shutter 的代码仓库,并利用 GNOME 的屏幕截图工具 的命令行工具进行开发,同时尽力保持用户界面和以往的操作习惯不变

如下图所示,这个项目与你从 Shutter 那里期待的体验几乎无二,你可以在界面上发现所有熟悉的选项。在运行着 Ubuntu 23.10 的虚拟机上用这个工具截图时,我并未感到有什么两样。

我还发现,按只截图窗口或选取特定区域也变得容易了。只是那个通常用来截图特定窗口或桌面的下拉菜单似乎并未起到应有的功能,反而变得有点挪作他用。

? 这是我在 Ubuntu 23.10 的 Wayland 上进行选区捕捉的结果。

如果你想截图你系统中特定的桌面空间,你需要切换到那个桌面,使 Shutter 转到同一屏幕,然后使用“ 桌面 Desktop ”选项进行截图。

对于只截图窗口也是类似,只要将应用和 Shutter 切到同一屏幕,使用“ 窗口 Window ”选项就能够截取窗口的截图。

至此已经足够了。我真心期待 Shutter 的开发更上层楼,希望我们能看到此类改进被融入原有的项目中。

? 如何下载运行于 Wayland 的 Shutter?

在开始下载和安装之前,请确保你已经彻底卸载了你之前安装的任何版本的 Shutter。

接下来,你有两种获取这个 Shutter 变体的方法可选。第一种方法是直接访问它的 GitHub 仓库,下载提供的 “.deb” 文件。

在 Wayland 上的 Shutter(GitHub)

而我更为推荐的是第二种方法,尤其适用于 Ubuntu 23.10 或者更高的版本,因为它可以自动处理所有的依赖关系。你只需要运行以下的命令即可:

setfacl -m u:_apt:rx .
wget https://github.com/mvivarelli/shutter-on-wayland/raw/master/shutter-on-wayland_0.99.4-6_all.deb
sudo apt -f install ./shutter-on-wayland_0.99.4-6_all.deb

如果安装后你在启动 Shutter 时遇到了卡顿或者延迟的问题,那么你可以通过重启你的系统来解决此类问题。

? 对于这个项目,你有什么样的想法呢?你会考虑使用它吗?


via: https://news.itsfoss.com/shutter-wayland-linux/

作者:Sourav Rudra 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出