2023-11-26

Chromebook の Linux コンテナ

Google ChromeOS は、Google が設計した OS です。Linux カーネルをベースにしており、Google Chrome ウェブブラウザをメインのユーザインタフェース (UI) として使用しています。そのため、ChromeOS は主にウェブアプリケーションをサポートしています。

2年ほど前、家電量販店のコジマで見かけて衝動買いをした Lenovo Ideapad Duet が、自分が持っている唯一の ARM 系プロセッサを搭載した Chromebook です [1] [2]。ChromeOS では Linux のコンテナ (Debian 11) を起動できます [3]

ChromeOS で起動した Linux コンテナ

Linux が利用できるだから、Python で GUI アプリを作って Chromebook でも利用しようと意気込んだのですが、当時 ARM プロセッサ対応の PySide6 が pip (Python Package Index, PyPI) で利用できなかったので、やむなく Kivy を評価し始めましたが、長続きしませんでした。😭

自分が作りたい類の GUI アプリは、チャートやテーブルを多用するので、PySide6 の方が圧倒的に作りやすかったからです。

そんな PySide6 ですが、現行バージョン 6.6.0 で ARM プロセッサーにも対応したようです。😁

ARM 版 Chromebook の hello.py (PySide6) の実行例

なんだか俄然、Chromebook で Linux コンテナを利用するモチベーションがあがってきました。Lenovo Ideapad Duet に搭載されているプロセッサは MediaTek® P60T (8C, 4x A73 @2.0GHz + 4x A53 @2.0GHz) 、新しいプロセッサではないので非力です。しかし、ARM 系プロセッサを(Andoroid を除く)Linux でじっくり使ったことがないので、Chromebook の Linux コンテナでしばらく使い込んでみたいと思っています。

また、Chromebook の Linux コンテナ上で Python の GUI プログラムを利用するにあたって、調べたことなどを紹介していく予定です。

使用したサンプル

