mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-01 21:50:13 +08:00
commit
04af29179e
@ -0,0 +1,79 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (chenmu-kk)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-12497-1.html)
|
||||
[#]: subject: (Developer happiness: What you need to know)
|
||||
[#]: via: (https://opensource.com/article/19/2/developer-happiness)
|
||||
[#]: author: (Bart Copeland https://opensource.com/users/bartcopeland)
|
||||
|
||||
开发者的福音:你需要知道的事
|
||||
======
|
||||
|
||||
> 开发者需要快速编程的工具和自由,而不会因为合规性和安全性所困扰。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202008/07/214051ki2zitovo8zzynco.jpg)
|
||||
|
||||
一个人需要合适的工具来完成工作。比如说没有比修车修到一半,却发现你没有完成工作的专业工具更沮丧的事情了。同样的道理也适用在开发者身上:你需要工具来发挥你的才能,而不会因为合规性和安全性的需求打断你的工作流程,这样你才可以更快速地编码。
|
||||
|
||||
根据 ActiveState 的研究《[2018 年开发者调查:开源运行时的痛苦][1]》显示,超过一半的开发人员(具体为 51%),每天只需要花费 1-4 小时的时间进行编程。换句话说,大多数开发人员花费不到一半的时间编程。根据调查,一半的开发人员认为安全是他们最大的担忧之一,但是由于公司政策方面的原因,67% 的开发人员选择在编程时不添加新的语言。
|
||||
|
||||
结果是开发人员不得不投入更多的精力在非编码的活动上,例如在构建软件和语言之后检查软件的安全性和合规性标准。而且由于公司政策的原因,他们无法选择适合的开发工具或语言。他们的满意度会下降,同时风险提高。
|
||||
|
||||
因此,开发人员无法将时间投入在高价值的工作上。这会带来额外的商业风险,因为他们的软件上市时间变慢了,并且公司因为没有授权开发人员在不受公司政策影响的前提下决定“最佳的”技术的能力,从而增加了技术债务。
|
||||
|
||||
### 纳入安全性和合规性的工作流程
|
||||
|
||||
我们如何解决这个问题呢?一种方式是通过四个简单的步骤将安全性和合规性的工作流程集成到软件开发中:
|
||||
|
||||
#### 1、集中你的力量
|
||||
|
||||
获得所有相关人员的支持,这是一个经常被遗忘但却至关重要的第一步。确保考虑到了广泛的利益相关者,包括:
|
||||
|
||||
* 开发运维(DevOps)
|
||||
* 开发人员
|
||||
* 信息安全
|
||||
* 合法/合规
|
||||
* IT 安全
|
||||
|
||||
利益相关者想要了解相关的商业利益,因此要为消除软件构建后的安全和合规性检查点提供坚实的理由。你可以在构建你的商业案例中考虑以下任何一个(或者全部)因素:节省时间、机会成本和开发人员生产力。在开发流程中,你也可以通过集成安全性和合规性来避免语言的改造。
|
||||
|
||||
#### 2、寻找可信赖的资源
|
||||
|
||||
接下来,选择可使用的可信资源,以及他们的许可证和安全要求。考虑到如下相关信息:
|
||||
|
||||
* 基于环境或应用程序类型的使用限制以及每种语言的版本控制
|
||||
* 允许哪些开源组件,例如,特定的程序包
|
||||
* 哪种环境类型可以使用哪种许可证(例如,研究与生产)
|
||||
* 安全级别的定义、可接受的脆弱性风险等级、什么样的风险级别会触发一个措施,这个措施是什么并且谁来负责它的执行
|
||||
|
||||
#### 3、从一开始就纳入安全性和合规性
|
||||
|
||||
纳入安全性和合规性的工作流程的结果是,它最终将安全性和合规性融入到代码的第一行。它消除了公司政策的拖累,因为你是按照规范进行编码,而不是必须事后解决问题。但要做到这一点,请考虑在构建代码时采用自动扫描代码的机制,以及使用无代理监控你的运行时代码。你可以节省时间,还可以通过编程实施策略来确保整个组织的合规性。
|
||||
|
||||
#### 4、监控、报告和更新
|
||||
|
||||
出现新的漏洞,新的补丁和版本也会出现。所以,将代码部署到生产中以及运行代码时,需要考虑安全性和合规性。你需要知道哪些(如果有的话)代码存在风险以及该代码在何处运行。所以,部署和运行代码的过程应该包括监视、报告和更新生产中的代码。
|
||||
|
||||
通过一开始就在你的软件开发过程中集成安全性和合规性,你还可以在部署后跟踪代码的运行位置,并在新的威胁出现时获得警报。你也能追踪当你的应用程序何时受到攻击,并通过自动执行软件策略做出响应。
|
||||
|
||||
如果你的软件开发过程中已经引入了安全性和合规性的工作流程,你将会提高你的生产率。你将能够通过增加编码时间、提高安全性和稳定性、以及在维护和发现安全性和合规性的威胁方面节省的成本和时间来衡量价值。
|
||||
|
||||
### 集成所带来的幸福
|
||||
|
||||
如果你不开发和更新软件,你的组织将无法前进。开发人员是公司成功的关键,这意味着他们需要快速编写代码的工具和自由。尽管合规性和安全性至关重要,但你不能让这个需求阻碍你的发展。开发人员显然很担心安全性,因此最好的办法就是“左移”,从一开始就集成安全性和合规性的工作流程。你将可以做更多的事情,在第一次就可以完成,而花费更少的时间进行代码更新。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/2/developer-happiness
|
||||
|
||||
作者:[Bart Copeland][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[chenmu-kk](https://github.com/chenmu-kk)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/bartcopeland
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.activestate.com/company/press/press-releases/activestate-developer-survey-examines-open-source-challenges/
|
@ -0,0 +1,82 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11733-1.html)
|
||||
[#]: subject: (Fedora Desktops – Memory Footprints)
|
||||
[#]: via: (https://fedoramagazine.org/fedora-desktops-memory-footprints/)
|
||||
[#]: author: (Troy Dawson https://fedoramagazine.org/author/tdawson/)
|
||||
|
||||
Fedora 上的桌面环境内存占用测试
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Fedora 中有 40 多种桌面环境(DE)。每种桌面环境都有自己的优点和缺点。通常,根据功能、外观和其它品质,选择桌面是一件非常个人的偏好。但有时,你选择的桌面环境还会受到硬件限制。
|
||||
|
||||
本文旨在帮助人们根据桌面环境占用的基准内存来比较 Fedora 桌面环境。为了缩小范围,我们仅查看具有正式 Fedora Live 镜像的桌面环境。
|
||||
|
||||
### 安装与系统配置
|
||||
|
||||
每个桌面环境都安装在自己的 KVM 虚拟机上。每个虚拟机都有 1 个 CPU、4GB 内存、15GB virtio 固态磁盘,以及 RHEL 8.0 kvm 上所有其他标准配置。
|
||||
|
||||
用于安装的镜像是标准的 Fedora 31 Live 镜像。对于 GNOME,该镜像是 “Fedora 工作站”。对于其它桌面,使用了相应的 Spin 版本。未对“<ruby>糖葫芦<rt>Sugar On A Stick</rt></ruby>”(SOAS)进行测试,因为它不容易安装到本地驱动器上。
|
||||
|
||||
用 Live CD 启动虚拟机,然后选择“安装到硬盘”。在安装过程中,仅使用默认值。创建了一个 root 用户和一个普通用户。安装并重新启动后,live 镜像已确认不在虚拟 CDROM 中。
|
||||
|
||||
每个桌面环境的设置都没有改动。它们每个都以 Live CD 环境中默认的设置运行。每个桌面环境都是通过普通用户登录的。打开了一个终端,在每台虚拟机中都使用 `sudo` 运行了 `dnf -y update`。在更新后,在该 sudo 终端中,每台虚拟机都运行 `/sbin/shutdown -h now` 以关闭。
|
||||
|
||||
### 测试方式
|
||||
|
||||
每台机器都已启动。桌面环境已通过普通用户登录。打开了三个桌面终端。xterm 从未使用过,始终用的是该桌面环境的终端,例如 konsole。
|
||||
|
||||
在一个终端中,启动 `top` 并按下 `M`,以显示按内存排序的进程。在另一个终端中,一个简单的 `while` 循环每 30 秒显示一次 `free -m`。第三个终端闲置。
|
||||
|
||||
然后,我等待了 5 分钟。这样就可以让所有启动的服务都启动完成。我记录了最终的 `free` 结果,以及 `top` 中最终的前三名内存使用者。
|
||||
|
||||
### 结果
|
||||
|
||||
* Cinnamon
|
||||
* 使用了624 MB
|
||||
* cinnamon 4.8% / Xorg 2.2% / dnfdragora 1.8%
|
||||
* GNOME
|
||||
* 使用了 612 MB
|
||||
* gnome-shell 6.9% / gnome-software 1.8% / ibus-x11 1.5%
|
||||
* KDE
|
||||
* 使用了 733 MB
|
||||
* plasmashell 6.2% / kwin\_x11 3.6% / akonadi\_mailfil 2.9%
|
||||
* LXDE
|
||||
* 使用了 318 MB
|
||||
* Xorg 1.9% / nm-applet 1.8% / dnfdragora 1.8%
|
||||
* LXQt
|
||||
* 使用了 391 MB
|
||||
* lxqt-panel 2.2% / pcmanfm-qt 2.1% / Xorg 2.1%
|
||||
* MATE
|
||||
* 使用了 465 MB
|
||||
* Xorg 2.5% / dnfdragora 1.8% / caja 1.5%
|
||||
* XFCE
|
||||
* 使用了 448 MB
|
||||
* Xorg 2.3% / xfwm4 2.0% / dnfdragora 1.8%
|
||||
|
||||
|
||||
|
||||
### 结论
|
||||
|
||||
我会让数字说明一切。
|
||||
|
||||
请记住,这些数字来自默认的 Live 安装环境。如果删除或添加服务和功能,则内存使用量将发生变化。但是,如果要根据内存消耗确定桌面环境,这是一个很好的基准。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/fedora-desktops-memory-footprints/
|
||||
|
||||
作者:[Troy Dawson][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/tdawson/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/11/desktop-memory-footprint-816x346.jpg
|
@ -0,0 +1,90 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (Morisun029)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11730-1.html)
|
||||
[#]: subject: (5 interview questions every Kubernetes job candidate should know)
|
||||
[#]: via: (https://opensource.com/article/19/12/kubernetes-interview-questions)
|
||||
[#]: author: (Jessica Repka https://opensource.com/users/jrepka)
|
||||
|
||||
每个 Kubernetes 应聘者应该知道的 5 个面试题
|
||||
======
|
||||
|
||||
> 如果你是要面试 Kubernetes 相关职位的应聘者,这里给出了要提问的问题以及这些问题的重要性。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201912/31/101229mmjumsesrzhh6rhk.jpg)
|
||||
|
||||
面试对面试官及候选人来说都很不容易。最近,我发现面试 Kubernetes 相关工作的候选人似乎尤其困难。为什么呢?一方面,很难找到可以回答他们问题的人。而且,不管回答者回答的怎样,都很难确定他们是否有正确的经验。
|
||||
|
||||
跳过这个问题,让我们来看看面试 [Kubernetes][2] 求职者时应该提问的一些问题。
|
||||
|
||||
### Kubernetes 是什么?
|
||||
|
||||
我发现这个问题是面试中最好的问题之一。我经常听到有人说“我用 Kubernetes 工作”,但是当我问道“Kubernetes 是什么”时,从来都没有得到过一个满意答案。
|
||||
|
||||
我最喜欢 [Chris Short][3] 给出的答案:“Kubernetes 就是带有一些 YAML 文件的 API 。”
|
||||
|
||||
虽然他的回答没有错,但我会给你更详细的解释。 Kubernetes 是一个可移植容器的编排工具,用于自动执行管理、监控、扩展和部署容器化应用程序。
|
||||
|
||||
我认为“用于部署容器化应用程序的编排工具”这个回答可能与你期望的答案差不多了。能回答出这个,我觉得已经很不错了。尽管许多人认为 Kubernetes 做了更多更棒的工作,但总的来说,它为其核心功能——容器编排——添加了许多 API。
|
||||
|
||||
我认为,这是你在面试中可以提问的最好的问题之一,因为它至少证明了候选人是否知道 Kubernetes 是什么。
|
||||
|
||||
### Kubernetes 的节点和 Pod 有什么区别?
|
||||
|
||||
该问题揭示了候选人对 Kubernetes 复杂性的初步了解。它将面试对话转换为对体系结构的概述,并可能导向许多有趣的后续细节问题。我已经听到了无数次关于该问题的错误解释了。
|
||||
|
||||
[节点][4] 是工作计算机。该计算机可以是虚拟机(VM)或物理计算机,具体取决于你是在虚拟机监控程序上运行还是在裸机上运行。该节点包含用于运行容器的服务,包括 kubelet、kube-proxy 和容器运行时。
|
||||
|
||||
[Pod][5] 包括:一个或多个**容器**、具有共享**网络**和**存储**,以及有关如何运行一起部署的容器的**规范**。这四个细节都很重要。更进一步,职位申请人应从技术层面解释,Pod 是 Kubernetes 可以创建和管理的最小可部署单元,而不是容器。
|
||||
|
||||
对于这个问题,我听到的最好的简答是:“节点是计算机,而 Pod 是容器运行于其中的东西。”这个区别很重要。Kubernetes 管理员的大部分工作是知道什么时间要部署什么,而节点的部署成本可能非常非常高,具体取决于它们的运行位置。我不希望有人一遍又一遍地部署节点,他们需要做的就是部署一堆 Pod。
|
||||
|
||||
### kubectl 是什么?(你怎么发音?)
|
||||
|
||||
这个问题是我优先级较高的问题之一,但可能与你和你的团队无关。在我的团队中,我们不会使用图形化界面来管理 Kubernetes 环境,我们使用命令行操作。
|
||||
|
||||
那么什么是 [kubectl][6]?它是 Kubernetes 的命令行界面。你可以从该界面获取并设置任何内容,从收集日志和事件到编辑部署环境和机密文件。随机提问候选人关于如何使用此工具对测试候选人对 kubectl 的熟悉度是很有帮助的。
|
||||
|
||||
你是怎么读的?好吧,你随便吧(对此有很大的分歧),但是我很高兴向你介绍我朋友 [Waldo][7] 的精彩视频演示。
|
||||
|
||||
### 命名空间是什么?
|
||||
|
||||
在多次面试中,我都没有得到关于这个问题的答案。我不确定在其他环境中使用的命名空间是否会在我所在的团队经常使用。我在这里给出一个简短的答案:命名空间是 Pod 中的虚拟集群。这种抽象可以使你将多个虚拟集保留在多个环境中以此来进行隔离。
|
||||
|
||||
### 容器是什么?
|
||||
|
||||
了解 Pod 中正在部署的内容总是有帮助的,因为如果都不知道其中部署的是什么,何谈部署?容器是打包代码及其所有依赖项的软件的标准单元。我收到了两个可以接受的答案,其中包括:a)精简的操作系统镜像,以及 b)在受限的操作系统环境中运行的应用程序。如果你可以叫得出使用 [Docker][8] 以外的其他容器的编排软件(例如你最喜欢的公共云的容器服务),则可以得到加分。
|
||||
|
||||
### 其他问题
|
||||
|
||||
如果你想知道为什么我没有在此问题列表中添加更多问题,那么我可以给出一个简单的答案:我所列出的这些问题是在面试候选人时应了解的最基本的问题。接下来的问题应该是基于具体的团队、环境及组织。当你仔细研究这些内容时,尝试寻找有关技术如何相互作用的有趣问题,以了解人们如何应对基础架构挑战。考虑一下你的团队最近遇到的挑战(中断),要求逐步进行部署,或者考虑改善团队积极想要改进的策略(例如减少部署时间)。问题越不抽象,对面试后真正重要的技能的询问就越多。
|
||||
|
||||
没有两个环境是完全相同的。这也适用于面试中。我在每次面试中都会混合提问。我也有一个测试面试者的小环境。我经常发现回答问题是最容易的部分,而你所做的工作才是对你的真正的考验。
|
||||
|
||||
我给面试官的最后一点建议是:如果你遇到一个很有潜力但没有经验的候选人时,请给他们一个证明自己的机会。如果当初没有人看到我的潜力,没有给我机会的话,我不会拥有今天的知识和经验。
|
||||
|
||||
还有哪些重要的问题?请留言告诉我们。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/kubernetes-interview-questions
|
||||
|
||||
作者:[Jessica Repka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[Morisun029](https://github.com/Morisun029)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jrepka
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/collab-team-pair-programming-code-keyboard.png?itok=kBeRTFL1 (Pair programming)
|
||||
[2]: https://kubernetes.io/
|
||||
[3]: https://twitter.com/ChrisShort
|
||||
[4]: https://kubernetes.io/docs/concepts/architecture/nodes/
|
||||
[5]: https://kubernetes.io/docs/concepts/workloads/pods/pod/
|
||||
[6]: https://kubernetes.io/docs/reference/kubectl/kubectl/
|
||||
[7]: https://opensource.com/article/18/12/kubectl-definitive-pronunciation-guide
|
||||
[8]: https://opensource.com/resources/what-docker
|
||||
[9]: https://enterprisersproject.com/article/2019/2/kubernetes-job-interview-questions-how-prepare
|
@ -1,20 +1,22 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11728-1.html)
|
||||
[#]: subject: (Customize your Linux desktop with KDE Plasma)
|
||||
[#]: via: (https://opensource.com/article/19/12/linux-kde-plasma)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
使用 KDE Plasma 定制 Linux 桌面
|
||||
======
|
||||
本文是 24 天 Linux 桌面特别系列的一部分。如果你认为没有太多机会进行自定义桌面,KDE Plasma 可能适合你。
|
||||
|
||||
> 本文是 24 天 Linux 桌面特别系列的一部分。如果你认为没有太多自定义桌面的需要,KDE Plasma 可能适合你。
|
||||
|
||||
![5 pengiuns floating on iceburg][1]
|
||||
|
||||
KDE 社区的 Plasma 桌面是开源桌面中的巅峰之作。KDE 很早就进入了 Linux 桌面市场,但是由于它的 基础 Qt 工具包当时没有完全开放的许可证,因此才有 [GNOME][2] 桌面。在此之后,Qt 开源了,并且 KDE(及其衍生产品,例如 [Trinity桌面][3])开始蓬勃发展。
|
||||
KDE 社区的 Plasma 桌面是开源桌面环境中的巅峰之作。KDE 很早就进入了 Linux 桌面环境市场,但是由于它的基础 Qt 工具包当时没有完全开放的许可证,因此才有 [GNOME][2] 桌面。在此之后,Qt 开源了,并且 KDE(及其衍生产品,例如 [Trinity 桌面][3])开始蓬勃发展。
|
||||
|
||||
你可能会在发行版的软件仓库中找到 KDE 桌面,或者可以下载并安装将 KDE 作为默认桌面的发行版。在安装之前,请注意,KDE 提供了完整、集成且强大的桌面体验,因此会同时安装几个 KDE 应用。如果你已经在运行其他桌面,那么将发现有几个冗余的应用(两个 PDF 阅读器、多个媒体播放器、两个或多个文件管理器,等等)。如果你只想尝试而不是一直使用 KDE 桌面,那么可以在虚拟机,如[GNOME Boxes][4]中安装基于 KDE 的发行版,也可以尝试使用可引导的操作系统,例如 [Porteus][5]。
|
||||
你可能会在发行版的软件仓库中找到 KDE 桌面,或者可以下载并安装将 KDE 作为默认桌面的发行版。在安装之前,请注意,KDE 提供了完整、集成且强大的桌面体验,因此会同时安装几个 KDE 应用。如果你已经在运行其他桌面,那么将发现有几个冗余的应用(两个 PDF 阅读器、多个媒体播放器、两个或多个文件管理器,等等)。如果你只想尝试而不是一直使用 KDE 桌面,那么可以在虚拟机,如 [GNOME Boxes][4] 中安装基于 KDE 的发行版,也可以尝试使用可引导的操作系统,例如 [Porteus][5]。
|
||||
|
||||
### KDE 桌面之旅
|
||||
|
||||
@ -22,15 +24,15 @@ KDE 社区的 Plasma 桌面是开源桌面中的巅峰之作。KDE 很早就进
|
||||
|
||||
![KDE Plasma desktop][7]
|
||||
|
||||
但是,使 KDE 与众不同的是,你几乎可以更改任何想要的东西。Qt 工具包可以以令人惊讶的方式分割和重新排列,这意味着你实质上可以使用 KDE 的部件作为基础来设计自己的桌面。桌面行为的可用设置也很多。KDE 可以充当标准桌面,平铺窗口管理器以及两者之间的任意形式。你可以通过窗口类、角色、类型、标题或它们的任意组合来创建自己的窗口规则,因此,如果希望特定应用的行为不同于其他行为,那么可以创建全局设置的例外。
|
||||
但是,使 KDE 与众不同的是,你几乎可以更改任何想要的东西。Qt 工具包可以以令人惊讶的方式分割和重新排列,这意味着你实质上可以使用 KDE 的部件作为基础来设计自己的桌面。桌面行为的可用设置也很多。KDE 可以充当标准桌面、平铺窗口管理器以及两者之间的任意形式。你可以通过窗口类、角色、类型、标题或它们的任意组合来创建自己的窗口规则,因此,如果希望特定应用的行为不同于其他行为,那么可以创建全局设置的例外。
|
||||
|
||||
此外,它还有丰富的小部件集合,使你可以自定义与桌面交互的方式。它有一个类似 GNOME 的全屏应用启动器,一个类似 Unity 的 dock 启动器和仅有图标的任务栏,一个传统的任务栏。你可以在屏幕的任何边缘上创建和放置面板。
|
||||
此外,它还有丰富的小部件集合,使你可以自定义与桌面交互的方式。它有一个类似 GNOME 的全屏应用启动器,一个类似 Unity 的 dock 启动器和仅有图标的任务栏,以及一个传统的任务栏。你可以在屏幕的任何边缘上创建和放置面板。
|
||||
|
||||
![A slightly customized KDE desktop][8]
|
||||
|
||||
实际上,它有太多的自定义项了,因此 KDE 最常见的批评之一是它的_太过可定制化_,所以请记住,自定义项是可选的。你可以在默认配置下使用 Plasma 桌面,并仅在你认为必要时逐步进行更改。Plasma 桌面配置选项最重要的不是它们的数目,而是它们容易发现和直观,它们都在系统设置应用或者右键单击中。
|
||||
实际上,它有太多的自定义项了,因此 KDE 最常见的批评之一是它的*太过可定制化*,所以请记住,自定义项是可选的。你可以在默认配置下使用 Plasma 桌面,并仅在你认为必要时逐步进行更改。Plasma 桌面配置选项最重要的不是它们的数目,而是它们容易发现和直观,它们都在系统设置应用或者右键单击中。
|
||||
|
||||
事实是,在 KDE 上,几乎绝不会只有一种方法可以完成任何给定的任务,并且它的用户将其视为其最大的优势。KDE 中没有隐含的工作流,只有默认的。并且可以更改所有默认设置,直到你需要桌面做的成为你的习惯。
|
||||
事实是,在 KDE 上,几乎绝不会只有一种方法可以完成任何给定的任务,并且它的用户将这个视为其最大的优势。KDE 中没有隐含的工作流,只有默认的。并且可以更改所有默认设置,直到你需要桌面做的成为你的习惯。
|
||||
|
||||
### 一致性和集成
|
||||
|
||||
@ -42,8 +44,6 @@ KDE 社区以一致性和集成为荣,出色的开发人员、社区管理以
|
||||
|
||||
KDE 适合所有人。使用其默认设置可获得流畅、原始的桌面体验,或对其进行自定义以使其成为自己专属。它是一个稳定、有吸引力且强大的桌面环境,可能有你想要在 Linux 完成要做的事的一切。
|
||||
|
||||
KDE 最初代表 Kool Desktop Environment,但现在被许多人称为 K Desktop。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/linux-kde-plasma
|
||||
@ -51,7 +51,7 @@ via: https://opensource.com/article/19/12/linux-kde-plasma
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
@ -67,4 +67,4 @@ via: https://opensource.com/article/19/12/linux-kde-plasma
|
||||
[8]: https://opensource.com/sites/default/files/uploads/advent-kde-dock.jpg (A slightly customized KDE desktop)
|
||||
[9]: https://kde.org/applications/
|
||||
[10]: https://opensource.com/life/16/5/how-use-digikam-photo-management
|
||||
[11]: https://opensource.com/sites/default/files/uploads/advent-kde.jpg (KDE on Porteus)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/advent-kde.jpg (KDE on Porteus)
|
@ -0,0 +1,113 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11732-1.html)
|
||||
[#]: subject: (App Highlight: Falkon Open Source Web Browser from KDE)
|
||||
[#]: via: (https://itsfoss.com/falkon-browser/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
应用推荐:来自 KDE 的 Falkon 开源 Web 浏览器
|
||||
======
|
||||
|
||||
> Falkon 是基于 QtWebEngine 的 Web 浏览器,它以前称为 Qupzilla。在本周的“应用推荐”中,我们来看看这个开源软件。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201912/31/105027tblfjlzn9xx8keud.png)
|
||||
|
||||
### Falkon:适用于 Linux(和 Windows)的开源 Web 浏览器
|
||||
|
||||
![][1]
|
||||
|
||||
首先,[Falkon][2] 并不是一款新的 Web 浏览器。它自 2010 年以来一直在开发中,但被称为 Qupzilla。2017 年,QupZilla 移到 KDE 旗下,并更名为 Falkon。处于 KDE 旗下意味着项目会按照 KDE 标准积极维护。
|
||||
|
||||
它使用 [QtWebEngine][3] 渲染引擎,这是 Chromium 核心的简化版本。
|
||||
|
||||
在本文中,我将仔细研究它提供的功能以及与 Linux 上其他主流 Web 浏览器的不同之处。
|
||||
|
||||
### Falkon 浏览器的功能
|
||||
|
||||
我认为它可能不是流行的浏览器,但我发现它足以浏览现代 Web 服务。让我重点介绍 Falkon 的主要功能,如果你觉得它很有趣,请尝试一下。
|
||||
|
||||
#### 简单的用户界面
|
||||
|
||||
![][4]
|
||||
|
||||
我知道这不完全是一项“功能”,但是用户体验(UX)至关重要。尽管是轻量级的浏览器,但你会拥有一个不错的界面。你不会觉得使用的是一款源自 2000 年早期的浏览器。
|
||||
|
||||
#### AdBlock 扩展
|
||||
|
||||
![][5]
|
||||
|
||||
它附带了 AdBlock 扩展程序,如果你想在浏览网站时摆脱广告,它的效果很好。你还可以自定义 AdBlock 扩展的行为。
|
||||
|
||||
#### DuckDuckGo 作为默认搜索引擎
|
||||
|
||||
如果你不喜欢使用 Google,那么这是一件好事,它将这个[主打隐私的搜索引擎][6]设为默认搜索引擎。
|
||||
|
||||
#### 会话管理器
|
||||
|
||||
![][7]
|
||||
|
||||
Falkon 在浏览器菜单的可用选项中包含了一个有用的会话管理器。你可以用它还原特定的近期会话,这应该能派上用场。
|
||||
|
||||
#### 扩展支持
|
||||
|
||||
![][8]
|
||||
|
||||
它确实支持添加扩展,但你不能从 Chrome/Firefox 插件市场添加扩展。你只能选择有限的一组扩展。
|
||||
|
||||
#### 主题支持
|
||||
|
||||
Falkon 还允许你在某种程度上自定义外观。如果要更改浏览器的外观,可以尝试一下。
|
||||
|
||||
![][9]
|
||||
|
||||
#### 其他基本设置
|
||||
|
||||
![][10]
|
||||
|
||||
我还尝试了其他设置/功能,例如添加书签或管理密码。或者,管理网页的通知。我还试了 YouTube,没有任何问题。
|
||||
|
||||
当然,这不是一个详尽的评测。但是,Falkon 确实拥有浏览器中需要的所有基本功能(除非你有特定要求)。
|
||||
|
||||
### 安装 Falkon 浏览器
|
||||
|
||||
你可以在软件中心轻松找到 Falkon 浏览器。如果没有,你可以从它的[官方下载页面][11]中找到 Flatpak/Snap 包。你还可以在下载页面上找到 Windows 的安装程序。
|
||||
|
||||
[Download Falkon Browser][11]
|
||||
|
||||
### 总结
|
||||
|
||||
作为 KDE 产品,Falkon 是为 KDE 桌面环境量身定制的。一些 KDE 发行版(例如 OpenMandriva)使用 Falkon 作为默认的 Web 浏览器。
|
||||
|
||||
Falkon 浏览器适合那些正在寻求轻巧且功能丰富的 Web 浏览器的人。值得尝试的另一个替代是 [Midori][12]。
|
||||
|
||||
我认为,除非你需要大量的扩展、跨平台同步并且需要访问某些特定于浏览器的网站,不然它是一个很好的替代浏览器。
|
||||
|
||||
你如何看待 Falkon 浏览器?请在下面的评论中让我知道你的想法。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/falkon-browser/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/falkon-browser-screenshot.jpg?ssl=1
|
||||
[2]: https://www.falkon.org/
|
||||
[3]: https://wiki.qt.io/QtWebEngine
|
||||
[4]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/falkon-browser.png?ssl=1
|
||||
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/falkon-adblock.jpg?ssl=1
|
||||
[6]: https://itsfoss.com/privacy-search-engines/
|
||||
[7]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/session-manager-falkon.jpg?ssl=1
|
||||
[8]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/falkon-browser-extensions.png?ssl=1
|
||||
[9]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/falkon-browser-theme.png?ssl=1
|
||||
[10]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/falkon-browser-preference.png?ssl=1
|
||||
[11]: https://www.falkon.org/download/
|
||||
[12]: https://itsfoss.com/midori-browser/
|
@ -0,0 +1,118 @@
|
||||
如何在 LibreOffice Writer 中创建一个电子书章节模板
|
||||
======
|
||||
|
||||
> 将电子书分成几章,创建电子书会更容易。这是设置模板的分步指南。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/02/102817vmflleeemehetblt.jpg)
|
||||
|
||||
对于很多人来说,使用 word 程序来撰写和发布一本电子书是一种最好的、最容易的、最熟悉的方法。但是,启动你的 word 程序和打字是不够的 —— 你需要遵循一种格式。
|
||||
|
||||
这就是模板的来源。一个模板确保你的电子书有一个一致的外观和体验。幸运的是,创建模板是快速和容易的,并你在其上花费的时间和努力将带给你一本更好感观的书。
|
||||
|
||||
在这篇文章中,我将陪伴你走过如何使用 LibreOffice Writer 创建一个简单的模板,可以用于一本电子书的各个章节。你可以将这个模板用于 PDF 和 EPUB 书籍,并加以修改来满足你的需要。
|
||||
|
||||
### 我的方法
|
||||
|
||||
为什么我着重于为一个章节创建一个模板,而不是为一整本书创建一个模板?因为编写和管理单独的章节比编写和管理单个庞大而僵化的文档更容易。
|
||||
|
||||
通过关注于单独的章节,你可以专心于你需要编写的东西。你可以很容易地四处移动这些章节,给审阅者发单独一章节比发送你的完整原稿更简单而高效。当你写完一章后,你可以简单地把你的章节拼凑在一起来出版这本书(我将在下面讨论如何做到这一点)。但是不要觉得你被这种方法所束缚 —— 如果你更喜欢在单个文件中编写,简单地调整在这篇文章中描述的步骤就行。
|
||||
|
||||
让我们开始吧。
|
||||
|
||||
### 设置页面
|
||||
|
||||
仅当你计划发布你的电子书为一本 PDF 书时,这是很重要的。设置页面意味着你的书将不包含大量的跨越屏幕的让眼睛疲劳的文本。
|
||||
|
||||
选择 **格式 > 页面** 来打开 **页面样式** 窗口。我的 PDF 电子书通常是 5x8 英寸高(大约 13x20cm ,对于我们这些生活在米制世界的人来说)。我也设置页边距为半英寸(约 1.25 cm)。这些是我较喜欢的尺寸;你可以使用任何适合你的尺寸。
|
||||
|
||||
![LibreOffice Page Style window][2]
|
||||
|
||||
*在 LibreOffice Writer 中的页面样式窗口让你设置页边距和页格式。*
|
||||
|
||||
接下来,添加页脚以显示页码。保持页面样式窗口打开着,并单击 **页脚** 选项卡。选择 **在页脚上** 并单击 **确定** 。
|
||||
|
||||
在该页面上,在页脚中单击,然后选择 **插入 > 字段 > 页码**。不要担心页码的位置和外观;我们接下来会处理。
|
||||
|
||||
### 设置你的样式
|
||||
|
||||
像模板本身一样,样式为你的文档提供一种一致的外观和体验。如果你想更改一个标题的字体或大小,例如,你只需要在一个地方完成它,而不是手动应用格式化到每个标题。
|
||||
|
||||
标准化的 LibreOffice 模板带有许多样式,你可以调整它们以适应你的需要。为此,按 `F11` 来打开 **样式和格式** 窗口。
|
||||
|
||||
![LibreOffice styles and formatting][4]
|
||||
|
||||
*使用样式和格式窗口更改字体和其它细节。*
|
||||
|
||||
在一个样式上右键单击,并选择 **修改** 来编辑它。 这里是我在每本中使用的主要样式:
|
||||
|
||||
样式 | 字体 | 间距 / 对齐方式
|
||||
:-: | :-: | :-:
|
||||
标题 1 | Liberation Sans, 36 pt | 上面 36 pt ,下面 48 pt ,左对齐
|
||||
标题 2 | Liberation Sans, 18 pt | 上面 12 pt ,下面 12 pt ,左对齐
|
||||
标题 3 | Liberation Sans, 14 pt | 上面 12 pt ,下面 12 pt ,左对齐
|
||||
正文 | Liberation Sans, 12 pt | 上面 12 pt ,下面 12 pt ,左对齐
|
||||
页脚 | Liberation Sans, 10 pt | 居中对齐
|
||||
|
||||
|
||||
![LibreOffice styles in action][6]
|
||||
|
||||
*这是当我们应用到电子书内容时的一个选择的样式外观。*
|
||||
|
||||
对于大多数书来说,这通常是最小要求。可以随便更改字体和空格来适应你的要求。
|
||||
|
||||
根据你正在编写书的类型,你可能也想创建或修改项目符合样式,以及数字列表、引号、代码示例、数字符号等等。只需要记住始终如一地使用字体以及其大小。
|
||||
|
||||
### 保存你的模板
|
||||
|
||||
选择 **文件 > 另存为** 。在保持对话框中,从格式列表中选择 “ODF 文本文档模板 (.ott)” 。这将保存模板为一个模板,以后你可以快速调用该模板。
|
||||
|
||||
保存模板的最佳位置是 LibreOffice 模板文件夹。例如,在 Linux 中,那是在你的家目录下,在 `.config/libreoffice/4/user/template` 下。
|
||||
|
||||
### 编写你的书
|
||||
|
||||
在你开始编写前,在你的计算机上创建一个文件夹,它将为你的书保存所有的文件 —— 章节、图像、笔记等等。
|
||||
|
||||
当你准备编写时,启动 LibreOffice Writer 并选择 **文件 > 新建 > 模板** 。然后从列表中选择你的模板,并单击 **打开**。
|
||||
|
||||
![LibreOffice Writer template list][8]
|
||||
|
||||
*从你在 LibreOffice Writer 中设置的列表中选择你的模板,并开始编写。*
|
||||
|
||||
然后用一个描述性名称保存文档。
|
||||
|
||||
建议使用像 “第 1 章” 和 “第 2 章” 这样的惯例 —— 在某些时候,你可能决定重新编排你的章节,当你尝试管理这些章节时,它可能会变得混乱。然而,你可以在文件名称中放置章节数字,像 “第 1 章” 或 “一” 。如果你最后要重新排列全书的章节,像这样重命名更容易。
|
||||
|
||||
无需赘言,开始编写吧。记住在模板中使用样式来格式化文本 —— 这就是为什么你要创建模板,对吧?
|
||||
|
||||
### 发布你的电子书
|
||||
|
||||
在完成编写一堆章节后,并准备发布它们时,创建一个主控文档。把一个主控文档作为你所编写章节的一个容器。使用一个主控文档,你可以快速地组装你的书,并任意重新排序你的章节。LibreOffice 的帮助提供了使用[主控文档][9]的详细信息。
|
||||
|
||||
假设你想生成一个 PDF ,不要只是单击**直接导出到 PDF** 按钮。这将创建一个相当不错的 PDF ,但是你可能想优化它。为此,选择 **文件 > 导出为 PDF** ,并在 PDF 选项窗口中轻微调整。你可以在这篇 [LibreOffice Writer 文档][10] 中学到更多东西。
|
||||
|
||||
如果你想创建一个 EPUB 而不是一个 PDF,或者除了一个 PDF 以外,另外创建一个 EPUB,请安装 [Writer2EPUB][11] 扩展。Opensource.com 的 Bryan Behrenshausen 为这个扩展[分享了一些有用的说明][12] 。
|
||||
|
||||
### 结束语
|
||||
|
||||
我们在这里创建的模板是极简单的,但是你可以将其用于一本简单的书,或者作为构建一个复杂模板的起点。不管怎样,这个模板都会让你快速地开始编写和发布你的电子书。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/17/10/creating-ebook-chapter-template-libreoffice-writer
|
||||
|
||||
作者:[Scott Nesbitt][a]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/scottnesbitt
|
||||
[2]:https://opensource.com/sites/default/files/images/life-uploads/lo-page-style.png (LibreOffice Page Style window)
|
||||
[4]:https://opensource.com/sites/default/files/images/life-uploads/lo-paragraph-style.png (LibreOffice styles and formatting window)
|
||||
[5]:/file/374466
|
||||
[6]:https://opensource.com/sites/default/files/images/life-uploads/lo-styles-in-action.png (Example of LibreOffice styles)
|
||||
[8]:https://opensource.com/sites/default/files/images/life-uploads/lo-template-list.png (Template list - LibreOffice Writer)
|
||||
[9]:https://help.libreoffice.org/Writer/Working_with_Master_Documents_and_Subdocuments
|
||||
[10]:https://help.libreoffice.org/Common/Export_as_PDF
|
||||
[11]:http://writer2epub.it/en/
|
||||
[12]:https://opensource.com/life/13/8/how-create-ebook-open-source-way
|
230
published/202001/20190405 File sharing with Git.md
Normal file
230
published/202001/20190405 File sharing with Git.md
Normal file
@ -0,0 +1,230 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11768-1.html)
|
||||
[#]: subject: (File sharing with Git)
|
||||
[#]: via: (https://opensource.com/article/19/4/file-sharing-git)
|
||||
[#]: author: (Seth Kenlon)
|
||||
|
||||
用 Git 来共享文件
|
||||
======
|
||||
|
||||
> SparkleShare 是一个开源的基于 Git 的 Dropbox 风格的文件共享应用程序。在我们的系列文章中了解有关 Git 鲜为人知的用法。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/10/113645qb2ufvuu2ttimugv.png)
|
||||
|
||||
[Git][2] 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序,它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名,但它还有许多其他用途,可以让你的生活更轻松、更有条理。在这个 Git 系列中,我们将分享七种鲜为人知的使用 Git 的方法。
|
||||
|
||||
今天,我们将看看 SparkleShare,它使用 Git 作为文件共享的基础。
|
||||
|
||||
### 用于文件共享的 Git
|
||||
|
||||
Git 的优点之一是它具有固有的分发能力。它可用来建立共享。即使你只是与自己网络上的其他计算机共享资源库,Git 也会为从共享位置获取文件的行为带来透明性。
|
||||
|
||||
随着其界面的发展,Git 变得非常简单。虽然因用户而异,他们坐下来完成一些工作时的共同点仅仅是 `git pull` 或稍微复杂一点的 `git pull && git checkout -b my-branch`。但是,对于某些人来说,将*命令输入*到他们的计算机中的做法完全是令人困惑或烦恼的。计算机旨在使生活变得轻松,它擅长于重复性工作,因此有更简便的方法可以与 Git 共享文件。
|
||||
|
||||
### SparkleShare
|
||||
|
||||
[SparkleShare][3] 项目是一个基于 Git 的跨平台的、开源的 Dropbox 式的文件共享应用程序。它通过将文件拖放到专门指定的 SparkleShare 目录中的简单操作,自动执行所有 Git 命令,触发添加、提交、推送和拉取过程。因为它基于 Git,所以你可以获得基于差异(diff)的快速推送和拉取,并且继承了 Git 版本控制和后端基础设施(如 Git 挂钩)的所有优点。它可以完全自托管,也可以将其与 [GitLab][4]、GitHub、Bitbucket 等 Git 托管服务一起使用。此外,由于它基本上只是一个 Git 的前端,因此你可以在可能没有 SparkleShare 客户端但有 Git 客户端的设备上访问 SparkleShare 中的文件。
|
||||
|
||||
正如你获得 Git 的所有好处一样,你也会受到所有常见的 Git 限制:使用 SparkleShare 存储数百张照片、音乐和视频是不切实际的,因为 Git 是为文本而设计和优化的。Git 当然可以存储二进制文件的大文件,但是因为它可以跟踪历史记录,因此一旦将文件添加到其中,几乎就不可能完全删除它。这在某种程度上限制了 SparkleShare 对某些人的实用性,但使其非常适合许多工作流程,包括[日程安排][5]。
|
||||
|
||||
#### 安装 SparkleShare
|
||||
|
||||
SparkleShare 是跨平台的,可从[网站][6]获得适用于 Windows 和 Mac 的安装程序。对于 Linux,有一个 [Flatpak][7] 安装包,或者你可以在终端中运行以下命令:
|
||||
|
||||
```
|
||||
$ sudo flatpak remote-add flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
$ sudo flatpak install flathub org.sparkleshare.SparkleShare
|
||||
```
|
||||
|
||||
### 创建一个 Git 存储库
|
||||
|
||||
SparkleShare 并不是软件即服务(SaaS)。你在计算机上运行 SparkleShare 与 Git 存储库进行通信,而 SparkleShare 并不存储你的数据。如果你还没有与文件夹同步的 Git 存储库,则必须在启动 SparkleShare 之前创建一个文件夹。你有三个选择:托管的 Git、自托管 Git 或自托管 SparkleShare。
|
||||
|
||||
#### 托管的 Git
|
||||
|
||||
SparkleShare 可以使用你能访问的任何 Git 存储库进行存储,因此,如果你拥有 GitLab 或任何其他托管服务的帐户(或创建一个),则它可以成为 SparkleShare 的后端。例如,开源 [Notabug.org][8] 服务是一个类似于 GitHub 和 GitLab 的 Git 托管服务,但其独特性足以证明 SparkleShare 的灵活性。根据用户界面的不同,不同的托管服务创建新存储库的方法也有所不同,但是所有主要存储库都遵循相同的通用模型。
|
||||
|
||||
首先,在托管服务中找到创建新项目或存储库的按钮,单击它以开始。然后逐步完成存储库的创建过程,为存储库提供名称、隐私级别(存储库通常默认为公共),以及是否使用 `README` 文件初始化存储库。无论你是否需要个 `README` 文件,请初始化建立一个。使用一个文件来创建存储库不是绝对必要的,但是它会强制 Git 主机实例化存储库中的 `master` 分支,这有助于确保前端应用程序(例如 SparkleShare)具有要提交并推送的分支。即使文件是几乎空的 `README` 文件,也可以用来查看该文件以确认你已连接成功。
|
||||
|
||||
![Creating a Git repository][9]
|
||||
|
||||
创建存储库后,获取其用于 SSH 克隆的 URL。就像从 Git 项目获得其 URL 一样,你也可以获取此 URL:导航至存储库页面并查找 “Clone” 按钮或字段。
|
||||
|
||||
![Cloning a URL on GitHub][10]
|
||||
|
||||
*GitHub 的克隆 URL。*
|
||||
|
||||
![Cloning a URL on GitLab][11]
|
||||
|
||||
*GitLab 的克隆 URL。*
|
||||
|
||||
这是 SparkleShare 用于获取数据的地址,因此请记下它。你的 Git 存储库现已配置好。
|
||||
|
||||
#### 自托管的 Git
|
||||
|
||||
你可以使用 SparkleShare 访问你有权访问的任何计算机上的 Git 存储库。除了一个 Git 裸存储库外,无需任何特殊设置。但是,如果你想将对 Git 存储库的访问权授予其他任何人,则应运行 [Gitolite][12] 之类的 Git 管理器或 SparkleShare 自己的 Dazzle 服务器来帮助你管理 SSH 密钥和帐户。至少,创建一个特定于 Git 的用户,以便有权访问你的 Git 存储库的用户不会自动获得对服务器其余部分的访问权限。
|
||||
|
||||
以 Git 用户身份登录服务器(如果你非常擅长管理用户和组权限,则可以以自己的用户登录)并创建存储库:
|
||||
|
||||
```
|
||||
$ mkdir ~/sparkly.git
|
||||
$ cd ~/sparkly.git
|
||||
$ git init --bare .
|
||||
```
|
||||
|
||||
你的 Git 存储库现已配置好。
|
||||
|
||||
#### Dazzle
|
||||
|
||||
SparkleShare 的开发人员提供了一个名为 [Dazzle][13] 的 Git 管理系统,以帮助你自托管 Git 存储库。
|
||||
|
||||
在你的服务器上,将 Dazzle 应用程序下载到你的路径中的某个位置:
|
||||
|
||||
```
|
||||
$ curl https://raw.githubusercontent.com/hbons/Dazzle/master/dazzle.sh --output ~/bin/dazzle
|
||||
$ chmod +x ~/bin/dazzle
|
||||
```
|
||||
|
||||
Dazzle 设置了一个特定于 Git 和 SparkleShare 的用户,并且还基于 SparkleShare 应用程序生成的密钥实现了访问权限。现在,只需设置一个项目:
|
||||
|
||||
```
|
||||
$ dazzle create sparkly
|
||||
```
|
||||
|
||||
你的服务器现在已经配置好,可以用作 SparkleShare 托管了。
|
||||
|
||||
### 配置 SparkleShare
|
||||
|
||||
首次启动 SparkleShare 时,系统会提示你配置 SparkleShare 用于存储的服务器。这个过程可能看起来像一个首次运行的安装向导,但实际上是在 SparkleShare 中设置新共享位置的通常过程。与许多共享驱动器应用程序不同,使用 SparkleShare 可以一次配置多个位置。你配置的第一个共享位置并不比你以后可以配置的任何共享位置更重要,并且你也不用注册 SparkleShare 或任何其他服务。你只是将 SparkleShare 指向 Git 存储库,以便它知道如何使第一个 SparkleShare 文件夹保持同步。
|
||||
|
||||
在第一个屏幕上,给出一个身份信息,SparkleShare 将在代表你进行的 Git 提交记录中使用这些信息。你可以使用任何内容,甚至可以使用不代表任何意义的伪造信息。它仅用于提交消息,如果你对审查 Git 后端进程没有兴趣,你可能甚至看不到它们。
|
||||
|
||||
下一个屏幕提示你选择主机类型。如果你使用的是 GitLab、GitHub、Planio 或 Bitbucket,则可以选择一个适当的。否则,请选择“自己的服务器”。
|
||||
|
||||
![Choosing a Sparkleshare host][14]
|
||||
|
||||
在此屏幕底部,你必须输入 SSH 的克隆 URL。如果你是自托管的 Git,则地址类似于 `<ssh://username@example.com>`,而远程路径是为此目的而创建的 Git 存储库的绝对路径。
|
||||
|
||||
根据上面的自托管示例,我虚构的服务器的地址为 `ssh://git@example.com:22122`(`:22122` 表示一个非标准的 SSH 端口),远程路径为 `/home/git/sparkly.git`。
|
||||
|
||||
如果我改用 Notabug.org 帐户,则上例中的地址为 `ssh://git@notabug.org`,路径为 `seth/sparkly.git`。
|
||||
|
||||
SparkleShare 首次尝试连接到主机时会失败,因为你尚未将 SparkleShare 客户端 ID(特定于 SparkleShare 应用程序的 SSH 密钥)复制到 Git 主机。这是预料之中的,所以不要取消该过程。将 SparkleShare 设置窗口保持打开状态,并从系统任务栏中的 SparkleShare 图标处获取客户端 ID。然后将客户端 ID 复制到剪贴板,以便可以将其添加到 Git 主机。
|
||||
|
||||
![Getting the client ID from Sparkleshare][16]
|
||||
|
||||
#### 将你的客户端 ID 添加到托管的 Git 帐户
|
||||
|
||||
除了较小的 UI 差异外,在任何托管服务上添加 SSH 密钥(所有客户端 ID 都是这样)的过程基本上是相同的。在你的 Git 主机的 Web 仪表板中,导航到你的用户设置,然后找到 “SSH 密钥”类别。单击“添加新密钥”按钮(或类似按钮),然后粘贴你的 SparkleShare 客户端 ID 的内容。
|
||||
|
||||
![Adding an SSH key][17]
|
||||
|
||||
保存密钥。如果你希望其他人(例如协作者或家庭成员)能够访问同一存储库,则他们必须向你提供其 SparkleShare 客户端 ID,以便你可以将其添加到帐户中。
|
||||
|
||||
#### 将你的客户端 ID 添加到自托管的 Git 帐户
|
||||
|
||||
SparkleShare 客户端 ID 只是一个 SSH 密钥,因此将其复制并粘贴到 Git 用户的 `~/.ssh/authorized_keys` 文件中。
|
||||
|
||||
#### 使用 Dazzle 添加你的客户 ID
|
||||
|
||||
如果你使用 Dazzle 管理 SparkleShare 项目,请使用以下命令添加客户端 ID:
|
||||
|
||||
```
|
||||
$ dazzle link
|
||||
```
|
||||
|
||||
当 Dazzle 提示你输入该 ID 时,请粘贴在 SparkleShare 菜单中找到的客户端 ID。
|
||||
|
||||
### 使用 SparkleShare
|
||||
|
||||
将客户端 ID 添加到 Git 主机后,在 SparkleShare 窗口中单击“重试”按钮以完成设置。克隆存储库完成后,你可以关闭 SparkleShare 设置窗口,并在你的家目录中找到一个新的 `SparkleShare` 文件夹。如果你设置了带有托管服务的 Git 存储库,并选择包括 `README` 文件或许可证文件,则可以在 SparkleShare 目录中看到它们。
|
||||
|
||||
![Sparkleshare file manager][18]
|
||||
|
||||
此外,有一些隐藏目录,你可以通过在文件管理器中显示隐藏目录来查看。
|
||||
|
||||
![Showing hidden files in GNOME][19]
|
||||
|
||||
使用 SparkleShare 的方式与使用计算机上任何目录的方式相同:将文件放入其中。每当将文件或目录放入 SparkleShare 文件夹时,它都会在后台复制到你的 Git 存储库。
|
||||
|
||||
#### 排除某些文件
|
||||
|
||||
由于 Git 从设计上就是要记住*一切*,因此你可能希望从记录中排除特定的文件类型。排除一些文件是有原因的。通过定义摆脱 SparkleShare 管理的文件,可以避免意外复制大文件。你还可以为自己设计一种方案,使你可以将存储在一个目录中的逻辑上属于同一个文件(例如,MIDI 文件及其 .flac 导出文件),但是可以自己手动备份大文件,而同时让 SparkleShare 备份基于文本的文件。
|
||||
|
||||
如果在系统的文件管理器中看不到隐藏的文件,请显示它们。导航到你的 SparkleShare 文件夹,然后到代表你的存储库的目录,找到一个名为 `.gitignore` 的文件,然后在文本编辑器中将其打开。你可以在 `.gitignore` 中输入文件扩展名或文件名(每行一个),任何与你列出的文件匹配的文件都会被忽略(如文件名所示)。
|
||||
|
||||
```
|
||||
Thumbs.db
|
||||
$RECYCLE.BIN/
|
||||
.DS_Store
|
||||
._*
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
.directory
|
||||
.Trash-*
|
||||
*.wav
|
||||
*.ogg
|
||||
*.flac
|
||||
*.mp3
|
||||
*.m4a
|
||||
*.opus
|
||||
*.jpg
|
||||
*.png
|
||||
*.mp4
|
||||
*.mov
|
||||
*.mkv
|
||||
*.avi
|
||||
*.pdf
|
||||
*.djvu
|
||||
*.epub
|
||||
*.od{s,t}
|
||||
*.cbz
|
||||
```
|
||||
|
||||
你知道最经常遇到哪些文件类型,因此请集中精力处理最有可能潜入你的 SparkleShare 目录的文件。如果你想稍微矫枉过正一些,可以在 Notabug.org 以及整个网上找到 `.gitignore` 文件的好集合。
|
||||
|
||||
通过将这些条目保存在 `.gitignore` 文件中,你可以将不需要发送到 Git 主机的大文件放在 SparkleShare 目录中,SparkleShare 将完全忽略它们。当然,这意味着你需要确保它们可以备份或通过其他方式分发给你的 SparkleShare 协作者。
|
||||
|
||||
### 自动化
|
||||
|
||||
[自动化][20] 是我们与计算机达成的默契之一:计算机执行重复的、无聊的工作,而我们人类要么不擅长做这些,要么不擅长记忆这些。SparkleShare 是一种很好的、简单的自动执行例行数据分发的方法。但不管怎么说,这并不适合每个 Git 存储库。它没有用于高级 Git 功能的接口,它没有暂停按钮或手动管理的操作。没关系,因为它的使用范围是有意限制的。SparkleShare 可以完成它计划要做的事情,它做得很好,而且它是你无需关心的一个 Git 存储库。
|
||||
|
||||
如果你想使用这种稳定的、看不见的自动化,请尝试一下 SparkleShare。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/file-sharing-git
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003499_01_cloud21x_cc.png?itok=5UwC92dO
|
||||
[2]: https://git-scm.com/
|
||||
[3]: http://www.sparkleshare.org/
|
||||
[4]: http://gitlab.com
|
||||
[5]: https://opensource.com/article/19/4/calendar-git
|
||||
[6]: http://sparkleshare.org
|
||||
[7]: /business/16/8/flatpak
|
||||
[8]: http://notabug.org
|
||||
[9]: https://opensource.com/sites/default/files/uploads/git-new-repo.jpg (Creating a Git repository)
|
||||
[10]: https://opensource.com/sites/default/files/uploads/github-clone-url.jpg (Cloning a URL on GitHub)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/gitlab-clone-url.jpg (Cloning a URL on GitLab)
|
||||
[12]: http://gitolite.org
|
||||
[13]: https://github.com/hbons/Dazzle
|
||||
[14]: https://opensource.com/sites/default/files/uploads/sparkleshare-host.jpg (Choosing a Sparkleshare host)
|
||||
[15]: mailto:git@notabug.org
|
||||
[16]: https://opensource.com/sites/default/files/uploads/sparkleshare-clientid.jpg (Getting the client ID from Sparkleshare)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/git-ssh-key.jpg (Adding an SSH key)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/sparkleshare-file-manager.jpg (Sparkleshare file manager)
|
||||
[19]: https://opensource.com/sites/default/files/uploads/gnome-show-hidden-files.jpg (Showing hidden files in GNOME)
|
||||
[20]: /downloads/ansible-quickstart
|
226
published/202001/20190406 Run a server with Git.md
Normal file
226
published/202001/20190406 Run a server with Git.md
Normal file
@ -0,0 +1,226 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11795-1.html)
|
||||
[#]: subject: (Run a server with Git)
|
||||
[#]: via: (https://opensource.com/article/19/4/server-administration-git)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth/users/seth)
|
||||
|
||||
使用 Git 来管理 Git 服务器
|
||||
======
|
||||
|
||||
> 借助 Gitolite,你可以使用 Git 来管理 Git 服务器。在我们的系列文章中了解这些鲜为人知的 Git 用途。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/18/132045yrr1pb9n497tfbiy.png)
|
||||
|
||||
正如我在系列文章中演示的那样,[Git][2] 除了跟踪源代码外,还可以做很多事情。信不信由你,Git 甚至可以管理你的 Git 服务器,因此你可以或多或少地使用 Git 本身来运行 Git 服务器。
|
||||
|
||||
当然,这涉及除日常使用 Git 之外的许多组件,其中最重要的是 [Gitolite][3],该后端应用程序可以管理你使用 Git 的每个细微的配置。Gitolite 的优点在于,由于它使用 Git 作为其前端接口,因此很容易将 Git 服务器管理集成到其他基于 Git 的工作流中。Gitolite 可以精确控制谁可以访问你服务器上的特定存储库以及他们具有哪些权限。你可以使用常规的 Linux 系统工具自行管理此类事务,但是如果有好几个用户和不止一两个仓库,则需要大量的工作。
|
||||
|
||||
Gitolite 的开发人员做了艰苦的工作,使你可以轻松地为许多用户提供对你的 Git 服务器的访问权,而又不让他们访问你的整个环境 —— 而这一切,你可以使用 Git 来完成全部工作。
|
||||
|
||||
Gitolite 并**不是**图形化的管理员和用户面板。优秀的 [Gitea][4] 项目可提供这种体验,但是本文重点介绍 Gitolite 的简单优雅和令人舒适的熟悉感。
|
||||
|
||||
### 安装 Gitolite
|
||||
|
||||
假设你的 Git 服务器运行在 Linux 上,则可以使用包管理器安装 Gitolite(在 CentOS 和 RHEL 上为 `yum`,在 Debian 和 Ubuntu 上为 `apt`,在 OpenSUSE 上为 `zypper` 等)。例如,在 RHEL 上:
|
||||
|
||||
```
|
||||
$ sudo yum install gitolite3
|
||||
```
|
||||
|
||||
许多发行版的存储库提供的仍是旧版本的 Gitolite,但最新版本为版本 3。
|
||||
|
||||
你必须具有对服务器的无密码 SSH 访问权限。如果愿意,你可以使用密码登录服务器,但是 Gitolite 依赖于 SSH 密钥,因此必须配置使用密钥登录的选项。如果你不知道如何配置服务器以进行无密码 SSH 访问,请首先学习如何进行操作(Steve Ovens 的 Ansible 文章的[设置 SSH 密钥身份验证][5]部分对此进行了很好的说明)。这是加强服务器管理的安全以及运行 Gitolite 的重要组成部分。
|
||||
|
||||
### 配置 Git 用户
|
||||
|
||||
如果没有 Gitolite,则如果某人请求访问你在服务器上托管的 Git 存储库时,则必须向该人提供用户帐户。Git 提供了一个特殊的外壳,即 `git-shell`,这是一个仅执行 Git 任务的特别的特定 shell。这可以让你有个只能通过非常受限的 Shell 环境来过滤访问你的服务器的用户。
|
||||
|
||||
这个解决方案是一个办法,但通常意味着用户可以访问服务器上的所有存储库,除非你具有用于组权限的良好模式,并在创建新存储库时严格遵循这些权限。这种方式还需要在系统级别进行大量手动配置,这通常是只有特定级别的系统管理员才能做的工作,而不一定是通常负责 Git 存储库的人员。
|
||||
|
||||
Gitolite 通过为需要访问任何存储库的每个人指定一个用户名来完全回避此问题。默认情况下,该用户名是 `git`,并且由于 Gitolite 的文档中假定使用的是它,因此在学习该工具时保留它是一个很好的默认设置。对于曾经使用过 GitLab 或 GitHub 或任何其他 Git 托管服务的人来说,这也是一个众所周知的约定。
|
||||
|
||||
Gitolite 将此用户称为**托管用户**。在服务器上创建一个帐户以充当托管用户(我习惯使用 `git`,因为这是惯例):
|
||||
|
||||
```
|
||||
$ sudo adduser --create-home git
|
||||
```
|
||||
|
||||
为了控制该 `git` 用户帐户,该帐户必须具有属于你的有效 SSH 公钥。你应该已经进行了设置,因此复制你的公钥(**而不是你的私钥**)添加到 `git` 用户的家目录中:
|
||||
|
||||
```
|
||||
$ sudo cp ~/.ssh/id_ed25519.pub /home/git/
|
||||
$ sudo chown git:git /home/git/id_ed25519.pub
|
||||
```
|
||||
|
||||
如果你的公钥不以扩展名 `.pub` 结尾,则 Gitolite 不会使用它,因此请相应地重命名该文件。切换为该用户帐户以运行 Gitolite 的安装程序:
|
||||
|
||||
```
|
||||
$ sudo su - git
|
||||
$ gitolite setup --pubkey id_ed25519.pub
|
||||
```
|
||||
|
||||
安装脚本运行后,`git` 的家用户目录将有一个 `repository` 目录,该目录(目前)包含存储库 `git-admin.git` 和 `testing.git`。这就是该服务器所需的全部设置,现在请登出 `git` 用户。
|
||||
|
||||
### 使用 Gitolite
|
||||
|
||||
管理 Gitolite 就是编辑 Git 存储库中的文本文件,尤其是 `gitolite-admin.git` 中的。你不会通过 SSH 进入服务器来进行 Git 管理,并且 Gitolite 也建议你不要这样尝试。在 Gitolite 服务器上存储你和你的用户的存储库是个**裸**存储库,因此最好不要使用它们。
|
||||
|
||||
```
|
||||
$ git clone git@example.com:gitolite-admin.git gitolite-admin.git
|
||||
$ cd gitolite-admin.git
|
||||
$ ls -1
|
||||
conf
|
||||
keydir
|
||||
```
|
||||
|
||||
该存储库中的 `conf` 目录包含一个名为 `gitolite.conf` 的文件。在文本编辑器中打开它,或使用 `cat` 查看其内容:
|
||||
|
||||
```
|
||||
repo gitolite-admin
|
||||
RW+ = id_ed22519
|
||||
|
||||
repo testing
|
||||
RW+ = @all
|
||||
```
|
||||
|
||||
你可能对该配置文件的功能有所了解:`gitolite-admin` 代表此存储库,并且 `id_ed25519` 密钥的所有者具有读取、写入和管理 Git 的权限。换句话说,不是将用户映射到普通的本地 Unix 用户(因为所有用户都使用 `git` 用户托管用户身份),而是将用户映射到 `keydir` 目录中列出的 SSH 密钥。
|
||||
|
||||
`testing.git` 存储库使用特殊组符号为访问服务器的每个人提供了全部权限。
|
||||
|
||||
#### 添加用户
|
||||
|
||||
如果要向 Git 服务器添加一个名为 `alice` 的用户,Alice 必须向你发送她的 SSH 公钥。Gitolite 使用文件名的 `.pub` 扩展名左边的任何内容作为该 Git 用户的标识符。不要使用默认的密钥名称值,而是给密钥指定一个指示密钥所有者的名称。如果用户有多个密钥(例如,一个用于笔记本电脑,一个用于台式机),则可以使用子目录来避免文件名冲突。例如,Alice 在笔记本电脑上使用的密钥可能是默认的 `id_rsa.pub`,因此将其重命名为`alice.pub` 或类似名称(或让用户根据其计算机上的本地用户帐户来命名密钥),然后将其放入 `gitolite-admin.git/keydir/work/laptop/` 目录中。如果她从她的桌面计算机发送了另一个密钥,命名为 `alice.pub`(与上一个相同),然后将其添加到 `keydir/home/desktop/` 中。另一个密钥可能放到 `keydir/home/desktop/` 中,依此类推。Gitolite 递归地在 `keydir` 中搜索与存储库“用户”相匹配的 `.pub` 文件,并将所有匹配项视为相同的身份。
|
||||
|
||||
当你将密钥添加到 `keydir` 目录时,必须将它们提交回服务器。这是一件很容易忘记的事情,这里有一个使用自动化的 Git 应用程序(例如 [Sparkleshare][7])的真正的理由,因此任何更改都将立即提交给你的 Gitolite 管理员。第一次忘记提交和推送,在浪费了三个小时的你和你的用户的故障排除时间之后,你会发现 Gitolite 是使用 Sparkleshare 的完美理由。
|
||||
|
||||
```
|
||||
$ git add keydir
|
||||
$ git commit -m 'added alice-laptop-0.pub'
|
||||
$ git push origin HEAD
|
||||
```
|
||||
|
||||
默认情况下,Alice 可以访问 `testing.git` 目录,因此她可以使用该目录测试连接性和功能。
|
||||
|
||||
#### 设置权限
|
||||
|
||||
与用户一样,目录权限和组也是从你可能习惯的的常规 Unix 工具中抽象出来的(或可从在线信息查找)。在 `gitolite-admin.git/conf` 目录中的 `gitolite.conf` 文件中授予对项目的权限。权限分为四个级别:
|
||||
|
||||
* `R` 允许只读。在存储库上具有 `R` 权限的用户可以克隆它,仅此而已。
|
||||
* `RW` 允许用户执行分支的快进推送、创建新分支和创建新标签。对于大多数用户来说,这个基本上就像是一个“普通”的 Git 存储库。
|
||||
* `RW+` 允许可能具有破坏性的 Git 动作。用户可以执行常规的快进推送、回滚推送、变基以及删除分支和标签。你可能想要或不希望将其授予项目中的所有贡献者。
|
||||
* `-` 明确拒绝访问存储库。这与未在存储库的配置中列出的用户相同。
|
||||
|
||||
通过调整 `gitolite.conf` 来创建一个新的存储库或修改现有存储库的权限。例如,授予 Alice 权限来管理一个名为 `widgets.git` 的新存储库:
|
||||
|
||||
```
|
||||
repo gitolite-admin
|
||||
RW+ = id_ed22519
|
||||
|
||||
repo testing
|
||||
RW+ = @all
|
||||
|
||||
repo widgets
|
||||
RW+ = alice
|
||||
```
|
||||
|
||||
现在,Alice(也仅有 Alice 一个人)可以克隆该存储库:
|
||||
|
||||
```
|
||||
[alice]$ git clone git@example.com:widgets.git
|
||||
Cloning into 'widgets'...
|
||||
warning: You appear to have cloned an empty repository.
|
||||
```
|
||||
|
||||
在第一次推送时,Alice 必须使用 `-u` 选项将其分支发送到空存储库(如同她在任何 Git 主机上做的一样)。
|
||||
|
||||
为了简化用户管理,你可以定义存储库组:
|
||||
|
||||
```
|
||||
@qtrepo = widgets
|
||||
@qtrepo = games
|
||||
|
||||
repo gitolite-admin
|
||||
RW+ = id_ed22519
|
||||
|
||||
repo testing
|
||||
RW+ = @all
|
||||
|
||||
repo @qtrepo
|
||||
RW+ = alice
|
||||
```
|
||||
|
||||
正如你可以创建组存储库一样,你也可以对用户进行分组。默认情况下存在一个用户组:`@all`。如你所料,它包括所有用户,无一例外。你也可以创建自己的组:
|
||||
|
||||
```
|
||||
@qtrepo = widgets
|
||||
@qtrepo = games
|
||||
|
||||
@developers = alice bob
|
||||
|
||||
repo gitolite-admin
|
||||
RW+ = id_ed22519
|
||||
|
||||
repo testing
|
||||
RW+ = @all
|
||||
|
||||
repo @qtrepo
|
||||
RW+ = @developers
|
||||
```
|
||||
|
||||
与添加或修改密钥文件一样,对 `gitolite.conf` 文件的任何更改都必须提交并推送以生效。
|
||||
|
||||
### 创建存储库
|
||||
|
||||
默认情况下,Gitolite 假设存储库的创建是从上至下进行。例如,有权访问 Git 服务器的项目经理创建了一个项目存储库,并通过 Gitolite 管理仓库添加了开发人员。
|
||||
|
||||
实际上,你可能更愿意向用户授予创建存储库的权限。Gitolite 称这些为“<ruby>野生仓库(通配仓库)<rt>wild repos</rt></ruby>”(我不确定这是关于仓库的形成方式的描述,还是指配置文件所需的通配符)。这是一个例子:
|
||||
|
||||
```
|
||||
@managers = alice bob
|
||||
|
||||
repo foo/CREATOR/[a-z]..*
|
||||
C = @managers
|
||||
RW+ = CREATOR
|
||||
RW = WRITERS
|
||||
R = READERS
|
||||
```
|
||||
|
||||
第一行定义了一组用户:该组称为 `@managers`,其中包含用户 `alice` 和 `bob`。下一行设置了通配符允许创建尚不存在的存储库,放在名为 `foo` 的目录下的创建该存储库的用户名的子目录中。例如:
|
||||
|
||||
```
|
||||
[alice]$ git clone git@example.com:foo/alice/cool-app.git
|
||||
Cloning into cool-app'...
|
||||
Initialized empty Git repository in /home/git/repositories/foo/alice/cool-app.git
|
||||
warning: You appear to have cloned an empty repository.
|
||||
```
|
||||
|
||||
野生仓库的创建者可以使用一些机制来定义谁可以读取和写入其存储库,但是他们是有范围限定的。在大多数情况下,Gitolite 假定由一组特定的用户来管理项目权限。一种解决方案是使用 Git 挂钩来授予所有用户对 `gitolite-admin` 的访问权限,以要求管理者批准将更改合并到 master 分支中。
|
||||
|
||||
### 了解更多
|
||||
|
||||
Gitolite 具有比此介绍性文章所涵盖的更多功能,因此请尝试一下。其[文档][8]非常出色,一旦你通读了它,就可以自定义 Gitolite 服务器,以向用户提供你喜欢的任何级别的控制。Gitolite 是一种维护成本低、简单的系统,你可以安装、设置它,然后基本上就可以将其忘却。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/server-administration-git
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/server_data_system_admin.png?itok=q6HCfNQ8 (computer servers processing data)
|
||||
[2]: https://git-scm.com/
|
||||
[3]: http://gitolite.com
|
||||
[4]: http://gitea.io
|
||||
[5]: Setting%20up%20SSH%20key%20authentication
|
||||
[6]: mailto:git@example.com
|
||||
[7]: https://opensource.com/article/19/4/file-sharing-git
|
||||
[8]: http://gitolite.com/gitolite/quick_install.html
|
@ -0,0 +1,333 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11810-1.html)
|
||||
[#]: subject: (Getting started with OpenSSL: Cryptography basics)
|
||||
[#]: via: (https://opensource.com/article/19/6/cryptography-basics-openssl-part-1)
|
||||
[#]: author: (Marty Kalin https://opensource.com/users/mkalindepauledu/users/akritiko/users/clhermansen)
|
||||
|
||||
OpenSSL 入门:密码学基础知识
|
||||
======
|
||||
|
||||
> 想要入门密码学的基础知识,尤其是有关 OpenSSL 的入门知识吗?继续阅读。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/23/142249fpnhyqz9y2cz1exe.jpg)
|
||||
|
||||
本文是使用 [OpenSSL][2] 的密码学基础知识的两篇文章中的第一篇,OpenSSL 是在 Linux 和其他系统上流行的生产级库和工具包。(要安装 OpenSSL 的最新版本,请参阅[这里][3]。)OpenSSL 实用程序可在命令行使用,程序也可以调用 OpenSSL 库中的函数。本文的示例程序使用的是 C 语言,即 OpenSSL 库的源语言。
|
||||
|
||||
本系列的两篇文章涵盖了加密哈希、数字签名、加密和解密以及数字证书。你可以从[我的网站][4]的 ZIP 文件中找到这些代码和命令行示例。
|
||||
|
||||
让我们首先回顾一下 OpenSSL 名称中的 SSL。
|
||||
|
||||
### OpenSSL 简史
|
||||
|
||||
<ruby>[安全套接字层][5]<rt>Secure Socket Layer</rt></ruby>(SSL)是 Netscape 在 1995 年发布的一种加密协议。该协议层可以位于 HTTP 之上,从而为 HTTPS 提供了 S:<ruby>安全<rt>secure</rt></ruby>。SSL 协议提供了各种安全服务,其中包括两项在 HTTPS 中至关重要的服务:
|
||||
|
||||
* <ruby>对等身份验证<rt>Peer authentication</rt></ruby>(也称为相互质询):连接的每一边都对另一边的身份进行身份验证。如果 Alice 和 Bob 要通过 SSL 交换消息,则每个人首先验证彼此的身份。
|
||||
* <ruby>机密性<rt>Confidentiality</rt></ruby>:发送者在通过通道发送消息之前先对其进行加密。然后,接收者解密每个接收到的消息。此过程可保护网络对话。即使窃听者 Eve 截获了从 Alice 到 Bob 的加密消息(即*中间人*攻击),Eve 会发现他无法在计算上解密此消息。
|
||||
|
||||
反过来,这两个关键 SSL 服务与其他不太受关注的服务相关联。例如,SSL 支持消息完整性,从而确保接收到的消息与发送的消息相同。此功能是通过哈希函数实现的,哈希函数也随 OpenSSL 工具箱一起提供。
|
||||
|
||||
SSL 有多个版本(例如 SSLv2 和 SSLv3),并且在 1999 年出现了一个基于 SSLv3 的类似协议<ruby>传输层安全性<rt>Transport Layer Security</rt></ruby>(TLS)。TLSv1 和 SSLv3 相似,但不足以相互配合工作。不过,通常将 SSL/TLS 称为同一协议。例如,即使正在使用的是 TLS(而非 SSL),OpenSSL 函数也经常在名称中包含 SSL。此外,调用 OpenSSL 命令行实用程序以 `openssl` 开始。
|
||||
|
||||
除了 man 页面之外,OpenSSL 的文档是零零散散的,鉴于 OpenSSL 工具包很大,这些页面很难以查找使用。命令行和代码示例可以将主要主题集中起来。让我们从一个熟悉的示例开始(使用 HTTPS 访问网站),然后使用该示例来选出我们感兴趣的加密部分进行讲述。
|
||||
|
||||
### 一个 HTTPS 客户端
|
||||
|
||||
此处显示的 `client` 程序通过 HTTPS 连接到 Google:
|
||||
|
||||
```
|
||||
/* compilation: gcc -o client client.c -lssl -lcrypto */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <openssl/bio.h> /* BasicInput/Output streams */
|
||||
#include <openssl/err.h> /* errors */
|
||||
#include <openssl/ssl.h> /* core library */
|
||||
#define BuffSize 1024
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void init_ssl() {
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
}
|
||||
|
||||
void cleanup(SSL_CTX* ctx, BIO* bio) {
|
||||
SSL_CTX_free(ctx);
|
||||
BIO_free_all(bio);
|
||||
}
|
||||
|
||||
void secure_connect(const char* hostname) {
|
||||
char name[BuffSize];
|
||||
char request[BuffSize];
|
||||
char response[BuffSize];
|
||||
|
||||
const SSL_METHOD* method = TLSv1_2_client_method();
|
||||
if (NULL == method) report_and_exit("TLSv1_2_client_method...");
|
||||
|
||||
SSL_CTX* ctx = SSL_CTX_new(method);
|
||||
if (NULL == ctx) report_and_exit("SSL_CTX_new...");
|
||||
|
||||
BIO* bio = BIO_new_ssl_connect(ctx);
|
||||
if (NULL == bio) report_and_exit("BIO_new_ssl_connect...");
|
||||
|
||||
SSL* ssl = NULL;
|
||||
|
||||
/* 链路 bio 通道,SSL 会话和服务器端点 */
|
||||
|
||||
sprintf(name, "%s:%s", hostname, "https");
|
||||
BIO_get_ssl(bio, &ssl); /* 会话 */
|
||||
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* 鲁棒性 */
|
||||
BIO_set_conn_hostname(bio, name); /* 准备连接 */
|
||||
|
||||
/* 尝试连接 */
|
||||
if (BIO_do_connect(bio) <= 0) {
|
||||
cleanup(ctx, bio);
|
||||
report_and_exit("BIO_do_connect...");
|
||||
}
|
||||
|
||||
/* 验证信任库,检查证书 */
|
||||
if (!SSL_CTX_load_verify_locations(ctx,
|
||||
"/etc/ssl/certs/ca-certificates.crt", /* 信任库 */
|
||||
"/etc/ssl/certs/")) /* 其它信任库 */
|
||||
report_and_exit("SSL_CTX_load_verify_locations...");
|
||||
|
||||
long verify_flag = SSL_get_verify_result(ssl);
|
||||
if (verify_flag != X509_V_OK)
|
||||
fprintf(stderr,
|
||||
"##### Certificate verification error (%i) but continuing...\n",
|
||||
(int) verify_flag);
|
||||
|
||||
/* 获取主页作为示例数据 */
|
||||
sprintf(request,
|
||||
"GET / HTTP/1.1\x0D\x0AHost: %s\x0D\x0A\x43onnection: Close\x0D\x0A\x0D\x0A",
|
||||
hostname);
|
||||
BIO_puts(bio, request);
|
||||
|
||||
/* 从服务器读取 HTTP 响应并打印到输出 */
|
||||
while (1) {
|
||||
memset(response, '\0', sizeof(response));
|
||||
int n = BIO_read(bio, response, BuffSize);
|
||||
if (n <= 0) break; /* 0 代表流结束,< 0 代表有错误 */
|
||||
puts(response);
|
||||
}
|
||||
|
||||
cleanup(ctx, bio);
|
||||
}
|
||||
|
||||
int main() {
|
||||
init_ssl();
|
||||
|
||||
const char* hostname = "www.google.com:443";
|
||||
fprintf(stderr, "Trying an HTTPS connection to %s...\n", hostname);
|
||||
secure_connect(hostname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
可以从命令行编译和执行该程序(请注意 `-lssl` 和 `-lcrypto` 中的小写字母 `L`):
|
||||
|
||||
```
|
||||
gcc -o client client.c -lssl -lcrypto
|
||||
```
|
||||
|
||||
该程序尝试打开与网站 [www.google.com][13] 的安全连接。在与 Google Web 服务器的 TLS 握手过程中,`client` 程序会收到一个或多个数字证书,该程序会尝试对其进行验证(但在我的系统上失败了)。尽管如此,`client` 程序仍继续通过安全通道获取 Google 主页。该程序取决于前面提到的安全工件,尽管在上述代码中只着重突出了数字证书。但其它工件仍在幕后发挥作用,稍后将对它们进行详细说明。
|
||||
|
||||
通常,打开 HTTP(非安全)通道的 C 或 C++ 的客户端程序将使用诸如*文件描述符*或*网络套接字*之类的结构,它们是两个进程(例如,这个 `client` 程序和 Google Web 服务器)之间连接的端点。另一方面,文件描述符是一个非负整数值,用于在程序中标识该程序打开的任何文件类的结构。这样的程序还将使用一种结构来指定有关 Web 服务器地址的详细信息。
|
||||
|
||||
这些相对较低级别的结构不会出现在客户端程序中,因为 OpenSSL 库会将套接字基础设施和地址规范等封装在更高层面的安全结构中。其结果是一个简单的 API。下面首先看一下 `client` 程序示例中的安全性详细信息。
|
||||
|
||||
* 该程序首先加载相关的 OpenSSL 库,我的函数 `init_ssl` 中对 OpenSSL 进行了两次调用:
|
||||
|
||||
```
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
```
|
||||
* 下一个初始化步骤尝试获取安全*上下文*,这是建立和维护通往 Web 服务器的安全通道所需的信息框架。如对 OpenSSL 库函数的调用所示,在示例中使用了 TLS 1.2:
|
||||
|
||||
```
|
||||
const SSL_METHOD* method = TLSv1_2_client_method(); /* TLS 1.2 */
|
||||
```
|
||||
|
||||
如果调用成功,则将 `method` 指针被传递给库函数,该函数创建类型为 `SSL_CTX` 的上下文:
|
||||
|
||||
```
|
||||
SSL_CTX* ctx = SSL_CTX_new(method);
|
||||
```
|
||||
|
||||
`client` 程序会检查每个关键的库调用的错误,如果其中一个调用失败,则程序终止。
|
||||
* 现在还有另外两个 OpenSSL 工件也在发挥作用:SSL 类型的安全会话,从头到尾管理安全连接;以及类型为 BIO(<ruby>基本输入/输出<rt>Basic Input/Output</rt></ruby>)的安全流,用于与 Web 服务器进行通信。BIO 流是通过以下调用生成的:
|
||||
|
||||
```
|
||||
BIO* bio = BIO_new_ssl_connect(ctx);
|
||||
```
|
||||
|
||||
请注意,这个最重要的上下文是其参数。`BIO` 类型是 C 语言中 `FILE` 类型的 OpenSSL 封装器。此封装器可保护 `client` 程序与 Google 的网络服务器之间的输入和输出流的安全。
|
||||
* 有了 `SSL_CTX` 和 `BIO`,然后程序在 SSL 会话中将它们组合在一起。三个库调用可以完成工作:
|
||||
|
||||
```
|
||||
BIO_get_ssl(bio, &ssl); /* 会话 */
|
||||
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* 鲁棒性 */
|
||||
BIO_set_conn_hostname(bio, name); /* 准备连接 */
|
||||
```
|
||||
|
||||
安全连接本身是通过以下调用建立的:
|
||||
|
||||
```
|
||||
BIO_do_connect(bio);
|
||||
```
|
||||
|
||||
如果最后一个调用不成功,则 `client` 程序终止;否则,该连接已准备就绪,可以支持 `client` 程序与 Google Web 服务器之间的机密对话。
|
||||
|
||||
在与 Web 服务器握手期间,`client` 程序会接收一个或多个数字证书,以认证服务器的身份。但是,`client` 程序不会发送自己的证书,这意味着这个身份验证是单向的。(Web 服务器通常配置为**不**需要客户端证书)尽管对 Web 服务器证书的验证失败,但 `client` 程序仍通过了连接到 Web 服务器的安全通道继续获取 Google 主页。
|
||||
|
||||
为什么验证 Google 证书的尝试会失败?典型的 OpenSSL 安装目录为 `/etc/ssl/certs`,其中包含 `ca-certificates.crt` 文件。该目录和文件包含着 OpenSSL 自带的数字证书,以此构成<ruby>信任库<rt>truststore</rt></ruby>。可以根据需要更新信任库,尤其是可以包括新信任的证书,并删除不再受信任的证书。
|
||||
|
||||
`client` 程序从 Google Web 服务器收到了三个证书,但是我的计算机上的 OpenSSL 信任库并不包含完全匹配的证书。如目前所写,`client` 程序不会通过例如验证 Google 证书上的数字签名(一个用来证明该证书的签名)来解决此问题。如果该签名是受信任的,则包含该签名的证书也应受信任。尽管如此,`client` 程序仍继续获取页面,然后打印出 Google 的主页。下一节将更详细地介绍这些。
|
||||
|
||||
### 客户端程序中隐藏的安全性
|
||||
|
||||
让我们从客户端示例中可见的安全工件(数字证书)开始,然后考虑其他安全工件如何与之相关。数字证书的主要格式标准是 X509,生产级的证书由诸如 [Verisign][14] 的<ruby>证书颁发机构<rt>Certificate Authority</rt></ruby>(CA)颁发。
|
||||
|
||||
数字证书中包含各种信息(例如,激活日期和失效日期以及所有者的域名),也包括发行者的身份和*数字签名*(这是加密过的*加密哈希*值)。证书还具有未加密的哈希值,用作其标识*指纹*。
|
||||
|
||||
哈希值来自将任意数量的二进制位映射到固定长度的摘要。这些位代表什么(会计报告、小说或数字电影)无关紧要。例如,<ruby>消息摘要版本 5<rt>Message Digest version 5</rt></ruby>(MD5)哈希算法将任意长度的输入位映射到 128 位哈希值,而 SHA1(<ruby>安全哈希算法版本 1<rt>Secure Hash Algorithm version 1</rt></ruby>)算法将输入位映射到 160 位哈希值。不同的输入位会导致不同的(实际上在统计学上是唯一的)哈希值。下一篇文章将会进行更详细的介绍,并着重介绍什么使哈希函数具有加密功能。
|
||||
|
||||
数字证书的类型有所不同(例如根证书、中间证书和最终实体证书),并形成了反映这些证书类型的层次结构。顾名思义,*根*证书位于层次结构的顶部,其下的证书继承了根证书所具有的信任。OpenSSL 库和大多数现代编程语言都具有 X509 数据类型以及处理此类证书的函数。来自 Google 的证书具有 X509 格式,`client` 程序会检查该证书是否为 `X509_V_OK`。
|
||||
|
||||
X509 证书基于<ruby>公共密钥基础结构<rt>public-key infrastructure</rt></ruby>(PKI),其中包括的算法(RSA 是占主导地位的算法)用于生成*密钥对*:公共密钥及其配对的私有密钥。公钥是一种身份:[Amazon][15] 的公钥对其进行标识,而我的公钥对我进行标识。私钥应由其所有者负责保密。
|
||||
|
||||
成对出现的密钥具有标准用途。可以使用公钥对消息进行加密,然后可以使用同一个密钥对中的私钥对消息进行解密。私钥也可以用于对文档或其他电子工件(例如程序或电子邮件)进行签名,然后可以使用该对密钥中的公钥来验证签名。以下两个示例补充了一些细节。
|
||||
|
||||
在第一个示例中,Alice 将她的公钥分发给全世界,包括 Bob。然后,Bob 用 Alice 的公钥加密邮件,然后将加密的邮件发送给 Alice。用 Alice 的公钥加密的邮件将可以用她的私钥解密(假设是她自己的私钥),如下所示:
|
||||
|
||||
```
|
||||
+------------------+ encrypted msg +-------------------+
|
||||
Bob's msg--->|Alice's public key|--------------->|Alice's private key|---> Bob's msg
|
||||
+------------------+ +-------------------+
|
||||
```
|
||||
|
||||
理论上可以在没有 Alice 的私钥的情况下解密消息,但在实际情况中,如果使用像 RSA 这样的加密密钥对系统,则在计算上做不到。
|
||||
|
||||
现在,第二个示例,请对文档签名以证明其真实性。签名算法使用密钥对中的私钥来处理要签名的文档的加密哈希:
|
||||
|
||||
```
|
||||
+-------------------+
|
||||
Hash of document--->|Alice's private key|--->Alice's digital signature of the document
|
||||
+-------------------+
|
||||
```
|
||||
|
||||
假设 Alice 以数字方式签署了发送给 Bob 的合同。然后,Bob 可以使用 Alice 密钥对中的公钥来验证签名:
|
||||
|
||||
```
|
||||
+------------------+
|
||||
Alice's digital signature of the document--->|Alice's public key|--->verified or not
|
||||
+------------------+
|
||||
```
|
||||
|
||||
假若没有 Alice 的私钥,就无法轻松伪造 Alice 的签名:因此,Alice 有必要保密她的私钥。
|
||||
|
||||
在 `client` 程序中,除了数字证书以外,这些安全性都没有明确展示。下一篇文章使用使用 OpenSSL 实用程序和库函数的示例填充更多详细的信息。
|
||||
|
||||
### 命令行的 OpenSSL
|
||||
|
||||
同时,让我们看一下 OpenSSL 命令行实用程序:特别是在 TLS 握手期间检查来自 Web 服务器的证书的实用程序。调用 OpenSSL 实用程序可以使用 `openssl` 命令,然后添加参数和标志的组合以指定所需的操作。
|
||||
|
||||
看看以下命令:
|
||||
|
||||
```
|
||||
openssl list-cipher-algorithms
|
||||
```
|
||||
|
||||
该输出是组成<ruby>加密算法套件<rt>cipher suite<rt></ruby>的相关算法的列表。下面是列表的开头,加了澄清首字母缩写词的注释:
|
||||
|
||||
```
|
||||
AES-128-CBC ## Advanced Encryption Standard, Cipher Block Chaining
|
||||
AES-128-CBC-HMAC-SHA1 ## Hash-based Message Authentication Code with SHA1 hashes
|
||||
AES-128-CBC-HMAC-SHA256 ## ditto, but SHA256 rather than SHA1
|
||||
...
|
||||
```
|
||||
|
||||
下一条命令使用参数 `s_client` 将打开到 [www.google.com][13] 的安全连接,并在屏幕上显示有关此连接的所有信息:
|
||||
|
||||
```
|
||||
openssl s_client -connect www.google.com:443 -showcerts
|
||||
```
|
||||
|
||||
端口号 443 是 Web 服务器用于接收 HTTPS(而不是 HTTP 连接)的标准端口号。(对于 HTTP,标准端口为 80)Web 地址 www.google.com:443 也出现在 `client` 程序的代码中。如果尝试连接成功,则将显示来自 Google 的三个数字证书以及有关安全会话、正在使用的加密算法套件以及相关项目的信息。例如,这是开头的部分输出,它声明*证书链*即将到来。证书的编码为 base64:
|
||||
|
||||
|
||||
```
|
||||
Certificate chain
|
||||
0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=www.google.com
|
||||
i:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEijCCA3KgAwIBAgIQdCea9tmy/T6rK/dDD1isujANBgkqhkiG9w0BAQsFADBU
|
||||
MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZpY2VzMSUw
|
||||
...
|
||||
```
|
||||
|
||||
诸如 Google 之类的主要网站通常会发送多个证书进行身份验证。
|
||||
|
||||
输出以有关 TLS 会话的摘要信息结尾,包括加密算法套件的详细信息:
|
||||
|
||||
```
|
||||
SSL-Session:
|
||||
Protocol : TLSv1.2
|
||||
Cipher : ECDHE-RSA-AES128-GCM-SHA256
|
||||
Session-ID: A2BBF0E4991E6BBBC318774EEE37CFCB23095CC7640FFC752448D07C7F438573
|
||||
...
|
||||
```
|
||||
|
||||
`client` 程序中使用了协议 TLS 1.2,`Session-ID` 唯一地标识了 `openssl` 实用程序和 Google Web 服务器之间的连接。`Cipher` 条目可以按以下方式进行解析:
|
||||
|
||||
* `ECDHE`(<ruby>椭圆曲线 Diffie-Hellman(临时)<rt>Elliptic Curve Diffie Hellman Ephemeral</rt></ruby>)是一种用于管理 TLS 握手的高效的有效算法。尤其是,ECDHE 通过确保连接双方(例如,`client` 程序和 Google Web 服务器)使用相同的加密/解密密钥(称为*会话密钥*)来解决“密钥分发问题”。后续文章会深入探讨该细节。
|
||||
* `RSA`(Rivest Shamir Adleman)是主要的公共密钥密码系统,并以 1970 年代末首次描述了该系统的三位学者的名字命名。这个正在使用的密钥对是使用 RSA 算法生成的。
|
||||
* `AES128`(<ruby>高级加密标准<rt>Advanced Encryption Standard</rt></ruby>)是一种<ruby>块式加密算法<rt>block cipher</rt></ruby>,用于加密和解密<ruby>位块<rt>blocks of bits</rt></ruby>。(另一种算法是<ruby>流式加密算法<rt>stream cipher</rt></ruby>,它一次加密和解密一个位。)这个加密算法是对称加密算法,因为使用同一个密钥进行加密和解密,这首先引起了密钥分发问题。AES 支持 128(此处使用)、192 和 256 位的密钥大小:密钥越大,安全性越好。
|
||||
|
||||
通常,像 AES 这样的对称加密系统的密钥大小要小于像 RSA 这样的非对称(基于密钥对)系统的密钥大小。例如,1024 位 RSA 密钥相对较小,而 256 位密钥则当前是 AES 最大的密钥。
|
||||
* `GCM`(<ruby>伽罗瓦计数器模式<rt>Galois Counter Mode</rt></ruby>)处理在安全对话期间重复应用的加密算法(在这种情况下为 AES128)。AES128 块的大小仅为 128 位,安全对话很可能包含从一侧到另一侧的多个 AES128 块。GCM 非常有效,通常与 AES128 搭配使用。
|
||||
* `SHA256`(<ruby>256 位安全哈希算法<rt>Secure Hash Algorithm 256 bits</rt></ruby>)是我们正在使用的加密哈希算法。生成的哈希值的大小为 256 位,尽管使用 SHA 甚至可以更大。
|
||||
|
||||
加密算法套件正在不断发展中。例如,不久前,Google 使用 RC4 流加密算法(RSA 的 Ron Rivest 后来开发的 Ron's Cipher 版本 4)。 RC4 现在有已知的漏洞,这大概部分导致了 Google 转换为 AES128。
|
||||
|
||||
### 总结
|
||||
|
||||
我们通过安全的 C Web 客户端和各种命令行示例对 OpenSSL 做了首次了解,使一些需要进一步阐明的主题脱颖而出。[下一篇文章会详细介绍][17],从加密散列开始,到对数字证书如何应对密钥分发挑战为结束的更全面讨论。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/6/cryptography-basics-openssl-part-1
|
||||
|
||||
作者:[Marty Kalin][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mkalindepauledu/users/akritiko/users/clhermansen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/BUSINESS_3reasons.png?itok=k6F3-BqA (A lock on the side of a building)
|
||||
[2]: https://www.openssl.org/
|
||||
[3]: https://www.howtoforge.com/tutorial/how-to-install-openssl-from-source-on-linux/
|
||||
[4]: http://condor.depaul.edu/mkalin
|
||||
[5]: https://en.wikipedia.org/wiki/Transport_Layer_Security
|
||||
[6]: https://en.wikipedia.org/wiki/Netscape
|
||||
[7]: http://www.opengroup.org/onlinepubs/009695399/functions/perror.html
|
||||
[8]: http://www.opengroup.org/onlinepubs/009695399/functions/exit.html
|
||||
[9]: http://www.opengroup.org/onlinepubs/009695399/functions/sprintf.html
|
||||
[10]: http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html
|
||||
[11]: http://www.opengroup.org/onlinepubs/009695399/functions/memset.html
|
||||
[12]: http://www.opengroup.org/onlinepubs/009695399/functions/puts.html
|
||||
[13]: http://www.google.com
|
||||
[14]: https://www.verisign.com
|
||||
[15]: https://www.amazon.com
|
||||
[16]: http://www.google.com:443
|
||||
[17]: https://opensource.com/article/19/6/cryptography-basics-openssl-part-2
|
@ -1,18 +1,20 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (chen-ni)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11746-1.html)
|
||||
[#]: subject: (How to make an old computer useful again)
|
||||
[#]: via: (https://opensource.com/article/19/7/how-make-old-computer-useful-again)
|
||||
[#]: author: (Howard Fosdick https://opensource.com/users/howtechhttps://opensource.com/users/don-watkinshttps://opensource.com/users/suehlehttps://opensource.com/users/aseem-sharmahttps://opensource.com/users/sethhttps://opensource.com/users/marcobravohttps://opensource.com/users/dragonbitehttps://opensource.com/users/don-watkinshttps://opensource.com/users/jamesfhttps://opensource.com/users/seth)
|
||||
[#]: author: (Howard Fosdick https://opensource.com/users/howtech)
|
||||
|
||||
如何把你的老爷机重新利用起来
|
||||
======
|
||||
按照下面各个步骤,让你的老爷机焕然一新
|
||||
![Person typing on a 1980's computer][1]
|
||||
|
||||
你的地下室里有一台用来落灰的旧电脑?为什么不把它利用起来呢?如果你常用的电脑坏了,又想用一个比手机更大的屏幕上网,这时候一台备用电脑可能就派上用场了。或者,它也可以充当一台全家人共用的廉价辅助电脑,甚至还可以改造为一台复古游戏机。
|
||||
> 按照下面各个步骤,让你的老爷机焕然一新。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/04/064017ztl28cn6jjhnjnaj.jpg)
|
||||
|
||||
你的地下室里是不是有一台用来落灰的旧电脑?为什么不把它利用起来呢?如果你常用的电脑坏了,又想用一个比手机更大的屏幕上网,这时候一台备用电脑可能就派上用场了。或者,它也可以充当一台全家人共用的廉价备用电脑,甚至还可以改造为一台复古游戏机。
|
||||
|
||||
哪怕是一台有十余年历史的老爷机,只要选对了软件,也可以胜任很多新电脑能够完成的任务。其中的关键在于,要使用开源软件。
|
||||
|
||||
@ -22,7 +24,7 @@
|
||||
|
||||
第一步是检查硬件是否运转正常。如果在这个环节忽略了一个问题,后面可能会让你非常头疼。
|
||||
|
||||
灰尘是电子器件的天敌,所以第一件事应该是打开机箱,清除灰尘。[压缩空气设备][2]这时候可能会派上用场。请注意,进行任何接触电脑的操作时,都应该确保你是[触地][3]的。此外,_避免_ 清洁布与任何物体发生摩擦。即使是一次小到你无法察觉的静电放电,也可能会导致电路损毁。
|
||||
灰尘是电子器件的天敌,所以第一件事应该是打开机箱,清除灰尘。[压缩空气设备][2]这时候可能会派上用场。请注意,进行任何接触电脑的操作时,都应该确保[接地][3]。此外,**不要**用清洁布擦拭任何东西。即使是一次小到你无法察觉的静电放电,也可能会导致电路损毁。
|
||||
|
||||
清洁工作完成之后,关闭机箱,检查是否所有硬件都可以正常工作。需要测试的项目包括:
|
||||
|
||||
@ -31,23 +33,23 @@
|
||||
* 主板
|
||||
* 外围设备(DVD 驱动器、USB 接口、声卡,等等)
|
||||
|
||||
首先,将计算机启动面板([UEFI][4] 或者是 [BIOS][5]面板)上的诊断测试依次运行一遍。如果不知道按哪个程序功能按键进入你电脑的启动面板,可以参考 [这份列表][6]。
|
||||
首先,将计算机启动界面([UEFI][4] 或者是 [BIOS][5] 界面)上的诊断测试依次运行一遍。如果不知道按哪个功能键进入你电脑的启动界面,可以参考 [这份列表][6]。
|
||||
|
||||
此外,也可以使用诸如 [Hirens BootCD][7] 和 [Ultimate Boot CD][8] 之类的免费资源工具包,进行启动面板覆盖不到的测试。这些资源工具包涵盖了数百个测试程序,并且都是免费的,尽管不都开源。运行这些工具包无需安装任何软件,因为它们都是从 U 盘或者 DVD 驱动器启动的。
|
||||
此外,也可以使用诸如 [Hirens BootCD][7] 和 [Ultimate Boot CD][8] 之类的免费资源工具包,进行启动界面覆盖不到的测试。这些资源工具包涵盖了数百个测试程序,并且都是免费的,尽管不都开源。运行这些工具包无需安装任何软件,因为它们都是从 U 盘或者 DVD 驱动器启动的。
|
||||
|
||||
测试一定要彻底!对于内存和硬盘来说,基础测试还不够,应该运行深度测试,哪怕是运行一整夜也无妨。只有这样,才能够查出那些不易发现的瞬时故障。
|
||||
|
||||
如果发现了问题,可以参考我的[硬件故障排除快速指南][9],帮你解决最常见的硬件问题。
|
||||
如果发现了问题,可以参考我的[硬件故障排除快速指南][9],可以帮你解决最常见的硬件问题。
|
||||
|
||||
### 选择软件
|
||||
|
||||
电脑翻新的关键在于,根据手头的硬件资源,恰如其分地安装软件。最核心的三种硬件资源分别是:
|
||||
|
||||
1. 处理器(核数、速度)
|
||||
1. 处理器(内核数、速度)
|
||||
2. 内存
|
||||
3. 显存
|
||||
|
||||
可以在启动时的 UEFI/BIOS 面板上,弄清楚你电脑的硬件资源。记得抄下数据,以免遗忘。接下来,可以在 [CPU Benchmark][10] 网站上查看你的处理器,该网站除了提供 CPU 的背景资料,还提供一个 CPU 的性能分数。
|
||||
可以在启动时的 UEFI/BIOS 界面上,弄清楚你电脑的硬件资源。记得抄下数据,以免遗忘。接下来,可以在 [CPU Benchmark][10] 网站上查看你的处理器,该网站除了提供 CPU 的背景资料,还提供一个 CPU 的性能分数。
|
||||
|
||||
了解了硬件性能之后,就可以选择能够在硬件上高效运行的软件了。软件的选择涉及四个重要的层面:
|
||||
|
||||
@ -56,19 +58,19 @@
|
||||
3. 浏览器
|
||||
4. 应用
|
||||
|
||||
一个优秀的 Linux 发行版可以满足全部四个层面。不要试图使用已经停止维护的 Windows 版本,例如 Windows 8、Vista 或者 XP,哪怕已经安装在电脑上了。恶意软件的 [风险][11] 你是承受不起的。明智的做法是,使用一个更抗病毒的最新版本操作系统。
|
||||
一个优秀的 Linux 发行版可以满足上述全部四个层面。不要试图使用已经停止维护的 Windows 版本,例如 Windows 8、Vista 或者 XP,哪怕已经安装在电脑上了。恶意软件的[风险][11]你是承受不起的。明智的做法是,使用一个更抗病毒的最新版本操作系统。
|
||||
|
||||
那 Windows 7 呢?[维护宽限期][12] 至 2020 年 1 月 14 日结束,也就是说,在此日期之前你还可以获得安全补丁,之后就想都别想了。现在正是迁出 Windows 7 的绝佳时机。
|
||||
那 Windows 7 呢?[维护宽限期][12]至 2020 年 1 月 14 日结束,也就是说,在此日期之前你还可以获得安全补丁,之后就想都别想了。现在正是迁出 Windows 7 的绝佳时机。
|
||||
|
||||
Linux 的巨大优势在于,有许多专门为过时硬件设计的[发行版][13]。此外,[桌面环境][14]和操作系统在 Linux 的设计中是分开的,你可以自行选择搭配。这一点非常重要,因为桌面环境对低端系统的性能有很大影响。(对于 Windows 和 MacOS 来说,你选择的操作系统版本决定了桌面环境,没得可选。)
|
||||
|
||||
Linux 的另一个好处是,拥有数以千计免费且开源的应用,不需要担心激活或者许可的问题。此外,Linux 是可移植的,可以在不同的分区、硬盘、设备或计算机之间,对操作系统和应用进行复制、移动或克隆。(Windows 则不然,使用注册表将系统捆绑在了所安装的计算机上。)
|
||||
Linux 的另一个好处是,拥有数以千计自由且开源的应用,不需要担心激活或者许可的问题。此外,Linux 是可移植的,可以在不同的分区、硬盘、设备或计算机之间,对操作系统和应用进行复制、移动或克隆。(Windows 则不然,使用注册表将系统捆绑在了所安装的计算机上。)
|
||||
|
||||
### 翻新后的电脑能够做什么?
|
||||
|
||||
这里讨论的是大约在 2006 年到 2013 年之间生产的双核计算机,尤其是装载了 [Intel 酷睿 2][15] CPU 或 [AMD 速龙 64 X2][16] 系列处理器的计算机。它们的 [CPU 分数][10] 大多在 1000 到 4000 分之间。这种电脑卖不了几个钱,但用在运行轻量级 Linux 软件上,性能还是足够的。
|
||||
|
||||
有一点需要注意:你的电脑应该至少拥有 2 GB 内存,如果不够就进行升级。就我翻新过的电脑来说,使用者用到的内存(不包括数据缓存)一般在 0.5 到 2 GB 之间,很少超过 2 GB。如果将内存升级到 2GB,系统就不至于进行 _swap_,即将硬盘当做内存使用。如果想要获得良好性能,这一点十分关键。
|
||||
有一点需要注意:你的电脑应该至少拥有 2 GB 内存,如果不够就进行升级。就我翻新过的电脑来说,使用者用到的内存(不包括数据缓存)一般在 0.5 到 2 GB 之间,很少超过 2 GB。如果将内存升级到 2GB,系统就不至于进行**内存交换**,即将硬盘当做内存使用。如果想要获得良好性能,这一点十分关键。
|
||||
|
||||
以我自己为例,我用来写作这篇文章的是一台有十年机龄的翻新电脑。取下 1 GB 内存条之后,它只剩下了 1 GB 内存,然后就慢得像一只乌龟。用它浏览网页,或者是做类似的事情,都让人感到灰心,甚至痛苦。一旦把内存条重新装回去,有了 2 GB 内存,它就立马回到了可以使用的状态。
|
||||
|
||||
@ -76,19 +78,19 @@ Linux 的另一个好处是,拥有数以千计免费且开源的应用,不
|
||||
|
||||
### 翻新电脑的局限
|
||||
|
||||
那么,这些老爷机又有什么局限呢?由于它们的并发比不上最先进的计算机,所以应该使用轻量级浏览器,同时拦截广告(广告正是让网页加载变慢的罪魁祸首)。如果可以使用虚拟专用网络(VPN)拦截广告,为处理器免除广告加载的负荷,那就再好不过。此外,还需要禁用视频自动播放、Flash 以及网页动效;将浏览器上的标签页控制在少数几个,不要同时打开 20 个;以及下载可以开启/禁用 JavaScript 的浏览器扩展。
|
||||
那么,这些老爷机又有什么局限呢?由于它们的并发性能比不上最先进的计算机,所以应该使用轻量级浏览器,同时拦截广告(广告正是让网页加载变慢的罪魁祸首)。如果可以使用虚拟专用网络(VPN)拦截广告,为处理器免除加载广告的负荷,那就再好不过。此外,还需要禁用视频自动播放、Flash 以及网页动画效果;将浏览器上的标签页控制在少数几个,不要同时打开 20 个;以及下载可以开启/禁用 JavaScript 的浏览器扩展。
|
||||
|
||||
让处理器专注于你目前正在做的事情吧,不要同时打开一堆应用,也不要在后台运行很多程序。图像编辑和视频编辑的高级工具可能会很慢;至于运行虚拟机,还是别想了吧。
|
||||
|
||||
那游戏呢?开源软件仓库提供了数以千计的游戏。这就是我为什么将显存列为三种核心硬件资源之一。如果你的电脑没有显卡,那很可能只有 32 或者 64 MB 的显存。可以增加一张显卡,将显存提升到 256 或者 512 MB,这样一来,处理器密集型的游戏就会顺畅很多了。如果不确定你的电脑有多少显存,可以参考[这里][17]。请注意,需要确保显卡与电脑的[显卡插槽][18](AGP、PCI-Express 或者 PCI)相匹配,同时使用正确的[连接线][19](VGA、DVI 或者 HDMI)。
|
||||
那游戏呢?开源软件仓库提供了数以千计的游戏。这就是我为什么将显存列为三种核心硬件资源之一。如果你的电脑没有独立显卡,那很可能只有 32 或者 64 MB 的显存。可以增加一张显卡,将显存提升到 256 或者 512 MB,这样一来,处理器密集型的游戏就会顺畅很多了。如果不确定你的电脑有多少显存,可以参考[这里][17]。请注意,需要确保显卡与电脑的[显卡插槽][18](AGP、PCI-Express 或者 PCI)相匹配,同时使用正确的[连接线][19](VGA、DVI 或者 HDMI)。
|
||||
|
||||
#### 与 Windows 系统的兼容性如何?
|
||||
|
||||
许多人关心 Linux 与 Windows 系统的兼容性如何。首先,对于任何一个 Windows 程序,都有一个 [Linux 的替代版本][20]。
|
||||
许多人关心 Linux 与 Windows 系统的兼容性如何。首先,对于任何一个 Windows 程序,都有一个 [Linux 版本的替代品][20]。
|
||||
|
||||
退一步说,即使你真的必须要运行某个特定的 Windows 程序,通常也可以借助 [Wine][21] 在 Linux 上运行。可以在 [Wine 数据库][22] 里查找一下你的应用,看看是否可以在 Wine 上运行,顺便学一些特殊安装技巧。[Winetricks][23] 和 [PlayOnLinux][24] 这两个辅助工具可以帮助你进行安装和配置。
|
||||
|
||||
Wine 的另一个优势是可以运行 Windows 的老版本,例如 Vista、XP、ME/98/95 和 3.1。我认识一个家伙,搭建了一台特别赞的游戏电脑,然后用来玩 XP 上的老游戏。借助 [DOSBox][26],你甚至还可以运行数以千计的[免费 DOS 程序][25]。但是有一点需要注意,如果 Windows 程序可以运行,那么 Windows [病毒][27]也同样可以。你需要保护 Linux 上的 Wine 环境,正如保护任何其他 Windows 环境一样。
|
||||
Wine 的另一个优势是可以运行旧版本 Windows(例如 Vista、XP、ME/98/95 和 3.1)上的程序。我认识一个家伙,搭建了一台特别赞的游戏电脑,然后用来玩 XP 上的老游戏。借助 [DOSBox][26],你甚至还可以运行数以千计的[免费 DOS 程序][25]。但是有一点需要注意,如果 Windows 程序可以运行,那么 Windows [病毒][27]也同样可以。你需要保护 Linux 上的 Wine 环境,正如保护任何其他 Windows 环境一样。
|
||||
|
||||
对了,与 Microsoft Office 的兼容性又如何呢?我使用的是 LibreOffice,经常编辑并交换 Word 和 Excel 文件,完全没有问题。不过,你应该避免使用隐晦或者过于专业的功能。
|
||||
|
||||
@ -96,7 +98,7 @@ Wine 的另一个优势是可以运行 Windows 的老版本,例如 Vista、XP
|
||||
|
||||
假设选择了 Linux 作为操作系统,那么你还需要选择桌面环境、浏览器和各种应用。最简单的方法是,安装一个包含了你所需要的一切的发行版。
|
||||
|
||||
通过从 [live USB][28] U 盘或者 DVD 启动,你无需安装任何程序,就可以尝试不同的发行版。关于在 Linux 或 Windows 上创建可启动的 Linux 的方法,可以参考 [这里][29]。
|
||||
通过从 [live USB][28] U 盘或者 DVD 启动,你无需安装任何程序,就可以尝试不同的发行版。关于在 Linux 或 Windows 上创建可启动的 Linux 的方法,可以参考[这里][29]。
|
||||
|
||||
我进行电脑翻新的目的是做慈善,所以无法指望电脑使用者具备任何相应知识。我需要的发行版应该具有以下特性:
|
||||
|
||||
@ -110,11 +112,11 @@ Wine 的另一个优势是可以运行 Windows 的老版本,例如 Vista、XP
|
||||
* 更重视可靠性,而不是尖端功能
|
||||
* 可以通过图形化用户界面进行设置,而不是只能通过文本文件进行设置
|
||||
|
||||
许多发行版都能够满足上面的要求。我曾经尝试成功的有 [Mint/Xfce][30]、[Xubuntu,][31] 和 [Lubuntu][32]。前两个发行版使用 Xfce 桌面环境,第三个使用 LXQt。相比 GNOME、Unity、KDE、MATE 和 Cinnamon 这些桌面环境,运行上面这两种桌面环境只需要[更少][33]的处理器和内存资源。
|
||||
许多发行版都能够满足上面的要求。我曾经尝试成功的有 [Mint/Xfce][30]、[Xubuntu][31] 和 [Lubuntu][32]。前两个发行版使用 Xfce 桌面环境,第三个使用 LXQt。相比 GNOME、Unity、KDE、MATE 和 Cinnamon 这些桌面环境,运行上面这两种桌面环境只需要[更少][33]的处理器和内存资源。
|
||||
|
||||
Xfce 和 LXQt 用起来非常简单,我的客户们之前从未见过 Linux,但是都可以自如使用这些简单的、菜单驱动的用户界面。
|
||||
|
||||
对于旧电脑来说,运行最快速、最高效的浏览器是一件非常重要的事情。[很多人觉得][34] Chromium 是最好的浏览器;此外,我还安装了 Firefox Quantum,因为大家比较熟悉它,并且[它的性能][35]可以和 [Chromium 的性能][36]媲美。我还加上了 Opera,因为它速度快,而且有一些独特功能,比如内置的广告拦截,以及免费的 [虚拟专用网络][37]。Opera 是免费的,但并非开源。
|
||||
对于旧电脑来说,运行最快速、最高效的浏览器是一件非常重要的事情。[很多人觉得][34] Chromium 是最好的浏览器;此外,我还安装了 Firefox Quantum,因为大家比较熟悉它,并且[它的性能][35]可以和 [Chromium 的性能][36]媲美。我还加上了 Opera,因为它速度快,而且有一些独特功能,比如内置的广告拦截,以及免费的[虚拟专用网络][37]。Opera 是免费的,但并非开源。
|
||||
|
||||
无论你使用什么浏览器,一定要拦截广告和追踪器,尽量降低浏览器的负荷。此外,除非得到你的明确批准,否则视频和 Flash 都不应该被允许运行。
|
||||
|
||||
@ -122,7 +124,7 @@ Xfce 和 LXQt 用起来非常简单,我的客户们之前从未见过 Linux,
|
||||
|
||||
### 行动起来吧
|
||||
|
||||
你是否会对翻新后的电脑感到满意呢?就我来说,我所使用的两台电脑的机龄都已经超过十年了,其中一个装载的是 Intel 双核处理器([eMachines T5274a][38]),另一个装载的是 AMD 速龙 64 x2 处理器([HP dc5750][39]),两台电脑都有 2 GB 内存。它们和我的另一台具备四核 i5 处理器和 16 GB 内存 的电脑一样,完全能够胜任我的办公工作。如果说有什么功能是这两台电脑缺失的,那就是运行虚拟机了。
|
||||
你是否会对翻新后的电脑感到满意呢?就我来说,我所使用的两台电脑的机龄都已经超过十年了,其中一个装载的是 Intel 双核处理器([eMachines T5274a][38]),另一个装载的是 AMD 速龙 64 x2 处理器([HP dc5750][39]),两台电脑都有 2 GB 内存。它们和我的另一台具备四核 i5 处理器和 16 GB 内存的电脑一样,完全能够胜任我的办公工作。如果说有什么功能是这两台电脑缺失的,那就是运行虚拟机了。
|
||||
|
||||
我们生活在一个非常神奇的年代。只需要付出一点努力,就可以将一台机龄在 5 到 12 年之间的旧电脑,翻新为一台具备实用价值的机器。还有什么比这更有趣吗?
|
||||
|
||||
@ -133,11 +135,11 @@ via: https://opensource.com/article/19/7/how-make-old-computer-useful-again
|
||||
作者:[Howard Fosdick][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[chen-ni](https://github.com/chen-ni)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/howtechhttps://opensource.com/users/don-watkinshttps://opensource.com/users/suehlehttps://opensource.com/users/aseem-sharmahttps://opensource.com/users/sethhttps://opensource.com/users/marcobravohttps://opensource.com/users/dragonbitehttps://opensource.com/users/don-watkinshttps://opensource.com/users/jamesfhttps://opensource.com/users/seth
|
||||
[a]: https://opensource.com/users/howtech
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/1980s-computer-yearbook.png?itok=eGOYEKK- (Person typing on a 1980's computer)
|
||||
[2]: https://www.amazon.com/s/ref=nb_sb_noss_1?url=search-alias%3Daps&field-keywords=compressed+air+for+computers&rh=i%3Aaps%2Ck%3Acompressed+air+for+computers
|
@ -0,0 +1,127 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11752-1.html)
|
||||
[#]: subject: (An advanced look at Python interfaces using zope.interface)
|
||||
[#]: via: (https://opensource.com/article/19/9/zopeinterface-python-package)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshezhttps://opensource.com/users/lauren-pritchetthttps://opensource.com/users/sethhttps://opensource.com/users/drmjg)
|
||||
|
||||
借助 zope.interface 深入了解 Python 接口
|
||||
======
|
||||
|
||||
> Zope.interface 可以帮助声明存在哪些接口,是由哪些对象提供的,以及如何查询这些信息。
|
||||
|
||||
![Snake charmer cartoon with a yellow snake and a blue snake][1]
|
||||
|
||||
`zope.interface` 库可以克服 Python 接口设计中的歧义性。让我们来研究一下。
|
||||
|
||||
### 隐式接口不是 Python 之禅
|
||||
|
||||
[Python 之禅][2] 很宽松,但是有点自相矛盾,以至于你可以用它来例证任何东西。让我们来思考其中最著名的原则之一:“显示胜于隐式”。
|
||||
|
||||
传统上,在 Python 中会隐含的一件事是预期的接口。比如函数已经记录了它期望一个“类文件对象”或“序列”。但是什么是类文件对象呢?它支持 `.writelines`吗?`.seek` 呢?什么是一个“序列”?是否支持步进切片,例如 `a[1:10:2]`?
|
||||
|
||||
最初,Python 的答案是所谓的“鸭子类型”,取自短语“如果它像鸭子一样行走,像鸭子一样嘎嘎叫,那么它可能就是鸭子”。换句话说,“试试看”,这可能是你能得到的最具隐式的表达。
|
||||
|
||||
为了使这些内容显式地表达出来,你需要一种方法来表达期望的接口。[Zope][3] Web 框架是最早用 Python 编写的大型系统之一,它迫切需要这些东西来使代码明确呈现出来,例如,期望从“类似用户的对象”获得什么。
|
||||
|
||||
`zope.interface` 由 Zope 开发,但作为单独的 Python 包发布。`Zope.interface` 可以帮助声明存在哪些接口,是由哪些对象提供的,以及如何查询这些信息。
|
||||
|
||||
想象编写一个简单的 2D 游戏,它需要各种东西来支持精灵界面(LCTT 译注:“<ruby>精灵<rt> Sprite</rt></ruby>”是指游戏面板中各个组件)。例如,表示一个边界框,但也要表示对象何时与一个框相交。与一些其他语言不同,在 Python 中,将属性访问作为公共接口一部分是一种常见的做法,而不是实现 getter 和 setter。边界框应该是一个属性,而不是一个方法。
|
||||
|
||||
呈现精灵列表的方法可能类似于:
|
||||
|
||||
```
|
||||
def render_sprites(render_surface, sprites):
|
||||
"""
|
||||
sprites 应该是符合 Sprite 接口的对象列表:
|
||||
* 一个名为 "bounding_box" 的属性,包含了边界框
|
||||
* 一个名为 "intersects" 的方法,它接受一个边界框并返回 True 或 False
|
||||
"""
|
||||
pass # 一些做实际渲染的代码
|
||||
```
|
||||
|
||||
该游戏将具有许多处理精灵的函数。在每个函数中,你都必须在随附文档中指定预期。
|
||||
|
||||
此外,某些函数可能期望使用更复杂的精灵对象,例如具有 Z 序的对象。我们必须跟踪哪些方法需要 Sprite 对象,哪些方法需要 SpriteWithZ 对象。
|
||||
|
||||
如果能够使精灵是显式而直观的,这样方法就可以声明“我需要一个精灵”,并有个严格定义的接口,这不是很好吗?来看看 `zope.interface`。
|
||||
|
||||
```
|
||||
from zope import interface
|
||||
|
||||
class ISprite(interface.Interface):
|
||||
|
||||
bounding_box = interface.Attribute(
|
||||
"边界框"
|
||||
)
|
||||
|
||||
def intersects(box):
|
||||
"它和一个框相交吗?"
|
||||
```
|
||||
|
||||
乍看起来,这段代码有点奇怪。这些方法不包括 `self`,而包含 `self` 是一种常见的做法,并且它有一个**属性**。这是在 `zope.interface` 中声明接口的方法。这看起来很奇怪,因为大多数人不习惯严格声明接口。
|
||||
|
||||
这样做的原因是接口显示了如何调用方法,而不是如何定义方法。因为接口不是超类,所以它们可以用来声明数据属性。
|
||||
|
||||
下面是一个能带有圆形精灵的接口的一个实现:
|
||||
|
||||
```
|
||||
@implementer(ISprite)
|
||||
@attr.s(auto_attribs=True)
|
||||
class CircleSprite:
|
||||
x: float
|
||||
y: float
|
||||
radius: float
|
||||
|
||||
@property
|
||||
def bounding_box(self):
|
||||
return (
|
||||
self.x - self.radius,
|
||||
self.y - self.radius,
|
||||
self.x + self.radius,
|
||||
self.y + self.radius,
|
||||
)
|
||||
|
||||
def intersects(self, box):
|
||||
# 当且仅当至少一个角在圆内时,方框与圆相交
|
||||
top_left, bottom_right = box[:2], box[2:]
|
||||
for choose_x_from (top_left, bottom_right):
|
||||
for choose_y_from (top_left, bottom_right):
|
||||
x = choose_x_from[0]
|
||||
y = choose_y_from[1]
|
||||
if (((x - self.x) ` 2 + (y - self.y) ` 2) <=
|
||||
self.radius ` 2):
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
这**显式**声明了实现了该接口的 `CircleSprite` 类。它甚至能让我们验证该类是否正确实现了接口:
|
||||
|
||||
```
|
||||
from zope.interface import verify
|
||||
|
||||
def test_implementation():
|
||||
sprite = CircleSprite(x=0, y=0, radius=1)
|
||||
verify.verifyObject(ISprite, sprite)
|
||||
```
|
||||
|
||||
这可以由 pytest、nose 或其他测试框架运行,它将验证创建的精灵是否符合接口。测试通常是局部的:它不会测试仅在文档中提及的内容,甚至不会测试方法是否可以在没有异常的情况下被调用!但是,它会检查是否存在正确的方法和属性。这是对单元测试套件一个很好的补充,至少可以防止简单的拼写错误通过测试。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/9/zopeinterface-python-package
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshezhttps://opensource.com/users/lauren-pritchetthttps://opensource.com/users/sethhttps://opensource.com/users/drmjg
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/getting_started_with_python.png?itok=MFEKm3gl (Snake charmer cartoon with a yellow snake and a blue snake)
|
||||
[2]: https://en.wikipedia.org/wiki/Zen_of_Python
|
||||
[3]: http://zope.org
|
@ -0,0 +1,107 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11822-1.html)
|
||||
[#]: subject: (How the Linux screen tool can save your tasks – and your sanity – if SSH is interrupted)
|
||||
[#]: via: (https://www.networkworld.com/article/3441777/how-the-linux-screen-tool-can-save-your-tasks-and-your-sanity-if-ssh-is-interrupted.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
如果 SSH 被中断,Linux screen 工具如何拯救你的任务以及理智
|
||||
======
|
||||
|
||||
> 当你需要确保长时间运行的任务不会在 SSH 会话中断时被杀死时,Linux screen 命令可以成为救生员。以下是使用方法。
|
||||
|
||||
![](https://images.idgesg.net/images/article/2019/09/working_w_screen-shs-100812448-large.jpg)
|
||||
|
||||
如果因 SSH 会话断开而不得不重启一个耗时的进程,那么你可能会很高兴了解一个有趣的工具,可以用来避免此问题:`screen` 工具。
|
||||
|
||||
`screen` 是一个终端多路复用器,它使你可以在单个 SSH 会话中运行多个终端会话,并随时从它们之中脱离或重新接驳。做到这一点的过程非常简单,仅涉及少数命令。
|
||||
|
||||
要启动 `screen` 会话,只需在 SSH 会话中键入 `screen`。 然后,你可以开始启动需要长时间运行的进程,并在适当的时候键入 `Ctrl + A Ctrl + D` 从会话中脱离,然后键入 `screen -r` 重新接驳。
|
||||
|
||||
如果你要运行多个 `screen` 会话,更好的选择是为每个会话指定一个有意义的名称,以帮助你记住正在处理的任务。使用这种方法,你可以在启动每个会话时使用如下命令命名:
|
||||
|
||||
```
|
||||
$ screen -S slow-build
|
||||
```
|
||||
|
||||
一旦运行了多个会话,要重新接驳到一个会话,需要从列表中选择它。在以下命令中,我们列出了当前正在运行的会话,然后再重新接驳其中一个。请注意,一开始这两个会话都被标记为已脱离。
|
||||
|
||||
```
|
||||
$ screen -ls
|
||||
There are screens on:
|
||||
6617.check-backups (09/26/2019 04:35:30 PM) (Detached)
|
||||
1946.slow-build (09/26/2019 02:51:50 PM) (Detached)
|
||||
2 Sockets in /run/screen/S-shs
|
||||
```
|
||||
|
||||
然后,重新接驳到该会话要求你提供分配给会话的名称。例如:
|
||||
|
||||
```
|
||||
$ screen -r slow-build
|
||||
```
|
||||
|
||||
在脱离的会话中,保持运行状态的进程会继续进行处理,而你可以执行其他工作。如果你使用这些 `screen` 会话之一来查询 `screen` 会话情况,可以看到当前重新接驳的会话再次显示为 `Attached`。
|
||||
|
||||
```
|
||||
$ screen -ls
|
||||
There are screens on:
|
||||
6617.check-backups (09/26/2019 04:35:30 PM) (Attached)
|
||||
1946.slow-build (09/26/2019 02:51:50 PM) (Detached)
|
||||
2 Sockets in /run/screen/S-shs.
|
||||
```
|
||||
|
||||
你可以使用 `-version` 选项查询正在运行的 `screen` 版本。
|
||||
|
||||
```
|
||||
$ screen -version
|
||||
Screen version 4.06.02 (GNU) 23-Oct-17
|
||||
```
|
||||
|
||||
### 安装 screen
|
||||
|
||||
如果 `which screen` 未在屏幕上提供信息,则可能你的系统上未安装该工具。
|
||||
|
||||
```
|
||||
$ which screen
|
||||
/usr/bin/screen
|
||||
```
|
||||
|
||||
如果你需要安装它,则以下命令之一可能适合你的系统:
|
||||
|
||||
```
|
||||
sudo apt install screen
|
||||
sudo yum install screen
|
||||
```
|
||||
|
||||
当你需要运行耗时的进程时,如果你的 SSH 会话由于某种原因断开连接,则可能会中断这个耗时的进程,那么 `screen` 工具就会派上用场。而且,如你所见,它非常易于使用和管理。
|
||||
|
||||
以下是上面使用的命令的摘要:
|
||||
|
||||
```
|
||||
screen -S <process description> 开始会话
|
||||
Ctrl+A Ctrl+D 从会话中脱离
|
||||
screen -ls 列出会话
|
||||
screen -r <process description> 重新接驳会话
|
||||
```
|
||||
|
||||
尽管还有更多关于 `screen` 的知识,包括可以在 `screen` 会话之间进行操作的其他方式,但这已经足够帮助你开始使用这个便捷的工具了。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3441777/how-the-linux-screen-tool-can-save-your-tasks-and-your-sanity-if-ssh-is-interrupted.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.youtube.com/playlist?list=PL7D2RMSmRO9J8OTpjFECi8DJiTQdd4hua
|
||||
[2]: https://www.networkworld.com/article/3440100/take-the-intelligent-route-with-consumption-based-storage.html?utm_source=IDG&utm_medium=promotions&utm_campaign=HPE20773&utm_content=sidebar ( Take the Intelligent Route with Consumption-Based Storage)
|
||||
[3]: https://www.facebook.com/NetworkWorld/
|
||||
[4]: https://www.linkedin.com/company/network-world
|
61
published/202001/20191015 How GNOME uses Git.md
Normal file
61
published/202001/20191015 How GNOME uses Git.md
Normal file
@ -0,0 +1,61 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11806-1.html)
|
||||
[#]: subject: (How GNOME uses Git)
|
||||
[#]: via: (https://opensource.com/article/19/10/how-gnome-uses-git)
|
||||
[#]: author: (Molly de Blanc https://opensource.com/users/mollydb)
|
||||
|
||||
一个非技术人员对 GNOME 项目使用 GitLab 的感受
|
||||
======
|
||||
|
||||
> 将 GNOME 项目集中在 GitLab 上的决定为整个社区(不只是开发人员)带来了好处。
|
||||
|
||||
![red panda][1]
|
||||
|
||||
“您的 GitLab 是什么?”这是我在 [GNOME 基金会][2]工作的第一天被问到的第一个问题之一,该基金会是支持 GNOME 项目(包括[桌面环境][3]、[GTK][4] 和 [GStreamer][5])的非盈利组织。此人问的是我在 [GNOME 的 GitLab 实例][6]上的用户名。我在 GNOME 期间,经常有人要求我提供我的 GitLab。
|
||||
|
||||
我们使用 GitLab 进行几乎所有操作。通常情况下,我会收到一些<ruby>提案<rt>issue</rt></ruby>和参考错误报告,有时还需要修改文件。我不是以开发人员或系统管理员的身份进行此操作的。我参与了“参与度、包容性和多样性(I&D)”团队。我为 GNOME 朋友们撰写新闻通讯,并采访该项目的贡献者。我为 GNOME 活动提供赞助。我不写代码,但我每天都使用 GitLab。
|
||||
|
||||
在过去的二十年中,GNOME 项目的管理采用了各种方式。该项目的不同部分使用不同的系统来跟踪代码更改、协作以及作为项目和社交空间共享信息。但是,该项目决定,它需要更加地一体化,这从构思到完成大约花费了一年的时间。
|
||||
|
||||
GNOME 希望切换到单个工具供整个社区使用的原因很多。外部项目与 GNOME 息息相关,并为它们提供更简单的与资源交互的方式对于项目至关重要,无论是支持社区还是发展生态系统。我们还希望更好地跟踪 GNOME 的指标,即贡献者的数量、贡献的类型和数量以及项目不同部分的开发进度。
|
||||
|
||||
当需要选择一种协作工具时,我们考虑了我们需要的东西。最重要的要求之一是它必须由 GNOME 社区托管。由第三方托管并不是一种选择,因此像 GitHub 和 Atlassian 这样的服务就不在考虑之中。而且,当然了,它必须是自由软件。很快,唯一真正的竞争者出现了,它就是 GitLab。我们希望确保进行贡献很容易。GitLab 具有诸如单点登录的功能,该功能允许人们使用 GitHub、Google、GitLab.com 和 GNOME 帐户登录。
|
||||
|
||||
我们认为 GitLab 是一条出路,我们开始从许多工具迁移到单个工具。GNOME 董事会成员 [Carlos Soriano][7] 领导这项改变。在 GitLab 和 GNOME 社区的大力支持下,我们于 2018 年 5 月完成了该过程。
|
||||
|
||||
人们非常希望迁移到 GitLab 有助于社区的发展,并使贡献更加容易。由于 GNOME 以前使用了许多不同的工具,包括 Bugzilla 和 CGit,因此很难定量地评估这次切换对贡献量的影响。但是,我们可以更清楚地跟踪一些统计数据,例如在 2018 年 6 月至 2018 年 11 月之间关闭了近 10,000 个提案,合并了 7,085 个合并请求。人们感到社区在发展壮大,越来越受欢迎,而且贡献实际上也更加容易。
|
||||
|
||||
人们因不同的原因而开始使用自由软件,重要的是,可以通过为需要软件的人提供更好的资源和更多的支持来公平竞争。Git 作为一种工具已被广泛使用,并且越来越多的人使用这些技能来参与到自由软件当中。自托管的 GitLab 提供了将 Git 的熟悉度与 GitLab 提供的功能丰富、用户友好的环境相结合的绝佳机会。
|
||||
|
||||
切换到 GitLab 已经一年多了,变化确实很明显。持续集成(CI)为开发带来了巨大的好处,并且已经完全集成到 GNOME 的几乎每个部分当中。不进行代码开发的团队也转而使用 GitLab 生态系统进行工作。无论是使用问题跟踪来管理分配的任务,还是使用版本控制来共享和管理资产,就连“参与度、包容性和多样性(I&D)”这样的团队都已经使用了 GitLab。
|
||||
|
||||
一个社区,即使是一个正在开发的自由软件,也很难适应新技术或新工具。在类似 GNOME 的情况下,这尤其困难,该项目[最近已经 22 岁了] [8]。像 GNOME 这样经过了 20 多年建设的项目,太多的人和组织使用了太多的部件,但迁移工作之所以能实现,这要归功于 GNOME 社区的辛勤工作和 GitLab 的慷慨帮助。
|
||||
|
||||
在为使用 Git 进行版本控制的项目工作时,我发现很方便。这是一个令人感觉舒适和熟悉的系统,是一个在工作场所和爱好项目之间保持一致的工具。作为 GNOME 社区的新成员,能够参与并使用 GitLab 真是太好了。作为社区建设者,看到这样结果是令人鼓舞的:越来越多的相关项目加入并进入生态系统;新的贡献者和社区成员对该项目做出了首次贡献;以及增强了衡量我们正在做的工作以了解其成功和成功的能力。
|
||||
|
||||
如此多的做着完全不同的事情(例如他们正在从事的不同工作以及所使用的不同技能)的团队同意汇集在一个工具上(尤其是被认为是跨开源的标准工具),这一点很棒。作为 GNOME 的贡献者,我真的非常感谢我们使用了 GitLab。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/10/how-gnome-uses-git
|
||||
|
||||
作者:[Molly de Blanc][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mollydb
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/redpanda_firefox_pet_animal.jpg?itok=aSpKsyna (red panda)
|
||||
[2]: https://www.gnome.org/foundation/
|
||||
[3]: https://gnome.org/
|
||||
[4]: https://www.gtk.org/
|
||||
[5]: https://gstreamer.freedesktop.org/
|
||||
[6]: https://gitlab.gnome.org/
|
||||
[7]: https://twitter.com/csoriano1618?lang=en
|
||||
[8]: https://opensource.com/article/19/8/poll-favorite-gnome-version
|
@ -0,0 +1,137 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11774-1.html)
|
||||
[#]: subject: (Open source interior design with Sweet Home 3D)
|
||||
[#]: via: (https://opensource.com/article/19/10/interior-design-sweet-home-3d)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
Sweet Home 3D 开放源码室内设计
|
||||
======
|
||||
|
||||
> 在你去真实世界购物前,在虚拟世界中尝试家具布局、配色方案等等。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/12/092633eyc3c9f7u3gfgj4e.jpg)
|
||||
|
||||
这里有关于如何装饰房间的三大流派:
|
||||
|
||||
1. 购买一堆家具,并把它们塞进房间。
|
||||
2. 仔细测量每件家具,计算房间的理论容量,然后把它们全部塞进房间,忽略你在床上放置一个书架的事实。
|
||||
3. 使用一台计算机进行预先可视化。
|
||||
|
||||
之前,我还实践了鲜为人知的第四个方式:不要家具。然而,自从我成为一个远程工作者,我发现家庭办公需要一些便利的设施,像一张桌子和一张椅子,一个用于存放参考书和技术手册的书架等等。因此,我一直在制定一个使用实际的家具来迁移我的生活和工作空间的计划,在该*计划*上强调由实木制作,而不是牛奶箱子(或胶水和锯末板之类的东西)。我最不想做的一件事:从旧货市场淘到得宝贝带回家时,发现其进不了门,或者比另一件家具大很多。
|
||||
|
||||
是时候做专业人士该做的事了,是时候做视觉预览了。
|
||||
|
||||
### 开放源码室内设计
|
||||
|
||||
[Sweet Home 3D][2] 是一个开源的(GPLv2)室内设计应用程序,可以帮助你绘制你的住宅平面图,然后定义、重新调整大小以及安排家具。你可以使用精确的尺寸来完成这些,精确到一厘米以下,而不使用任何数学运算,仅使用简单的拖拽操作就行。当你完成后,你可以以 3D 方式查看结果。在 Sweet Home 3D 中规划你家的室内设计,就和在 Word 程序中创建基本的表格一样简单。
|
||||
|
||||
### 安装
|
||||
|
||||
Sweet Home 3D 是一个 [Java][3] 应用程序,因此它是平台通用的。它运行在任何可以运行 Java 的操作系统上,包括 Linux、Windows、MacOS 和 BSD 。不用理会你的操作系统,你可以从网站[下载][4]该应用程序。
|
||||
|
||||
* 在 Linux 上,[untar][5] 存档文件。在 `SweetHome3D` 文件上右键单击,并选择**属性**。在**权限**选项卡中,授予文件可执行权限。
|
||||
* 在 MacOS 和 Windows 上,展开存档文件并启动应用程序。当系统提示时,你必需授予它权限来在你的系统上运行。
|
||||
|
||||
![Sweet Home 3D permissions][6]
|
||||
|
||||
在 Linux 上,你也可以像一个 Snap 软件包一样安装 Sweet Home 3D ,前提是你已经安装并启用 **snapd**。
|
||||
|
||||
### 成功的测量
|
||||
|
||||
首先:打开你的卷尺。为充分利用 Sweet Home 3D,你必须知道你所计划的生活空间的实际尺寸。你可能需要测量精度到毫米或 1/16 英寸;你可以自己把握对偏差幅度的容忍度。但是你必需获得基本的尺寸,包括测量墙壁和门窗。
|
||||
|
||||
用你最好的判断力来判断常识。例如,当测量门时,包括门框;虽然从技术上讲它不是*门*本身的一部分,但它可能是你不想用家具遮挡的一部分墙壁空间。
|
||||
|
||||
![Measure twice, execute once][7]
|
||||
|
||||
### 创建一间房间
|
||||
|
||||
当你第一次启动 Sweet Home 3D 时,它会以其默认查看模式来打开一个空白的画布,蓝图视图在顶部面板中,3D 渲染在底部面板中。在我的 [Slackware][8] 桌面计算机上,它可以很好地工作,不过我的桌面计算机也是我的视频编辑和游戏计算机,所以它有一个极好的 3D 渲染显卡。在我的笔记本计算机上,这种视图模式是非常慢的。为了最好的性能(尤其是在一台计没有 3D 渲染的专用计算机上),转到窗口顶部的 **3D 视图** 菜单,并选择 **虚拟访问** 。这个视图模式基于虚拟访客的位置从地面视图渲染你的工作。这意味着你可以控制渲染的内容和时机。
|
||||
|
||||
不管你计算机是否强力,切换到这个视图的有意义的,因为地表以上的 3D 渲染不比蓝图平面图向你提供更多有用的详细信息。在你更改视图模式后,你可以开始设计。
|
||||
|
||||
第一步是定义你家的墙壁。使用**创建墙壁**工具完成,可以在顶部工具栏的**手形**图标右侧找到。绘制墙壁很简单:单击你想要墙壁开始的位置,单击以锚定位置,不断单击锚定,直到你的房间完成。
|
||||
|
||||
![Drawing walls in Sweet Home 3D][9]
|
||||
|
||||
在你闭合墙壁后,按 `Esc` 来退出工具。
|
||||
|
||||
#### 定义一间房间
|
||||
|
||||
Sweet Home 3D 在你如何创建墙壁的问题上是灵活的。你可以先绘制你房子的外部边界,然后再细分内部,或者你可以绘制每个房间作为结成一体的“容器”,最终形成你房子所占的空间量。这种灵活性是能做到的,因为在现实生活中和在 Sweet Home 3D 中,墙壁并不总是用来定义一间房间。为定义一间房间,使用在顶部工具栏的**创建墙壁**按钮右侧的**创建房间**按钮。
|
||||
|
||||
如果房间的地板空间是通过四面墙所定义,你需要做的全部的定义是像一间房间一样在四面墙壁内双击来圈占地方。Sweet Home 3D 将定义该空间为一间房间,并根据你的喜好,以英尺或米为单位向你提供房间的面积。
|
||||
|
||||
对于不规则的房间,你必需使用每次单击来手动定义房间的每个墙角。根据房间形状的复杂性,你可能不得不进行试验来发现你是否需要从你的原点来顺时针或逆时针工作,以避免奇怪的莫比斯条形地板。不过,一般来说,定义一间房间的地板空间是简单的。
|
||||
|
||||
![Defining rooms in Sweet Home 3D][10]
|
||||
|
||||
在你给定房间一层地板后,你可以更改到**箭头**工具,并在房间上双击来给予它一个名称。你也可以设置地板、墙壁、天花板和踢脚线的颜色及纹理。
|
||||
|
||||
![Modifying room floors, ceilings, etc. in Sweet Home 3D][11]
|
||||
|
||||
默认情况下,这些都不会在蓝图视图中渲染。为启用在你蓝图面板中的房间渲染,转到**文件**菜单并选择**首选项**。在**首选项**面板中,设置**平面图中房间渲染**为**地板颜色或纹理**。
|
||||
|
||||
### 门和窗
|
||||
|
||||
在你完成基本的地板平面图后,你可以长期地切换到**箭头**工具。
|
||||
|
||||
你可以在 Sweet Home 3D 的左栏中的**门和窗**类别下找到门和窗。你有很多选择,所以选择最接近你家的东西。
|
||||
|
||||
![Moving a door in Sweet Home 3D][12]
|
||||
|
||||
为放置一扇门或窗到你的平面图中,在你的蓝图平面图中的合适的墙壁上拖拽门或窗。要调整它的位置和大小,请双击门或窗。
|
||||
|
||||
### 添加家具
|
||||
|
||||
随着基本平面图完成,这部分工作感觉像是结束了!从这点继续,你可以摆弄家具布置以及其它装饰。
|
||||
|
||||
你可以在左栏中找到家具,按预期的方式来组织每个房间。你可以拖拽任何项目到你的蓝图平面图中,当你的鼠标悬停在项目的区域上时,使用可视化工具控制方向和大小。在任何项目上双击双击来调整它的颜色和成品表面。
|
||||
|
||||
### 查看和导出
|
||||
|
||||
为了看看你未来的家将会看起来是什么样子,在你的蓝图视图中拖拽“人”图标到一个房间中。
|
||||
|
||||
![Sweet Home 3D rendering][13]
|
||||
|
||||
你可以在现实和空间感受之间找到自己的平衡,你的想象力是你唯一的限制。你可以从 Sweet Home 3D [下载页面][4]获取附加的有用的资源来添加到你的家中。你甚至可以使用**库编辑器**应用程序创建你自己的家具和纹理,它可以从该项目的网站下载。
|
||||
|
||||
Sweet Home 3D 可以导出你的蓝图平面图为 SVG 格式,以便在 [Inkscape][14] 中使用,并且它可以导出你的 3D 模型为 OBJ 格式,以便在 [Blender][15] 中使用。为导出你的蓝图,转到**平面图**菜单,并选择**导出为 SVG 格式**。为导出一个 3D 模型,转到 **3D 视图** 菜单并选择**导出为 OBJ 格式**。
|
||||
|
||||
你也可以拍摄你家的"快照,以便于不打开 Sweet Home 3D 而回顾你的想法。为创建一个快照,转到 **3D 视图**菜单并选择**创建照片**。快照是按照蓝图视图中的人的图标的角度展现的,因此按照需要调整,然后在**创建照片**窗口中单击**创建**按钮。如果你对快照满意,单击**保存**。
|
||||
|
||||
### 甜蜜的家
|
||||
|
||||
在 Sweet Home 3D 中有更多的特色。你可以添加一片天空和一片草坪,为你的照片定位光线,设置天花板高度,给你房子添加另一楼层等等。不管你是打算租一套公寓,还是买一套房子,或是(尚)不存在的房子,Sweet Home 3D 是一款简单迷人的应用程序,当你匆忙购买家具时,它可以帮助你快乐地做出更好的购买选择。因此,你终于可以停止在厨房的柜台上吃早餐以及蹲在地上工作了。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/10/interior-design-sweet-home-3d
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/LIFE_housing.png?itok=s7i6pQL1 (Several houses)
|
||||
[2]: http://www.sweethome3d.com/
|
||||
[3]: https://opensource.com/resources/java
|
||||
[4]: http://www.sweethome3d.com/download.jsp
|
||||
[5]: https://opensource.com/article/17/7/how-unzip-targz-file
|
||||
[6]: https://opensource.com/sites/default/files/uploads/sweethome3d-permissions.png (Sweet Home 3D permissions)
|
||||
[7]: https://opensource.com/sites/default/files/images/life/sweethome3d-measure.jpg (Measure twice, execute once)
|
||||
[8]: http://www.slackware.com/
|
||||
[9]: https://opensource.com/sites/default/files/uploads/sweethome3d-walls.jpg (Drawing walls in Sweet Home 3D)
|
||||
[10]: https://opensource.com/sites/default/files/uploads/sweethome3d-rooms.jpg (Defining rooms in Sweet Home 3D)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/sweethome3d-rooms-modify.jpg (Modifying room floors, ceilings, etc. in Sweet Home 3D)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/sweethome3d-move.jpg (Moving a door in Sweet Home 3D)
|
||||
[13]: https://opensource.com/sites/default/files/uploads/sweethome3d-view.jpg (Sweet Home 3D rendering)
|
||||
[14]: http://inkscape.org
|
||||
[15]: http://blender.org
|
204
published/202001/20191017 Intro to the Linux useradd command.md
Normal file
204
published/202001/20191017 Intro to the Linux useradd command.md
Normal file
@ -0,0 +1,204 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lxbwolf)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11756-1.html)
|
||||
[#]: subject: (Intro to the Linux useradd command)
|
||||
[#]: via: (https://opensource.com/article/19/10/linux-useradd-command)
|
||||
[#]: author: (Alan Formy-Duval https://opensource.com/users/alanfdoss)
|
||||
|
||||
Linux useradd 命令介绍
|
||||
======
|
||||
|
||||
> 使用 useradd 命令来添加用户(并且根据需要修改账号)。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/06/225733hv1u7f4z4vbt8u5x.jpg)
|
||||
|
||||
任何计算机系统中,添加用户都是最重要的事之一;本文着重介绍如何在 Linux 系统中添加用户。
|
||||
|
||||
正式开始之前,我先提三个概念。首先,跟大多数操作系统一样,Linux 用户也需要一个账号才能登录。本文只介绍本地账号,不涉及网络账号,如 LDAP。其次,每个账号都有一个名字(username)和一个对应的数字(用户 ID)。最后,每个用户通常都在一个组内,每个组都有一个名字和一个组 ID。
|
||||
|
||||
你可能已经想到了,Linux 提供了添加用户的命令行工具,就是 `useradd` 命令。有些版本也叫 `adduser`。为了方便使用,很多发行版上这个命令是一个指向 `useradd` 命令的符号链接。
|
||||
|
||||
```
|
||||
$ file `which adduser`
|
||||
/usr/sbin/adduser: symbolic link to useradd
|
||||
```
|
||||
|
||||
来看一下 `useradd`。
|
||||
|
||||
> 注意:本文描述的默认环境是 Red Hat Enterprise Linux 8.0。你可能会发现本文描述的这些文件和某些默认值与某些 Linux 发行版或其他 Unix 操作系统(FreeBSD 或 Solaris)偶尔会有差异。
|
||||
|
||||
### 默认处理
|
||||
|
||||
`useradd` 的基本用法相当简单:提供一个用户名就可以添加一个用户。
|
||||
|
||||
```bash
|
||||
$ sudo useradd sonny
|
||||
```
|
||||
|
||||
在本例中,`useradd` 命令创建了一个名为 `sonny` 的账号。此命令同时创建了一个同名的组,`sonny` 被放进了这个组,这个组也是 `sonny` 账号的主组。命令执行时,根据配置文件 `/etc/default/useradd` 和 `/etc/login.defs` 中的不同设置,也会有其他的参数,如语言和 shell。对于一个私人系统或微小的单服务商业环境,这些参数已经足够了。
|
||||
|
||||
上面两个文件控制 `useradd` 的处理,用户的信息保存在 `/etc` 目录下的一些其他文件中,关于这些信息的讲解会贯穿全文。
|
||||
|
||||
| 文件 | 描述 | 域 (加粗的表示由 useradd 命令设置) |
|
||||
| ------ | ------------------------------------ | ------------------------------------------------------------ |
|
||||
| `passwd` | 存储用户账号信息 | **用户名**:未使用:**UID**:**GID**:**备注**:**家目录**:**shell** |
|
||||
| `shadow` | 存储用户账号的安全信息 | **用户名**:加密密码:上次修改时间:最短使用天数:最长使用天数间:**修改前警示天数**:**过期后宽限时间**:未使用 |
|
||||
| `group` | 存储组信息 | **组名**:未使用:**GID**:**成员列表** |
|
||||
|
||||
### 自定义处理
|
||||
|
||||
当管理员需要更好地控制账号时,可以使用命令行来自定义,如指定一个用户的 ID。
|
||||
|
||||
#### 用户和组 ID
|
||||
|
||||
默认情况下,`useradd` 试图使用相同的用户 ID(UID)和主组 ID(GID),但也不完全是。虽然 UID 与 GID 相同不是必须的,但如果相同,会更方便管理员管理。
|
||||
|
||||
下面的场景就是一个 GID 与 UID 不同的例子。现在我添加另一账号,名为 Timmy。通过使用 `getent` 命令来比较 `sonny` 和 `timmy` 两个账号,显示两个用户和对应的主组。
|
||||
|
||||
```bash
|
||||
$ getent passwd sonny timmy
|
||||
sonny:x:1001:1002:Sonny:/home/sonny:/bin/bash
|
||||
timmy:x:1002:1003::/home/timmy:/bin/bash
|
||||
|
||||
$ getent group sonny timmy
|
||||
sonny:x:1002:
|
||||
timmy:x:1003:
|
||||
```
|
||||
|
||||
不幸的是,两者的 UID 和 GID 都不相同。因为默认的处理是,创建用户时,把下一个可用的 UID 赋给用户,然后把同一个数字作为主组 ID 赋给它。然而,当要使用的组 ID 已经被使用时,就再把下一个可用的 GID 赋给它。为了弄清细节,我猜想 1001 这个 GID 已经被使用了,用一个命令确认了一下。
|
||||
|
||||
```bash
|
||||
$ getent group 1001
|
||||
book:x:1001:alan
|
||||
```
|
||||
|
||||
`book` 的 ID 是 `1001`,因此新创建的用户的 GID 都有偏移量 1。这就是为什么系统管理员在用户创建过程中需要多设置一些值的一个实例。为了解决这个问题,我必须先确定下一个可用的 UID 和 GID 是否相同。确定下一个可用值时,可以使用 `getent group` 和 `getent passwd` 命令,通过 `-u` 参数传递要确认的值。
|
||||
|
||||
```bash
|
||||
$ sudo useradd -u 1004 bobby
|
||||
|
||||
$ getent passwd bobby; getent group bobby
|
||||
bobby:x:1004:1004::/home/bobby:/bin/bash
|
||||
bobby:x:1004:
|
||||
```
|
||||
|
||||
另一个需要指定 ID 的场景是,通过 NFS 访问远程系统上的文件时。对于一个给定的用户,当 NFS 所有客户端和服务系统的 ID 都一样时,管理员更容易控制。在我的文章 [使用 autofs 挂载 NFS][2] 中有详细介绍。
|
||||
|
||||
### 更多自定义
|
||||
|
||||
一些其他的指定用户信息的参数也用得比较频繁。这里是一些你会经常用到的参数的概括例子。
|
||||
|
||||
#### 注释
|
||||
|
||||
注释选项是通过 `-c` 参数指定的一个解释文本字段,可以提供一段简短的描述或其他信息。
|
||||
|
||||
```bash
|
||||
$ sudo useradd -c "Bailey is cool" bailey
|
||||
$ getent passwd bailey
|
||||
bailey:x:1011:1011:Bailey is cool:/home/bailey:/bin/bash
|
||||
```
|
||||
|
||||
#### 组
|
||||
|
||||
一个用户可以被指定一个主组和多个次组。`-g` 参数指定主组名称或 GID。如果不指定,`useradd` 会以用户名创建一个主组(前面演示过)。`-G`(大写)参数用一个逗号分隔的组列表来指定此用户所属的组,这些组就是次组。
|
||||
|
||||
```bash
|
||||
$ sudo useradd -G tgroup,fgroup,libvirt milly
|
||||
$ id milly
|
||||
uid=1012(milly) gid=1012(milly) groups=1012(milly),981(libvirt),4000(fgroup),3000(tgroup)
|
||||
```
|
||||
|
||||
#### 家目录
|
||||
|
||||
`useradd` 的默认处理是,在 `/home` 目录下创建用户的家目录。然而,下面的参数可以改写家目录的基础目录。`-b` 设置另一个可以创建家目录的基础目录。例如指定 `/home2` 而不是 `/home`。
|
||||
|
||||
```bash
|
||||
$ sudo useradd -b /home2 vicky
|
||||
$ getent passwd vicky
|
||||
vicky:x:1013:1013::/home2/vicky:/bin/bash
|
||||
```
|
||||
|
||||
`-d` 参数可以指定一个与用户名不同的家目录。
|
||||
|
||||
```bash
|
||||
$ sudo useradd -d /home/ben jerry
|
||||
$ getent passwd jerry
|
||||
jerry:x:1014:1014::/home/ben:/bin/bash
|
||||
```
|
||||
|
||||
#### 目录模板
|
||||
|
||||
指定 `-k` 参数会在创建新用户时,复制 `/etc/skel` 目录下的所有文件到用户的家目录中。这些文件通常是 shell 配置文件,当然也可以是系统管理员想在新建用户时使用的任何文件。
|
||||
|
||||
#### Shell
|
||||
|
||||
`-s` 参数可以指定 shell。如果不指定,则使用默认的 shell。例如,下面的例子中 ,配置文件中定义的 shell 是 `bash`,但 `wally` 这个用户指定的是 `zsh`。
|
||||
|
||||
```bash
|
||||
SHELL=/bin/bash
|
||||
|
||||
$ sudo useradd -s /usr/bin/zsh wally
|
||||
$ getent passwd wally
|
||||
wally:x:1004:1004::/home/wally:/usr/bin/zsh
|
||||
```
|
||||
|
||||
#### 安全
|
||||
|
||||
安全是用户管理的重中之重,因此 `useradd` 命令也提供了很多关于安全的选项。可以使用 `-e` 参数,以 YYYY-MM-DD 的格式指定一个用户的过期时间。
|
||||
|
||||
```bash
|
||||
$ sudo useradd -e 20191231 sammy
|
||||
$ sudo getent shadow sammy
|
||||
sammy:!!:18171:0:99999:7::20191231:
|
||||
```
|
||||
|
||||
当密码过期时,账号也会自动失效。`-f` 参数指定密码过期后经过几天账号失效。如果设为 0,则立即失效。
|
||||
|
||||
```bash
|
||||
$ sudo useradd -f 30 willy
|
||||
$ sudo getent shadow willy
|
||||
willy:!!:18171:0:99999:7:30::
|
||||
```
|
||||
|
||||
### 实例
|
||||
|
||||
生产环境中,创建一个用户账号时会用到多个参数。例如,我要创建一个 Perry 账号,可能会用下面的命令:
|
||||
|
||||
```bash
|
||||
$ sudo useradd -u 1020 -c "Perry Example" \
|
||||
-G tgroup -b /home2 \
|
||||
-s /usr/bin/zsh \
|
||||
-e 20201201 -f 5 perry
|
||||
```
|
||||
|
||||
查看前面的内容来理解每个选项。用下面的命令确认结果:
|
||||
|
||||
```bash
|
||||
$ getent passwd perry; getent group perry; getent shadow perry; id perry
|
||||
perry:x:1020:1020:Perry Example:/home2/perry:/usr/bin/zsh
|
||||
perry:x:1020:
|
||||
perry:!!:18171:0:99999:7:5:20201201:
|
||||
uid=1020(perry) gid=1020(perry) groups=1020(perry),3000(tgroup)
|
||||
```
|
||||
|
||||
### 一点小建议
|
||||
|
||||
`useradd` 命令是所有 Unix(不仅仅是 Linux)系统管理员都必知必会的命令。由于用户创建不能出错,需要第一次就正确,所以理解它的每一个选项很重要。这意味着你需要有一套深思熟虑的命名约定,包括为整个企业环境而不仅仅是一个单系统预留一个专用的 UID/GID 范围,尤其是你为一个成长中的组织工作时。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/10/linux-useradd-command
|
||||
|
||||
作者:[Alan Formy-Duval][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lxbwolf](https://github.com/lxbwolf)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alanfdoss
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/connection_people_team_collaboration.png?itok=0_vQT8xV (people in different locations who are part of the same team)
|
||||
[2]: https://opensource.com/article/18/6/using-autofs-mount-nfs-shares
|
@ -0,0 +1,62 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (alim0x)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11831-1.html)
|
||||
[#]: subject: (My Linux story: Learning Linux in the 90s)
|
||||
[#]: via: (https://opensource.com/article/19/11/learning-linux-90s)
|
||||
[#]: author: (Mike Harris https://opensource.com/users/mharris)
|
||||
|
||||
我的 Linux 故事:在 90 年代学习 Linux
|
||||
======
|
||||
|
||||
> 这是一个关于我如何在 WiFi 时代之前学习 Linux 的故事,那时的发行版还以 CD 的形式出现。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/29/213829t00wmwu2w0z502zg.jpg)
|
||||
|
||||
大部分人可能不记得 1996 年时计算产业或日常生活世界的样子。但我很清楚地记得那一年。我那时候是堪萨斯中部一所高中的二年级学生,那是我的自由与开源软件(FOSS)旅程的开端。
|
||||
|
||||
我从这里开始进步。我在 1996 年之前就开始对计算机感兴趣。我在我家的第一台 Apple ][e 上启蒙成长,然后多年之后是 IBM Personal System/2。(是的,在这过程中有一些代际的跨越。)IBM PS/2 有一个非常激动人心的特性:一个 1200 波特的 Hayes 调制解调器。
|
||||
|
||||
我不记得是怎样了,但在那不久之前,我得到了一个本地 [BBS][2] 的电话号码。一旦我拨号进去,我可以得到本地的一些其他 BBS 的列表,我的网络探险就此开始了。
|
||||
|
||||
在 1995 年,[足够幸运][3]的人拥有了家庭互联网连接,每月可以使用不到 30 分钟。那时的互联网不像我们现代的服务那样,通过卫星、光纤、有线电视同轴电缆或任何版本的铜线提供。大多数家庭通过一个调制解调器拨号,它连接到他们的电话线上。(这时离移动电话无处不在的时代还早得很,大多数人只有一部家庭电话。)尽管这还要取决你所在的位置,但我不认为那时有很多独立的互联网服务提供商(ISP),所以大多数人从仅有的几家大公司获得服务,包括 America Online,CompuServe 以及 Prodigy。
|
||||
|
||||
你能获取到的服务速率非常低,甚至在拨号上网革命性地达到了顶峰的 56K,你也只能期望得到最高 3.5Kbps 的速率。如果你想要尝试 Linux,下载一个 200MB 到 800MB 的 ISO 镜像或(更加切合实际的)一套软盘镜像要贡献出时间、决心,以及减少电话的使用。
|
||||
|
||||
我走了一条简单一点的路:在 1996 年,我从一家主要的 Linux 发行商订购了一套 “tri-Linux” CD 集。这些光盘提供了三个发行版,我的这套包含了 Debian 1.1(Debian 的第一个稳定版本)、Red Hat Linux 3.0.3 以及 Slackware 3.1(代号 Slackware '96)。据我回忆,这些光盘是从一家叫做 [Linux Systems Labs][4] 的在线商店购买的。这家在线商店如今已经不存在了,但在 90 年代和 00 年代早期,这样的发行商很常见。这些是多光盘 Linux 套件。这是 1998 年的一套光盘,你可以了解到他们都包含了什么:
|
||||
|
||||
![A tri-linux CD set][5]
|
||||
|
||||
![A tri-linux CD set][6]
|
||||
|
||||
在 1996 年夏天一个命中注定般的日子,那时我住在堪萨斯一个新的并且相对较为乡村的城市,我做出了安装并使用 Linux 的第一次尝试。在 1996 年的整个夏天,我尝试了那套三张 Linux CD 套件里的全部三个发行版。他们都在我母亲的老 Pentium 75MHz 电脑上完美运行。
|
||||
|
||||
我最终选择了 [Slackware][7] 3.1 作为我的首选发行版,相比其它发行版可能更多的是因为它的终端的外观,这是决定选择一个发行版前需要考虑的重要因素。
|
||||
|
||||
我将系统设置完毕并运行了起来。我连接到一家 “不太知名的” ISP(一家这个区域的本地服务商),通过我家的第二条电话线拨号(为了满足我的所有互联网使用而订购)。那就像在天堂一样。我有一台完美运行的双系统(Microsoft Windows 95 和 Slackware 3.1)电脑。我依然拨号进入我所知道和喜爱的 BBS,游玩在线 BBS 游戏,比如 Trade Wars、Usurper 以及 Legend of the Red Dragon。
|
||||
|
||||
我能够记得在 EFNet(IRC)上 #Linux 频道上渡过的日子,帮助其他用户,回答他们的 Linux 问题以及和版主们互动。
|
||||
|
||||
在我第一次在家尝试使用 Linux 系统的 20 多年后,已经是我进入作为 Red Hat 顾问的第五年,我仍然在使用 Linux(现在是 Fedora)作为我的日常系统,并且依然在 IRC 上帮助想要使用 Linux 的人们。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/learning-linux-90s
|
||||
|
||||
作者:[Mike Harris][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[alim0x](https://github.com/alim0x)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mharris
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/bus-cloud.png?itok=vz0PIDDS (Sky with clouds and grass)
|
||||
[2]: https://en.wikipedia.org/wiki/Bulletin_board_system
|
||||
[3]: https://en.wikipedia.org/wiki/Global_Internet_usage#Internet_users
|
||||
[4]: https://web.archive.org/web/19961221003003/http://lsl.com/
|
||||
[5]: https://opensource.com/sites/default/files/20191026_142009.jpg (A tri-linux CD set)
|
||||
[6]: https://opensource.com/sites/default/files/20191026_142020.jpg (A tri-linux CD set)
|
||||
[7]: http://slackware.com
|
@ -0,0 +1,168 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11735-1.html)
|
||||
[#]: subject: (How to cohost GitHub and GitLab with Ansible)
|
||||
[#]: via: (https://opensource.com/article/19/11/how-host-github-gitlab-ansible)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
如何使用 Ansible 同步 GitHub 和 GitLab
|
||||
======
|
||||
|
||||
> 通过使用 Ansible 镜像 Git 存储库,保护对重要项目的访问。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/01/104040fbdu68wosg8o99wg.jpg)
|
||||
|
||||
开源无处不在。它在家里的计算机上、在工作场所的计算机上、在互联网上,并且很多都由 [Git][2] 管理。由于 Git 是分布式的,因此许多人也将其视为一种众包的备份解决方案。从理论上讲,每当有人将 Git 存储库克隆到其本地计算机时,他们就创建了该项目源代码的备份。如果有 100 个人这样做,则存储库就有 100 个备份副本。
|
||||
|
||||
从理论上讲,这可以缓解“灾难”的影响,例如当项目维护者[突然决定删除存储库][3]或[莫名其妙地阻止所有流量][4],导致开发人员们无头苍蝇般地寻找谁拥有主分支的最新版本。类似的,整个代码托管站点也会消失。没有人会想到 Google Code、Microsoft CodePlex 或 Gitorious 会在鼎盛时期将被关闭。
|
||||
|
||||
简而言之,如果在过去的几十年中互联网教给了我们一些东西,那就是依靠互联网神奇地创建备份并不是冗余的最可靠途径。
|
||||
|
||||
此外,对于许多人来说,很多开源项目都托管在 GitHub 上是个问题 —— GitHub 并不是开放平台。许多开发人员和用户都希望支持诸如 GitLab 之类的堆栈并与之交互,它具有开源社区版本。
|
||||
|
||||
### 使用 Ansible 管理 Git
|
||||
|
||||
Git 的去中心方式对于解决这个问题很有用。使用纯 Git,你可以使用一个 `push` 命令轻松地将其推到两个或多个存储库。但是,为了使其在发生意外故障时有用,你必须经常与 Git 存储库进行交互(特别是推送)。此外,即使你可能永远不会自己推送或拉出代码,也可能有一些要备份的存储库。
|
||||
|
||||
但是,使用 Ansible,你可以自动执行项目主分支(或其他任何分支)的 Git 拉取,然后自动进行存储库到“异地”镜像的 Git 推送。换句话说,你可以让你的计算机定期从 GitHub 拉取并推送到 GitLab 或 [Gitolite][5] 或 Gitea(或你喜欢的任何 Git 托管主机)。
|
||||
|
||||
### Ansible 模块
|
||||
|
||||
如果不是因其出色的模块集合,那么 Ansible 就没那么出色。像 Python 的第三方库或 Linux 的应用程序一样,这个技术引擎的一个有用而令人惊讶的简单技巧是,Ansible 以其他人贡献的组件而闻名。因为本文正在研究如何有效和可靠地备份 Git 存储库,所以这里使用的模块是 [Git 模块][6]和 [ini_file][7] 模块。
|
||||
|
||||
首先,创建一个名为 `mirror.yaml` 的文件作为<ruby>剧本<rt>playbook</rt></ruby>。你可以像通常使用 Ansible 一样,从 `name` 和 `task` 条目开始。本示例将 `localhost` 添加到 `hosts` 列表中,以便在控制器计算机(你现在坐在前面的计算机)上运行<ruby>动作<rt>play</rt></ruby>,但是在现实生活中,你可能会在特定的主机或一组网络上的主机上运行它。
|
||||
|
||||
```
|
||||
---
|
||||
- name: "Mirror a Git repo with Ansible"
|
||||
hosts: localhost
|
||||
tasks:
|
||||
```
|
||||
|
||||
### Git 拉取和克隆
|
||||
|
||||
如果要进行备份,则需要最新代码的副本。明显,在 Git 仓库中实现这一目标的方法是执行 `git pull`。 但是,`pull` 会假定克隆已经存在,而写得很好的 Ansible 动作(Ansible 脚本)则尽可能少的假定。最好告诉 Ansible 先克隆存储库。
|
||||
|
||||
将你的第一个任务添加到剧本:
|
||||
|
||||
```
|
||||
---
|
||||
- name: "Mirror a Git repo with Ansible"
|
||||
hosts: localhost
|
||||
vars:
|
||||
git_dir: /tmp/soso.git
|
||||
tasks:
|
||||
|
||||
- name: "Clone the git repo"
|
||||
git:
|
||||
repo: 'https://github.com/ozkl/soso.git'
|
||||
dest: '{{ git_dir }}'
|
||||
clone: yes
|
||||
update: yes
|
||||
```
|
||||
|
||||
这个例子使用了开源的、类似于 Unix 的操作系统 soso 作为我要镜像的存储库。这是一个完全任意的选择,绝不意味着我对该存储库的未来缺乏信心。它还使用变量来引用目标文件夹 `/tmp/soso.git`,这很方便,并且如果以后你希望将它扩展为一个通用的镜像脚本也会受益。在现实生活中,你的工作机上可能会比 `/tmp` 具有更永久的位置,例如 `/home/gitmirrors/soso.git` 或 `/opt/gitmirrors/soso.git`。
|
||||
|
||||
运行你的剧本:
|
||||
|
||||
```
|
||||
$ ansible-playbook mirror.yaml
|
||||
```
|
||||
|
||||
首次运行该剧本时,Ansible 会正确检测到 Git 存储库在本地尚不存在,因此将其克隆。
|
||||
|
||||
```
|
||||
PLAY [Ansible Git mirror] ********
|
||||
|
||||
TASK [Gathering Facts] ***********
|
||||
ok: [localhost]
|
||||
|
||||
TASK [Clone git repo] ************
|
||||
changed: [localhost]
|
||||
|
||||
PLAY RECAP ***********************
|
||||
localhost: ok=2 changed=1 failed=0 [...]
|
||||
```
|
||||
|
||||
如果你再次运行该剧本,Ansible 会正确检测到自上次运行以来没有任何更改,并且会报告未执行任何操作:
|
||||
|
||||
```
|
||||
localhost: ok=2 changed=0 failed=0 [...]
|
||||
```
|
||||
|
||||
接下来,必须指示 Ansible 将存储库推送到另一个 Git 服务器。
|
||||
|
||||
### Git 推送
|
||||
|
||||
Ansible 中的 Git 模块不提供 `push` 功能,因此该过程的一部分是手动的。但是,在将存储库推送到备用镜像之前,你必须具有一个镜像,并且必须将镜像配置为备用<ruby>远程服务器<rt>remote</rt></ruby>。
|
||||
|
||||
首先,必须将备用的远程服务器添加到 Git 配置。因为 Git 配置文件是 INI 样式的配置,所以你可以使用 `ini_file` Ansible 模块轻松地添加所需的信息。将此添加到你的剧本:
|
||||
|
||||
```
|
||||
- name: "Add alternate remote"
|
||||
ini_file: dest={{ git_dir }}/.git/config section='remote \"mirrored\"' option=url value='git@gitlab.com:example/soso-mirror.git'
|
||||
tags: configuration
|
||||
```
|
||||
|
||||
为此,你必须在目标服务器上有一个空的存储库(在本例中为 [GitLab.com][9])。如果需要在剧本中创建目标存储库,可以按照 Steve Ovens 的出色文章《[如何使用 Ansible 通过 SSH 设置 Git 服务器][10]》来完成。
|
||||
|
||||
最后,直接使用 Git 将 HEAD 推送到备用远程服务器:
|
||||
|
||||
```
|
||||
- name: "Push the repo to alternate remote"
|
||||
shell: 'git --verbose --git-dir={{ git_dir }}/.git push mirrored HEAD'
|
||||
```
|
||||
|
||||
像往常一样运行该剧本,然后使该过程自动化,这样你就不必再次直接运行它了。你可以使用变量和特定的 Git 命令来调整脚本以适应你的需求,但是通过常规的拉取和推送操作,可以确保驻留在一台服务器上的重要项目可以安全地镜像到另一台服务器上。
|
||||
|
||||
这是完整的剧本,供参考:
|
||||
|
||||
```
|
||||
---
|
||||
- name: "Mirror a Git repository with Ansible"
|
||||
hosts: localhost
|
||||
vars:
|
||||
git_dir: /tmp/soso.git
|
||||
|
||||
tasks:
|
||||
|
||||
- name: "Clone the Git repo"
|
||||
git:
|
||||
repo: 'https://github.com/ozkl/soso.git'
|
||||
dest: '{{ git_dir }}'
|
||||
clone: yes
|
||||
update: yes
|
||||
|
||||
- name: "Add alternate remote"
|
||||
ini_file: dest={{ git_dir }}/.git/config section='remote \"mirrored\"' option=url value='git@gitlab.com:example/soso-mirror.git'
|
||||
tags: configuration
|
||||
|
||||
- name: "Push the repo to alternate remote"
|
||||
shell: 'git --verbose --git-dir={{ git_dir }}/.git push mirrored HEAD'
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/how-host-github-gitlab-ansible
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/programming-code-keyboard-laptop.png?itok=pGfEfu2S (Hands programming)
|
||||
[2]: https://opensource.com/resources/what-is-git
|
||||
[3]: https://github.com/AntiMicro/antimicro/issues/3
|
||||
[4]: https://opensource.com/article/19/10/how-community-saved-artwork-creative-commons
|
||||
[5]: https://opensource.com/article/19/4/server-administration-git
|
||||
[6]: https://docs.ansible.com/ansible/latest/modules/git_module.html
|
||||
[7]: https://docs.ansible.com/ansible/latest/modules/ini_file_module.html
|
||||
[8]: mailto:git@gitlab.com
|
||||
[9]: http://GitLab.com
|
||||
[10]: https://opensource.com/article/17/8/ansible-environment-management
|
@ -0,0 +1,395 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11780-1.html)
|
||||
[#]: subject: (Simulate gravity in your Python game)
|
||||
[#]: via: (https://opensource.com/article/19/11/simulate-gravity-python)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
在你的 Python 游戏中模拟引力
|
||||
======
|
||||
|
||||
> 学习如何使用 Python 的 Pygame 模块编程电脑游戏,并开始操作引力。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/223923k6t4zmvvmo6tfmmg.png)
|
||||
|
||||
真实的世界充满了运动和生活。物理学使得真实的生活如此忙碌和动态。物理学是物质在空间中运动的方式。既然一个电脑游戏世界没有物质,它也就没有物理学规律,使用游戏程序员不得不*模拟*物理学。
|
||||
|
||||
从大多数电脑游戏来说,这里基本上仅有两个方面的物理学是重要的:引力和碰撞。
|
||||
|
||||
当你[添加一个敌人][2]到你的游戏中时,你实现了一些碰撞检测,但是这篇文章要添加更多的东西,因为引力需要碰撞检测。想想为什么引力可能涉及碰撞。如果你不能想到任何原因,不要担心 —— 它会随着你开发示例代码工作而且显然。
|
||||
|
||||
在真实世界中的引力是有质量的物体来相互吸引的倾向性。物体(质量)越大,它施加越大的引力作用。在电脑游戏物理学中,你不必创建质量足够大的物体来证明引力的正确;你可以在电脑游戏世界本身中仅编程一个物体落向假设的最大的对象的倾向。
|
||||
|
||||
### 添加一个引力函数
|
||||
|
||||
记住你的玩家已经有了一个决定动作的属性。使用这个属性来将玩家精灵拉向屏幕底部。
|
||||
|
||||
在 Pygame 中,较高的数字更接近屏幕的底部边缘。
|
||||
|
||||
在真实的世界中,引力影响一切。然而,在平台游戏中,引力是有选择性的 —— 如果你添加引力到你的整个游戏世界,你的所有平台都将掉到地上。反之,你可以仅添加引力到你的玩家和敌人精灵中。
|
||||
|
||||
首先,在你的 `Player` 类中添加一个 `gravity` 函数:
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # 玩家掉落的多快
|
||||
```
|
||||
|
||||
这是一个简单的函数。首先,不管你的玩家是否想运动,你设置你的玩家垂直运动。也就是说,你已经编程你的玩家总是在下降。这基本上就是引力。
|
||||
|
||||
为使引力函数生效,你必须在你的主循环中调用它。这样,当每一个处理循环时,Python 都应用下落运动到你的玩家。
|
||||
|
||||
在这代码中,添加第一行到你的循环中:
|
||||
|
||||
```
|
||||
player.gravity() # 检查引力
|
||||
player.update()
|
||||
```
|
||||
|
||||
启动你的游戏来看看会发生什么。要注意,因为它发生的很快:你是玩家从天空上下落,马上掉出了你的游戏屏幕。
|
||||
|
||||
你的引力模拟是工作的,但是,也许太好了。
|
||||
|
||||
作为一次试验,尝试更改你玩家下落的速度。
|
||||
|
||||
### 给引力添加一个地板
|
||||
|
||||
你的游戏没有办法发现你的角色掉落出世界的问题。在一些游戏中,如果一个玩家掉落出世界,该精灵被删除,并在某个新的位置重生。在另一些游戏中,玩家会丢失分数或一条生命。当一个玩家掉落出世界时,不管你想发生什么,你必须能够侦测出玩家何时消失在屏幕外。
|
||||
|
||||
在 Python 中,要检查一个条件,你可以使用一个 `if` 语句。
|
||||
|
||||
你必需查看你玩家**是否**正在掉落,以及你的玩家掉落的程度。如果你的玩家掉落到屏幕的底部,那么你可以做*一些事情*。简化一下,设置玩家精灵的位置为底部边缘上方 20 像素。
|
||||
|
||||
使你的 `gravity` 函数看起来像这样:
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # 玩家掉落的多快
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty
|
||||
```
|
||||
|
||||
然后,启动你的游戏。你的精灵仍然下落,但是它停在屏幕的底部。不过,你也许不能*看到*你在地面层之上的精灵。一个简单的解决方法是,在精灵碰撞游戏世界的底部后,通过添加另一个 `-ty` 到它的新 Y 位置,从而使你的精灵弹跳到更高处:
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # 玩家掉落的多快
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
```
|
||||
|
||||
现在你的玩家在屏幕底部弹跳,恰好在你地面精灵上面。
|
||||
|
||||
你的玩家真正需要的是反抗引力的方法。引力问题是,你不能反抗它,除非你有一些东西来推开引力作用。因此,在接下来的文章中,你将添加地面和平台碰撞以及跳跃能力。在这期间,尝试应用引力到敌人精灵。
|
||||
|
||||
到目前为止,这里是全部的代码:
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# draw a world
|
||||
# add a player and player control
|
||||
# add player movement
|
||||
# add enemy and basic collision
|
||||
# add platform
|
||||
# add gravity
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
self.image.convert_alpha()
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn a player
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
self.score = 1
|
||||
self.images = []
|
||||
for i in range(1,9):
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
img.convert_alpha()
|
||||
img.set_colorkey(ALPHA)
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
control player movement
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update sprite position
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# moving left
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# moving right
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# collisions
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn an enemy
|
||||
'''
|
||||
def __init__(self,x,y,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img))
|
||||
#self.image.convert_alpha()
|
||||
#self.image.set_colorkey(ALPHA)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.x = x
|
||||
self.rect.y = y
|
||||
self.counter = 0
|
||||
|
||||
def move(self):
|
||||
'''
|
||||
enemy movement
|
||||
'''
|
||||
distance = 80
|
||||
speed = 8
|
||||
|
||||
if self.counter >= 0 and self.counter <= distance:
|
||||
self.rect.x += speed
|
||||
elif self.counter >= distance and self.counter <= distance*2:
|
||||
self.rect.x -= speed
|
||||
else:
|
||||
self.counter = 0
|
||||
|
||||
self.counter += 1
|
||||
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
|
||||
enemy_list = pygame.sprite.Group() # create enemy group
|
||||
enemy_list.add(enemy) # add enemy to group
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return enemy_list
|
||||
|
||||
def loot(lvl,lloc):
|
||||
print(lvl)
|
||||
|
||||
def ground(lvl,gloc,tx,ty):
|
||||
ground_list = pygame.sprite.Group()
|
||||
i=0
|
||||
if lvl == 1:
|
||||
while i < len(gloc):
|
||||
ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
def platform(lvl,tx,ty):
|
||||
plat_list = pygame.sprite.Group()
|
||||
ploc = []
|
||||
i=0
|
||||
if lvl == 1:
|
||||
ploc.append((0,worldy-ty-128,3))
|
||||
ploc.append((300,worldy-ty-256,3))
|
||||
ploc.append((500,worldy-ty-128,4))
|
||||
|
||||
while i < len(ploc):
|
||||
j=0
|
||||
while j <= ploc[i][2]:
|
||||
plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'ground.png')
|
||||
plat_list.add(plat)
|
||||
j=j+1
|
||||
print('run' + str(i) + str(ploc[i]))
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return plat_list
|
||||
|
||||
'''
|
||||
Setup
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # frame rate
|
||||
ani = 4 # animation cycles
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # spawn player
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # how fast to move
|
||||
|
||||
eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 #tile size
|
||||
ty = 64 #tile size
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
plat_list = Level.platform( 1,tx,ty )
|
||||
|
||||
'''
|
||||
Main loop
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
print("LEFT")
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
print("RIGHT")
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.gravity() # check gravity
|
||||
player.update()
|
||||
player_list.draw(world)
|
||||
enemy_list.draw(world)
|
||||
ground_list.draw(world)
|
||||
plat_list.draw(world)
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
这是仍在进行中的关于使用 [Pygame][4] 模块来在 [Python 3][3] 在创建电脑游戏的第七部分。先前的文章是:
|
||||
|
||||
* [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][5]
|
||||
* [使用 Python 和 Pygame 模块构建一个游戏框架][6]
|
||||
* [如何在你的 Python 游戏中添加一个玩家][7]
|
||||
* [用 Pygame 使你的游戏角色移动起来][8]
|
||||
* [如何向你的 Python 游戏中添加一个敌人][2]
|
||||
* [在 Pygame 游戏中放置平台][9]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/simulate-gravity-python
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/space_stars_cosmic.jpg?itok=bE94WtN- (Cosmic stars in outer space)
|
||||
[2]: https://linux.cn/article-10883-1.html
|
||||
[3]: https://www.python.org/
|
||||
[4]: https://www.pygame.org
|
||||
[5]: https://linux.cn/article-9071-1.html
|
||||
[6]: https://linux.cn/article-10850-1.html
|
||||
[7]: https://linux.cn/article-10858-1.html
|
||||
[8]: https://linux.cn/article-10874-1.html
|
||||
[9]: https://linux.cn/article-10902-1.html
|
@ -0,0 +1,232 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to write a Python web API with Django)
|
||||
[#]: via: (https://opensource.com/article/19/11/python-web-api-django)
|
||||
[#]: author: (Rachel Waston https://opensource.com/users/rachelwaston)
|
||||
|
||||
如何借助 Django 来编写一个 Python Web API
|
||||
======
|
||||
|
||||
> Django 是 Python API 开发中最流行的框架之一,在这个教程中,我们来学习如何使用它。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/11/142503aio2325mk5weiozz.jpg)
|
||||
|
||||
[Django][2] 所有 Web 框架中最全面的,也是最受欢迎的一个。自 2005 年以来,其流行度大幅上升。
|
||||
|
||||
Django 是由 Django 软件基金会维护,并且获得了社区的大力支持,在全球拥有超过 11,600 名成员。在 Stack Overflow 上,约有 191,000 个带 Django 标签的问题。Spotify、YouTube 和 Instagram 等都使用 Django 来构建应用程序和数据管理。
|
||||
|
||||
本文演示了一个简单的 API,通过它可以使用 HTTP 协议的 GET 方法来从服务器获取数据。
|
||||
|
||||
### 构建一个项目
|
||||
|
||||
首先,为你的 Django 应用程序创建一个目录结构,你可以在系统的任何位置创建:
|
||||
|
||||
```
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
```
|
||||
|
||||
然后,在项目目录中创建一个虚拟环境来隔离本地包依赖关系:
|
||||
|
||||
```
|
||||
$ python3 -m venv env
|
||||
$ source env/bin/activate
|
||||
```
|
||||
|
||||
在 Windows 上,使用命令 `env\Scripts\activate` 来激活虚拟环境。
|
||||
|
||||
### 安装 Django 和 Django REST framework
|
||||
|
||||
然后,安装 Django 和 Django REST 模块:
|
||||
|
||||
```
|
||||
$ pip3 install django
|
||||
$ pip3 install djangorestframework
|
||||
```
|
||||
|
||||
### 实例化一个新的 Django 项目
|
||||
|
||||
现在你的应用程序已经有了一个工作环境,你必须实例化一个新的 Django 项目。与 [Flask][3] 这样微框架不同的是,Django 有专门的命令来创建(注意第一条命令后的 `.` 字符)。
|
||||
|
||||
```
|
||||
$ django-admin startproject tutorial .
|
||||
$ cd tutorial
|
||||
$ django-admin startapp quickstart
|
||||
```
|
||||
|
||||
Django 使用数据库来管理后端,所以你应该在开始开发之前同步数据库,数据库可以通过 `manage.py` 脚本管理,它是在你运行 `django-admin` 命令时创建的。因为你现在在 `tutorial` 目录,所以使用 `../` 符号来运行脚本,它位于上一层目录:
|
||||
|
||||
```
|
||||
$ python3 ../manage.py makemigrations
|
||||
No changes detected
|
||||
$ python4 ../manage.py migrate
|
||||
Operations to perform:
|
||||
Apply all migrations: admin, auth, contenttypes, sessions
|
||||
Running migrations:
|
||||
Applying contenttypes.0001_initial... OK
|
||||
Applying auth.0001_initial... OK
|
||||
Applying admin.0001_initial... OK
|
||||
Applying admin.0002_logentry_remove_auto_add... OK
|
||||
Applying admin.0003_logentry_add_action_flag_choices... OK
|
||||
Applying contenttypes.0002_remove_content_type_name... OK
|
||||
Applying auth.0002_alter_permission_name_max_length... OK
|
||||
Applying auth.0003_alter_user_email_max_length... OK
|
||||
Applying auth.0004_alter_user_username_opts... OK
|
||||
Applying auth.0005_alter_user_last_login_null... OK
|
||||
Applying auth.0006_require_contenttypes_0002... OK
|
||||
Applying auth.0007_alter_validators_add_error_messages... OK
|
||||
Applying auth.0008_alter_user_username_max_length... OK
|
||||
Applying auth.0009_alter_user_last_name_max_length... OK
|
||||
Applying auth.0010_alter_group_name_max_length... OK
|
||||
Applying auth.0011_update_proxy_permissions... OK
|
||||
Applying sessions.0001_initial... OK
|
||||
```
|
||||
|
||||
### 在 Django 中创建用户
|
||||
|
||||
创建一个名为 `admin`,示例密码为 `password123` 的初始用户:
|
||||
|
||||
```
|
||||
$ python3 ../manage.py createsuperuser \
|
||||
--email admin@example.com \
|
||||
--username admin
|
||||
```
|
||||
|
||||
在提示时创建密码。
|
||||
|
||||
### 在 Django 中实现序列化和视图
|
||||
|
||||
为了使 Django 能够将信息传递给 HTTP GET 请求,必须将信息对象转化为有效的响应数据。Django 为此实现了“序列化类” `serializers`。
|
||||
|
||||
在你的项目中,创建一个名为 `quickstart/serializers.py` 的新模块,使用它来定义一些序列化器,模块将用于数据展示:
|
||||
|
||||
```
|
||||
from django.contrib.auth.models import User, Group
|
||||
from rest_framework import serializers
|
||||
|
||||
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['url', 'username', 'email', 'groups']
|
||||
|
||||
class GroupSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Group
|
||||
fields = ['url', 'name']
|
||||
```
|
||||
|
||||
Django 中的[视图][5]是一个接受 Web 请求并返回 Web 响应的函数。响应可以是 HTML、HTTP 重定向、HTTP 错误、JSON 或 XML 文档、图像或 TAR 文件,或者可以是从 Internet 获得的任何其他内容。要创建视图,打开 `quickstart/views.py` 并输入以下代码。该文件已经存在,并且其中包含一些示例文本,保留这些文本并将以下代码添加到文件中:
|
||||
|
||||
```
|
||||
from django.contrib.auth.models import User, Group
|
||||
from rest_framework import viewsets
|
||||
from tutorial.quickstart.serializers import UserSerializer, GroupSerializer
|
||||
|
||||
class UserViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API 允许查看或编辑用户
|
||||
"""
|
||||
queryset = User.objects.all().order_by('-date_joined')
|
||||
serializer_class = UserSerializer
|
||||
|
||||
class GroupViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API 允许查看或编辑组
|
||||
"""
|
||||
queryset = Group.objects.all()
|
||||
serializer_class = GroupSerializer
|
||||
```
|
||||
|
||||
### 使用 Django 生成 URL
|
||||
|
||||
现在,你可以生成 URL 以便人们可以访问你刚起步的 API。在文本编辑器中打开 `urls.py` 并将默认示例代码替换为以下代码:
|
||||
|
||||
```
|
||||
from django.urls import include, path
|
||||
from rest_framework import routers
|
||||
from tutorial.quickstart import views
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'users', views.UserViewSet)
|
||||
router.register(r'groups', views.GroupViewSet)
|
||||
|
||||
# 使用自动路由 URL
|
||||
# 还有登录 URL
|
||||
urlpatterns = [
|
||||
path('', include(router.urls)),
|
||||
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||
]
|
||||
```
|
||||
|
||||
### 调整你的 Django 项目设置
|
||||
|
||||
这个示例项目的设置模块存储在 `tutorial/settings.py` 中,因此在文本编辑器中将其打开,然后在 `INSTALLED_APPS` 列表的末尾添加 `rest_framework`:
|
||||
|
||||
```
|
||||
INSTALLED_APPS = [
|
||||
...
|
||||
'rest_framework',
|
||||
]
|
||||
```
|
||||
|
||||
### 测试 Django API
|
||||
|
||||
现在,你可以测试构建的 API。首先,从命令行启动内置服务器:
|
||||
|
||||
```
|
||||
$ python3 manage.py runserver
|
||||
```
|
||||
|
||||
你可以通过使用 `curl` 导航至 URL `http://localhost:8000/users` 来访问 API:
|
||||
|
||||
```
|
||||
$ curl --get http://localhost:8000/users/?format=json
|
||||
[{"url":"http://localhost:8000/users/1/?format=json","username":"admin","email":"admin@example.com","groups":[]}]
|
||||
```
|
||||
|
||||
使用 Firefox 或你选择的[开源浏览器][6]:
|
||||
|
||||
![一个简单的 Django API][7]
|
||||
|
||||
有关使用 Django 和 Python 的 RESTful API 的更多深入知识,参考出色的 [Django 文档][8]。
|
||||
|
||||
### 为什么要使用 Djago?
|
||||
|
||||
Django 的主要优点:
|
||||
|
||||
1. Django 社区的规模正在不断扩大,因此即使你做一个复杂项目,也会有大量的指导资源。
|
||||
2. 默认包含模板、路由、表单、身份验证和管理工具等功能,你不必寻找外部工具,也不必担心第三方工具会引入兼容性问题。
|
||||
3. 用户、循环和条件的简单结构使你可以专注于编写代码。
|
||||
4. 这是一个成熟且经过优化的框架,它非常快速且可靠。
|
||||
|
||||
Django 的主要缺点:
|
||||
|
||||
1. Django 很复杂!从开发人员视角的角度来看,它可能比简单的框架更难学。
|
||||
2. Django 有一个很大的生态系统。一旦你熟悉它,这会很棒,但是当你深入学习时,它可能会令人感到无所适从。
|
||||
|
||||
对你的应用程序或 API 来说,Django 是绝佳选择。下载并熟悉它,开始开发一个迷人的项目!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/python-web-api-django
|
||||
|
||||
作者:[Rachel Waston][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/rachelwaston
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/python-programming-code-keyboard.png?itok=fxiSpmnd (Hands on a keyboard with a Python book )
|
||||
[2]: https://www.djangoproject.com/
|
||||
[3]: https://opensource.com/article/19/11/python-web-api-flask
|
||||
[4]: mailto:admin@example.com
|
||||
[5]: https://docs.djangoproject.com/en/2.2/topics/http/views/
|
||||
[6]: https://opensource.com/article/19/7/open-source-browsers
|
||||
[7]: https://opensource.com/sites/default/files/uploads/django-api.png (A simple Django API)
|
||||
[8]: https://docs.djangoproject.com/en/2.2
|
177
published/202001/20191130 7 maker gifts for kids and teens.md
Normal file
177
published/202001/20191130 7 maker gifts for kids and teens.md
Normal file
@ -0,0 +1,177 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11762-1.html)
|
||||
[#]: subject: (7 maker gifts for kids and teens)
|
||||
[#]: via: (https://opensource.com/article/19/11/maker-gifts-kids)
|
||||
[#]: author: (Jess Weichler https://opensource.com/users/cyanide-cupcake)
|
||||
|
||||
给儿童和青少年的 7 件创客礼物
|
||||
======
|
||||
|
||||
> 这份礼物指南使你轻松完成节日礼物的采购,它们可给婴儿、儿童、青少年及年龄更大的人们带来创造和创新能力。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/08/140516t4ewey9ryu24tpz5.jpg)
|
||||
|
||||
还在纠结这个假期给年轻人买什么礼物?这是我精选的开源礼物,这些礼物将激发未来的创意和灵感。
|
||||
|
||||
### 蜂鸟机器人套件
|
||||
|
||||
![Hummingbird Robotics Kit][2]
|
||||
|
||||
**年龄:**8 岁 - 成人
|
||||
|
||||
**这是什么:**[蜂鸟机器人套件][3]是一套完整的机器人套件,带有微控制器、电机、LED 和传感器。机器人的大脑具有特殊的端口,小手可以轻松地将其连接到机器人的组件上。蜂鸟套件并没有身体,而是鼓励用户自己创建一个。
|
||||
|
||||
**为什么我喜欢它:**蜂鸟可以使用多种编程语言 —— 从可视化编程(BirdBlox、MakeCode、Snap)到代码编程(Python 和 Java)—— 可以随着用户编码技能的提高而可扩展。所有组件均与你在电子商店中找到的组件完全相同,没有像其他机器人套件那样被塑料所遮盖。这使机器人的内部工作不再神秘,并在你需要时易于采购更多零件。
|
||||
|
||||
由于没有固定组装项目,因此蜂鸟是发挥创造力的完美机器人。
|
||||
|
||||
蜂鸟具有开源的软件和固件。它适用于 Linux、Windows、Mac、Chromebook、Android 和 iOS。
|
||||
|
||||
**费用:**起价为 99 美元。
|
||||
|
||||
### Makey Makey 经典版
|
||||
|
||||
![Makey Makey Classic][4]
|
||||
|
||||
**年龄:** 6岁 - 成人
|
||||
|
||||
**这是什么:** [Makey Makey 经典版][5]可将任何导电物体(从棉花糖到你的朋友)变成计算机钥匙。
|
||||
|
||||
你可以使用鳄鱼夹将 Makey Makey 连接到你选择的导电物体上。然后,通过同时触摸两个导电物体来闭合接地和任何触发键之间的电路。Makey Makey 是一种安全的方法,可以安全地在家中探索电力,同时创造与计算机进行交互的有趣方式。
|
||||
|
||||
**为什么我喜欢它:** Makey Makey 可以与 Scratch 开发的视频游戏搭配使用,以创建独特的控制器,使用户进一步沉浸在游戏中。从用卫生纸卷和铝箔制成的工具到互动艺术和故事,可能性是无限的。它可以在具有 USB 端口的 Linux、Windows 和 Mac 计算机上使用。
|
||||
|
||||
**费用:** 49.95 美金
|
||||
|
||||
### Arduino Uno
|
||||
|
||||
![Arduino Uno][6]
|
||||
|
||||
**年龄:** 10 岁 - 成人
|
||||
|
||||
**这是什么:** Arduino 是随同电子套件购买的微控制器,也可以单独购买,它们具有多种版本,而我最喜欢 [Arduino Uno][7]。你可以根据需要从任何电子商店购买其他组件,例如 LED、电机和传感器。
|
||||
|
||||
**为什么我喜欢它:** Arduino Uno 的文档很完善,因此创客们很容易在线上找到教程。Arduino 可以实现从简单到复杂的各种电子项目。Arduino 具有开源的固件和硬件。它适用于 Linux、Mac 和 Windows。
|
||||
|
||||
**费用:** 主板的起价为 22.00 美元。总成本取决于项目和技能水平。
|
||||
|
||||
### DIY 创客套件
|
||||
|
||||
![A maker kit assembled in a quick trip to the hardware store][8]
|
||||
|
||||
**年龄**:8 岁 - 成人
|
||||
|
||||
**这是什么:**当今许多创客、发明家和程序员都是从鼓捣碰巧出现在身边东西开始的。你可以快速前往最近的电子产品商店,为家里的年轻人创建一套出色的创客工具包。这是我的创客工具包中的内容:
|
||||
|
||||
* 护目镜
|
||||
* 锤子
|
||||
* 钉子和螺丝
|
||||
* 碎木
|
||||
* 螺丝起子
|
||||
* 电线
|
||||
* LED
|
||||
* 压电蜂鸣器
|
||||
* 马达
|
||||
* 带引线的 AA 电池组
|
||||
* 剪线钳
|
||||
* 纸板
|
||||
* 美纹纸胶带
|
||||
* 废布
|
||||
* 纽扣
|
||||
* 线程
|
||||
* 针
|
||||
* 拉链
|
||||
* 钩子
|
||||
* 一个很酷的工具盒,用来存放所有东西
|
||||
|
||||
**我为什么喜欢它:**还记得小时候,你把父母带回家的空纸箱变成了宇宙飞船、房屋或超级计算机吗?这就是为大孩子们准备的 DIY 创客工具包。
|
||||
|
||||
原始的组件使孩子们可以尝试并运用他们的想象力。DIY 创客工具包可以完全针对接收者定制。可以放入一些接受这份礼品的人可能从未想到过用之发挥创意的某些组件,例如为下水道提供一些 LED 或木工结构。
|
||||
|
||||
**费用:**不等
|
||||
|
||||
### 启发式游戏篮
|
||||
|
||||
![Heuristic play kit][9]
|
||||
|
||||
**年龄:** 8 个月至 5 岁
|
||||
|
||||
**这是什么:**启发式游戏篮充满了由天然、无毒材料制成的有趣物品,可供婴幼儿使用其五种感官进行探索。这是一种开放式、自娱自乐的游戏。其想法是,成年人将监督(但不指导)儿童使用篮子及其物品半小时,然后将篮子拿走,等下一次再玩。
|
||||
|
||||
创建带有常见家用物品的可爱游戏篮很容易。尝试包括质地、声音、气味、形状和重量各不相同的物品。这里有一些想法可以帮助您入门。
|
||||
|
||||
* 漏勺或脊状柳条篮可容纳所有物品
|
||||
* 木勺子
|
||||
* 金属打蛋器和汤匙
|
||||
* 板刷
|
||||
* 海绵
|
||||
* 小型鸡蛋纸箱
|
||||
* 纸板管
|
||||
* 小擀面杖
|
||||
* 带纹理的毛巾
|
||||
* 岩石
|
||||
* 手铃
|
||||
* 钩针桌巾
|
||||
* 带盖的小铁罐
|
||||
|
||||
游戏篮中不应包括任何容易破碎的东西或足够小到可以装入纸巾卷的东西,因为它们有窒息危险,应将所有物品彻底清洁后再交给孩子。
|
||||
|
||||
**我为什么喜欢它:**游戏篮非常适合感官发育,并可以帮助幼儿提出问题和探索周围的世界。这是培养创客思维方式的重要组成部分!
|
||||
|
||||
很容易获得适合这个游戏篮的物品。你可能已经在家中或附近的二手商店里找到了很多有趣的物品。幼儿使用游戏篮的方式与婴儿不同。随着孩子们开始模仿成人生活并通过他们的游戏讲故事,这些物品将随孩子一起成长。
|
||||
|
||||
**费用:**不等
|
||||
|
||||
### 《Hello Ruby》
|
||||
|
||||
![Hello Ruby book cover][10]
|
||||
|
||||
**年龄**:5-8 岁
|
||||
|
||||
**这是什么:** 《[Hello Ruby][11]:编码历险记》是 Linda Liukas 的插图书,通过有趣的故事讲述了一个遇到各种问题和朋友(每个都用一个码代表)的女孩,向孩子们介绍了编程概念。Liukas 还有其他副标题为《互联网探险》和《计算机内的旅程》的《Hello Ruby》系列书籍,而《编码历险记》已以 20 多种语言出版。
|
||||
|
||||
**为什么我喜欢它:**作者在书中附带了许多免费、有趣和无障碍的活动,可以从 Hello Ruby 网站下载和打印这些活动。这些活动教授编码概念、还涉及艺术表达、沟通、甚至时间安排。
|
||||
|
||||
**费用:**精装书的标价为 17.99 美元,但你可以通过本地或在线书店以较低的价格买到这本书。
|
||||
|
||||
### 《编程少女:学会编程和改变世界》
|
||||
|
||||
![Girls Who Code book cover][12]
|
||||
|
||||
**年龄**:10 岁 - 成人
|
||||
|
||||
**内容是什么:**由《编程少女》的创始人 Reshma Saujani 撰写,《[编程少女:学会编程和改变世界][13]》为年轻女孩(以及男孩)提供了科技领域的实用信息。它涵盖了广泛的主题,包括编程语言、用例、术语和词汇、职业选择以及技术行业人士的个人简介和访谈。
|
||||
|
||||
**为什么我喜欢它:**本书以讲述了大多数面向成年人的网站都没有的技术故事。这些技术涉及许多学科,对于年轻人来说,重要的是要了解他们可以使用它来解决现实世界中的问题并有所作为。
|
||||
|
||||
**成本:**精装书的标价为 17.99 美元,平装书的标价为 10.99 美元,但你可以通过本地或在线书店以更低的价格找到。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/maker-gifts-kids
|
||||
|
||||
作者:[Jess Weichler][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/cyanide-cupcake
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/OSDC_gift_giveaway_box_520x292.png?itok=w1YQhNH1 (Gift box opens with colors coming out)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/hummingbird.png (Hummingbird Robotics Kit)
|
||||
[3]: https://www.birdbraintechnologies.com/hummingbirdbit/
|
||||
[4]: https://opensource.com/sites/default/files/uploads/makeymakey2.jpg (Makey Makey Classic)
|
||||
[5]: https://makeymakey.com/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/arduinouno.jpg (Arduino Uno)
|
||||
[7]: https://www.arduino.cc/
|
||||
[8]: https://opensource.com/sites/default/files/makerbox-makerkit.jpg (A maker kit assembled in a quick trip to the hardware store)
|
||||
[9]: https://opensource.com/sites/default/files/makerbox-sensorykit.jpg (Heuristic play kit)
|
||||
[10]: https://opensource.com/sites/default/files/uploads/helloruby2.jpg (Hello Ruby book cover)
|
||||
[11]: https://www.helloruby.com/
|
||||
[12]: https://opensource.com/sites/default/files/uploads/girlswhocodebook.jpg (Girls Who Code book cover)
|
||||
[13]: https://girlswhocode.com/book/girls-code-learn-code-change-world/
|
@ -0,0 +1,504 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (cycoe)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11790-1.html)
|
||||
[#]: subject: (Add jumping to your Python platformer game)
|
||||
[#]: via: (https://opensource.com/article/19/12/jumping-python-platformer-game)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
为你的 Python 平台类游戏添加跳跃功能
|
||||
======
|
||||
|
||||
> 在本期使用 Python Pygame 模块编写视频游戏中,学会如何使用跳跃来对抗重力。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/16/214917c8mxn82fot82fx88.jpg)
|
||||
|
||||
在本系列的 [前一篇文章][2] 中,你已经模拟了重力。但现在,你需要赋予你的角色跳跃的能力来对抗重力。
|
||||
|
||||
跳跃是对重力作用的暂时延缓。在这一小段时间里,你是向*上*跳,而不是被重力拉着向下落。但你一旦到达了跳跃的最高点,重力就会重新发挥作用,将你拉回地面。
|
||||
|
||||
在代码中,这种变化被表示为变量。首先,你需要为玩家精灵建立一个变量,使得 Python 能够跟踪该精灵是否正在跳跃中。一旦玩家精灵开始跳跃,他就会再次受到重力的作用,并被拉回最近的物体。
|
||||
|
||||
### 设置跳跃状态变量
|
||||
|
||||
你需要为你的 `Player` 类添加两个新变量:
|
||||
|
||||
* 一个是为了跟踪你的角色是否正在跳跃中,可通过你的玩家精灵是否站在坚实的地面来确定
|
||||
* 一个是为了将玩家带回地面
|
||||
|
||||
将如下两个变量添加到你的 `Player` 类中。在下方的代码中,注释前的部分用于提示上下文,因此只需要添加最后两行:
|
||||
|
||||
```
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
# 此处是重力相关变量
|
||||
self.collide_delta = 0
|
||||
self.jump_delta = 6
|
||||
```
|
||||
|
||||
第一个变量 `collide_delta` 被设为 0 是因为在正常状态下,玩家精灵没有处在跳跃中的状态。另一个变量 `jump_delta` 被设为 6,是为了防止精灵在第一次进入游戏世界时就发生反弹(实际上就是跳跃)。当你完成了本篇文章的示例,尝试把该变量设为 0 看看会发生什么。
|
||||
|
||||
### 跳跃中的碰撞
|
||||
|
||||
如果你是跳到一个蹦床上,那你的跳跃一定非常优美。但是如果你是跳向一面墙会发生什么呢?(千万不要去尝试!)不管你的起跳多么令人印象深刻,当你撞到比你更大更硬的物体时,你都会立马停下。(LCTT 译注:原理参考动量守恒定律)
|
||||
|
||||
为了在你的视频游戏中模拟这一点,你需要在你的玩家精灵与地面等东西发生碰撞时,将 `self.collide_delta` 变量设为 0。如果你的 `self.collide_delta` 不是 0 而是其它的什么值,那么你的玩家就会发生跳跃,并且当你的玩家与墙或者地面发生碰撞时无法跳跃。
|
||||
|
||||
在你的 `Player` 类的 `update` 方法中,将地面碰撞相关代码块修改为如下所示:
|
||||
|
||||
```
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
self.collide_delta = 0 # 停止跳跃
|
||||
if self.rect.y > g.rect.y:
|
||||
self.health -=1
|
||||
print(self.health)
|
||||
```
|
||||
|
||||
这段代码块检查了地面精灵和玩家精灵之间发生的碰撞。当发生碰撞时,它会将玩家 Y 方向的坐标值设置为游戏窗口的高度减去一个瓷砖的高度再减去另一个瓷砖的高度。以此保证了玩家精灵是站在地面*上*,而不是嵌在地面里。同时它也将 `self.collide_delta` 设为 0,使得程序能够知道玩家未处在跳跃中。除此之外,它将 `self.movey` 设为 0,使得程序能够知道玩家当前未受到重力的牵引作用(这是游戏物理引擎的奇怪之处,一旦玩家落地,也就没有必要继续将玩家拉向地面)。
|
||||
|
||||
此处 `if` 语句用来检测玩家是否已经落到地面之*下*,如果是,那就扣除一点生命值作为惩罚。此处假定了你希望当你的玩家落到地图之外时失去生命值。这个设定不是必需的,它只是平台类游戏的一种惯例。更有可能的是,你希望这个事件能够触发另一些事件,或者说是一种能够让你的现实世界玩家沉迷于让精灵掉到屏幕之外的东西。一种简单的恢复方式是在玩家精灵掉落到地图之外时,将 `self.rect.y` 重新设置为 0,这样它就会在地图上方重新生成,并落到坚实的地面上。
|
||||
|
||||
### 撞向地面
|
||||
|
||||
模拟的重力使你玩家的 Y 坐标不断增大(LCTT 译注:此处原文中为 0,但在 Pygame 中越靠下方 Y 坐标应越大)。要实现跳跃,完成如下代码使你的玩家精灵离开地面,飞向空中。
|
||||
|
||||
在你的 `Player` 类的 `update` 方法中,添加如下代码来暂时延缓重力的作用:
|
||||
|
||||
```
|
||||
if self.collide_delta < 6 and self.jump_delta < 6:
|
||||
self.jump_delta = 6*2
|
||||
self.movey -= 33 # 跳跃的高度
|
||||
self.collide_delta += 6
|
||||
self.jump_delta += 6
|
||||
```
|
||||
|
||||
根据此代码所示,跳跃使玩家精灵向空中移动了 33 个像素。此处是*负* 33 是因为在 Pygame 中,越小的数代表距离屏幕顶端越近。
|
||||
|
||||
不过此事件视条件而定,只有当 `self.collide_delta` 小于 6(缺省值定义在你 `Player` 类的 `init` 方法中)并且 `self.jump_delta` 也于 6 的时候才会发生。此条件能够保证直到玩家碰到一个平台,才能触发另一次跳跃。换言之,它能够阻止空中二段跳。
|
||||
|
||||
在某些特殊条件下,你可能不想阻止空中二段跳,或者说你允许玩家进行空中二段跳。举个栗子,如果玩家获得了某个战利品,那么在他被敌人攻击到之前,都能够拥有空中二段跳的能力。
|
||||
|
||||
当你完成本篇文章中的示例,尝试将 `self.collide_delta` 和 `self.jump_delta` 设置为 0,从而获得百分之百的几率触发空中二段跳。
|
||||
|
||||
### 在平台上着陆
|
||||
|
||||
目前你已经定义了在玩家精灵摔落地面时的抵抗重力条件,但此时你的游戏代码仍保持平台与地面置于不同的列表中(就像本文中做的很多其他选择一样,这个设定并不是必需的,你可以尝试将地面作为另一种平台)。为了允许玩家精灵站在平台之上,你必须像检测地面碰撞一样,检测玩家精灵与平台精灵之间的碰撞。将如下代码放于你的 `update` 方法中:
|
||||
|
||||
```
|
||||
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
|
||||
for p in plat_hit_list:
|
||||
self.collide_delta = 0 # 跳跃结束
|
||||
self.movey = 0
|
||||
```
|
||||
|
||||
但此处还有一点需要考虑:平台悬在空中,也就意味着玩家可以通过从上面或者从下面接触平台来与之互动。
|
||||
|
||||
确定平台如何与玩家互动取决于你,阻止玩家从下方到达平台也并不稀奇。将如下代码加到上方的代码块中,使得平台表现得像天花板或者说是藤架。只有在玩家精灵跳得比平台上沿更高时才能跳到平台上,但会阻止玩家从平台下方跳上来:
|
||||
|
||||
```
|
||||
if self.rect.y > p.rect.y:
|
||||
self.rect.y = p.rect.y+ty
|
||||
else:
|
||||
self.rect.y = p.rect.y-ty
|
||||
```
|
||||
|
||||
此处 `if` 语句代码块的第一个子句阻止玩家精灵从平台正下方跳到平台上。如果它检测到玩家精灵的坐标比平台更大(在 Pygame 中,坐标更大意味着在屏幕的更下方),那么将玩家精灵新的 Y 坐标设置为当前平台的 Y 坐标加上一个瓷砖的高度。实际效果就是保证玩家精灵距离平台一个瓷砖的高度,防止其从下方穿过平台。
|
||||
|
||||
`else` 子句做了相反的事情。当程序运行到此处时,如果玩家精灵的 Y 坐标*不*比平台的更大,意味着玩家精灵是从空中落下(不论是由于玩家刚刚从此处生成,或者是玩家执行了跳跃)。在这种情况下,玩家精灵的 Y 坐标被设为平台的 Y 坐标减去一个瓷砖的高度(切记,在 Pygame 中更小的 Y 坐标代表在屏幕上的更高处)。这样就能保证玩家在平台*上*,除非他从平台上跳下来或者走下来。
|
||||
|
||||
你也可以尝试其他的方式来处理玩家与平台之间的互动。举个栗子,也许玩家精灵被设定为处在平台的“前面”,他能够无障碍地跳跃穿过平台并站在上面。或者你可以设计一种平台会减缓而又不完全阻止玩家的跳跃过程。甚至你可以通过将不同平台分到不同列表中来混合搭配使用。
|
||||
|
||||
### 触发一次跳跃
|
||||
|
||||
目前为此,你的代码已经模拟了所有必需的跳跃条件,但仍缺少一个跳跃触发器。你的玩家精灵的 `self.jump_delta` 初始值被设置为 6,只有当它比 6 小的时候才会触发更新跳跃的代码。
|
||||
|
||||
为跳跃变量设置一个新的设置方法,在你的 `Player` 类中创建一个 `jump` 方法,并将 `self.jump_delta` 设为小于 6 的值。通过使玩家精灵向空中移动 33 个像素,来暂时减缓重力的作用。
|
||||
|
||||
|
||||
```
|
||||
def jump(self,platform_list):
|
||||
self.jump_delta = 0
|
||||
```
|
||||
|
||||
不管你相信与否,这就是 `jump` 方法的全部。剩余的部分在 `update` 方法中,你已经在前面实现了相关代码。
|
||||
|
||||
要使你游戏中的跳跃功能生效,还有最后一件事情要做。如果你想不起来是什么,运行游戏并观察跳跃是如何生效的。
|
||||
|
||||
问题就在于你的主循环中没有调用 `jump` 方法。先前你已经为该方法创建了一个按键占位符,现在,跳跃键所做的就是将 `jump` 打印到终端。
|
||||
|
||||
### 调用 jump 方法
|
||||
|
||||
在你的主循环中,将*上*方向键的效果从打印一条调试语句,改为调用 `jump` 方法。
|
||||
|
||||
注意此处,与 `update` 方法类似,`jump` 方法也需要检测碰撞,因此你需要告诉它使用哪个 `plat_list`。
|
||||
|
||||
```
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
player.jump(plat_list)
|
||||
```
|
||||
|
||||
如果你倾向于使用空格键作为跳跃键,使用 `pygame.K_SPACE` 替代 `pygame.K_UP` 作为按键。另一种选择,你可以同时使用两种方式(使用单独的 `if` 语句),给玩家多一种选择。
|
||||
|
||||
现在来尝试你的游戏吧!在下一篇文章中,你将让你的游戏卷动起来。
|
||||
|
||||
![Pygame 平台类游戏][3]
|
||||
|
||||
以下是目前为止的所有代码:
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# draw a world
|
||||
# add a player and player control
|
||||
# add player movement
|
||||
# add enemy and basic collision
|
||||
# add platform
|
||||
# add gravity
|
||||
# add jumping
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x 坐标,y 坐标,图像宽度,图像高度,图像文件
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
self.image.convert_alpha()
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
生成一个玩家
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
self.collide_delta = 0
|
||||
self.jump_delta = 6
|
||||
self.score = 1
|
||||
self.images = []
|
||||
for i in range(1,9):
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
img.convert_alpha()
|
||||
img.set_colorkey(ALPHA)
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
def jump(self,platform_list):
|
||||
self.jump_delta = 0
|
||||
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
控制玩家移动
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
更新精灵位置
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# 向左移动
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# 向右移动
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# 碰撞
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
#print(self.health)
|
||||
|
||||
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
|
||||
for p in plat_hit_list:
|
||||
self.collide_delta = 0 # stop jumping
|
||||
self.movey = 0
|
||||
if self.rect.y > p.rect.y:
|
||||
self.rect.y = p.rect.y+ty
|
||||
else:
|
||||
self.rect.y = p.rect.y-ty
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
self.collide_delta = 0 # stop jumping
|
||||
if self.rect.y > g.rect.y:
|
||||
self.health -=1
|
||||
print(self.health)
|
||||
|
||||
if self.collide_delta < 6 and self.jump_delta < 6:
|
||||
self.jump_delta = 6*2
|
||||
self.movey -= 33 # how high to jump
|
||||
self.collide_delta += 6
|
||||
self.jump_delta += 6
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
生成一个敌人
|
||||
'''
|
||||
def __init__(self,x,y,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img))
|
||||
self.movey = 0
|
||||
#self.image.convert_alpha()
|
||||
#self.image.set_colorkey(ALPHA)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.x = x
|
||||
self.rect.y = y
|
||||
self.counter = 0
|
||||
|
||||
|
||||
def move(self):
|
||||
'''
|
||||
敌人移动
|
||||
'''
|
||||
distance = 80
|
||||
speed = 8
|
||||
|
||||
self.movey += 3.2
|
||||
|
||||
if self.counter >= 0 and self.counter <= distance:
|
||||
self.rect.x += speed
|
||||
elif self.counter >= distance and self.counter <= distance*2:
|
||||
self.rect.x -= speed
|
||||
else:
|
||||
self.counter = 0
|
||||
|
||||
self.counter += 1
|
||||
|
||||
if not self.rect.y >= worldy-ty-ty:
|
||||
self.rect.y += self.movey
|
||||
|
||||
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
|
||||
for p in plat_hit_list:
|
||||
self.movey = 0
|
||||
if self.rect.y > p.rect.y:
|
||||
self.rect.y = p.rect.y+ty
|
||||
else:
|
||||
self.rect.y = p.rect.y-ty
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.rect.y = worldy-ty-ty
|
||||
|
||||
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # 生成敌人
|
||||
enemy_list = pygame.sprite.Group() # 创建敌人组
|
||||
enemy_list.add(enemy) # 将敌人添加到敌人组
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return enemy_list
|
||||
|
||||
def loot(lvl,lloc):
|
||||
print(lvl)
|
||||
|
||||
def ground(lvl,gloc,tx,ty):
|
||||
ground_list = pygame.sprite.Group()
|
||||
i=0
|
||||
if lvl == 1:
|
||||
while i < len(gloc):
|
||||
ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
def platform(lvl,tx,ty):
|
||||
plat_list = pygame.sprite.Group()
|
||||
ploc = []
|
||||
i=0
|
||||
if lvl == 1:
|
||||
ploc.append((0,worldy-ty-128,3))
|
||||
ploc.append((300,worldy-ty-256,3))
|
||||
ploc.append((500,worldy-ty-128,4))
|
||||
|
||||
while i < len(ploc):
|
||||
j=0
|
||||
while j <= ploc[i][2]:
|
||||
plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'ground.png')
|
||||
plat_list.add(plat)
|
||||
j=j+1
|
||||
print('run' + str(i) + str(ploc[i]))
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return plat_list
|
||||
|
||||
'''
|
||||
Setup
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # 帧率
|
||||
ani = 4 # 动画循环
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # 生成玩家
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # how fast to move
|
||||
jump = -24
|
||||
|
||||
eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 # 瓷砖尺寸
|
||||
ty = 64 # 瓷砖尺寸
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
plat_list = Level.platform( 1,tx,ty )
|
||||
|
||||
'''
|
||||
主循环
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
print("LEFT")
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
print("RIGHT")
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
player.jump(plat_list)
|
||||
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
# world.fill(BLACK)
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.gravity() # 检查重力
|
||||
player.update()
|
||||
player_list.draw(world) # 刷新玩家位置
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
ground_list.draw(world) # 刷新地面
|
||||
plat_list.draw(world) # 刷新平台
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
本期是使用 [Pygame][5] 模块在 [Python 3][4] 中创建视频游戏连载系列的第 7 期。往期文章为:
|
||||
|
||||
* [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][6]
|
||||
* [使用 Python 和 Pygame 模块构建一个游戏框架][7]
|
||||
* [如何在你的 Python 游戏中添加一个玩家][8]
|
||||
* [用 Pygame 使你的游戏角色移动起来][9]
|
||||
* [如何向你的 Python 游戏中添加一个敌人][10]
|
||||
* [在 Pygame 游戏中放置平台][11]
|
||||
* [在你的 Python 游戏中模拟引力][2]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/jumping-python-platformer-game
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/arcade_game_gaming.jpg?itok=84Rjk_32 (Arcade games)
|
||||
[2]: https://linux.cn/article-11780-1.html
|
||||
[3]: https://opensource.com/sites/default/files/uploads/pygame-jump.jpg (Pygame platformer)
|
||||
[4]: https://www.python.org/
|
||||
[5]: https://www.pygame.org/
|
||||
[6]: https://linux.cn/article-9071-1.html
|
||||
[7]: https://linux.cn/article-10850-1.html
|
||||
[8]: https://linux.cn/article-10858-1.html
|
||||
[9]: https://linux.cn/article-10874-1.html
|
||||
[10]: https://linux.cn/article-10883-1.html
|
||||
[11]: https://linux.cn/article-10902-1.html
|
@ -0,0 +1,73 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11814-1.html)
|
||||
[#]: subject: (What's your favorite terminal emulator?)
|
||||
[#]: via: (https://opensource.com/article/19/12/favorite-terminal-emulator)
|
||||
[#]: author: (Opensource.com https://opensource.com/users/admin)
|
||||
|
||||
你最喜欢的终端模拟器是什么?
|
||||
======
|
||||
|
||||
> 我们让社区讲述他们在终端仿真器方面的经验。以下是我们收到的一些回复。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/24/000846qsmpz7s7spig77qg.jpg)
|
||||
|
||||
终端仿真器的偏好可以说明一个人的工作流程。无鼠标操作能力是否必须具备?你想要标签页还是窗口?对于终端仿真器你还有什么选择的原因?是否有酷的因素?欢迎参加调查或给我们留下评论,告诉我们你最喜欢的终端模拟器。你尝试过多少种终端仿真器呢?
|
||||
|
||||
我们让社区讲述他们在终端仿真器方面的经验。以下是我们收到的一些回复。
|
||||
|
||||
“我最喜欢的终端仿真器是用 Powerline 定制的 Tilix。我喜欢它支持在一个窗口中打开多个终端。” —Dan Arel
|
||||
|
||||
“[urxvt][2]。它可以通过文件简单配置,轻巧,并且在大多数程序包管理器存储库中都很容易找到。” —Brian Tomlinson
|
||||
|
||||
“即使我不再使用 GNOME,gnome-terminal 仍然是我的首选。:)” —Justin W. Flory
|
||||
|
||||
“现在 FC31 上的 Terminator。我刚刚开始使用它,我喜欢它的分屏功能,对我来说感觉很轻巧。我正在研究它的插件。” —Marc Maxwell
|
||||
|
||||
“不久前,我切换到了 Tilix,它完成了我需要终端执行的所有工作。:) 多个窗格、通知,很精简,用来运行我的 tmux 会话很棒。” —Kevin Fenzi
|
||||
|
||||
“alacritty。它针对速度进行了优化,是用 Rust 实现的,并且具有很多常规功能,但是老实说,我只关心一个功能:可配置的字形间距,使我可以进一步压缩字体。” —Alexander Sosedkin
|
||||
|
||||
“我是个老古板:KDE Konsole。如果是远程会话,请使用 tmux。” —Marcin Juszkiewicz
|
||||
|
||||
“在 macOS 上用 iTerm2。是的,它是开源的。:-) 在 Linux 上是 Terminator。” —Patrick Mullins
|
||||
|
||||
“我现在已经使用 alacritty 一两年了,但是最近我在全屏模式下使用 cool-retro-term,因为我必须运行一个输出内容有很多的脚本,而它看起来很酷,让我感觉很酷。这对我很重要。” —Nick Childers
|
||||
|
||||
“我喜欢 Tilix,部分是因为它擅长免打扰(我通常全屏运行它,里面是 tmux),而且还提供自定义热链接支持:在我的终端中,像 ‘rhbz#1234’ 之类的文本是将我带到 Bugzilla 的热链接。类似的还有 LaunchPad 提案,OpenStack 的 Gerrit 更改 ID 等。” —Lars Kellogg-Stedman
|
||||
|
||||
“Eterm,在使用 Vintage 配置文件的 cool-retro-term 中,演示效果也最好。” —Ivan Horvath
|
||||
|
||||
“Tilix +1。这是 GNOME 用户最好的选择,我是这么觉得的!” —Eric Rich
|
||||
|
||||
“urxvt。快速、小型、可配置、可通过 Perl 插件扩展,这使其可以无鼠标操作。” —Roman Dobosz
|
||||
|
||||
“Konsole 是最好的,也是 KDE 项目中我唯一使用的应用程序。所有搜索结果都高亮显示是一个杀手级功能,据我所知没有任何其它 Linux 终端有这个功能(如果能证明我错了,那我也很高兴)。最适合搜索编译错误和输出日志。” —Jan Horak
|
||||
|
||||
“我过去经常使用 Terminator。现在我在 Tilix 中克隆了它的主题(深色主题),而感受一样好。它可以在选项卡之间轻松移动。就是这样。” —Alberto Fanjul Alonso
|
||||
|
||||
“我开始使用的是 Terminator,自从差不多过去这三年,我已经完全切换到 Tilix。” —Mike Harris
|
||||
|
||||
“我使用下拉式终端 X。这是 GNOME 3 的一个非常简单的扩展,使我始终可以通过一个按键(对于我来说是`F12`)拉出一个终端。它还支持制表符,这正是我所需要的。 ” —Germán Pulido
|
||||
|
||||
“xfce4-terminal:支持 Wayland、缩放、无边框、无标题栏、无滚动条 —— 这就是我在 tmux 之外全部想要的终端仿真器的功能。我希望我的终端仿真器可以尽可能多地使用屏幕空间,我通常在 tmux 窗格中并排放着编辑器(Vim)和 repl。” —Martin Kourim
|
||||
|
||||
“别问,问就是 Fish ! ;-)” —Eric Schabell
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/favorite-terminal-emulator
|
||||
|
||||
作者:[Opensource.com][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/admin
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/osdc_terminals_0.png?itok=XwIRERsn (Terminal window with green text)
|
||||
[2]: https://opensource.com/article/19/10/why-use-rxvt-terminal
|
@ -0,0 +1,320 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lxbwolf)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11778-1.html)
|
||||
[#]: subject: (Lessons learned from programming in Go)
|
||||
[#]: via: (https://opensource.com/article/19/12/go-common-pitfalls)
|
||||
[#]: author: (Eduardo Ferreira https://opensource.com/users/edufgf)
|
||||
|
||||
Go 并发编程中的经验教训
|
||||
======
|
||||
|
||||
> 通过学习如何定位并发处理的陷阱来避免未来处理这些问题时的困境。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/150539n217ak1vcf717uzx.jpg)
|
||||
|
||||
在复杂的分布式系统进行任务处理时,你通常会需要进行并发的操作。在 [Mode.net][2] 公司,我们每天都要和实时、快速和灵活的软件打交道。而没有一个高度并发的系统,就不可能构建一个毫秒级的动态地路由数据包的全球专用网络。这个动态路由是基于网络状态的,尽管这个过程需要考虑众多因素,但我们的重点是链路指标。在我们的环境中,链路指标可以是任何跟网络链接的状态和当前属性(如链接延迟)有关的任何内容。
|
||||
|
||||
### 并发探测链接监控
|
||||
|
||||
我们的动态路由算法 [H.A.L.O.][4](<ruby>逐跳自适应链路状态最佳路由<rt>Hop-by-Hop Adaptive Link-State Optimal Routing</rt></ruby>)部分依赖于链路指标来计算路由表。这些指标由位于每个 PoP(<ruby>存活节点<rt>Point of Presence</rt></ruby>)上的独立组件收集。PoP 是表示我们的网络中单个路由实体的机器,通过链路连接并分布在我们的网络拓扑中的各个位置。某个组件使用网络数据包探测周围的机器,周围的机器回复数据包给前者。从接收到的探测包中可以获得链路延迟。由于每个 PoP 都有不止一个临近节点,所以这种探测任务实质上是并发的:我们需要实时测量每个临近连接点的延迟。我们不能串行地处理;为了计算这个指标,必须尽快处理每个探测。
|
||||
|
||||
![latency computation graph][6]
|
||||
|
||||
### 序列号和重置:一个重新排列场景
|
||||
|
||||
我们的探测组件互相发送和接收数据包,并依靠序列号进行数据包处理。这旨在避免处理重复的包或顺序被打乱的包。我们的第一个实现依靠特殊的序列号 0 来重置序列号。这个数字仅在组件初始化时使用。主要的问题是我们考虑了递增的序列号总是从 0 开始。在该组件重启后,包的顺序可能会重新排列,某个包的序列号可能会轻易地被替换成重置之前使用过的值。这意味着,后继的包都会被忽略掉,直到排到重置之前用到的序列值。
|
||||
|
||||
### UDP 握手和有限状态机
|
||||
|
||||
这里的问题是该组件重启前后的序列号是否一致。有几种方法可以解决这个问题,经过讨论,我们选择了实现一个带有清晰状态定义的三步握手协议。这个握手过程在初始化时通过链接建立会话。这样可以确保节点通过同一个会话进行通信且使用了适当的序列号。
|
||||
|
||||
为了正确实现这个过程,我们必须定义一个有清晰状态和过渡的有限状态机。这样我们就可以正确管理握手过程中的所有极端情况。
|
||||
|
||||
![finite state machine diagram][7]
|
||||
|
||||
会话 ID 由握手的初始化程序生成。一个完整的交换顺序如下:
|
||||
|
||||
1. 发送者发送一个 `SYN(ID)` 数据包。
|
||||
2. 接收者存储接收到的 `ID` 并发送一个 `SYN-ACK(ID)`。
|
||||
3. 发送者接收到 `SYN-ACK(ID)` 并发送一个 `ACK(ID)`。它还发送一个从序列号 0 开始的数据包。
|
||||
4. 接收者检查最后接收到的 `ID`,如果 ID 匹配,则接受 `ACK(ID)`。它还开始接受序列号为 0 的数据包。
|
||||
|
||||
### 处理状态超时
|
||||
|
||||
基本上,每种状态下你都需要处理最多三种类型的事件:链接事件、数据包事件和超时事件。这些事件会并发地出现,因此你必须正确处理并发。
|
||||
|
||||
* 链接事件包括网络连接或网络断开的变化,相应的初始化一个链接会话或断开一个已建立的会话。
|
||||
* 数据包事件是控制数据包(`SYN`/`SYN-ACK`/`ACK`)或只是探测响应。
|
||||
* 超时事件在当前会话状态的预定超时时间到期后触发。
|
||||
|
||||
这里面临的最主要的问题是如何处理并发的超时到期和其他事件。这里很容易陷入死锁和资源竞争的陷阱。
|
||||
|
||||
### 第一种方法
|
||||
|
||||
本项目使用的语言是 [Golang][8]。它确实提供了原生的同步机制,如自带的通道和锁,并且能够使用轻量级线程来进行并发处理。
|
||||
|
||||
![gophers hacking together][9]
|
||||
|
||||
*gopher 们聚众狂欢*
|
||||
|
||||
首先,你可以设计两个分别表示我们的会话和超时处理程序的结构体。
|
||||
|
||||
```go
|
||||
type Session struct {
|
||||
State SessionState
|
||||
Id SessionId
|
||||
RemoteIp string
|
||||
}
|
||||
|
||||
type TimeoutHandler struct {
|
||||
callback func(Session)
|
||||
session Session
|
||||
duration int
|
||||
timer *timer.Timer
|
||||
}
|
||||
```
|
||||
|
||||
`Session` 标识连接会话,内有表示会话 ID、临近的连接点的 IP 和当前会话状态的字段。
|
||||
|
||||
`TimeoutHandler` 包含回调函数、对应的会话、持续时间和指向调度计时器的指针。
|
||||
|
||||
每一个临近连接点的会话都包含一个保存调度 `TimeoutHandler` 的全局映射。
|
||||
|
||||
```
|
||||
SessionTimeout map[Session]*TimeoutHandler
|
||||
```
|
||||
|
||||
下面方法注册和取消超时:
|
||||
|
||||
```go
|
||||
// schedules the timeout callback function.
|
||||
func (timeout* TimeoutHandler) Register() {
|
||||
timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time.Second, func() {
|
||||
timeout.callback(timeout.session)
|
||||
})
|
||||
}
|
||||
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.timer == nil {
|
||||
return
|
||||
}
|
||||
timeout.timer.Stop()
|
||||
}
|
||||
```
|
||||
|
||||
你可以使用类似下面的方法来创建和存储超时:
|
||||
|
||||
```go
|
||||
func CreateTimeoutHandler(callback func(Session), session Session, duration int) *TimeoutHandler {
|
||||
if sessionTimeout[session] == nil {
|
||||
sessionTimeout[session] := new(TimeoutHandler)
|
||||
}
|
||||
|
||||
timeout = sessionTimeout[session]
|
||||
timeout.session = session
|
||||
timeout.callback = callback
|
||||
timeout.duration = duration
|
||||
return timeout
|
||||
}
|
||||
```
|
||||
|
||||
超时处理程序创建后,会在经过了设置的 `duration` 时间(秒)后执行回调函数。然而,有些事件会使你重新调度一个超时处理程序(与 `SYN` 状态时的处理一样,每 3 秒一次)。
|
||||
|
||||
为此,你可以让回调函数重新调度一次超时:
|
||||
|
||||
```go
|
||||
func synCallback(session Session) {
|
||||
sendSynPacket(session)
|
||||
|
||||
// reschedules the same callback.
|
||||
newTimeout := NewTimeoutHandler(synCallback, session, SYN_TIMEOUT_DURATION)
|
||||
newTimeout.Register()
|
||||
|
||||
sessionTimeout[state] = newTimeout
|
||||
}
|
||||
```
|
||||
|
||||
这次回调在新的超时处理程序中重新调度自己,并更新全局映射 `sessionTimeout`。
|
||||
|
||||
### 数据竞争和引用
|
||||
|
||||
你的解决方案已经有了。可以通过检查计时器到期后超时回调是否执行来进行一个简单的测试。为此,注册一个超时,休眠 `duration` 秒,然后检查是否执行了回调的处理。执行这个测试后,最好取消预定的超时时间(因为它会重新调度),这样才不会在下次测试时产生副作用。
|
||||
|
||||
令人惊讶的是,这个简单的测试发现了这个解决方案中的一个问题。使用 `cancel` 方法来取消超时并没有正确处理。以下顺序的事件会导致数据资源竞争:
|
||||
|
||||
1. 你有一个已调度的超时处理程序。
|
||||
2. 线程 1:
|
||||
1. 你接收到一个控制数据包,现在你要取消已注册的超时并切换到下一个会话状态(如发送 `SYN` 后接收到一个 `SYN-ACK`)
|
||||
2. 你调用了 `timeout.Cancel()`,这个函数调用了 `timer.Stop()`。(请注意,Golang 计时器的停止不会终止一个已过期的计时器。)
|
||||
3. 线程 2:
|
||||
1. 在取消调用之前,计时器已过期,回调即将执行。
|
||||
2. 执行回调,它调度一次新的超时并更新全局映射。
|
||||
4. 线程 1:
|
||||
1. 切换到新的会话状态并注册新的超时,更新全局映射。
|
||||
|
||||
两个线程并发地更新超时映射。最终结果是你无法取消注册的超时,然后你也会丢失对线程 2 重新调度的超时的引用。这导致处理程序在一段时间内持续执行和重新调度,出现非预期行为。
|
||||
|
||||
### 锁也解决不了问题
|
||||
|
||||
使用锁也不能完全解决问题。如果你在处理所有事件和执行回调之前加锁,它仍然不能阻止一个过期的回调运行:
|
||||
|
||||
```go
|
||||
func (timeout* TimeoutHandler) Register() {
|
||||
timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time._Second_, func() {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
timeout.callback(timeout.session)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
现在的区别就是全局映射的更新是同步的,但是这还是不能阻止在你调用 `timeout.Cancel()` 后回调的执行 —— 这种情况出现在调度计时器过期了但是还没有拿到锁的时候。你还是会丢失一个已注册的超时的引用。
|
||||
|
||||
### 使用取消通道
|
||||
|
||||
你可以使用取消通道,而不必依赖不能阻止到期的计时器执行的 golang 函数 `timer.Stop()`。
|
||||
|
||||
这是一个略有不同的方法。现在你可以不用再通过回调进行递归地重新调度;而是注册一个死循环,这个循环接收到取消信号或超时事件时终止。
|
||||
|
||||
新的 `Register()` 产生一个新的 go 线程,这个线程在超时后执行你的回调,并在前一个超时执行后调度新的超时。返回给调用方一个取消通道,用来控制循环的终止。
|
||||
|
||||
```go
|
||||
func (timeout *TimeoutHandler) Register() chan struct{} {
|
||||
cancelChan := make(chan struct{})
|
||||
|
||||
go func () {
|
||||
select {
|
||||
case _ = <- cancelChan:
|
||||
return
|
||||
case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):
|
||||
func () {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
timeout.callback(timeout.session)
|
||||
} ()
|
||||
}
|
||||
} ()
|
||||
|
||||
return cancelChan
|
||||
}
|
||||
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.cancelChan == nil {
|
||||
return
|
||||
}
|
||||
timeout.cancelChan <- struct{}{}
|
||||
}
|
||||
```
|
||||
|
||||
这个方法给你注册的所有超时提供了取消通道。一个取消调用向通道发送一个空结构体并触发取消操作。然而,这并不能解决前面的问题;可能在你通过通道取消之前以及超时线程拿到锁之前,超时时间就已经到了。
|
||||
|
||||
这里的解决方案是,在拿到锁**之后**,检查一下超时范围内的取消通道。
|
||||
|
||||
```go
|
||||
case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):
|
||||
func () {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
select {
|
||||
case _ = <- handler.cancelChan:
|
||||
return
|
||||
default:
|
||||
timeout.callback(timeout.session)
|
||||
}
|
||||
} ()
|
||||
}
|
||||
```
|
||||
|
||||
最终,这可以确保在拿到锁之后执行回调,不会触发取消操作。
|
||||
|
||||
### 小心死锁
|
||||
|
||||
这个解决方案看起来有效;但是还是有个隐患:[死锁][10]。
|
||||
|
||||
请阅读上面的代码,试着自己找到它。考虑下描述的所有函数的并发调用。
|
||||
|
||||
这里的问题在取消通道本身。我们创建的是无缓冲通道,即发送的是阻塞调用。当你在一个超时处理程序中调用取消函数时,只有在该处理程序被取消后才能继续处理。问题出现在,当你有多个调用请求到同一个取消通道时,这时一个取消请求只被处理一次。当多个事件同时取消同一个超时处理程序时,如连接断开或控制包事件,很容易出现这种情况。这会导致死锁,可能会使应用程序停机。
|
||||
|
||||
![gophers on a wire, talking][11]
|
||||
|
||||
*有人在听吗?*
|
||||
|
||||
(已获得 Trevor Forrey 授权。)
|
||||
|
||||
这里的解决方案是创建通道时指定缓存大小至少为 1,这样向通道发送数据就不会阻塞,也显式地使发送变成非阻塞的,避免了并发调用。这样可以确保取消操作只发送一次,并且不会阻塞后续的取消调用。
|
||||
|
||||
```go
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.cancelChan == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case timeout.cancelChan <- struct{}{}:
|
||||
default:
|
||||
// can’t send on the channel, someone has already requested the cancellation.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 总结
|
||||
|
||||
在实践中你学到了并发操作时出现的常见错误。由于其不确定性,即使进行大量的测试,也不容易发现这些问题。下面是我们在最初的实现中遇到的三个主要问题:
|
||||
|
||||
#### 在非同步的情况下更新共享数据
|
||||
|
||||
这似乎是个很明显的问题,但如果并发更新发生在不同的位置,就很难发现。结果就是数据竞争,由于一个更新会覆盖另一个,因此对同一数据的多次更新中会有某些更新丢失。在我们的案例中,我们是在同时更新同一个共享映射里的调度超时引用。(有趣的是,如果 Go 检测到在同一个映射对象上的并发读写,会抛出致命错误 — 你可以尝试下运行 Go 的[数据竞争检测器](https://golang.org/doc/articles/race_detector.html))。这最终会导致丢失超时引用,且无法取消给定的超时。当有必要时,永远不要忘记使用锁。
|
||||
|
||||
![gopher assembly line][13]
|
||||
|
||||
*不要忘记同步 gopher 们的工作*
|
||||
|
||||
#### 缺少条件检查
|
||||
|
||||
在不能仅依赖锁的独占性的情况下,就需要进行条件检查。我们遇到的场景稍微有点不一样,但是核心思想跟[条件变量][14]是一样的。假设有个一个生产者和多个消费者使用一个共享队列的经典场景,生产者可以将一个元素添加到队列并唤醒所有消费者。这个唤醒调用意味着队列中的数据是可访问的,并且由于队列是共享的,消费者必须通过锁来进行同步访问。每个消费者都可能拿到锁;然而,你仍然需要检查队列中是否有元素。因为在你拿到锁的瞬间并不知道队列的状态,所以还是需要进行条件检查。
|
||||
|
||||
在我们的例子中,超时处理程序收到了计时器到期时发出的“唤醒”调用,但是它仍需要检查是否已向其发送了取消信号,然后才能继续执行回调。
|
||||
|
||||
![gopher boot camp][15]
|
||||
|
||||
*如果你要唤醒多个 gopher,可能就需要进行条件检查*
|
||||
|
||||
#### 死锁
|
||||
|
||||
当一个线程被卡住,无限期地等待一个唤醒信号,但是这个信号永远不会到达时,就会发生这种情况。死锁可以通过让你的整个程序停机来彻底杀死你的应用。
|
||||
|
||||
在我们的案例中,这种情况的发生是由于多次发送请求到一个非缓冲且阻塞的通道。这意味着向通道发送数据只有在从这个通道接收完数据后才能返回。我们的超时线程循环迅速从取消通道接收信号;然而,在接收到第一个信号后,它将跳出循环,并且再也不会从这个通道读取数据。其他的调用会一直被卡住。为避免这种情况,你需要仔细检查代码,谨慎处理阻塞调用,并确保不会发生线程饥饿。我们例子中的解决方法是使取消调用成为非阻塞调用 — 我们不需要阻塞调用。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/go-common-pitfalls
|
||||
|
||||
作者:[Eduardo Ferreira][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lxbwolf](https://github.com/lxbwolf)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/edufgf
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/go-golang.png?itok=OAW9BXny (Goland gopher illustration)
|
||||
[2]: http://mode.net
|
||||
[3]: https://en.wikipedia.org/wiki/Metrics_%28networking%29
|
||||
[4]: https://people.ece.cornell.edu/atang/pub/15/HALO_ToN.pdf
|
||||
[5]: https://en.wikipedia.org/wiki/Point_of_presence
|
||||
[6]: https://opensource.com/sites/default/files/uploads/image2_0_3.png (latency computation graph)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/image3_0.png (finite state machine diagram)
|
||||
[8]: https://golang.org/
|
||||
[9]: https://opensource.com/sites/default/files/uploads/image4.png (gophers hacking together)
|
||||
[10]: https://en.wikipedia.org/wiki/Deadlock
|
||||
[11]: https://opensource.com/sites/default/files/uploads/image5_0_0.jpg (gophers on a wire, talking)
|
||||
[12]: https://golang.org/doc/articles/race_detector.html
|
||||
[13]: https://opensource.com/sites/default/files/uploads/image6.jpeg (gopher assembly line)
|
||||
[14]: https://en.wikipedia.org/wiki/Monitor_%28synchronization%29#Condition_variables
|
||||
[15]: https://opensource.com/sites/default/files/uploads/image7.png (gopher boot camp)
|
@ -0,0 +1,467 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11819-1.html)
|
||||
[#]: subject: (Enable your Python game player to run forward and backward)
|
||||
[#]: via: (https://opensource.com/article/19/12/python-platformer-game-run)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
使你的 Python 游戏玩家能够向前和向后跑
|
||||
======
|
||||
> 使用 Pygame 模块来使你的 Python 平台开启侧滚效果,来让你的玩家自由奔跑。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/25/220636x5mabbl47xvtsk55.jpg)
|
||||
|
||||
这是仍在进行中的关于使用 Pygame 模块来在 Python 3 中在创建电脑游戏的第九部分。先前的文章是:
|
||||
|
||||
* [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][2]
|
||||
* [使用 Python 和 Pygame 模块构建一个游戏框架][3]
|
||||
* [如何在你的 Python 游戏中添加一个玩家][4]
|
||||
* [用 Pygame 使你的游戏角色移动起来][5]
|
||||
* [如何向你的 Python 游戏中添加一个敌人][6]
|
||||
* [在 Pygame 游戏中放置平台][12]
|
||||
* [在你的 Python 游戏中模拟引力][7]
|
||||
* [为你的 Python 平台类游戏添加跳跃功能][8]
|
||||
|
||||
在这一系列关于使用 [Pygame][10] 模块来在 [Python 3][9] 中创建电脑游戏的先前文章中,你已经设计了你的关卡设计布局,但是你的关卡的一些部分可能已近超出你的屏幕的可视区域。在平台类游戏中,这个问题的普遍解决方案是,像术语“<ruby>侧滚<rt>side-scroller</rt></ruby>”表明的一样,滚动。
|
||||
|
||||
滚动的关键是当玩家精灵接近屏的幕边缘时,使在玩家精灵周围的平台移动。这样给予一种错觉,屏幕是一个在游戏世界中穿梭追拍的"摄像机"。
|
||||
|
||||
这个滚动技巧需要两个在屏幕边缘的绝对区域,在绝对区域内的点处,在世界滚动期间,你的化身静止不动。
|
||||
|
||||
### 在侧滚动条中放置卷轴
|
||||
|
||||
如果你希望你的玩家能够后退,你需要一个触发点来向前和向后。这两个点仅仅是两个变量。设置它们各个距各个屏幕边缘大约 100 或 200 像素。在你的设置部分中创建变量。在下面的代码中,前两行用于上下文说明,所以仅需要添加这行后的代码:
|
||||
|
||||
```
|
||||
player_list.add(player)
|
||||
steps = 10
|
||||
forwardX = 600
|
||||
backwardX = 230
|
||||
```
|
||||
|
||||
在主循环中,查看你的玩家精灵是否在 `forwardx` 或 `backwardx` 滚动点处。如果是这样,向左或向右移动使用的平台,取决于世界是向前或向后移动。在下面的代码中,代码的最后三行仅供你参考:
|
||||
|
||||
```
|
||||
# scroll the world forward
|
||||
if player.rect.x >= forwardx:
|
||||
scroll = player.rect.x - forwardx
|
||||
player.rect.x = forwardx
|
||||
for p in plat_list:
|
||||
p.rect.x -= scroll
|
||||
|
||||
# scroll the world backward
|
||||
if player.rect.x <= backwardx:
|
||||
scroll = backwardx - player.rect.x
|
||||
player.rect.x = backwardx
|
||||
for p in plat_list:
|
||||
p.rect.x += scroll
|
||||
|
||||
## scrolling code above
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.gravity() # check gravity
|
||||
player.update()
|
||||
```
|
||||
|
||||
启动你的游戏,并尝试它。
|
||||
|
||||
![Scrolling the world in Pygame][11]
|
||||
|
||||
滚动像预期的一样工作,但是你可能注意到一个发生的小问题,当你滚动你的玩家和非玩家精灵周围的世界时:敌人精灵不随同世界滚动。除非你要你的敌人精灵要无休止地追逐你的玩家,你需要修改敌人代码,以便当你的玩家快速撤退时,敌人被留在后面。
|
||||
|
||||
### 敌人卷轴
|
||||
|
||||
在你的主循环中,你必须对卷轴平台为你的敌人的位置的应用相同的规则。因为你的游戏世界将(很可能)有不止一个敌人在其中,该规则应该被应用于你的敌人列表,而不是一个单独的敌人精灵。这是分组类似元素到列表中的优点之一。
|
||||
|
||||
前两行用于上下文注释,所以只需添加这两行后面的代码到你的主循环中:
|
||||
|
||||
```
|
||||
# scroll the world forward
|
||||
if player.rect.x >= forwardx:
|
||||
scroll = player.rect.x - forwardx
|
||||
player.rect.x = forwardx
|
||||
for p in plat_list:
|
||||
p.rect.x -= scroll
|
||||
for e in enemy_list:
|
||||
e.rect.x -= scroll
|
||||
```
|
||||
|
||||
来滚向另一个方向:
|
||||
|
||||
```
|
||||
# scroll the world backward
|
||||
if player.rect.x <= backwardx:
|
||||
scroll = backwardx - player.rect.x
|
||||
player.rect.x = backwardx
|
||||
for p in plat_list:
|
||||
p.rect.x += scroll
|
||||
for e in enemy_list:
|
||||
e.rect.x += scroll
|
||||
```
|
||||
|
||||
再次启动游戏,看看发生什么。
|
||||
|
||||
这里是到目前为止你已经为这个 Python 平台所写所有的代码:
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# draw a world
|
||||
# add a player and player control
|
||||
# add player movement
|
||||
# add enemy and basic collision
|
||||
# add platform
|
||||
# add gravity
|
||||
# add jumping
|
||||
# add scrolling
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
self.image.convert_alpha()
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn a player
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
self.collide_delta = 0
|
||||
self.jump_delta = 6
|
||||
self.score = 1
|
||||
self.images = []
|
||||
for i in range(1,9):
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
img.convert_alpha()
|
||||
img.set_colorkey(ALPHA)
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
def jump(self,platform_list):
|
||||
self.jump_delta = 0
|
||||
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
control player movement
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update sprite position
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# moving left
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# moving right
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# collisions
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
#print(self.health)
|
||||
|
||||
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
|
||||
for p in plat_hit_list:
|
||||
self.collide_delta = 0 # stop jumping
|
||||
self.movey = 0
|
||||
if self.rect.y > p.rect.y:
|
||||
self.rect.y = p.rect.y+ty
|
||||
else:
|
||||
self.rect.y = p.rect.y-ty
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
self.collide_delta = 0 # stop jumping
|
||||
if self.rect.y > g.rect.y:
|
||||
self.health -=1
|
||||
print(self.health)
|
||||
|
||||
if self.collide_delta < 6 and self.jump_delta < 6:
|
||||
self.jump_delta = 6*2
|
||||
self.movey -= 33 # how high to jump
|
||||
self.collide_delta += 6
|
||||
self.jump_delta += 6
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn an enemy
|
||||
'''
|
||||
def __init__(self,x,y,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img))
|
||||
self.movey = 0
|
||||
#self.image.convert_alpha()
|
||||
#self.image.set_colorkey(ALPHA)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.x = x
|
||||
self.rect.y = y
|
||||
self.counter = 0
|
||||
|
||||
|
||||
def move(self):
|
||||
'''
|
||||
enemy movement
|
||||
'''
|
||||
distance = 80
|
||||
speed = 8
|
||||
|
||||
self.movey += 3.2
|
||||
|
||||
if self.counter >= 0 and self.counter <= distance:
|
||||
self.rect.x += speed
|
||||
elif self.counter >= distance and self.counter <= distance*2:
|
||||
self.rect.x -= speed
|
||||
else:
|
||||
self.counter = 0
|
||||
|
||||
self.counter += 1
|
||||
|
||||
if not self.rect.y >= worldy-ty-ty:
|
||||
self.rect.y += self.movey
|
||||
|
||||
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
|
||||
for p in plat_hit_list:
|
||||
self.movey = 0
|
||||
if self.rect.y > p.rect.y:
|
||||
self.rect.y = p.rect.y+ty
|
||||
else:
|
||||
self.rect.y = p.rect.y-ty
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.rect.y = worldy-ty-ty
|
||||
|
||||
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
|
||||
enemy_list = pygame.sprite.Group() # create enemy group
|
||||
enemy_list.add(enemy) # add enemy to group
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return enemy_list
|
||||
|
||||
def loot(lvl,lloc):
|
||||
print(lvl)
|
||||
|
||||
def ground(lvl,gloc,tx,ty):
|
||||
ground_list = pygame.sprite.Group()
|
||||
i=0
|
||||
if lvl == 1:
|
||||
while i < len(gloc):
|
||||
ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
def platform(lvl,tx,ty):
|
||||
plat_list = pygame.sprite.Group()
|
||||
ploc = []
|
||||
i=0
|
||||
if lvl == 1:
|
||||
ploc.append((0,worldy-ty-128,3))
|
||||
ploc.append((300,worldy-ty-256,3))
|
||||
ploc.append((500,worldy-ty-128,4))
|
||||
|
||||
while i < len(ploc):
|
||||
j=0
|
||||
while j <= ploc[i][2]:
|
||||
plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'ground.png')
|
||||
plat_list.add(plat)
|
||||
j=j+1
|
||||
print('run' + str(i) + str(ploc[i]))
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return plat_list
|
||||
|
||||
'''
|
||||
Setup
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # frame rate
|
||||
ani = 4 # animation cycles
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # spawn player
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10
|
||||
forwardx = 600
|
||||
backwardx = 230
|
||||
|
||||
eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 #tile size
|
||||
ty = 64 #tile size
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
plat_list = Level.platform( 1,tx,ty )
|
||||
|
||||
'''
|
||||
Main loop
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
print("LEFT")
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
print("RIGHT")
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
player.jump(plat_list)
|
||||
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
# scroll the world forward
|
||||
if player.rect.x >= forwardx:
|
||||
scroll = player.rect.x - forwardx
|
||||
player.rect.x = forwardx
|
||||
for p in plat_list:
|
||||
p.rect.x -= scroll
|
||||
for e in enemy_list:
|
||||
e.rect.x -= scroll
|
||||
|
||||
# scroll the world backward
|
||||
if player.rect.x <= backwardx:
|
||||
scroll = backwardx - player.rect.x
|
||||
player.rect.x = backwardx
|
||||
for p in plat_list:
|
||||
p.rect.x += scroll
|
||||
for e in enemy_list:
|
||||
e.rect.x += scroll
|
||||
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.gravity() # check gravity
|
||||
player.update()
|
||||
player_list.draw(world) #refresh player position
|
||||
enemy_list.draw(world) # refresh enemies
|
||||
ground_list.draw(world) # refresh enemies
|
||||
plat_list.draw(world) # refresh platforms
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/python-platformer-game-run
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/open_gaming_games_roundup_news.png?itok=KM0ViL0f (Gaming artifacts with joystick, GameBoy, paddle)
|
||||
[2]: https://linux.cn/article-9071-1.html
|
||||
[3]: https://linux.cn/article-10850-1.html
|
||||
[4]: https://linux.cn/article-10858-1.html
|
||||
[5]: https://linux.cn/article-10874-1.html
|
||||
[6]: https://linux.cn/article-10883-1.html
|
||||
[7]: https://linux.cn/article-11780-1.html
|
||||
[8]: https://linux.cn/article-11790-1.html
|
||||
[9]: https://www.python.org/
|
||||
[10]: https://www.pygame.org/news
|
||||
[11]: https://opensource.com/sites/default/files/uploads/pygame-scroll.jpg (Scrolling the world in Pygame)
|
||||
[12]:https://linux.cn/article-10902-1.html
|
@ -0,0 +1,128 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11776-1.html)
|
||||
[#]: subject: (Make VLC More Awesome With These Simple Tips)
|
||||
[#]: via: (https://itsfoss.com/simple-vlc-tips/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
这些简单的技巧使 VLC 更加出色
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/090635eu9va9999rr4ue92.jpeg)
|
||||
|
||||
如果 [VLC][1] 不是最好的播放器,那它也是[最好的开源视频播放器][2]之一。大多数人不知道的是,它不仅仅是视频播放器。
|
||||
|
||||
你可以进行许多复杂的任务,如直播视频、捕捉设备等。只需打开菜单,你就可以看到它有多少选项。
|
||||
|
||||
我们有一个详细的教程,讨论一些[专业的 VLC 技巧][3],但这些对于普通用户太复杂。
|
||||
|
||||
这就是为什么我要写另一篇文章的原因,来向你展示一些可以在 VLC 中使用的简单技巧。
|
||||
|
||||
### 使用这些简单技巧让 VLC 做更多事
|
||||
|
||||
让我们看看除了播放视频文件之外,你还可以使用 VLC 做什么。
|
||||
|
||||
#### 1、使用 VLC 观看 YouTube 视频
|
||||
|
||||
![][4]
|
||||
|
||||
如果你不想在 [YouTube][5] 上观看令人讨厌的广告,或者只想体验没有打扰地观看 YouTube 视频,你可以使用 VLC。
|
||||
|
||||
是的,在 VLC 上流式传输 YouTube 视频是非常容易的。
|
||||
|
||||
只需启动 VLC 播放器,前往媒体设置,然后单击 ”Open Network Stream“ 或使用快捷方式 `CTRL + N`。
|
||||
|
||||
![][6]
|
||||
|
||||
接下来,你只需要粘贴要观看的视频的 URL。有一些选项可以调整,但通常你无需担心这些。如果你好奇,你可以点击 ”Advanced options“ 来探索。
|
||||
|
||||
你还可以通过这种方式向 YouTube 视频添加字幕。然而,[一个更简单的带字幕观看 Youtube 视频的办法是使用 Penguin 字幕播放器][7]。
|
||||
|
||||
#### 2、将视频转换为不同格式
|
||||
|
||||
![][8]
|
||||
|
||||
你可以[在 Linux 命令行使用 ffmpeg 转换视频][9]。你还可以使用图形工具,如 [HandBrake 转换视频格式][10]。
|
||||
|
||||
但是,如果你不想用一个单独的应用来转码视频,你可以使用 VLC 播放器来完成该工作。
|
||||
|
||||
为此,只需点击 VLC 上的媒体选项,然后单击 “Convert/Save”,或者在 VLC 播放器处于活动状态时按下快捷键 `CTRL + R`。接下来,你需要从计算机/硬盘或者 URL 导入你想保存/转换的的视频。
|
||||
|
||||
不管是什么来源,只需选择文件后点击 “Convert/Save” 按钮。你现在会看到另外一个窗口可以更改 “Profile” 设置。点击并选择你想转换的格式(并保存)。
|
||||
|
||||
你还可以在转换之前通过在屏幕底部设置目标文件夹来更改转换文件的存储路径。
|
||||
|
||||
#### 3、从源录制音频/视频
|
||||
|
||||
![Vlc Advanced Controls][11]
|
||||
|
||||
你是否想在 VLC 播放器中录制正在播放的音频/视频?
|
||||
|
||||
如果是的话,有一个简单的解决方案。只需通过 “View”,然后点击 “Advanced Controls”。
|
||||
|
||||
完成后,你会看到一个新按钮(包括 VLC 播放器中的红色录制按钮)。
|
||||
|
||||
#### 4、自动下载字幕
|
||||
|
||||
![][12]
|
||||
|
||||
是的,你可以[使用 VLC 自动下载字幕][13]。你甚至不必在单独的网站上查找字幕。你只需点击
|
||||
“View”->“VLSub”。
|
||||
|
||||
默认情况下,它是禁用的,因此当你单击该选项时,它会被激活,并允许你搜索/下载想要的字幕。
|
||||
|
||||
[VLC 还能让你使用简单的键盘快捷键同步字幕][14]
|
||||
|
||||
#### 5、截图
|
||||
|
||||
![][15]
|
||||
|
||||
你可以在观看视频时使用 VLC 获取一些视频的截图/图像。你只需在视频播放/暂停时右击播放器,你会看到一组选项,点击 “Video”->“Take Snapshot”。
|
||||
|
||||
如果安装了旧版本,你可能在右键时看到截图选项。
|
||||
|
||||
#### 额外技巧:给视频添加音频/视频效果
|
||||
|
||||
在菜单中,进入 “Tools” 选项。单击 “Effects and Filters”,或者在 VLC 播放器窗口中按 `CTRL + E` 打开选项。
|
||||
|
||||
好了,你可以观察你给视频添加的音频和视频效果了。你也许无法实时看到效果,因此你需要调整并保存来看发生了什么。
|
||||
|
||||
![][16]
|
||||
|
||||
我建议在修改视频之前保存一份原始视频备份。
|
||||
|
||||
#### 你最喜欢的 VLC 技巧是什么?
|
||||
|
||||
我分享了一些我最喜欢的 VLC 技巧。你知道什么你经常使用的很酷的 VLC 技巧吗?为什么不和我们分享呢?我可以把它添加到列表中。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/simple-vlc-tips/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.videolan.org/
|
||||
[2]: https://itsfoss.com/video-players-linux/
|
||||
[3]: https://itsfoss.com/vlc-pro-tricks-linux/
|
||||
[4]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/youtube-video-stream.jpg?ssl=1
|
||||
[5]: https://www.youtube.com/
|
||||
[6]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/youtube-video-play.jpg?ssl=1
|
||||
[7]: https://itsfoss.com/penguin-subtitle-player/
|
||||
[8]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/vlc-video-convert.jpg?ssl=1
|
||||
[9]: https://itsfoss.com/ffmpeg/
|
||||
[10]: https://itsfoss.com/handbrake/
|
||||
[11]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/vlc-advanced-controls.png?ssl=1
|
||||
[12]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/vlc-subtitles-automatic.png?ssl=1
|
||||
[13]: https://itsfoss.com/download-subtitles-automatically-vlc-media-player-ubuntu/
|
||||
[14]: https://itsfoss.com/how-to-synchronize-subtitles-with-movie-quick-tip/
|
||||
[15]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/vlc-snapshot.png?ssl=1
|
||||
[16]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/vlc-effects-screenshot.jpg?ssl=1
|
@ -0,0 +1,139 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11799-1.html)
|
||||
[#]: subject: (How to Add Border Around Text in GIMP)
|
||||
[#]: via: (https://itsfoss.com/gimp-text-outline/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
在 GIMP 中如何在文本周围添加边框
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/19/230506fzkyktqglfcyzkuh.jpg)
|
||||
|
||||
这个简单的教程介绍了在 [GIMP][1] 中显示文本的轮廓的步骤。文本轮廓可以帮助你在其它颜色下高亮显示该文本。
|
||||
|
||||
![Outlined Text created in GIMP][2]
|
||||
|
||||
让我们看看如何在你的文本周围添加一个边框。
|
||||
|
||||
### 在 GIMP 中添加文本轮廓
|
||||
|
||||
整个过程可以用这些简单的步骤描述:
|
||||
|
||||
* 创建文本,并复制它的轮廓路径
|
||||
* 添加一层新的透明层,并添加轮廓路径到透明层中
|
||||
* 更改轮廓的大小,给它添加一种不同的颜色
|
||||
|
||||
这就是全部的东西。不用担心,我将使用适当地截图详细的展示每个步骤。按照这个教程,你应该能够为文本添加轮廓,即使你在此之前从未使用过 GIMP 。
|
||||
|
||||
仅需要确保你已经 [在 Linux 上安装 GIMP][3],或者也可以使用的其它任何操作系统。
|
||||
|
||||
这篇教程在 GIMP 2.10 版本下演示。
|
||||
|
||||
#### 步骤 1: 创建你的主要文本,并复制它的轮廓
|
||||
|
||||
打开 GIMP ,并通过转到 “菜单 -> 文件 -> 新建” 来创建一个新的文件。你应该可以使用 `Ctrl+N` 键盘快捷键。
|
||||
|
||||
![Create New File][4]
|
||||
|
||||
你可以在这里选择画布的大小。你也可以选择要白色背景或一种透明背景。它在 “高级选项 -> 颜色” 配置文件下。
|
||||
|
||||
我选择默认的白色背景。它在以后能够更改。
|
||||
|
||||
现在从左边栏的工具箱中选择文本工具。
|
||||
|
||||
![Adding text in GIMP][5]
|
||||
|
||||
写你想要的文本。你可以根据你的选择以更改文本的字体、大小和对齐方式。我保持这篇文章的文本的默认左对齐。
|
||||
|
||||
我故意为文本选择一种浅色,以便难于阅读。在这篇教程中我将添加一个深色轮廓到这个浅色的文本。
|
||||
|
||||
![Text added in GIMP][6]
|
||||
|
||||
当你写完文本后,右键文本框并选择 “文本的路径” 。
|
||||
|
||||
![Right click on the text box and select ‘Path from Text’][7]
|
||||
|
||||
#### 步骤 2: 添加一个带有文本轮廓的透明层
|
||||
|
||||
现在,转到顶部菜单,转到“层”,并添加一个新层。
|
||||
|
||||
![Use Shift+Ctrl+N to add a new layer][8]
|
||||
|
||||
确保添加新层为透明的。你可以给它一个合适的名称,像“文本大纲”。单击确定来添加这个透明层。
|
||||
|
||||
![Add a transparent layer][9]
|
||||
|
||||
再次转到菜单,这次转到 “选择” ,并单击 “来自路径” 。你将看到你的文本应该被高亮显示。
|
||||
|
||||
![Go to Select and choose From Path][10]
|
||||
|
||||
总的来说,你只创建了一个透明层,它有像你的原文一样相同的文本(但是透明)。现在你需要做的是在这个层上增加文本的大小。
|
||||
|
||||
#### 步骤 3: 通过增加它的大小和更改它的颜色来添加文本轮廓
|
||||
|
||||
为此,再次在菜单中转到 “选择” ,这次选择 “增加”。这将允许增大透明层上的文本的大小。
|
||||
|
||||
![Grow the selection on the additional layer][11]
|
||||
|
||||
以 5 或 10 像素增加,或者你喜欢的任意像素。
|
||||
|
||||
![Grow it by 5 or 10 pixel][12]
|
||||
|
||||
你选择需要做是使用一种你选择的颜色来填充这个扩大的选择区。因为我的原文是浅色,在这里我将为轮廓使用背景色。
|
||||
|
||||
如果尚未选择的话,先选择你的主图像层。这些层在右侧栏中可视。然后转到工具箱并选择油漆桶工具。为你的轮廓选择想要的颜色。
|
||||
|
||||
选择使用该工具来填充黑色到你的选择区。记住。你填充文本外部的轮廓,而不是文本本身。
|
||||
|
||||
![Fill the outline of the text with a different color][13]
|
||||
|
||||
在这里你完成了很多。使用 `Ctrl+Shift+A` 来取消你当前的选择区。
|
||||
|
||||
![Outline added to the text][14]
|
||||
|
||||
如此,你现在已经在 GIMP 中成功地添加轮廓到你的文本。它是在白色背景中,如果你想要一个透明背景,只需要在右侧栏的图层菜单中删除背景层。
|
||||
|
||||
![Remove the white background layer if you want a transparent background][15]
|
||||
|
||||
如果你对结果感到满意,保存文件未 PNG 文件(来保留透明背景),或你喜欢的任何文件格式。
|
||||
|
||||
### 你使它工作了吗?
|
||||
|
||||
就这样。这就是你在 GIMP 中为添加一个文本轮廓而需要做的全部工作。
|
||||
|
||||
我希望你发现这个 GIMP 教程有帮助。你可能想查看另一个 [关于在 GIMP 中添加一个水印的简单教程][16]。
|
||||
|
||||
如果你有问题或建议,请在下面自由留言。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/gimp-text-outline/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.gimp.org/
|
||||
[2]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/outlined_text_GIMP.png?ssl=1
|
||||
[3]: https://itsfoss.com/gimp-2-10-release/
|
||||
[4]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/12/create_outline_text_gimp_1.jpeg?ssl=1
|
||||
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_2.jpg?ssl=1
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp-3.jpg?ssl=1
|
||||
[7]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_4.jpg?ssl=1
|
||||
[8]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_5.jpg?ssl=1
|
||||
[9]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_6.jpg?ssl=1
|
||||
[10]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_7.jpg?ssl=1
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_8.jpg?ssl=1
|
||||
[12]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_9.jpg?ssl=1
|
||||
[13]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_10.jpg?ssl=1
|
||||
[14]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_11.jpg?ssl=1
|
||||
[15]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/outline_text_gimp_12.jpg?ssl=1
|
||||
[16]: https://itsfoss.com/add-watermark-gimp-linux/
|
@ -0,0 +1,118 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11770-1.html)
|
||||
[#]: subject: (App Highlight: Open Source Disk Partitioning Tool GParted)
|
||||
[#]: via: (https://itsfoss.com/gparted/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
应用推荐:开源磁盘分区工具 GParted
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/10/220015lp58jjjt22xfppjj.jpg)
|
||||
|
||||
> GParted 是一个可用于 Linux 发行版的非常受欢迎且免费的分区编辑器。在这里,我们简要地介绍一下它提供的功能。
|
||||
|
||||
### GParted:一个自由开源的图形化分区管理器
|
||||
|
||||
![][1]
|
||||
|
||||
GParted 无疑是 [Linux 下的最好的分区管理器][2]之一。用户界面非常简单而可以完成任务。
|
||||
|
||||
在某些情况下,你最终还是会使用 [GParted][3] 来修复或格式化你的 USB 驱动器。我有一个[在 Ubuntu 中使用“磁盘”应用程序不能格式化的 USB 磁盘][4],而这是 GParted 用来救援的地方。
|
||||
|
||||
所以,它是一个非常有用的工具,有很多出色的功能。让我为你们重点介绍一下。
|
||||
|
||||
> 警告!
|
||||
>
|
||||
> 进行磁盘分区是一件危险的工作。除非绝对必要,否则不要做。否则,你可能最终擦除了整个磁盘。
|
||||
|
||||
### GParted 的功能
|
||||
|
||||
从简单地格式化任务到重要的分区任务,你可以使用 GParted 做很多事。我将使用一些屏幕截图突出关键功能,以帮助你在安装它前进一步了解它。
|
||||
|
||||
#### 创建分区表
|
||||
|
||||
你可以为新磁盘创建一个新的分区表,也可以擦除你现存的磁盘内容来修改分区表。
|
||||
|
||||
![][5]
|
||||
|
||||
你能够选择 msdos、gpt、atari,以及更多类型的分区表。
|
||||
|
||||
#### 创建、移动、标记、删除和修改分区表
|
||||
|
||||
你可以使用 GParted 中一系列选项来轻松地创建、标记、删除或修改分区表。
|
||||
|
||||
![][6]
|
||||
|
||||
当然,你必须要谨慎你要做的事情。
|
||||
|
||||
好的是,GParted 可以确保你不能直接应用任何更改 —— 在你点击应用更改之前,它将排队你选择的操作/任务,并在你执行前最后再要求确认一次。
|
||||
|
||||
顶部的对号标记符号 `✓` 可以让你确认更改,然后你的更改才会生效。
|
||||
|
||||
这是分区可用选项的另一个屏幕截图:
|
||||
|
||||
![][7]
|
||||
|
||||
#### 尝试数据救援
|
||||
|
||||
除了编辑分区以外,你也可以试着使用“尝试数据救援”功能以[在 Linux 中恢复丢失的数据][8],如下面的屏幕截图所示。
|
||||
|
||||
![][9]
|
||||
|
||||
值得注意的是,默认情况下你没有安装此功能 —— 你只是能看到这个选项。因此,要使这个数据恢复功能可以正常工作,你必须使用下面的命令来单独地安装 gpart (在基于 Ubuntu/Debian 的发行版上):
|
||||
|
||||
```
|
||||
sudo apt install gpart
|
||||
```
|
||||
|
||||
除了所有这些关键功能以外,它支持各种存储设备和文件系统。你可以从其官方网站的[功能列表][10]中了解到更多信息。
|
||||
|
||||
### 在 Ubuntu 和其它 Linux 发行版上安装 GParted
|
||||
|
||||
你可能已经预安装了 GParted 。所以,务必核实一下。如果你尚未安装它,则可以进入软件中心安装它。
|
||||
|
||||
如果你想使用终端,简单地键入下面的命令:
|
||||
|
||||
```
|
||||
sudo apt install gparted
|
||||
```
|
||||
|
||||
如上所述,如果你想要使用数据恢复选项,除了安装 gparted 软件包外,还应该安装 gpart 软件包。
|
||||
|
||||
如果你使用的是其它 Linux 发行版,你可以在各自的软件管理器中找到它,或者直接查看[官方下载操作说明][11]。
|
||||
|
||||
- [下载 GParted][11]
|
||||
|
||||
### 总结
|
||||
|
||||
当涉及处理磁盘管理和分区时,GParted 是一个非常有用且重要的工具。但是,因为显而易见的原因,当你使用它时,你必须要小心。
|
||||
|
||||
你尝试过 GParted 吗?你在 Linux 上使用的其它哪个分区工具?请在下面的评论中分享你的经验。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/gparted/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/gparted-screenshot.png?ssl=1
|
||||
[2]: https://itsfoss.com/partition-managers-linux/
|
||||
[3]: https://gparted.org/
|
||||
[4]: https://itsfoss.com/format-usb-drive-sd-card-ubuntu/
|
||||
[5]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/gparted-create-partition-table.png?ssl=1
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/12/gparted-modify-partitions.png?ssl=1
|
||||
[7]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/gparted-partition-options.png?ssl=1
|
||||
[8]: https://itsfoss.com/recover-deleted-files-linux/
|
||||
[9]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/gparted-attempt-data-rescue-feature.png?ssl=1
|
||||
[10]: https://gparted.org/features.php
|
||||
[11]: https://gparted.org/download.php
|
@ -1,50 +1,52 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lxbwolf)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11749-1.html)
|
||||
[#]: subject: (Kubernetes namespaces for beginners)
|
||||
[#]: via: (https://opensource.com/article/19/12/kubernetes-namespaces)
|
||||
[#]: author: (Jessica Cherry https://opensource.com/users/jrepka)
|
||||
|
||||
Kubernetes 命名空间入门
|
||||
======
|
||||
命名空间是什么?你为什么需要它?
|
||||
![Ship captain sailing the Kubernetes seas][1]
|
||||
|
||||
kubernetes 命名空间是什么?Shakespeare 以前写过,我们声称的命名空间,或者任何其他名字,仍是一个虚拟集群。命名空间,意味着 kubernetes 可以在单个集群上提供多个 kubernetes 的集群,类似一个对其主机进行抽象的虚拟机。[kubernetes 文档][2] 中的解释:
|
||||
> 命名空间是什么?你为什么需要它?
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/05/094542qnq3qc3nrkkrjanh.jpg)
|
||||
|
||||
kubernetes <ruby>命名空间<rt>namespace</rt></ruby>是什么?正如 Shakespeare 以前写过的,我们所谓的命名空间,或者任何其他名字,就是虚拟集群。通过虚拟集群,意味着 kubernetes 可以在单个集群上提供多个 kubernetes 的集群,类似一个在其主机抽象出来的虚拟机。[kubernetes 文档][2] 中的解释:
|
||||
|
||||
> kubernetes 在一个物理集群上提供了多个虚拟集群。这些虚拟集群被称为命名空间。
|
||||
|
||||
你为什么需要命名空间?一句话概括:隔离。
|
||||
你为什么需要命名空间?一言以蔽之:隔离。
|
||||
|
||||
隔离有很多优点,如它提供了安全和干净的环境。如果你是基础设施的所属者,并且为开发者提供环境,隔离就相当重要。你最不需要的就是,一个不熟悉你集群是如何搭建的人去修改系统配置 — 这可能导致所有人都无法登录。
|
||||
隔离有很多优点,如它提供了安全和干净的环境。如果你是基础设施的所属者,并且要为开发者提供环境,隔离就相当重要。你最不需要的就是,一个不熟悉你集群是如何搭建的人去修改系统配置 —— 这可能导致所有人都无法登录。
|
||||
|
||||
### 初始命名空间
|
||||
|
||||
一个集群的三个初始命名空间:**default**、**kube-system** 和 **kube-public**。虽然你可以用这三个命名空间作技术部署,但我还是推荐你把这三个命名空间留作系统配置用,而不是你的项目。
|
||||
一个集群的三个初始命名空间:`default`、`kube-system` 和 `kube-public`。虽然技术上你可以用这三个命名空间作部署,但我还是推荐你把这三个命名空间留作系统配置用,而不是你的项目。
|
||||
|
||||
* **Default** 某些部署没有指明命名空间,这样部署可以快速创建一个网格,但如果做了很多错误信息的部署,就很能去清理。我不去修改它,因为它在为某一个目的服务时,会在不止一种情况下误导我。
|
||||
* **Kube-system** 系统相关的所有对象组成的命名空间。任何此命名空间的部署都可能是危险的操作,可能对系统本身造成不可挽回的破坏。没错,我试过;所以我不推荐。
|
||||
* **Kube-public** 所有人可读,但是这个命名空间是为系统保留的。
|
||||
* `Default` 用于某些没有指明命名空间的部署,这是一种快速创建混乱的做法,如果你在没有正确信息的情况下做了很多部署,将很难清理。我不会去动它,因为它只有这一个用途,而且在不止一种情况下误导过我。
|
||||
* `Kube-system` 是 Kubernetes 系统相关的所有对象组成的命名空间。任何对此命名空间的部署都可能是危险的操作,可能对系统本身造成不可挽回的破坏。没错,我试过;所以我不推荐。
|
||||
* `Kube-public` 所有人可读,但是这个命名空间是为系统保留的。
|
||||
|
||||
### 用命名空间来实现隔离
|
||||
|
||||
我用了多种方式通过命名空间来实现隔离。我经常用命名空间来把多个用户项目分割到不同的环境。这种方式可以有效防止跨项目的污染,因为命名空间提供了独立的环境。例如,使用者可以安装不同版本的 Jenkins,如果它们的环境变量是在不同的命名空间,就不会冲突。
|
||||
我用了多种方式通过命名空间来实现隔离。我经常用命名空间来把多个用户项目分割到不同的环境。这种方式可以有效防止跨项目的污染,因为命名空间提供了独立的环境。例如,用户可以安装不同版本的 Jenkins,如果它们的环境变量是在不同的命名空间,就不会冲突。
|
||||
|
||||
这种隔离对于清理也很有帮助。如果部署组的多个项目被废弃,你可以用命令 `kubectl delete ns <$NAMESPACENAME>` 一键删除命名空间,清理命名空间内的所有东西。(请确认被删除的是正确的命名空间。我曾经在生产环境删除了错误的命名空间,这很不好。)
|
||||
这种隔离对于清理也很有帮助。如果开发小组的多个项目突然被废弃,你可以用命令 `kubectl delete ns <$NAMESPACENAME>` 一键删除命名空间,清理命名空间内的所有东西。(请确认被删除的是正确的命名空间。我曾经在生产环境删除了错误的命名空间,这很不好。)
|
||||
|
||||
如果你是基础设施所有者,请谨慎操作,因为这可能会引发其他团队的的故障或引发其他问题。例如,如果你创建了一个特定的命名空间,里面有 DNS 函数,其他人删除了它,那么命名空间内的所有 pod 和它们运行的应用都会被清空。所有的**删除**操作在真正实施之前都应该由同事(通过 [GitOps][3])评审一下。
|
||||
如果你是基础设施所有者,请谨慎操作,因为这可能会引发其他团队的的故障或引发其他问题。例如,如果你创建了一个特定的命名空间,里面有特殊的额外安全的 DNS 功能,但是其他人删除了它,那么命名空间内的所有 pod 和它们运行的应用都会被清空。所有的**删除**操作在真正实施之前都应该由同事(通过 [GitOps][3])评审一下。
|
||||
|
||||
虽然官方文档不建议 [10 人以下团队][2] 使用多个命名空间,但出于架构需要,在我自己的集群上还是用了多个命名空间。集群越干净越好。
|
||||
|
||||
### 关于命名空间管理员应该知道的
|
||||
|
||||
首先,命名空间不能嵌套。部署只能在一个命名空间中进行。对于版本化项目,你不一定要用命名空间,你可以使用标签来区分有相同名字的版本化应用。命名空间使用配额来为不同的用户划分资源;例如,*某个命名空间最多能有 x 个 node*。最后,所有的命名空间对于资源类型只能使用一个独一无二的名字。
|
||||
首先,命名空间不能嵌套。部署只能在一个命名空间中进行。对于版本化项目,你不一定要用命名空间,你可以使用标签来区分有相同名字的版本化应用。命名空间使用配额来为不同的用户划分资源;例如,*某个命名空间最多能有 x 个节点*。最后,所有的命名空间对于该资源类型只能使用一个独一无二的名字。
|
||||
|
||||
### 命名空间命令操作
|
||||
|
||||
你需要安装 [Minikube][4]、 [Helm][5] 和 [kubectl][6] 命令行,才能使用下面的命名空间命令。我的文章 [_安全浏览你的 DevOps 流水线_][7] 中有它们的安装教程,你也可以去每个工程的官方主页去找安装教程。我使用的是最新的 Minikube。手动安装很快,第一次就能成功运行。
|
||||
你需要安装 [Minikube][4]、[Helm][5] 和 [kubectl][6] 命令行,才能使用下面的命名空间命令。我的文章《[安全扫描你的 DevOps 流水线][7]》中有它们的安装教程,你也可以去每个项目的官方主页去找安装教程。我使用的是最新的 Minikube。手动安装很快,第一次就能成功运行。
|
||||
|
||||
获取你的第一组命名空间:
|
||||
|
||||
@ -63,7 +65,7 @@ jess@Athena:~$ kubectl create namespace athena
|
||||
namespace/athena created
|
||||
```
|
||||
|
||||
现在开发者可以部署到你创建的命名空间;例如,这里是一个简短的 Helm 结构信息:
|
||||
现在开发者可以部署到你创建的命名空间了;例如,这里是一个简短的 Helm chart:
|
||||
|
||||
```
|
||||
jess@Athena:~$ helm install teset-deploy stable/redis --namespace athena
|
||||
@ -74,7 +76,7 @@ STATUS: deployed
|
||||
REVISION: 1
|
||||
TEST SUITE: None
|
||||
NOTES:
|
||||
** Please be patient while the chart is being deployed **
|
||||
` Please be patient while the chart is being deployed `
|
||||
Redis can be accessed via port 6379 on the following DNS names from within your cluster:
|
||||
|
||||
teset-deploy-redis-master.athena.svc.cluster.local for read/write operations
|
||||
@ -98,6 +100,7 @@ export REDIS_PASSWORD=$(kubectl get secret --namespace athena teset-deploy-redis
|
||||
```
|
||||
|
||||
2. 使用 Redis CLI 连接:
|
||||
|
||||
```bash
|
||||
redis-cli -h teset-deploy-redis-master -a $REDIS_PASSWORD
|
||||
redis-cli -h teset-deploy-redis-slave -a $REDIS_PASSWORD
|
||||
@ -110,11 +113,10 @@ kubectl port-forward --namespace athena svc/teset-deploy-redis-master 6379:6379
|
||||
redis-cli -h 127.0.0.1 -p 6379 -a $REDIS_PASSWORD
|
||||
```
|
||||
|
||||
现在这一套部署已经完成了,你有一个在命名空间 **test-deploy** 中部署的图表。
|
||||
现在这一套部署已经完成了,你有一个在命名空间 `test-deploy` 中部署的 chart。
|
||||
|
||||
查看你的命名空间中有哪些 pod:
|
||||
|
||||
|
||||
```
|
||||
jess@Athena:~$ kubectl get pods --namespace athena
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
@ -127,7 +129,6 @@ teset-deploy-redis-slave-1 1/1 Running 0 90s
|
||||
|
||||
一键删除所有东西:
|
||||
|
||||
|
||||
```bash
|
||||
jess@Athena:~$ kubectl delete namespace athena
|
||||
namespace "athena" deleted
|
||||
@ -137,7 +138,6 @@ namespace "athena" deleted
|
||||
|
||||
再次检查一下所有东西是否被删除了:
|
||||
|
||||
|
||||
```bash
|
||||
jess@Athena:~$ kubectl get pods --all-namespaces
|
||||
NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||
@ -156,11 +156,11 @@ kube-system storage-provisioner 1/1 Running 0
|
||||
|
||||
### 命名空间实践
|
||||
|
||||
现在我是为了安全使用命名空间,如限制用户的权限。你可以限制所有的东西 — 从哪些角色可以访问命名空间,到命名空间可使用的集群资源(CPU 等)的配额等级。例如,我通过资源配额和基于角色的访问控制(role-based access control,RBAC)配置来确保只有允许的服务账号可以访问命名空间。
|
||||
当前我是出于安全考虑才使用命名空间,如限制用户的权限。你可以限制所有的东西 —— 从哪些角色可以访问命名空间,到命名空间可使用的集群资源(CPU 等)的配额等级。例如,我通过资源配额和<ruby>基于角色的访问控制<rt>role-based access control</rt></ruby>(RBAC)配置来确保只有允许的服务账号可以访问命名空间。
|
||||
|
||||
对于隔离方面的安全,我不希望我的私人 Jenkins 应用可以通过一个信任的本地网络被当做有公共 IP 地址的安全镜像来访问(我不得不假定,可能会做出妥协)。
|
||||
对于隔离方面的安全,我不希望我的私人 Jenkins 应用可以通过一个信任的本地网络被当做一个有公共 IP 地址的安全镜像来访问(我不得不假定,可能会被侵袭)。
|
||||
|
||||
如果你很难提前计算出到底要在你的云平台上部署多少 node(就我而言,在把我的私人服务器放到 [segfaulting][8] 之前可以部署多少个 node),那么命名空间在预算方面也很有用。虽然这超出了本文的讨论范围,而且很复杂,但值得你去调研和使用来防止你的集群过分扩展。
|
||||
如果你很难提前计算出到底要在你的云平台上部署多少节点(或者,就我而言,是在[搞崩][8]我的家庭服务器之前我能部署多少个),那么命名空间在预算方面也很有用。虽然这超出了本文的讨论范围,而且很复杂,但值得你去调研和使用来防止你的集群过分扩展。
|
||||
|
||||
### 总结
|
||||
|
||||
@ -173,7 +173,7 @@ via: https://opensource.com/article/19/12/kubernetes-namespaces
|
||||
作者:[Jessica Cherry][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lxbwolf](https://github.com/lxbwolf)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,69 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (Morisun029)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11803-1.html)
|
||||
[#]: subject: (4 ways to volunteer this holiday season)
|
||||
[#]: via: (https://opensource.com/article/19/12/ways-volunteer)
|
||||
[#]: author: (John Jones https://opensource.com/users/johnjones4)
|
||||
|
||||
假期志愿服务的 4 种方式
|
||||
======
|
||||
|
||||
> 想要洒播些节日的快乐吗?为开源组织做贡献,帮助有需要的社区。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/20/223730f7983z8atxp1tf4l.jpg)
|
||||
|
||||
当领导者们配置人员和资源以做出积极改变时,就会产生社会影响。但是,许多社会努力都缺乏能够为这些改变者提供服务的技术资源。然而,有些组织通过将想要做出改变的开发人员与迫切需要更好技术的社区和非营利组织联系起来,来促进技术进步。这些组织通常为特定的受众提供服务,并招募特定种类的技术人员,它们有一个共同点:开源。
|
||||
|
||||
作为开发人员,我们出于各种原因试图加入开源社区。有些是为了专业发展,有些是为了能够与广阔的网络上令人印象深刻的技术人员合作,还有其他人则是因为他们清楚自己的贡献对于项目的成功的必要性。为什么不将你作为开发人员的才华投入到需要它的地方,而同时又为开源组织做贡献呢?以下组织是实现此目标的一些主要事例。
|
||||
|
||||
### Code for America
|
||||
|
||||
“Code for America” 是在数字时代,政府如何依靠人民为人民服务的一个例子。通过其 Brigade Network,该组织在美国各个城市中组织了一个由志愿程序员、数据科学家、相关公民和设计师组成的全国联盟。这些本地分支机构定期举行聚会,向社区开放。这样既可以向小组推出新项目,又可以协调正在进行的工作。为了使志愿者与项目相匹配,该网站经常列出项目所需的特定技能,例如数据分析、内容创建和JavaScript。同时,Brigade 网站也会关注当地问题,分享自然灾害等共同经验,这些都可以促进成员之间的合作。例如,新奥尔良、休斯敦和坦帕湾团队合作开发了一个飓风响应网站,当灾难发生时,该网站可以快速响应不同的城市灾难情况。
|
||||
|
||||
想要加入该组织,请访问 [该网站][2] 获取 70 多个 Brigade 的清单,以及个人加入组织的指南。
|
||||
|
||||
### Code for Change
|
||||
|
||||
“Code for Change” 显示了即使在高中时期,也可以为社会做贡献。印第安纳波利斯的一群高中开发爱好者成立了自己的俱乐部,他们通过创建针对社区问题的开源软件解决方案来回馈当地组织。“Code for Change” 鼓励当地组织提出项目构想,学生团体加入并开发完全自由和开源的解决方案。该小组已经开发了诸如“蓝宝石”之类的项目,该项目优化了当地难民组织的志愿者管理系统,并建立了民权委员会的投诉表格,方便公民就他们所关心的问题在网上发表意见。
|
||||
|
||||
有关如何在你自己的社区中创建 “Code for Change”,[访问他们的网站][3]。
|
||||
|
||||
### Python for Good/Ruby for Good
|
||||
|
||||
“Python for Good” 和 “Ruby for Good” 是在俄勒冈州波特兰市和弗吉尼亚州费尔法克斯市举办的双年展活动,该活动将人们聚集在一起,为各自的社区开发和制定解决方案。
|
||||
|
||||
在周末,人们聚在一起聆听当地非营利组织的建议,并通过构建开源解决方案来解决他们的问题。 2017 年,“Ruby For Good” 参与者创建了 “Justice for Juniors”,该计划指导当前和以前被监禁的年轻人,并将他们重新融入社区。参与者还创建了 “Diaperbase”,这是一种库存管理系统,为美国各地的<ruby>尿布库<rt>diaper bank</rt></ruby>所使用。这些活动的主要目标之一是将看似不同的行业和思维方式的组织和个人聚集在一起,以谋求共同利益。公司可以赞助活动,非营利组织可以提交项目构想,各种技能的人都可以注册参加活动并做出贡献。通过两岸(美国大西洋和太平洋东西海岸)的努力,“Ruby for Good” 和 “Python for Good” 一直恪守“使世界变得更好”的座右铭。
|
||||
|
||||
“[Ruby for Good][4]” 在夏天举行,举办地点在弗吉尼亚州费尔法克斯的乔治•梅森大学。
|
||||
|
||||
### Social Coder
|
||||
|
||||
英国的 Ed Guiness 创建了 “Social Coder”,将志愿者和慈善机构召集在一起,为六大洲的非营利组织创建和使用开源项目。“Social Coder” 积极招募来自世界各地的熟练 IT 志愿者,并将其与通过 Social Coder 注册的慈善机构和非营利组织进行匹配。项目范围从简单的网站更新到整个移动应用程序的开发。
|
||||
|
||||
例如,PHASE Worldwide 是一个在尼泊尔支持工作的小型非政府组织,因为 “Social Coder”,它获得了利用开源技术的关键支持和专业知识。
|
||||
|
||||
有许多慈善机构已经与英国的 “Social Coder”进行了合作,也欢迎其它国家的组织加入。通过他们的网站,个人可以注册为社会软件项目工作,找到寻求帮助的组织和慈善机构。
|
||||
|
||||
对 “Social Coder” 的志愿服务感兴趣的个人可以 [在此][5]注册.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/ways-volunteer
|
||||
|
||||
作者:[John Jones][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[Morisun029](https://github.com/Morisun029)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/johnjones4
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/OSDC_gift_giveaway_box_520x292.png?itok=w1YQhNH1 (Gift box opens with colors coming out)
|
||||
[2]: https://brigade.codeforamerica.org/
|
||||
[3]: http://codeforchange.herokuapp.com/
|
||||
[4]: https://rubyforgood.org/
|
||||
[5]: https://socialcoder.org/Home/Programmer
|
@ -0,0 +1,101 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11734-1.html)
|
||||
[#]: subject: (Why Vim fans love the Herbstluftwm Linux window manager)
|
||||
[#]: via: (https://opensource.com/article/19/12/herbstluftwm-linux-desktop)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
为什么 Vim 爱好者喜欢 Herbstluftwm Linux 窗口管理器
|
||||
======
|
||||
|
||||
> 本文是 24 天 Linux 桌面特别系列的一部分。如果你是 Vim 爱好者,请试试 Herbstluftwm,这是一个受到 “Vim 方式”启发的平铺式 Linux 窗口管理器。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201912/31/215943pisvzwkm8mvsefhm.jpg)
|
||||
|
||||
每个人都喜欢 Vim(除了 Dvorak 和 Emacs 用户)。Vim 非常流行,它不仅仅是个编辑器,还有整个都使用 Vim 键绑定来浏览网页的 Web 浏览器,在非常流行的 [Zsh][2] 终端仿真器中也有 Vim 模式。此外,还有一个名为 [herbstluftwm][3] 的窗口管理器,它部分模仿了 “Vim 方式”。Herbstluftwm 取消了窗口,而是在程序载入和使用时使用平铺的贴片或者象限代替。你可以使用键盘(`Alt+h`、`Alt+j`、`Alt+k` 和 `Alt+l`)从一个平铺贴片移动到另一个平铺贴片。
|
||||
|
||||
可以从发行版软件仓库安装 Herbstluftwm。安装后,请退出当前桌面会话,以便可以登录到新会话。默认情况下,会话管理器(KDM、GDM、LightDM 或 XDM,具体取决于你的设置)将继续登录到以前的桌面,因此在登录之前必须覆盖它。
|
||||
|
||||
在 GDM 中:
|
||||
|
||||
![][5]
|
||||
|
||||
在 SDDM 中:
|
||||
|
||||
![][6]
|
||||
|
||||
### Herbstluftwm 桌面之旅
|
||||
|
||||
第一次登录 Herbstluftwm 时,你只会看到一个绿屏,边缘有深绿色边框。你所看到的是初始的平铺贴片,其中没有加载任何应用程序。要启动默认应用,请按 `Alt+Enter`。
|
||||
|
||||
![][4]
|
||||
|
||||
文档中强调使用 `$HOME/.config/herbstluftwm/autostart` 作为登录时启动重要应用的配置文件。对于不必在每次登录时启动的应用,可以使用 xterm 作为启动守护进程。与往常一样,在命令后加一个 `&` 符号会将 shell 的控制权返回给你。例如,要启动 Emacs:
|
||||
|
||||
```
|
||||
% emacs &
|
||||
```
|
||||
|
||||
这将在新的平铺贴片中启动 Emacs 窗口,然后返回命令行。
|
||||
|
||||
![Emacs running in Herbstluftwm][7]
|
||||
|
||||
#### 切换平铺贴片
|
||||
|
||||
要从一个平铺贴片移动到另一个平铺贴片,请使用经典的 `h`、`j`、`k` 或 `l` 与 `Alt` 的 Vim 导航组合键。例如,要从终端切换到其下方的平铺贴片中的应用(即屏幕底部),请按 `Alt+j`。要返回,请按 `Alt+k`。左移和右移分别为 `Alt+h` 和 `Alt+l`。
|
||||
|
||||
#### 分割屏幕
|
||||
|
||||
你可以使用 `Alt+o` 垂直分割屏幕,使用 `Alt+u` 水平分割屏幕。
|
||||
|
||||
要删除空白平铺贴片,请移动到该平铺贴片,然后按 `Alt+r`。
|
||||
|
||||
### 配置 herbstluftwm
|
||||
|
||||
除了 `Alt` 键绑定外,你还可通过 `herbstclient` 命令与 Herbstluftwm 进行通信。此命令可以在 shell 中交互,或者你可在配置文件中保留你的首选项。
|
||||
|
||||
你可以查看 Herbstluftwm 中的所有可用属性:
|
||||
|
||||
```
|
||||
$ herbstclient attr
|
||||
```
|
||||
|
||||
Herbstluftwm 的默认行为定义在其默认配置文件中,你可以将其复制到你的家目录并进行修改。启动后,Herbstluftwm 执行配置文件中包含的命令。例如,你觉得使用以 `Alt` 为中心的键绑定很笨拙(它传统上是应用内保留快捷键),那你可以在配置文件中更改触发 Herbstluftwm 操作的键:
|
||||
|
||||
```
|
||||
% mkdir ~/.config/herbstluftwm
|
||||
% cp /usr/xdg/herbstluftwm/autostart ~/.config/herbstluftwm
|
||||
% sed -i 's/Mod=Mod1/Mod=Mod4/' ~/.config/herbstluftwm
|
||||
% herbstclient reload
|
||||
```
|
||||
|
||||
这将使 Herbstluftwm 的修饰键更改为 `Super` 键(“Windows” 或 “Tux” 键,具体取决于你的键盘)。
|
||||
|
||||
使用 `autostart` 文件,你可以设置自定义键绑定,为特定类型的应用创建标签,以便以一致的方式平铺应用,并执行更多操作。
|
||||
|
||||
### 为何你要尝试 herbstluftwm
|
||||
|
||||
Herbstluftwm 是一个平铺窗口管理器的很好例子。它默认平铺窗口,并允许用户定义全局规则的例外。它使用类似 Vim 的导航方式,但可以快速简单地覆盖它。它可能是你一直在寻找的平铺管理器,所以请马上尝试一下。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/herbstluftwm-linux-desktop
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/openstack_python_vim_2.jpg?itok=4fza48WU (OpenStack source code (Python) in VIM)
|
||||
[2]: https://opensource.com/article/19/9/getting-started-zsh
|
||||
[3]: https://herbstluftwm.org
|
||||
[4]: https://opensource.com/sites/default/files/uploads/advent-herbsluftwm.png (Herbstluftwm)
|
||||
[5]: https://opensource.com/sites/default/files/advent-gdm_1.jpg
|
||||
[6]: https://opensource.com/sites/default/files/advent-kdm_0.jpg
|
||||
[7]: https://opensource.com/sites/default/files/uploads/advent-herbsluftwm-emacs.jpg (Emacs running in Herbstluftwm)
|
@ -1,8 +1,8 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11744-1.html)
|
||||
[#]: subject: (Pop!_OS vs Ubuntu: Which One is Better?)
|
||||
[#]: via: (https://itsfoss.com/pop-os-vs-ubuntu/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
@ -12,7 +12,7 @@ Pop!_OS 与 Ubuntu:哪个更棒?
|
||||
|
||||
好吧,你可能会发现从[最佳 Linux 发行版][1]列表中选择一个发行版很容易,但是,将两个类似的 Linux 发行版进行比较通常会令人困惑,就像 Pop!_OS 与 Ubuntu 一样。
|
||||
|
||||
有趣的是,Pop!\_OS 基于 [Ubuntu][2]。那么,Pop!\_OS 和 Ubuntu 之间有什么区别呢?为什么要从中选择一个呢?
|
||||
有趣的是,Pop!\_OS 是基于 [Ubuntu][2] 的。那么,Pop!\_OS 和 Ubuntu 之间有什么区别呢?为什么要从中选择一个呢?
|
||||
|
||||
在本文中,我将比较 Pop!_OS 和 Ubuntu(两者都是我的最爱)。
|
||||
|
||||
@ -24,7 +24,7 @@ Pop!_OS 与 Ubuntu:哪个更棒?
|
||||
|
||||
发现相似之处可帮助你区分其他差异之处。因此,让我们从一些明显的相似之处开始。
|
||||
|
||||
就像我提到的,Pop!\_OS 是基于 Ubuntu 之上的 Linux 发行版。因此,当你使用 Pop!_OS 时,你将获得使用 Ubuntu 的所有好处(从技术上说,其核心是一样的)。
|
||||
就像我提到的,Pop!\_OS 是基于 Ubuntu 之上的 Linux 发行版。因此,当你使用 Pop!\_OS 时,你将获得使用 Ubuntu 的所有好处(从技术上说,其核心是一样的)。
|
||||
|
||||
它们都默认带有 [GNOME 桌面环境][4],因此它们具有相似的用户界面(UI)。
|
||||
|
||||
@ -42,7 +42,7 @@ Pop!_OS 与 Ubuntu:哪个更棒?
|
||||
|
||||
除了外观之外,[Ubuntu 还通过添加程序坞和其他一些小花巧来定制了 GNOME 的体验][6]。如果你喜欢定制的 GNOME 体验,可能会发现它更好。
|
||||
|
||||
但是,如果你更喜欢纯粹的 GNOME 体验,默认情况下 Pop!_OS 会为你提供。
|
||||
但是,如果你更喜欢纯粹的 GNOME 体验,Pop!_OS 默认情况下为你提供的就是这样。
|
||||
|
||||
在你亲自尝试之前,我无法说服你。但是,Pop!_OS 中的总体配色方案、图标和主题可以说是令人愉悦的高级用户体验。
|
||||
|
||||
@ -60,7 +60,7 @@ Ubuntu 非常重视 Snap 软件包。这增加了它提供的应用程序的数
|
||||
|
||||
我为什么要说这个呢?
|
||||
|
||||
因为 Pop!_OS 具有其[自己的官方 PPA][8],并在默认情况下已启用。你会在此处找到一些有用的应用程序,例如 Android Studio、TensorFlow。无需下载 Android Studio 的 1GB 大的 Snap 程序包。只需使用 [apt-get install][9]就可以了。
|
||||
因为 Pop!_OS 具有其[自己的官方 PPA][8],并已默认启用。你会在此处找到一些有用的应用程序,例如 Android Studio、TensorFlow。无需下载 Android Studio 的 1GB 大的 Snap 程序包。只需使用 [apt-get install][9]就可以了。
|
||||
|
||||
#### 预装应用
|
||||
|
||||
@ -76,13 +76,13 @@ Ubuntu 非常重视 Snap 软件包。这增加了它提供的应用程序的数
|
||||
|
||||
![][11]
|
||||
|
||||
对于熟悉 Snap 程序包的用户来说,Ubuntu 的软件中心是比 Pop!_OS 商店更好的解决方案,因为你可以在软件中心中列出快照程序包。
|
||||
对于熟悉 Snap 程序包的用户来说,Ubuntu 的软件中心是比 Pop!_OS 商店更好的解决方案,因为你可以在软件中心中列出 Snap 程序包。
|
||||
|
||||
你无法在软件中心中过滤快照包,但是当你在软件中心中发现一个 Snap 包(查看应用程序来源的详细信息为“ Snap store ”/“Snapcraft”)时安装它就更容易了。
|
||||
你无法在软件中心中过滤 Snap 软件包,但是当你在软件中心中发现一个 Snap 软件包(查看应用程序来源的详细信息为 “Snap store”/“Snapcraft”)时安装它就更容易了。
|
||||
|
||||
如果你感到困惑,Pop!\_OS 也确实支持快照包。但是,你不会在 Pop!_OS 商店中找到它们,这是唯一的区别。
|
||||
可能你会感到困惑,Pop!\_OS 也确实支持 Snap 软件包。但是,你不会在 Pop!\_OS 商店中找到它们,这是唯一的区别。
|
||||
|
||||
如果不确定什么是 Snap 软件包及其功能,可以查看我们的文章[在 Linux 上安装 Snap 应用][12]。
|
||||
如果不确定什么是 Snap 软件包及其功能,可以查看我们的文章《[在 Linux 上安装 Snap 应用][12]》。
|
||||
|
||||
#### 单独的 NVIDIA/AMD ISO 文件
|
||||
|
||||
@ -96,9 +96,9 @@ Ubuntu 非常重视 Snap 软件包。这增加了它提供的应用程序的数
|
||||
|
||||
#### 可靠性与问题
|
||||
|
||||
毫无疑问,这两个[发行版都适合初学者][14],并且相当可靠。如果你想要更好的可靠性和更少的问题,则可能希望一直用长期支持(LTS)版本。
|
||||
毫无疑问,这两个[发行版都适合初学者][14],并且相当可靠。如果你想要更好的可靠性和更少的问题,则可能希望一直使用长期支持(LTS)版本。
|
||||
|
||||
当出现新版本的 Ubuntu 时,Pop!_OS 将在其上开发,并有可能解决用户在 Ubuntu 原始发行版上遇到的问题,然后再进行新的升级。这给他们带来了一点优势,但这没什么实质性的不同,因为这些修复最终都可以运用于 Ubuntu。
|
||||
当出现新版本的 Ubuntu 时,Pop!_OS 将在其上开发,并有可能解决用户在 Ubuntu 原始发行版上遇到的问题,然后再进行新的升级。这给它们带来了一点优势,但这没什么实质性的不同,因为这些修复最终都可以运用于 Ubuntu。
|
||||
|
||||
#### 性能
|
||||
|
||||
@ -110,7 +110,7 @@ Ubuntu 非常重视 Snap 软件包。这增加了它提供的应用程序的数
|
||||
|
||||
当然,你可以手动进行一些优化调整以满足要求——无论它们中的哪个不满足你的硬件配置。
|
||||
|
||||
但是,如果你想使用 System76 笔记本电脑,那么 Pop!\_OS 将可以证明自己是 [Linux 领域的苹果][15],因为 Pop!_OS 是针对其硬件量身定制的,与 Ubuntu 有所不同。
|
||||
但是,如果你想使用 System76 笔记本电脑,那么 Pop!\_OS 将可以证明自己是 [Linux 领域的苹果][15],因为 Pop!\_OS 是针对其硬件量身定制的,与 Ubuntu 有所不同。
|
||||
|
||||
#### 硬件兼容性
|
||||
|
||||
@ -131,7 +131,7 @@ via: https://itsfoss.com/pop-os-vs-ubuntu/
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,90 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11745-1.html)
|
||||
[#]: subject: (Chill out with the Linux Equinox Desktop Environment)
|
||||
[#]: via: (https://opensource.com/article/19/12/ede-linux-desktop)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
轻松使用 Linux Equinox 桌面环境
|
||||
======
|
||||
|
||||
> 本文是 24 天 Linux 桌面特别系列的一部分。EDE 不是最迷人、最小或效率最高的桌面。但它的柔和、安宁让人平静,它让人感到熟悉舒缓,它的图标主题也很有趣。
|
||||
|
||||
我还没有认真用过 Fast Light Toolkit([FLTK] [2]),但我是 C++ GUI 工具箱的粉丝,它非常易于学习,并且即使进行更新也很可靠。当我发现有一个用 FLTK 构建的桌面环境时,我很想尝试一下,并且我很快为我的决定感到高兴。[Equinox 桌面环境(EDE)][3] 是用 C++ 和 FLTK 为 Unix 桌面编写的快速、简单的桌面环境。它使用通用的桌面约定,因此外观和感觉都非常熟悉,并且在使用几天后,我发现它的简单性提供了我所喜欢的最低限度的优雅。
|
||||
|
||||
![EDE desktop][4]
|
||||
|
||||
### 安装 EDE
|
||||
|
||||
你可能会发现 EDE 包含在发行版的软件仓库中,但你也可以在它的 [SourceForge 仓库][5]中找到它。如果你已经在运行其他桌面环境,你可以安全地在同一系统上安装 EDE,因为它仅带来了一些额外的应用,这些应用程序特定于 EDE,因此它们不会妨碍你的其他桌面。
|
||||
|
||||
EDE 只是桌面环境,它使用 [Pekwm][6] 窗口管理器来处理布局。
|
||||
|
||||
安装 EDE 之后,注销当前的桌面会话,以便你可以登录到新的会话。默认情况下,会话管理器(KDM、GDM、LightDM 或 XDM,取决于你的设置)将继续登录到以前的桌面环境,因此你必须在登录之前覆盖该桌面环境。
|
||||
|
||||
在 GDM 中:
|
||||
|
||||
![][7]
|
||||
|
||||
在 SDDM 中:
|
||||
|
||||
![][8]
|
||||
|
||||
首次启动 EDE 时,可能会提示你确认一些启动任务。在我的 Slackware 工作站上,KDE 将某些服务标记为启动任务(例如 HPLIP 打印监视器和蓝牙守护程序),并且 EDE 在一个确认框中显示了它们。
|
||||
|
||||
![Importing desktop services in EDE][9]
|
||||
|
||||
设置一次后,你无需再次进行设置。
|
||||
|
||||
### EDE 桌面之旅
|
||||
|
||||
EDE 的布局恰恰是大多数人在桌面上所期望的布局:左侧的应用菜单、中间的任务栏、右侧的系统托盘。这是标准的现成布局,不会让任何人感到困惑。这是一个可以安全地装在 Linux 上并且可以自信地给任何没有使用过 Linux 的人使用的桌面。他们将以舒适的方式很快地适应这个桌面。
|
||||
|
||||
你可以单击鼠标右键打开一个小型桌面菜单。这可以让你在桌面上创建应用启动器、创建文件夹并设置一些主题选项。
|
||||
|
||||
屏幕底部的面板是可伸缩的,因此你可以根据需要在全屏模式下工作。单击屏幕右下角的时钟来调出日历和时区设置。
|
||||
|
||||
EDE 有一个小的配置应用,可用于设置一些简单的选项:
|
||||
|
||||
* 背景和图标设置
|
||||
* 屏幕保护程序设置
|
||||
* 时间和时钟
|
||||
* 系统铃声
|
||||
* 键盘
|
||||
* 偏好应用
|
||||
|
||||
![EDE Configuration Place][10]
|
||||
|
||||
EDE 的功能不多。没有文件管理器、文本编辑器、绘画程序或纸牌游戏。你必须安装你想在桌面运行的程序。这意味着你可以从各种各样的 Linux 应用中进行选择,并使用你最喜欢的。
|
||||
|
||||
### EDE 总结
|
||||
|
||||
我发现 EDE 至少作为一种“假日”桌面而言,它的简单性令人舒适。它值得一看。它不是最迷人的桌面,也不是最精简的、也不是最高效的。但它的柔和、安宁让人平静,它让人感到熟悉舒缓,并且它的图标主题充满乐趣和生气。EDE 是一个以其缓慢而稳定的步调而自豪的桌面。
|
||||
|
||||
如果你想放松心情使用一个干净和令人愉悦的界面,请试试 EDE。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/ede-linux-desktop
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/penguin.igloo_.png?itok=K92O7H6b (Linux penguin at the north pole beside an igloo)
|
||||
[2]: https://www.fltk.org/
|
||||
[3]: https://sourceforge.net/projects/ede/
|
||||
[4]: https://opensource.com/sites/default/files/uploads/advent-ede.jpg (EDE desktop)
|
||||
[5]: http://ede.sf.net
|
||||
[6]: https://opensource.com/article/19/12/pekwm-linux-desktop
|
||||
[7]: https://opensource.com/sites/default/files/advent-gdm_2.jpg
|
||||
[8]: https://opensource.com/sites/default/files/advent-kdm_1.jpg
|
||||
[9]: https://opensource.com/sites/default/files/uploads/advent-ede-init.jpg (Importing desktop services in EDE)
|
||||
[10]: https://opensource.com/sites/default/files/uploads/advent-ede-conf.jpg (EDE Configuration Place)
|
@ -0,0 +1,110 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11741-1.html)
|
||||
[#]: subject: (Darktable 3 Released With GUI Rework and New Features)
|
||||
[#]: via: (https://itsfoss.com/darktable-3-release/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
Darktable 3 发布,带来重新打造的 UI 及新的功能
|
||||
======
|
||||
|
||||
> 这是给摄影爱好者的圣诞节礼物。Darktable 3.0 刚刚发布了。
|
||||
|
||||
[Darktable][1] 是[在 Linux 上编辑 RAW 图像的最佳应用之一][2]。你可以将其视为 [Adobe Lightroom 的自由开源替代品][3]。
|
||||
|
||||
Darktable 3 是一个主版本更新,它带来了大量的功能改进和重新打造的用户界面。现在,GUI 完全由 GTK+ CSS 规则所控制,这使得整个 GUI 都可定制主题。它默认有八个主题。
|
||||
|
||||
借助 3000 多个提交和 553 个拉取请求,新版本修复了 66 个 bug 并添加了许多新功能。
|
||||
|
||||
让我们看看这个新版本带来了什么功能。
|
||||
|
||||
### Darktable 3.0 的新功能
|
||||
|
||||
![Darktable 3.0 Screenshot][4]
|
||||
|
||||
以下是重要的新功能:
|
||||
|
||||
* 重新打造的 UI
|
||||
* 用于处理 3D RGB Lut 变换的新模块
|
||||
* 对“降噪(轮廓化)”模块进行了许多改进
|
||||
* 添加了新的“剔除”模式和时间轴视图
|
||||
* 对“降噪(轮廓化)”模块进行了许多改进
|
||||
* 新色调均衡器的基本和电影 RGB 模块
|
||||
* 更好的 4K/5K 显示支持
|
||||
* 撤消/重做支持更多操作
|
||||
* 针对 CPU 和 SSE 路径的许多代码优化
|
||||
* 支持导出到 Google 相册
|
||||
* 支持更多的相机,白平衡预设和噪点配置
|
||||
* 大量的 bug 修复和功能改进
|
||||
|
||||
你可以在 [GitHub 发行说明][5]中了解所有更改。
|
||||
|
||||
### 在 Linux 上安装 Darktable 3.0
|
||||
|
||||
让我们看看如何获取最新的 Darktable 版本。
|
||||
|
||||
#### 在基于 Ubuntu 的发行版上安装 Darktable 3.0
|
||||
|
||||
Ubuntu 仓库中有 Darktable,但你不能得到马上最新的版本。对于 LTS 版本,它的更新可能需要几个月的时间。
|
||||
|
||||
不用担心!Darktable 提供了[自己的 PPA][6] 来在基于 Ubuntu 的发行版上安装最新版本。
|
||||
|
||||
不幸的是,此 Darktable PPA 尚未随新版本更新。
|
||||
|
||||
不用担心了!多亏了我们的朋友 [Ubuntu Handbook 的 Ji M][7],我们有一个非正式的 PPA,可以轻松地在 Ubuntu 和其他基于 Ubuntu 的发行版上安装 Darktable 3.0。
|
||||
|
||||
打开终端,并使用这些命令:
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:ubuntuhandbook1/darktable
|
||||
sudo apt update
|
||||
sudo apt install darktable
|
||||
```
|
||||
|
||||
#### 卸载 Darktable 3
|
||||
|
||||
要删除通过此 PPA 安装的 Darktable,你可以先卸载该应用:
|
||||
|
||||
```
|
||||
sudo apt remove darktable
|
||||
```
|
||||
|
||||
接着[删除 PPA][8]:
|
||||
|
||||
```
|
||||
sudo add-apt-repository -r ppa:ubuntuhandbook1/darktable
|
||||
```
|
||||
|
||||
#### 在其他 Linux 发行版上安装 Darktable
|
||||
|
||||
你可以等待到你的发行版的软件管理器提供新版本。
|
||||
|
||||
你也可以从 GitHub 发布页面(位于页面底部)下载 tarball 或所有源代码。
|
||||
|
||||
- [下载 Darktable 3.0][5]
|
||||
|
||||
使用 Darktable 3,你可以更好地编辑自己的假期照片:)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/darktable-3-release/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.darktable.org/
|
||||
[2]: https://itsfoss.com/raw-image-tools-linux/
|
||||
[3]: https://itsfoss.com/open-source-photoshop-alternatives/
|
||||
[4]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/12/darktable_3_screenshot.jpg?ssl=1
|
||||
[5]: https://github.com/darktable-org/darktable/releases/tag/release-3.0.0
|
||||
[6]: https://launchpad.net/~pmjdebruijn/+archive/ubuntu/darktable-release
|
||||
[7]: http://ubuntuhandbook.org/index.php/2019/12/install-darktable-3-0-0-ubuntu-18-04-19-10/
|
||||
[8]: https://itsfoss.com/how-to-remove-or-delete-ppas-quick-tip/
|
@ -0,0 +1,57 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11737-1.html)
|
||||
[#]: subject: (10 resources to boost your Git skills)
|
||||
[#]: via: (https://opensource.com/article/19/12/git-resources)
|
||||
[#]: author: (Joshua Allen Holm https://opensource.com/users/holmja)
|
||||
|
||||
10 个提高 Git 技能的资源
|
||||
======
|
||||
|
||||
> 年末总结一下 Git 的最佳表现。以下是告诉你可以在新的一年中以新奇的方式使用 Git 的十篇文章。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/02/093934q5ifu46u4nuq4lgi.jpg)
|
||||
|
||||
2019 年过去了,是时候回顾一下我们(opensource.com)今年发表的有关 Git 的前 10 篇文章了。
|
||||
|
||||
这些文章包括从用例和教程到一些非常有趣的(甚至有些非常规的)使用 Git 的方法。所有这些文章都可以帮助你提高 Git 技能,但是,如果你确实需要 Git 入门介绍,请务必查看我们的《[Git 入门:术语入门][2]》文章,并下载我们的 [Git 备忘单][3]。
|
||||
|
||||
1. Molly de Blanc 写的《[GNOME 项目如何使用 Git][4]》考察了 GNOME 项目采用的自托管 GitLab 实例的做法,该实例作为 GNOME 项目集中保存对 GNOME 做出的贡献的位置。De Blanc 从事于非编码职位,因此她的文章还探讨了切换到 GitLab 如何使主要角色不是编写代码的贡献者受益。
|
||||
2. Ahmad Awais 写的《[Emoji-Log:编写 Git 提交信息的新方法][5]》解释了如何使用 Emoji-Log(一种直白的、开源的 Git 提交日志标准)来编写更好、更用户友好的 Git 提交消息。本文概述了 Emoji-Log 背后的理论以及可添加到 `.bashrc`、`.zshrc` 或 `.gitconfig` 中的配置示例,以便你快速开始使用 Emoji-Log。
|
||||
3. 在《[用 Git 管理你的每日行程][6]》中,Seth Kenlon 解释了如何使用 Git 来管理日历。通过结合 Org 模式、Git 和其他一些工具,Kenlon 描述了如何创建可在设备之间同步的基于 Git 的日历工作流。
|
||||
4. 《[用 Git 作为聊天应用的后端][7]》,Seth Kenlon 探索了另一种使用 Git 的非常规方式。在本文中,他探讨了 GIC,这是一个用 Node.js 编写的聊天客户端,其中 Git 用作后端数据库。GIC 是一个概念验证模型,不是供产品环境使用的东西,但是它提供了一个有趣的示例,说明了使用 Git 的真正独特方法。
|
||||
5. Alan Formy-Duval 的《[Git 学习实用练习][8]》是一份他为提高 Git 技能而努力的编年史。他分享了自己的经验,以使其他人受益于他所学,而 Git 新手将从这篇对 Git 核心功能的出色概述中受益匪浅。
|
||||
6. 《[用 Git 帮助写作者更好地完成工作][9]》的作者 Seth Kenlon 着眼于写作者如何从基于 Git 的工作流中受益。本文介绍了如何使用 Git 和 Atom 利用 Markdown 进行书写。通过遵循本文中的示例,写作者可以轻松地将复杂的版本控制系统添加到其编写工作流中。
|
||||
7. Sachin Patil 在《[如何使用 Magit 管理 Git 项目][10]》中,解释了如何使用 Emacs 的 Magit 扩展来管理 Git 存储库。本文提供了有关如何在 Emacs 中执行一系列关键 Git 任务的清晰示例。这些示例带有说明各种任务的屏幕截图。
|
||||
8. 《[把“点文件”放到版本控制中][11]》的作者 Matthew Broberg 演示了如何使用 Git 存储放在主目录中的点文件。将你的点文件存储在 Git 存储库中,可以更轻松地在计算机之间共享系统配置,并且,如果你愿意的话,也可以与其他人共享配置。这篇文章也有不要共享哪些文件的警告,因为共享错误的点文件会将其暴露给其他人。
|
||||
9. Austin Dewey 的《[4 种用于 Git 加密的机密管理工具][12]》在本文中比较了 Git-crypt、BlackBox、SOPS 和 Transcrypt,研究了在 Git 中存储机密的工具。本文涵盖四个主要领域:支持的加密类型、每个项目的目标、项目的工作流和差异以及其他功能。
|
||||
10. Seth Kenlon 的《[使用 Git 管理 Git 服务器][13]》解释了如何使用 Gitolite 管理 Git 服务器。Gitolite 是使用 Git 来管理 Git 服务器的“管理杂项的后端应用程序”。本文演示了如何安装、配置和使用 Gitolite,并提供了清晰的示例详细说明了该过程的每个步骤。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/git-resources
|
||||
|
||||
作者:[Joshua Allen Holm][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/holmja
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/button_push_open_keyboard_file_organize.png?itok=KlAsk1gx (open source button on keyboard)
|
||||
[2]: https://opensource.com/article/19/2/git-terminology
|
||||
[3]: https://opensource.com/downloads/cheat-sheet-git
|
||||
[4]: https://opensource.com/article/19/10/how-gnome-uses-git
|
||||
[5]: https://linux.cn/article-10627-1.html
|
||||
[6]: https://linux.cn/article-11320-1.html
|
||||
[7]: https://linux.cn/article-11342-1.html
|
||||
[8]: https://opensource.com/article/19/5/practical-learning-exercise-git
|
||||
[9]: https://linux.cn/article-11499-1.html
|
||||
[10]: https://opensource.com/article/19/1/how-use-magit
|
||||
[11]: https://linux.cn/article-11419-1.html
|
||||
[12]: https://opensource.com/article/19/2/secrets-management-tools-git
|
||||
[13]: https://opensource.com/article/19/4/server-administration-git
|
@ -1,8 +1,8 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (chen-ni)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11791-1.html)
|
||||
[#]: subject: (Explained! Why Your Distribution Still Using an ‘Outdated’ Linux Kernel?)
|
||||
[#]: via: (https://itsfoss.com/why-distros-use-old-kernel/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
@ -10,7 +10,9 @@
|
||||
为什么你的发行版仍然在使用“过时的”Linux 内核?
|
||||
======
|
||||
|
||||
[检查一下你的系统所使用的 Linux 内核版本][1],你十有八九会发现,按照 Linux 内核官网提供的信息,该内核版本已经达到使用寿命终期了。
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/16/225806jbqyacu3loolobae.png)
|
||||
|
||||
[检查一下你的系统所使用的 Linux 内核版本][1],你十有八九会发现,按照 Linux 内核官网提供的信息,该内核版本已经达到使用寿命终期(EOL)了。
|
||||
|
||||
一个软件一旦达到了使用寿命终期,那么就意味着它再也不会得到 bug 修复和维护了。
|
||||
|
||||
@ -18,11 +20,11 @@
|
||||
|
||||
下面将逐一解答这些问题。
|
||||
|
||||
总结
|
||||
|
||||
上游内核维护与你的发行版的内核维护是两个不同的概念。
|
||||
|
||||
例如,根据 Linux 内核官网,Linux 内核 4.15 版本可能已经达到使用寿命终期了,但是在 2023 年 4 月之前,Ubuntu 18.04 长期维护版本将会继续使用这个版本,并通过向后移植安全补丁和修复 bug 来提供维护。
|
||||
> **总结**
|
||||
>
|
||||
> 上游内核维护与你的发行版的内核维护是两个不同的概念。
|
||||
>
|
||||
> 例如,根据 Linux 内核官网,Linux 内核 4.15 版本可能已经达到使用寿命终期了,但是在 2023 年 4 月之前,Ubuntu 18.04 长期维护版本将会继续使用这个版本,并通过向后移植安全补丁和修复 bug 来提供维护。
|
||||
|
||||
### 检查 Linux 内核版本,以及是否达到使用寿命终期
|
||||
|
||||
@ -35,13 +37,11 @@ uname -r
|
||||
我使用的是 Ubuntu 18.04,输出的 Linux 内核版本如下:
|
||||
|
||||
```
|
||||
[email protected]:~$ uname -r
|
||||
abhishek@itsfoss:~$ uname -r
|
||||
5.0.0-37-generic
|
||||
```
|
||||
|
||||
接下来,可以到 Linux 内核官网上看看哪些 Linux 内核版本仍然在维护状态。在网站主页上就可以看到相关信息。
|
||||
|
||||
[Linux 内核官网][2]
|
||||
接下来,可以到 [Linux 内核官网][2]上看看哪些 Linux 内核版本仍然在维护状态。在网站主页上就可以看到相关信息。
|
||||
|
||||
你看到的内核版本状态应该类似于下图:
|
||||
|
||||
@ -53,7 +53,7 @@ uname -r
|
||||
|
||||
不幸的是,Linux 内核的生命周期没有任何规律可循。不是说常规的内核稳定发布版可以得到 X 月的维护、长期维护版本(LTS)可以得到 Y 年的维护。没有这回事。
|
||||
|
||||
根据实际需求,可能会存在内核的多个 LTS 版本,其使用寿命终期各不相同。在[这个页面][5]上可以查到这些 LTS 版本的相关信息,包括推定的使用寿命终期。
|
||||
根据实际需求,可能会存在内核的多个 LTS 版本,其使用寿命终期各不相同。在[这个页面][5]上可以查到这些 LTS 版本的相关信息,包括计划的使用寿命终期。
|
||||
|
||||
那么问题来了:既然 Linux 内核官网上明确表示 5.0 版本的内核已经达到了使用寿命终期,Ubuntu 为什么还在提供这个内核版本呢?
|
||||
|
||||
@ -63,11 +63,11 @@ uname -r
|
||||
|
||||
你是否想过,为什么 Ubuntu/Debian/Fedora 等发行版被称为 Linux “发行版”?这是因为,它们“发行” Linux 内核。
|
||||
|
||||
这些发行版会对 Linux 内核进行不同的修改,并添加各种 GUI 元素(包括桌面环境,显示服务器等)以及软件,然后再呈现给用户。
|
||||
这些发行版会对 Linux 内核进行不同的修改,并添加各种 GUI 元素(包括桌面环境、显示服务器等)以及软件,然后再呈现给用户。
|
||||
|
||||
按照通常的工作流,Linux 发行版会选择一个内核,提供给其用户,然后在接下来的几个月、几年中,甚至是达到内核的使用寿命终期之后,仍然会继续使用该内核。
|
||||
|
||||
这样能够保障安全吗?其实是可以的,因为 _**发行版会通过向后移植全部的重要修补来维护内核**_。
|
||||
这样能够保障安全吗?其实是可以的,因为 **发行版会通过向后移植全部的重要修补来维护内核**。
|
||||
|
||||
换句话说,你的 Linux 发行版会确保 Linux 内核没有漏洞和 bug,并且已经通过向后移植获得了重要的新特性。在“过时的旧版本 Linux 内核”上,其实有着数以千计的改动。
|
||||
|
||||
@ -83,13 +83,13 @@ uname -r
|
||||
|
||||
新的 Linux 内核稳定版本每隔 2 到 3 个月发布一次,有不少用户跃跃欲试。
|
||||
|
||||
实话说,除非有十分充分的理由,否则不应该使用最新版本的稳定内核。你使用的发行版并不会提供这个选项,你也不能指望通过在键盘上敲出“_sudo apt give-me-the-latest-stable-kernel_”解决问题。
|
||||
实话说,除非有十分充分的理由,否则不应该使用最新版本的稳定内核。你使用的发行版并不会提供这个选项,你也不能指望通过在键盘上敲出 `sudo apt give-me-the-latest-stable-kernel` 解决问题。
|
||||
|
||||
此外,手动[安装主流 Linux 内核版本][8]本身就是一个挑战。即使安装成功,之后每次发布 bug 修复的时候,负责更新内核的就会是你了。此外,当新内核达到使用寿命终期之后,你就有责任将它升级到更新的内核版本了。和常规的[Ubuntu 更新][9]不同,内核升级无法通过 apt upgrade 完成。
|
||||
此外,手动[安装主流 Linux 内核版本][8]本身就是一个挑战。即使安装成功,之后每次发布 bug 修复的时候,负责更新内核的就会是你了。此外,当新内核达到使用寿命终期之后,你就有责任将它升级到更新的内核版本了。和常规的 [Ubuntu 更新][9]不同,内核升级无法通过 `apt upgrade` 完成。
|
||||
|
||||
同样需要记住的是,切换到主流内核之后,可能就无法使用你的发行版提供的一些驱动程序和补丁了。
|
||||
|
||||
正如 [Greg Kroah-Hartman][10]所言,“_**你能使用的最好的内核,就是别人在维护的内核。**_”除了你的 Linux 发行版之外,又有谁更胜任这份工作呢!
|
||||
正如 [Greg Kroah-Hartman][10]所言,“**你能使用的最好的内核,就是别人在维护的内核。**”除了你的 Linux 发行版之外,又有谁更胜任这份工作呢!
|
||||
|
||||
希望你对这个主题已经有了更好的理解。下回发现你的系统正在使用的内核版本已经达到使用寿命终期的时候,希望你不会感到惊慌失措。
|
||||
|
||||
@ -102,7 +102,7 @@ via: https://itsfoss.com/why-distros-use-old-kernel/
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[chen-ni](https://github.com/chen-ni)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user