hello.py
#!/usr/bin/env python
# coding: utf-8
import platform
import sys
import PySide6
from PySide6 import QtCore
from PySide6.QtGui import QFont
from PySide6.QtWidgets import (
QApplication,
QLabel,
QVBoxLayout,
QWidget,
)
class Hello(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
self.setWindowTitle('Hello World!')
print('> Platform', platform.platform())
print('> Python', sys.version)
print('> PySide', PySide6.__version__)
def init_ui(self):
layout = QVBoxLayout()
self.setLayout(layout)
label = QLabel('こんにちは、世界!')
font = QFont()
font.setPointSize(24)
label.setFont(font)
label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
layout.addWidget(label)
def main():
app = QApplication(sys.argv)
hello = Hello()
hello.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
view raw hello.py hosted with ❤ by GitHub

日本でも来年になったら発売されるかもしれない Chromebook Plus のプロセッサは、プロセッサの対象が x86_64 系だけのようで残念ですが[4] [5]、手頃な価格な ARM プロセッサ搭載の Chromebook で、ストレージ容量が大きければ買ってしまうかもしれません。、

参考サイト

  1. bitWalk's: Lenovo Ideapad Duet を衝動買い [2021-10-02]
  2. IdeaPad Duet Chromebook|コンパクト 2 in 1 タブレット|ZA6F0019JP | レノボ・ ジャパン
  3. Chromebook で Linux をセットアップする - Chromebook ヘルプ
  4. Chromebook Plus: more performance and AI capabilities [2023-10-02]
  5. Google、高品質でAI搭載の「Chromebook Plus」を定義 399ドルから - ITmedia NEWS [2023-10-03]

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



2023-11-24

EuroLinux Desktop 9

ポーランドの EuroLinux Sp. z o. o.(以降、EuroLinux 社)は、RHEL 6 のころから RHEL 互換パッケージをリリースしていたようですが、CentOS 8 の開発終了がアナウンスされた後 [1]、EuroLinux 8.3 から無料でも公開されるようになりました。

いつもお世話になっている DistrtoWatch.com で EuroLinux 9.3 のアナウンスが載っていたので、最近の EuroLinux の状況を確認しようとサイトをいろいろ見ていると EuroLinux Desktop 9 という名前を見つけました。なにが違うのか見てみたくて、仮想環境にインストールしてみました。

下記の EuroLinux のダウンロードサイトから、Download EuroLinux 9 Desktop for x86_64 下の ISO with Live mode のボタンをクリックして (ELD-9-x86_64-latest-eld-live.iso) をダウンロードしました。

EuroLinux の iso イメージが公開されているサイト

GNOME Boxes 上で EuroLinux Desktop 9 の Live OS を起動して、インストーラを起動してインストールし、dnf コマンドでアップデートをしました。

EuroLinux Desktop 9.3

GNOME shell の画面左上のアクティビティボタンは不評なのでしょうか、外見だけは Microsoft Windows のデスクトップに近い雰囲気にしています。と言っても、右下の9点アイコンは、アクティビティボタンと同じで、クリックすると GNOME のアプリ一覧が表示されます(ショートカットは ⊞ Win + A)。

アプリケーション一覧 (EnroLinux Desktop)

EuroLinux Desktop では、以下のような eld- ではじまるパッケージが使われています。GNOME の拡張パッケージもあります。

[bitwalk@eurolinux ~]$ rpm -qa | grep eld
eld-release-9.2-1.el9.noarch
eld-backgrounds-90.2-5.el9.noarch
eld-gnome-shell-40.9-3.el9.x86_64
eld-gnome-shell-extension-appindicator-53-1.el9.noarch
eld-gnome-shell-extension-dash-to-panel-44-3.el9.noarch
eld-gnome-shell-extension-ding-47-1.el9.noarch
eld-gnome-shell-extension-night-theme-switcher-51-2.el9.noarch
eld-libreoffice-ribbon-1-2.el9.noarch
eld-setup-9.2-1.el9.noarch
[bitwalk@eurolinux ~]$ 

EuroLinux は Oracle Linux、Rocky Linux、Alma Linux、RHEL などのシステムと互換性があると言っているだけです [2]。互換性を維持しつつ、EuroLinux ならではの特徴を出すのはアリなのでしょう。もうすこし評価を続ける必要がありますが、利便性が高いディストリビューションであれば大歓迎です。

参考サイト

  1. CentOS Project shifts focus to CentOS Stream – Blog.CentOS.org [2020-12-08]
  2. What is EuroLinux?

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



2023-11-23

PySide6 とダークモード

PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。Linux/X11, macOS および Microsoft Windows をサポートしています。配布ライセンスは LGPL で公開されています。

個人用途の GUI アプリはほとんどの場合 Python/PySide6 で作っていますが、ダークモードにした場合にそういったアプリの外観がどうなるのか、ちょっと調べました。

意識しだしたダークモード

いままで、デスクトップ環境のダークモードなんて気にしていませんでしたが、最近、個人用のノート PC を持ち歩くことが増えてきました。画面の明るさを適度に落とすことがバッテリーの消耗を抑えることに効くことが判っているので、更にバッテリーを長く使えるようにするには、黒基調の画面、すなわちダークモードで使えば効果的だろうと、今更ながらダークモードに注目するようになりました。

GNOME の場合

ダークモードにしても Python/PySide6 の GUI アプリには反映されません。

GNOME の「設定」→「外観」で、「暗い」(ダークモード)に設定

例えば、calculator.py というサンプルの場合です。

GNOME ダークモードでの calculator.py の実行例

残念ながらこのように Python/PySide6 のアプリの外観には反映されませんでした。GTK のアプリケーションでないので、しかたがないのかもしれません。

  • ちなみに、Python の PyGObject [2] モジュールを pip でインストール・ビルドして、簡単な GUI アプリを作って同じように GNOME デスクトップ環境下で試してみましたが、やはり GNOME のダークモードは反映されませんでした。

KDE Plasma の場合

ひょっとするとデスクトップ環境を変えると状況が変わるかもしれないと考え、試しにノート PC に Fedora KDE Desktop (KDE Plasma 5) をインストールして同じサンプルを実行してみました。

KDE Plasma の「KDE システム設定」→「外観」

デフォルトのライトモードで同じサンプルを実行すると下記のようになります。

KDE Plasma ライトモード (Breeze) での calculator.py の実行例

ダークモードのテーマに設定すると、Python/PySide6 のアプリの外観にも反映されました。😃

KDE Plasma ダークモード (Breeze Dark) での calculator.py の実行例

これは、アプリ側の CSS で明示的に色指定していない場合に有効のようです。

デスクトップ環境の KDE Plasma 5 は Qt ライブラリを利用していますが、Qt のバージョンは 5.15.11 です。一方、PySide6 (6.6.0) は Qt 6.6.0 を利用しています。そのため、直接的な依存関係はなさそうです。なにか環境変数でテーマが設定されているのかもしれないと env コマンドで調べましたが、ちょっと見ただけでは判りませんでした。

しくみが良く判っていないものの、Python/PySide6 のアプリの外観が、デスクトップ環境の外観の設定と同期するのはとても便利です。

それでは、いっそデスクトップ環境を KDE Plasma に変えてしまおうか、とはなかなかなりません(ちょっと、ぐらっときましたが…😅)。しかし、KDE Plasma をもう少し使ってみようかと考える動機にはなります。

動作環境と使用したサンプル

下記の OS 環境で動作確認をしています。

Fedora Workstation 39
Fedora KDE Desktop 39
x86_64
Python 3.11.6
PySide6 6.6.0

使用したサンプルは以下の通りです。敢えてアプリ側で色の指定をしないようにしています。

calculator.py
#!/usr/bin/env python
# coding: utf-8
import math
import queue
import re
import sys
from PySide6.QtCore import Qt, QByteArray
from PySide6.QtGui import QIcon, QPixmap
from PySide6.QtWidgets import (
QApplication,
QGridLayout,
QLayout,
QLCDNumber,
QPushButton,
QSizePolicy,
QWidget,
)
class Calculator(QWidget):
# Key Layout
keys_info = [
{"label": "C", "x": 0, "y": 1, "w": 1, "h": 1, "name": "Cls", "method": "on_clear"},
{"label": "√", "x": 1, "y": 1, "w": 1, "h": 1, "name": "Fnc", "method": "on_function"},
{"label": "±", "x": 2, "y": 1, "w": 1, "h": 1, "name": "Fnc", "method": "on_function"},
{"label": "÷", "x": 3, "y": 1, "w": 1, "h": 1, "name": "Ope", "method": "on_operation"},
{"label": "7", "x": 0, "y": 2, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "8", "x": 1, "y": 2, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "9", "x": 2, "y": 2, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "×", "x": 3, "y": 2, "w": 1, "h": 1, "name": "Ope", "method": "on_operation"},
{"label": "4", "x": 0, "y": 3, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "5", "x": 1, "y": 3, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "6", "x": 2, "y": 3, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "−", "x": 3, "y": 3, "w": 1, "h": 1, "name": "Ope", "method": "on_operation"},
{"label": "1", "x": 0, "y": 4, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "2", "x": 1, "y": 4, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "3", "x": 2, "y": 4, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "+", "x": 3, "y": 4, "w": 1, "h": 2, "name": "Ope", "method": "on_operation"},
{"label": "0", "x": 0, "y": 5, "w": 1, "h": 1, "name": "Key", "method": "on_number"},
{"label": "・", "x": 1, "y": 5, "w": 1, "h": 1, "name": "Key", "method": "on_dot"},
{"label": "=", "x": 2, "y": 5, "w": 1, "h": 1, "name": "Ope", "method": "on_equal"},
]
# max length
max_chars = 12
# operation flag
flag_dot = False
flag_operation = False
flag_error = False
# register for calculation
reg = queue.Queue()
# regular expression
re1 = re.compile("([\-0-9]+)\.$")
re2 = re.compile("([\-0-9]+\.)0$")
def __init__(self):
super().__init__()
self.init_ui()
self.setWindowTitle('Calculator')
self.setWindowIcon(QIcon(self.get_app_pixmap()))
self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint)
def init_ui(self):
grid = QGridLayout()
grid.setHorizontalSpacing(2)
grid.setVerticalSpacing(2)
# Reference
# https://stackoverflow.com/questions/16673074/how-can-i-fully-disable-resizing-a-window-including-the-resize-icon-when-the-mou
grid.setSizeConstraint(QLayout.SetFixedSize)
self.setLayout(grid)
# This is register
self.ent = Register()
# This is display value of register
self.lcd = QLCDNumber(self)
self.lcd.setDigitCount(self.max_chars + 2)
self.lcd.setSmallDecimalPoint(True)
self.lcd.display(self.ent.get_text())
# self.lcd.setStyleSheet("QLCDNumber {background-color:darkgreen; color:yellow;}")
self.lcd.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
grid.addWidget(self.lcd, 0, 0, 1, 4)
grid.setRowMinimumHeight(0, 40)
for key in self.keys_info:
but = QPushButton(key['label'])
method_name = key['method']
method = getattr(self, method_name)
but.clicked.connect(method)
but.setStyleSheet("QPushButton {font-size:12pt; padding:5px 30px;}")
but.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
grid.addWidget(but, key['y'], key['x'], key['h'], key['w'])
def get_app_pixmap(self):
base64data = b'iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHY0lEQVR4nO2dW2wUVRjH0QiIioDxycQEiYmm3Ha7tIspWInAznS3LaXddntvoTvTIndB9KkmBuILhFoUfTExXhMTgcQEgoI3UAFDUMTEUERAUaKGAH3ACB7P2bjD7ux1tjv7ne78f8mXZlN2+Hr+vzlz5ux2O2YMAAAAAAAAAAAAAAAAAAAAAAAAAAAAmfCo2ky3qg/w+p7XMC822sqlat+6FL0k4WcLaPe7Ve0T/r0bBehjODKGirZ9lr93BkWWlnhYWTWeN/wKb/gmdYB5kmCP+Wd0K/pmkl64cLx2lASD4yiyzUgkfEU/SB1aPmvtJh9jZ8bE1Y6BMlopFe2AlBJwO3dSB5avKq3SWVO4gf10eEqCAJe/u5NpawJsjj9MKcEgdd5xRK75pmnft7SD7e0vZ3+9/BC7/tqD0pV5UM1By1DDP4xlB3dNZXVdTSYB9BuzfT3TqXM3EIsUc/iXBqeRhzzaBYjWlZPjmdLSGi+Bqm2jzt2Ar4pPxTYnznzqgItJAFH7358W37OinaTO3YBPSVdjm5N12h/NAgyfGme+DFylzt3APJjU4RajAKLMPVPnbgABIAAEgAAQAAIUsQB/HL+Lrd6osooly9jKp6vY79/cM6Iwz345mXWsWMLKAuHI8f48MQECyCzAivX+uH/vb2thvx6dmFP4YodxYWNH3PHWPZO47QwBJBHg5tBtkTPV/JxcJDh7eDJb2NSecKzHl3ZDAFkFENXJp2vzc6xKIKb9RaHE8EWt2qBCAJkF+PmrSQnTthUJzn09iS1ubkv5/GzWFBCAUIDoGZwqRLWllf1y9N6kzzvPw/eFrD8PAkgmQHQmsBLmxWMTI2f4SMOHAJIIEJ3Os5HgNz6tB9rzEz4EkEiATBKIM/7YvgdYVWvy8HO9hYQAEgkgSoSYanpPVbmc+UUnwNDWR1l3W5CV+zVLg5epxPGWtQfZmW2PFESA6DU+1TSfz/CLSoCe9oa8Bm8urbOhYAIYErQ12xq+7AJciDYl3g6WafDn1yy3VYDKmmUFFUCUuOan60l8f6T/h7QCuNSwKiQQ4X/6QqnjZoDIaj+LGeDCkSKdAaJkuwArpjVANuFHS7yxU2wKOV4AGSofAlgJ35CguS1nCaQVYG5w3QSXovW982wFu7JzKnm4hRAgXfhiuhfX/FTfF68GnknySyejVgC3ou2ONrV2eS15uHYLkC58xbwTmEqCxg7LEkgsgP537HWYOlw7BcgUvnmhl0mCoUPZSyCvAKbGqMO1SwCr4WfzvCe5BKcP3QcBZBdAvF6fattXyWKTJ922sZgJxKuMEEBiAfrW+ZOHz1f12d7fp5Og+6laCCCzAN6anqThW72lSyWBt7qH/TsEAaQVwPyuYPFScK7388kkEG85xwwgsQCXjt8duQyIMzW8unrEL+yINYV4I+j8pd1s/SYffi9AdgFkKAgAASAABIAAEAACQAAIAAEgAASQBAgAARKaQ9lf1LkbUA+EU4s6dwPqgXBqUeduQD0QTi3q3A3MjbUeOSd9mXtW9qrSFwSAABAAAkAACAABnCnAnOCtt6W5VO08de4GEKAwteDVuogEInx3laZQ524AAQpb1HknAAEKVz4hQH//7dSZx2GXAMG9J9i8vi3MU7c68lU8drIATwzWM0+txtyKftmt9tZT525glwAVvZvjjiseO1kAT33sIlC/SJ27gV0ClAZWxB1XPHayAI67DUy2/w0BIAAEgACFF2DRuzXMu7yLlfqTvzKXa4njeXu62KL3qjP2UBqI/Uwl7Tp17gZOEMAb7spr8Oby6p0Ze3hsTcxH0yr629S5GzhBgNIl9v6xaA8/fsbLwIdVrHJ7g9gJbKms7L+DOncDJwggwwwQLeq8E3CCADKsAYpGALGjJzZ1zPf5Ix5MfryKvi2sMYsdQ6sCyFCLd/nZ/OdDzKVo/TP9fVOoczewKoDY1rVzOp238sWiFMCrdd7qWdH2U+duYFUAsbdvpwBz6tYUpQDumI/W5QvBf6hzN8AMUCABTD1T526ANQAEsCQA7gJipLVwFwABCAWQYR8AAhAKIMNOIATADAABqATAGiANThBAhoIAEMBZAuA9gQ4XoMK0YygeQwAHCSB29MS2rtjbF1+z2eGDAATYJYCdBQHyCASAABAAAkAAxwrgUvSrsY2FvjhNHnCxCeDbHf8na8SYU+du4Fa1U7HNVb+xnzzgYhNgwUv18T0r2knq3A14M9tjm5vb/hwLffYjecjFIoDvg0Dcx8NEZgBV20adu8Esf+8MPiXdiG2wLLSRVb++jzV/LuflYDQI4Nvjj5z5CeHzseZVQp17HLyhHeZBjVZ500ZW+9bBpEHUvHmAlTduSPo8VMoaoM47gZJgcJxL1T9O1XRZaENSAYQcEgzoaKqPPB5tLHXeSYlIoGiD5suBqFQfa8a/d0GCQZW+/h/TAWnDj2W2r2e6WKSIlSq/Q7iW7mPNXGpYhQSpSrsmxpCHv1W6az4AAAAAAAAAAAAAAAAAAAAAAAAAAJCT/wDRCjptVbAKIwAAAABJRU5ErkJggg=='
byte_array = QByteArray.fromBase64(base64data)
pixmap = QPixmap()
pixmap.loadFromData(byte_array)
return pixmap
def get_display_string(self, value):
"""
get_display_string
Argument
value : value to display
Return
string to display
"""
if self.flag_error:
return value
str_value = str(value)
self.ent.set_text(str_value)
m = pow(10.0, self.max_chars)
value_int = int(value)
if abs(value_int) > 0:
value_int_length = int(math.log10(abs(value_int))) + 1
if value_int_length < self.max_chars:
if abs(value - value_int) < 1 / m:
str_value = str(value_int)
else:
str_value = str(int(value * m) / m)
while len(str_value) > self.max_chars:
m = m / 10
str_value = str(int(value * m) / m)
else:
str_value = '{:.3e}'.format(value)
else:
if value < 1 / m:
str_value = '{:.3e}'.format(value)
else:
str_value = str(int(value * m) / m)
result = self.re2.match(str_value)
if result:
str_value = result.group(1)
return str_value
return str_value
def get_function_result(self, text, value):
"""
get_function_result
Arguments
text : function operator
value : value of function parameter
Return
value calculated specified function
"""
# sign
if text == "±":
return value * -1
# square root
if text == "√":
try:
return math.sqrt(value)
except Exception as e:
self.flag_error = True
# return e
return "Error"
def get_operator(self, text):
"""
get_operator
Argument
text : label string of calculator key pad
Return
operator string
"""
if text == "+":
return "+"
if text == "−":
return "-"
if text == "×":
return "*"
if text == "÷":
return "/"
def set_display(self, text):
"""
set_display
Argument
text : string to display
"""
self.lcd.display(text)
def zenkaku_to_hankaku(self, text):
"""
zenkaku_to_hankaku
Argument
text : zenkaku string
Return
hankaku (ascii) string
"""
# ref: https://qiita.com/YuukiMiyoshi/items/6ce77bf402a29a99f1bf
return text.translate(str.maketrans({chr(0xFF01 + i): chr(0x21 + i) for i in range(94)}))
# =========================================================================
# BINDINGS
# =========================================================================
def on_clear(self):
"""
on_clear
"""
# display
self.ent.init()
self.set_display(self.ent.get_text())
# clear flag
self.flag_dot = False
self.flag_operation = False
self.flag_error = False
def on_dot(self):
"""
on_dot
"""
if self.flag_error:
return
# flag
self.flag_dot = True
def on_equal(self):
"""
on_equal
"""
if self.flag_error:
return
expr = ""
while not self.reg.empty():
expr += self.reg.get()
expr += self.ent.get_text()
try:
result = eval(expr)
except Exception as e:
self.flag_error = True
# result = e
result = "Error"
disp_new = self.get_display_string(result)
# display
self.set_display(disp_new)
# flag
self.flag_operation = True
def on_function(self):
"""
on_function
"""
button = self.sender()
if self.flag_error:
return
# get current value displayed
value_current = float(self.ent.get_text())
# get string from key label
text = button.text()
value_new = self.get_function_result(text, value_current)
disp_new = self.get_display_string(value_new)
# display
self.set_display(disp_new)
# flag
self.flag_operation = True
def on_operation(self):
"""
on_operation
"""
button = self.sender()
if self.flag_error:
return
# get current string displayed
disp_current = self.ent.get_text()
self.reg.put(disp_current)
# get string from key label
text = button.text()
self.reg.put(self.get_operator(text))
# flag
self.flag_operation = True
self.flag_dot = False
def on_number(self):
"""
on_number
"""
button = self.sender()
if self.flag_error:
return
# get current string displayed
disp_current = self.ent.get_text()
# get string from key label
text = button.text()
text_ascii = self.zenkaku_to_hankaku(text)
# update string to display
if self.flag_operation:
disp_new = text_ascii + "."
self.flag_operation = False
else:
if disp_current == "0.":
if self.flag_dot:
disp_new = disp_current + text_ascii
else:
disp_new = text_ascii + "."
else:
# check charcter length (digit)
if len(disp_current) > self.max_chars:
return
if self.flag_dot:
disp_new = disp_current + text_ascii
else:
result = self.re1.match(disp_current)
if result:
disp_new = result.group(1) + text_ascii + "."
else:
disp_new = disp_current + text_ascii
self.ent.set_text(disp_new)
self.set_display(disp_new)
class Register():
text = None
def __init__(self):
self.init()
def init(self):
self.text = '0.'
def get_text(self):
return (self.text)
def set_text(self, str):
self.text = str
def main():
app = QApplication(sys.argv)
calc = Calculator()
calc.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()
view raw calculator.py hosted with ❤ by GitHub

参考サイト

  1. Qt for Python - Qt Wiki
  2. Overview — PyGObject

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



2023-11-21

KDE Plasma 5.81.80

KDE Plasma は、主に Linux 向けに開発されているデスクトップ環境です。現在リリースされている KDE Plasma 5 は、Qt 5 ライブラリを利用していますが、その後継である Qt 6 を利用した KDE Plasma 6 がリリースに向けて開発中です。

先週、仮想環境にインストールした KDE neon unstable 版をアップデートしたところ(neon-unstable-2023119-1118.iso 相当)、KDE Plasma のバージョンが 5.81.0 から 5.81.80 になっていました。また、壁紙の右下の文字が「KDE Plasma 5.81.0」から「KDE Plasma 6.0 Dev」に変わりました。

そろそろ、新しい機能について詳しく調べてまとめていこうと思います。

GNOME Boxes 上にインストールした neon-unstable-20231113-1400.iso を最新にアップデート(11/20 時点)

KDE Plasma 6 の開発版に触れてみたい方は、KDE プロジェクトがリリースしている Linux ディストロである KDE neon の unstable 版で試すことができます [1]

依然として、KDE Plasma のメジャーバージョンが上がる際の、移行時の開発バージョン番号の規則がどうなっているのかが判っていません。💦

参考サイト

  1. KDE neon

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



2023-11-18

不変な Fedora Workstation

Fedora Linux は Red Hat 社が支援するコミュニティ Fedora Project で開発されているディストリビューションで、最新の技術を積極的に取り込むことで知られています。また Fedora Linux の開発成果が、後にリリースされる Red Hat Enterprise Linux, RHEL に取り込まれています。Fedora Linux は、おおむね春と秋の年二回の頻度で新しい版がリリースされています。

Fedora Linux は多様な用途に対応するため、いくつものエディションを公開しています。現時点 (Fedora Linux 39) で以下の5つのエディションが用意されています。

  • Fedora Workstation
    • ラップトップやデスクトップ PC 向け(ワークステーション用途)
  • Fedora Server
    • サーバー向け
  • Fedora IoT
    • エッジデバイス上で利用することを想定
  • Fedora Cloud
    • クラウドの仮想環境で利用することを想定、ポピュラーなクラウドシステム向けのイメージを提供
  • Fedora Core OS
    • 自動更新、最小化、モノリシック、コンテナに特化、クラスタ用に設計されていますが、スタンドアロンでも利用可能。Kubernetes 用に最適化されています。

さらにワークステーション用途には、以下のバリエーションがあります。

  • Fedora Spins
    • 標準 (GNOME) 以外のデスクトップ環境
      • KDE Plasma
      • Xfce
      • Cinnamon
      • MATE-Compiz
      • i3
      • LXQt
      • LXDE
      • SOAS
      • Phosh
      • Sway
      • Budgie
  • Fedora Labs
    • 目的に応じた厳選されたソフトウェアとコンテンツをバンドル
      • Astronomy
      • Comp Neuro
      • Design Suite
      • Games
      • Jam
      • Python Classroom
      • Security Lab
      • Robotics Suite
      • Scientic
  • Immutable Desktops
    • Immutable なワークステーション
      • Fedora Silverblue
      • Fedora Kinoite
      • Fedora Onyx
      • Fedora Sericea
今回のテーマ

Fedora Workstation のバリエーションの中で、Immutable なブランドを紹介します。

Immutable な Fedora Workstation とは

Fedora Linux では、パッケージ単位でのインストールや更新をする従来のものに加えて、OS のツリー構造を一括まとめてアトミックなイメージとして管理できる、immutable(不変)な仕組みを持ったブランドも提供されています。

※ Fedora ○×△ という名称が付けられているため、本稿では「ブランド」と呼称することにします。

ワークステーション用途(Fedora Workstation エディション)で、最初にリリースされた immutable なブランドは Fedora Silverblue ですが、現在ではデスクトップ環境別にそれぞれ4つのブランド名が付けられて OS イメージが公開されています。

Fedora Silverblue

デスクトップ環境は GNOME です。

Silverblue という名称に特別な意味が込められてはいないということですが [1]、もともとこの開発プロジェクトでは、Silverleaf という名前にしたかったようなのです。しかし、いろいろな事情によりかなわずに、Silverblue という名前に落ち着いたそうです。そのため、ロゴは Silverleaf を連想させる葉っぱ状のものになっています [2]

Fedora Silverblue プロジェクトサイト

Fedora Kinoite

デスクトップ環境は KDE Plasma です。

Knoite の意味は、参考サイト [3] によると下記であるとされています。

う~ん、最後の説明はなんだか変ですねぇ。

Fedora Kinoite プロジェクトサイト

Fedora Onyx

デスクトップ環境は Budgie です。Budgie は、Solus という Linux ディストロのプロジェクトで開発されています。

この Fedora Onyx は、Fedora Linux 39 のリリース時に公開された新しいプロジェクトです [5]

Onyx は Kinoite と同様に鉱物のオニキスを指していると思われますが、命名した理由についての記載をみつけられませんでした。Fedora Onyx のロゴも見つけられませんでしたが、まだこのプロジェクトが新しいため、準備が追いついていないのかもしれません。

Fedora Onyx プロジェクトサイト

Fedora Sericea

デスクトップ環境は Sway タイリング・ウィンドウ・マネージャーです。タイリング・ウィンドウ・マネージャーとは、マウスやタッチパッド、その他のポインティングデバイスを使わずに環境を操作したいユーザー向けの環境です。個人的にはこの手のウィンドウマネージャの操作には全然慣れていません。

Sericea と命名した理由について、参考サイト [6] に以下のように説明されていました。

  • Sericea は Sway のように 'S' で始まります。
  • Terminalia Sericea は木です。ostree もそうです。
  • Terminalia Sericea は Silverleaf としても知られています。Silverleaf は、後に Fedora Silverblue となるプロジェクト名の最有力候補の一つでした。
  • Terminalia Sericea をしばらく眺めていると、やがて Sway のロゴとの類似点に気づくでしょう。

ちなみに左が Sway のロゴです。

Fedora Sericea プロジェクトサイト

Immutable な Fedora Workstation の構成

OS の構成は、rpm-ostree を利用した、rpm パッケージを追加できる(ハイブリッドな)OS イメージの管理になっています。

Fedora Linux で利用できる rpm パッケージは(パッケージ名が判っていれば)追加できるので、やろうと思えば必要なパッケージを rpm-ostree のコマンド操作ですべて追加することもできます。しかし、特にデスクトップ用途のアプリケーションについては、flatpak を利用してユーザーアカウント領域に追加することもできます。いやむしろ、flatpak を利用する方が一般的でしょう。

Immutable な Fedora Workstation の構成

アプリケーション開発には、Toolbox [8] というプライベートなコンテナを利用して(dnf コマンドで rpm パッケージをインストールする)Fedora Workstation と同等な環境を利用することができます。

Immutable は Fedora Workstation の未来か?

Fedora 29 がリリースされたタイミングで Fedora Silverblue が登場した時には、Fedora Workstation の未来は Fedora Silverblue になるのかもしれない、と何となく感じていました。しかし今では、従来のものと Immutable な OS は共存していくのではないだろうかと思うようになりました。

つまり、どちらもサポートできるだけの力が開発プロジェクトにあれば、敢えて選択肢を削るようなことはしないと思うからです。

追記 [2024-02-24]

Fedora Linux 40 でマーケティング/ポリシーが変更され、上記 rpm-ostree を利用している不変な(アトミックな)デスクトップ用イメージ群は、それぞれ Silverblue / Kinoite / Onyx / Sericea の名前を維持しつつ "Immutable Desktops" から "Fedora Atomic Desktops" という名称に再分類されるようです。[Releases/40/ChangeSet より]

参考サイト

  1. What is Silverblue? - Fedora Magazine [2019-07-12]
  2. Frequently Asked Questions (FAQ) :: Fedora Docs
  3. Changes/Fedora Kinoite - Fedora Project Wiki
  4. Changes/Fedora Onyx - Fedora Project Wiki
  5. Fedora Linux 39 is officially here! - Fedora Magazine [2023-11-07]
  6. Frequently Asked Questions (FAQ) :: Fedora Docs
  7. bitWalk's: Linux ディストロ探訪(10) 〜 Fedora Silverblue 〜 [2019-11-24]
  8. Toolbox :: Fedora Docs Site
  9. bitWalk's: Fedora Silverblue と Toolbox [2020-11-03]
  10. bitWalk's: Fedora Silverblue はデスクトップ Linux の未来か? [2021-11-06]
  11. コンテナの実行やセキュリティに特化した「システムの変更が不可能なLinuxディストリビューション」8選 - GIGAZINE [2023-04-08]

 

古い資料ですが、OSTree についてわかりやすく説明されています。

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース