İnxigui denemem

inxi nin gui halini gtk4 ile uyarladım.

4 Beğeni

Elinize sağlık, güzel olmuş.
Sağda gösterilen bilgiler soldaki listede ait olduğu başlık vurgulansa daha iyi olabilir.
Ayrıca GitHub’da .deb dosyasının “Releases” bölümünde gözükmesi…

2 Beğeni

Pardus hakkında uygulaması varken artık buna gerek var mı? :smile: Ben terminalden inxi yi kullanmayı seviyorum pratik oluyor. Birde guisini yapayım dedim.

Çok güzel bir uygulama ve daha da güzelleştirilebilir.

Bir zamanlar windows kullanırken, Piriform’un Speccy uygulamasını mutlaka bilgisayarda tutardım. Bu da onun güzel bir alternatifi.

EK:
Hocam github deponuza baktığım zaman control dosyanız "adınız soyadınız <email adresiniz @ mail . com> " olarak kalmış. Orayı elle değiştirerek paketleyin bir dahaki sefere.

1 Beğeni

son hali

1 Beğeni

Şu anda programınız çok iyi bir durumda. Başka bir genel sistem programının bir sekmesi ya da açılır modülü olarak yapabilirsiniz. Daha önce burada paylaştığım QFast Image Toolbox gibi. Ama GUI olarak öyle değil.

Sadece öneri.

Pencerenin default boyutlarını biraz daha küçültün ve fixleyin. Bu durumda pencere başlık çubuğundaki büyüt butonu kaybolur ve sabit olur (isteğe bağlı).

İlerde oksijen ikon takımından güzel donanım ikonları alarak programda kullanabilirsiniz (yine isteğe bağlı).

Bugün sistem özetindeki serial: superuser required ifadesi dikkatimi çekti.
Bu bilgi de bazen gerekebiliyor.
Kullanıcının sistem özetindeki 2 seri numarasını görebilmesi için gerekli yönetici yetkisiyle başlatmak da mümkün.
İstenirse belki ileride, “mükelleştirme” adına, sola bir kutu daha eklenip (ör. “Seri Numaralarını Göster” ya da "Yönetici Modu"vs.) buna tıklayınca mevcut uygulama penceresi kapanıp yerine yönetici modunda pencere açılması sağlanabilir.
Ayrıca, kullanıcı sistemi karanlık/koyu temasını kullanıyorsa, yönetici modunda açılınca diğerleri (ör. GParted, Synaptic vb.) gibi bu neden mevcut sistem temasına uymuyor, anlamadım…

Sağ olsun, Claude bunu yapıverdi, üstelik pencere kapatıp açmadan ve dolayısıyla tema sorununa hiç girmeden.

import gi
import subprocess
import os
import threading

gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, GLib

class InxiSadePanel(Adw.Application):
	def __init__(self):
		super().__init__(application_id='com.debian.inxi.final')
		self.aktif_buton = None

	def do_activate(self):
		self.win = Adw.ApplicationWindow(application=self)
		self.win.set_title("Sistem Bilgi Merkezi (Inxi Gui)")

		width, height = 1000, 700

		self.win.set_default_size(width, height)

		ana_kutu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		self.win.set_content(ana_kutu)

		# Üst Araç Çubuğu
		header = Adw.HeaderBar()
		self.spinner = Gtk.Spinner()
		header.pack_end(self.spinner)
		ana_kutu.append(header)

		# İçerik Düzeni
		paned = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		ana_kutu.append(paned)

		# SOL MENÜ
		self.sol_menu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
		self.sol_menu.set_size_request(240, -1)
		self.sol_menu.set_margin_start(15)
		self.sol_menu.set_margin_end(15)
		self.sol_menu.set_margin_top(15)
		self.sol_menu.set_margin_bottom(15)

		paned.append(self.sol_menu)

		kategoriler = [
			("🏠 Sistem Özeti", "-b"), ("💻 İşlemci (CPU)", "-C"),
			("🖼️ Ekran Kartı (GPU)", "-G"), ("💾 Bellek (RAM)", "-m"),
			("💽 Disk Bilgisi", "-D"), ("🌐 Ağ Kartları", "-N"),
			("📊 Ses Sistemi", "-A"), ("🌡️ Sıcaklıklar", "-s"),
			("📦 Yazılım Depoları", "-r"), ("🔋 Pil Durumu", "-B"),
			("📄 Tam Rapor", "-F")
		]

		for isim, param in kategoriler:
			# Butonu oluştur
			btn = Gtk.Button(label=isim)
			btn.add_css_class("pill") # Yuvarlak stil

			# Metni sola yasla
			label = btn.get_child()
			if isinstance(label, Gtk.Label):
				label.set_xalign(0)
				label.set_margin_start(10)

			btn.param = param
			btn.connect('clicked', self.on_button_clicked)
			self.sol_menu.append(btn)

			# Başlangıçta "Sistem Özeti" butonunu seçili yap
			if param == "-b":
				self.vurgula_butonu(btn)

		# Ayırıcı ekle
		separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
		separator.set_margin_top(5)
		separator.set_margin_bottom(5)
		self.sol_menu.append(separator)

		# Seri Numaraları butonu (sudo gerektirir)
		btn_serial = Gtk.Button(label="🔑 Seri Numaraları")
		btn_serial.add_css_class("pill")

		label = btn_serial.get_child()
		if isinstance(label, Gtk.Label):
			label.set_xalign(0)
			label.set_margin_start(10)

		btn_serial.param = "serial"
		btn_serial.connect('clicked', self.on_serial_clicked)
		self.sol_menu.append(btn_serial)

		# SAĞ PANEL (Metin Çıktısı)
		vbox_sag = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		vbox_sag.set_hexpand(True)
		vbox_sag.set_margin_top(15)
		vbox_sag.set_margin_bottom(15)
		vbox_sag.set_margin_end(15)
		paned.append(vbox_sag)

		scrolled = Gtk.ScrolledWindow()
		scrolled.set_vexpand(True)
		scrolled.set_has_frame(True)

		self.metin_alani = Gtk.TextView(editable=False, monospace=True)
		self.metin_alani.set_left_margin(12)
		self.metin_alani.set_top_margin(12)
		self.metin_alani.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) # Uzun satırları böl

		scrolled.set_child(self.metin_alani)
		vbox_sag.append(scrolled)

		self.win.present()
		self.islem_baslat("-b")

	def vurgula_butonu(self, buton):
		"""Tıklanan butonu mavi yapar ve eski butonu normale döndürür."""
		if self.aktif_buton:
			# Önceki butonun vurgusunu kaldır
			self.aktif_buton.remove_css_class("suggested-action")

		# Yeni butonu vurgula
		self.aktif_buton = buton
		self.aktif_buton.add_css_class("suggested-action")

	def on_button_clicked(self, btn):
		self.vurgula_butonu(btn)
		self.islem_baslat(btn.param)

	def on_serial_clicked(self, btn):
		self.vurgula_butonu(btn)
		# Şifre dialogu göster
		self.sifre_iste()

	def sifre_iste(self):
		"""Kullanıcıdan sudo şifresi isteyip doğrulayan dialog"""
		dialog = Adw.MessageDialog.new(self.win)
		dialog.set_heading("Yönetici Yetkisi Gerekli")
		dialog.set_body("Seri numaralarını görüntülemek için sudo şifrenizi girin:")

		# Şifre girişi için entry
		entry = Gtk.PasswordEntry()
		entry.set_show_peek_icon(True)
		entry.set_margin_top(10)
		entry.set_margin_bottom(10)
		entry.set_margin_start(10)
		entry.set_margin_end(10)

		# Entry'yi dialog'a ekle
		dialog.set_extra_child(entry)

		dialog.add_response("cancel", "İptal")
		dialog.add_response("ok", "Tamam")
		dialog.set_response_appearance("ok", Adw.ResponseAppearance.SUGGESTED)
		dialog.set_default_response("ok")
		dialog.set_close_response("cancel")

		# Enter tuşu ile onaylama
		entry.connect("activate", lambda e: dialog.response("ok"))

		dialog.connect("response", self.sifre_dialog_cevap, entry)
		dialog.present()

	def sifre_dialog_cevap(self, dialog, response, entry):
		"""Şifre dialogundan gelen cevabı işle"""
		if response == "ok":
			sifre = entry.get_text()
			if sifre:
				self.spinner.start()
				thread = threading.Thread(target=self.arkaplan_islem_sudo, args=(sifre,))
				thread.daemon = True
				thread.start()
		dialog.close()

	def islem_baslat(self, param):
		self.spinner.start()

		thread = threading.Thread(target=self.arkaplan_islem, args=(param,))
		thread.daemon = True
		thread.start()

	def arkaplan_islem(self, param):
		# Türkçe karakter desteği ve renksiz çıktı (-c 0)
		env = os.environ.copy()
		env["LC_ALL"] = "tr_TR.UTF-8"
		env["LANG"] = "tr_TR.UTF-8"
		try:
			res = subprocess.run(['inxi', param, '-c', '0'], capture_output=True, text=True, env=env)
			cikti = res.stdout if res.stdout else "Veri bulunamadı veya bu donanım mevcut değil."
		except Exception as e:
			cikti = f"Hata: inxi yüklü mü? \nDetay: {str(e)}"

		GLib.idle_add(self.metni_yaz, cikti)

	def arkaplan_islem_sudo(self, sifre):
		"""Sudo ile inxi'yi çalıştır ve seri numaralarını ayıkla"""

		try:
			# Önce sudo -k ile timestamp'i sıfırla
			subprocess.run(['sudo', '-k'], check=False)

			# script komutu ile pseudo-terminal oluştur
			# Bu inxi'nin interactive mode'da çalıştığını düşünmesini sağlar
			process = subprocess.Popen(
				['sudo', '-S', '-p', '', 'script', '-qec',
				 'inxi -M -B -c 0', '/dev/null'],
				stdin=subprocess.PIPE,
				stdout=subprocess.PIPE,
				stderr=subprocess.PIPE,
				text=True,
				env={'LC_ALL': 'tr_TR.UTF-8', 'LANG': 'tr_TR.UTF-8'}
			)

			# Şifreyi gönder
			stdout, stderr = process.communicate(input=sifre + '\n', timeout=10)

			if process.returncode != 0:
				if 'incorrect password' in stderr.lower() or 'sorry' in stderr.lower():
					cikti = "❌ Hatalı şifre girdiniz."
				else:
					cikti = f"❌ Hata oluştu:\n{stderr}"
			else:
				# Sadece seri numaralarını ayıkla
				cikti = self.seri_numaralari_ayikla(stdout)

		except subprocess.TimeoutExpired:
			process.kill()
			cikti = "❌ İşlem zaman aşımına uğradı."
		except Exception as e:
			cikti = f"❌ Hata: {str(e)}"

		GLib.idle_add(self.metni_yaz, cikti)

	def seri_numaralari_ayikla(self, tam_cikti):
		"""inxi çıktısından sadece gerçek seri numaralarını ayıklayan fonksiyon"""
		satirlar = tam_cikti.split('\n')
		seri_bilgileri = []

		for satir in satirlar:
			# "serial:" içeren ve <filter> OLMAYAN satırları bul
			if 'serial:' in satir.lower() and '<filter>' not in satir.lower():
				seri_bilgileri.append(satir.strip())

		if seri_bilgileri:
			return '\n'.join(seri_bilgileri)
		else:
			return "ℹ️  Bu sistemde görüntülenebilir seri numarası bulunamadı."

	def metni_yaz(self, metin):
		buffer = self.metin_alani.get_buffer()
		buffer.set_text(metin)
		self.spinner.stop()
		return False

if __name__ == "__main__":
	app = InxiSadePanel()
	app.run(None)
1 Beğeni

Claude gerçekten çok başarılı özellikle kod verip şu sorun var dediğinde yaptığı analizi ve çözümü genelde isabetli . Ücret verilebilecek bir yapay zeka . Tecrubeme göre tabiki bu . Yaptığım uygulamada kritik sorunları çözdü. Tek sıkıntı ücretsiz olduğu için çok kısıtlı bir veri hakkın var .

Sadi hocam,
Linux tarafında bir uygulamayı sudo yetkisi ile çalıştırırsanız, o program sistemin mevcut tema ayarından bağımsız açılır. Bu yeni birşey değil, ya da bir arıza değil.

Son verdiğini kodu biraz da ben kurcaladım. Çünkü verdiğiniz haliyle bende parola girsem de sonuç için “superuser gerekli” diyordu. Nedenini çözmeye çalıştım, hatta programın kendi parola yöntemini terk edip pkexec uyguladım. Hiçbir şey değişmedi. Hatta, inix’in bazı sonuçları filtrelemesini durdurmak için -z parametresi uygulattım. Ama birşey değişmedi.

Fakat şöyle bir durum var: Programı sudo yetkisiyle (sudo python3 program_adı.py) çalıştırırsam, (farkettiğiniz gibi aktif temadan bağımsız açılır), Bu kez sonuçları göstermeye başlıyor.

Bende anakart için “N/A” gösteriyor. Bunun nedeni PC’nin toplama olması. Bu ibareyi görenler şaşırmamalı; bu bir sorun değildir.

Kurcaladığım son hali:

import gi
import subprocess
import os
import threading

gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, GLib

class InxiSadePanel(Adw.Application):
	def __init__(self):
		super().__init__(application_id='com.debian.inxi.final')
		self.aktif_buton = None

	def do_activate(self):
		self.win = Adw.ApplicationWindow(application=self)
		self.win.set_title("Sistem Bilgi Merkezi (Inxi Gui)")

		width, height = 1000, 700
		self.win.set_default_size(width, height)

		ana_kutu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		self.win.set_content(ana_kutu)

		# Üst Araç Çubuğu
		header = Adw.HeaderBar()
		self.spinner = Gtk.Spinner()
		header.pack_end(self.spinner)
		ana_kutu.append(header)

		# İçerik Düzeni
		paned = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		ana_kutu.append(paned)

		# SOL MENÜ
		self.sol_menu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
		self.sol_menu.set_size_request(240, -1)
		self.sol_menu.set_margin_start(15)
		self.sol_menu.set_margin_end(15)
		self.sol_menu.set_margin_top(15)
		self.sol_menu.set_margin_bottom(15)

		paned.append(self.sol_menu)

		kategoriler = [
			("🏠 Sistem Özeti", "-b"), ("💻 İşlemci (CPU)", "-C"),
			("🖼️ Ekran Kartı (GPU)", "-G"), ("💾 Bellek (RAM)", "-m"),
			("💽 Disk Bilgisi", "-D"), ("🌐 Ağ Kartları", "-N"),
			("📊 Ses Sistemi", "-A"), ("🌡️ Sıcaklıklar", "-s"),
			("📦 Yazılım Depoları", "-r"), ("🔋 Pil Durumu", "-B"),
			("📄 Tam Rapor", "-F")
		]

		for isim, param in kategoriler:
			btn = Gtk.Button(label=isim)
			btn.add_css_class("pill")
			label = btn.get_child()
			if isinstance(label, Gtk.Label):
				label.set_xalign(0)
				label.set_margin_start(10)

			btn.param = param
			btn.connect('clicked', self.on_button_clicked)
			self.sol_menu.append(btn)

			if param == "-b":
				self.vurgula_butonu(btn)

		separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
		separator.set_margin_top(5)
		separator.set_margin_bottom(5)
		self.sol_menu.append(separator)

		# Seri Numaraları butonu
		btn_serial = Gtk.Button(label="🔑 Seri Numaraları")
		btn_serial.add_css_class("pill")
		label = btn_serial.get_child()
		if isinstance(label, Gtk.Label):
			label.set_xalign(0)
			label.set_margin_start(10)

		btn_serial.connect('clicked', self.on_serial_clicked)
		self.sol_menu.append(btn_serial)

		# SAĞ PANEL
		vbox_sag = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		vbox_sag.set_hexpand(True)
		vbox_sag.set_margin_top(15)
		vbox_sag.set_margin_bottom(15)
		vbox_sag.set_margin_end(15)
		paned.append(vbox_sag)

		scrolled = Gtk.ScrolledWindow()
		scrolled.set_vexpand(True)
		scrolled.set_has_frame(True)

		self.metin_alani = Gtk.TextView(editable=False, monospace=True)
		self.metin_alani.set_left_margin(12)
		self.metin_alani.set_top_margin(12)
		self.metin_alani.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)

		scrolled.set_child(self.metin_alani)
		vbox_sag.append(scrolled)

		self.win.present()
		self.islem_baslat("-b")

	def vurgula_butonu(self, buton):
		if self.aktif_buton:
			self.aktif_buton.remove_css_class("suggested-action")
		self.aktif_buton = buton
		self.aktif_buton.add_css_class("suggested-action")

	def on_button_clicked(self, btn):
		self.vurgula_butonu(btn)
		self.islem_baslat(btn.param)

	def on_serial_clicked(self, btn):
		self.vurgula_butonu(btn)
		self.spinner.start()
		thread = threading.Thread(target=self.arkaplan_islem_pkexec)
		thread.daemon = True
		thread.start()

	def islem_baslat(self, param):
		self.spinner.start()
		thread = threading.Thread(target=self.arkaplan_islem, args=(param,))
		thread.daemon = True
		thread.start()

	def arkaplan_islem(self, param):
		env = os.environ.copy()
		env["LC_ALL"] = "tr_TR.UTF-8"
		try:
			# Standart kullanımda -z parametresi olmadan çağırıyoruz
			res = subprocess.run(['inxi', param, '-c', '0'], capture_output=True, text=True, env=env)
			cikti = res.stdout if res.stdout else "Veri bulunamadı."
		except Exception as e:
			cikti = f"Hata: {str(e)}"
		GLib.idle_add(self.metni_yaz, cikti)

	def arkaplan_islem_pkexec(self):
		"""Filtreleme hatası düzeltilmiş pkexec çağrısı"""
		try:
			# -z parametresi inxi'nin filtrelemesini kapatır (bazı sürümlerde değer almaz)
			# -M (Machine), -B (Battery), -D (Disk) için en detaylı çıktıyı isteriz
			komut = ['pkexec', 'inxi', '-M', '-B', '-D', '-xx', '-c', '0', '-z']
			
			res = subprocess.run(komut, capture_output=True, text=True, env={'LC_ALL': 'tr_TR.UTF-8'})
			
			if res.returncode == 0:
				cikti = self.seri_numaralari_ayikla(res.stdout)
			elif res.returncode == 126:
				cikti = "❌ Yetkilendirme kullanıcı tarafından iptal edildi."
			else:
				# Eğer -z hata verirse (eski inxi), parametresiz dene
				komut_yedek = ['pkexec', 'inxi', '-M', '-B', '-D', '-xx', '-c', '0']
				res_yedek = subprocess.run(komut_yedek, capture_output=True, text=True, env={'LC_ALL': 'tr_TR.UTF-8'})
				if res_yedek.returncode == 0:
					cikti = self.seri_numaralari_ayikla(res_yedek.stdout)
				else:
					cikti = f"❌ Hata (Kod {res.returncode}):\n{res.stderr}"

		except Exception as e:
			cikti = f"❌ Sistem hatası: {str(e)}"

		GLib.idle_add(self.metni_yaz, cikti)

	def seri_numaralari_ayikla(self, tam_cikti):
		satirlar = tam_cikti.split('\n')
		seri_bilgileri = []
		for satir in satirlar:
			if 'serial:' in satir.lower():
				seri_bilgileri.append(satir.strip())

		if seri_bilgileri:
			# Çıktıda hala <filter> veya <superuser required> var mı kontrol edelim
			temiz_liste = []
			for s in seri_bilgileri:
				if '<superuser required>' in s:
					temiz_liste.append(s.replace('<superuser required>', '[Root Yetkisiyle Bile Erişilemedi]').strip())
				else:
					temiz_liste.append(s)
			
			return "✅ Seri Numarası Bilgileri:\n\n" + '\n'.join(temiz_liste)
		else:
			return "ℹ️ Yönetici yetkisi sağlandı ancak sistem seri numarası raporlamadı.\n\nTam Çıktı:\n" + tam_cikti

	def metni_yaz(self, metin):
		buffer = self.metin_alani.get_buffer()
		buffer.set_text(metin)
		self.spinner.stop()
		return False

if __name__ == "__main__":
	app = InxiSadePanel()
	app.run(None)

En çok hoşuma giden şey de, inix’in GPU sıcaklığı dahil sistemi ciğerlerine kadar inceleyip göstermesi oldu. Bu inix güzelmiş, cidden beğendim. Pardus Donanım Bilgisi uygulamasından çok daha iyi bence.

1 Beğeni
  • İşte Claude birkaç denemeden ve benden aldığı geri bildirimlerden sonra bu sorunu çözmeyi başardı (yukarıda geliştirilmiş kodu kopyaladım).
  • Uygulamaların sudo veya pkexec ile çalışması halinde mevcut kullanıcı ortamından root ortamına geçtiklerinde aynı temayı kullanmaları için bir iki ince ayar yapmak gerekiyor ve ben onları hep yaparım - bu sayede GParted, Synaptic, Timeshift pencereleri normal pencerelerle tıpatıp aynı görünür.
  • Epeydir Python öğrenmek istiyordum fakat olmadı, şimdi de Yapay Zeka galiba buna gerek bırakmıyor (sonuçta böyle küçük amatör işler için istiyordum). Claude ücretsiz kullanıcısı olarak bana çok yardımcı oluyor. Mesela, şimdi de bu kodu çok dilli (başlangıç olarak Türkçe ve İngilizce) kullanıma uygun hale getirmemi sağlıyor. Hazır olunca paylaşırım burada.
2 Beğeni

Ortalığı biraz neşelendirdim:

import gi
import subprocess
import os
import threading
import re

gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, GLib, Pango

class InxiSadePanel(Adw.Application):
	def __init__(self):
		super().__init__(application_id='com.debian.inxi.final')
		self.aktif_buton = None

	def do_activate(self):
		self.win = Adw.ApplicationWindow(application=self)
		self.win.set_title("Sistem Bilgi Merkezi")

		width, height = 1050, 750
		self.win.set_default_size(width, height)

		# CSS ile Koyu Speccy Teması ve Yazı Tipi Ayarı
		style_provider = Gtk.CssProvider()
		css_data = """
			.sidebar {
				background-color: #1e1e1e;
				border-right: 1px solid #111111;
			}
			.sidebar button {
				color: #cccccc;
				margin: 2px 8px;
				padding: 10px;
				border-radius: 4px;
				font-family: 'Liberation Sans', sans-serif;
				font-size: 10pt;
			}
			.sidebar button:hover {
				background-color: #333333;
				color: #ffffff;
			}
			.report-area {
				background-color: #252525;
				color: #e0e0e0;
				/* modify_font yerine CSS font tanımı: */
				font-family: 'Liberation Sans', sans-serif;
				font-size: 11pt;
			}
			.header-label {
				font-weight: bold;
				font-size: 13px;
				color: #555555;
				margin-left: 15px;
				text-transform: uppercase;
				letter-spacing: 1px;
			}
			scrolledwindow {
				background-color: #252525;
			}
		"""
		style_provider.load_from_data(css_data.encode('utf-8'))
		
		Gtk.StyleContext.add_provider_for_display(
			Gdk.Display.get_default(),
			style_provider,
			Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
		)

		ana_kutu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		self.win.set_content(ana_kutu)

		header = Adw.HeaderBar()
		self.spinner = Gtk.Spinner()
		header.pack_end(self.spinner)
		ana_kutu.append(header)

		paned = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		ana_kutu.append(paned)

		# SOL MENÜ
		self.sol_menu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
		self.sol_menu.set_size_request(250, -1)
		self.sol_menu.add_css_class("sidebar")
		paned.append(self.sol_menu)

		lbl_head = Gtk.Label(label="BİLEŞENLER")
		lbl_head.set_margin_top(25)
		lbl_head.set_margin_bottom(15)
		lbl_head.set_xalign(0)
		lbl_head.add_css_class("header-label")
		self.sol_menu.append(lbl_head)

		kategoriler = [
			("🏠 Sistem Özeti", "-b"), ("💻 İşlemci (CPU)", "-C"),
			("🖼️ Ekran Kartı (GPU)", "-G"), ("💾 Bellek (RAM)", "-m"),
			("💽 Disk Bilgisi", "-D"), ("🌐 Ağ Kartları", "-N"),
			("📊 Ses Sistemi", "-A"), ("🌡️ Sıcaklıklar", "-s"),
			("📦 Yazılım Depoları", "-r"), ("🔋 Pil Durumu", "-B"),
			("📄 Tam Rapor", "-F")
		]

		for isim, param in kategoriler:
			btn = Gtk.Button(label=isim)
			btn.set_has_frame(False)
			label = btn.get_child()
			if isinstance(label, Gtk.Label):
				label.set_xalign(0)
				label.set_margin_start(10)

			btn.param = param
			btn.connect('clicked', self.on_button_clicked)
			self.sol_menu.append(btn)

			if param == "-b":
				self.vurgula_butonu(btn)

		spacer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		spacer.set_vexpand(True)
		self.sol_menu.append(spacer)

		btn_serial = Gtk.Button(label="🔑 Seri Numaraları")
		btn_serial.set_has_frame(False)
		btn_serial.set_margin_bottom(20)
		label_s = btn_serial.get_child()
		if isinstance(label_s, Gtk.Label):
			label_s.set_xalign(0)
			label_s.set_margin_start(10)
		btn_serial.connect('clicked', self.on_serial_clicked)
		self.sol_menu.append(btn_serial)

		# SAĞ PANEL
		vbox_sag = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		vbox_sag.set_hexpand(True)
		paned.append(vbox_sag)

		scrolled = Gtk.ScrolledWindow()
		scrolled.set_vexpand(True)
		
		self.metin_alani = Gtk.TextView(editable=False)
		self.metin_alani.add_css_class("report-area")
		self.metin_alani.set_left_margin(40)
		self.metin_alani.set_right_margin(40)
		self.metin_alani.set_top_margin(40)
		self.metin_alani.set_bottom_margin(40)
		self.metin_alani.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
		self.metin_alani.set_cursor_visible(False)
		
		# Bold ve Paragraf etiketleri
		self.buffer = self.metin_alani.get_buffer()
		self.buffer.create_tag("bold", weight=Pango.Weight.BOLD, foreground="#3498db")
		self.buffer.create_tag("paragraf", pixels_below_lines=15)

		scrolled.set_child(self.metin_alani)
		vbox_sag.append(scrolled)

		self.win.present()
		self.islem_baslat("-b")

	def vurgula_butonu(self, buton):
		if self.aktif_buton:
			self.aktif_buton.remove_css_class("suggested-action")
		self.aktif_buton = buton
		self.aktif_buton.add_css_class("suggested-action")

	def on_button_clicked(self, btn):
		self.vurgula_butonu(btn)
		self.islem_baslat(btn.param)

	def on_serial_clicked(self, btn):
		self.vurgula_butonu(btn)
		self.spinner.start()
		thread = threading.Thread(target=self.arkaplan_islem_pkexec)
		thread.daemon = True
		thread.start()

	def islem_baslat(self, param):
		self.spinner.start()
		thread = threading.Thread(target=self.arkaplan_islem, args=(param,))
		thread.daemon = True
		thread.start()

	def arkaplan_islem(self, param):
		env = os.environ.copy()
		env["LC_ALL"] = "tr_TR.UTF-8"
		try:
			res = subprocess.run(['inxi', param, '-c', '0'], capture_output=True, text=True, env=env)
			cikti = res.stdout if res.stdout else "Veri alınamadı."
		except Exception as e:
			cikti = f"Hata: {str(e)}"
		GLib.idle_add(self.metni_formatli_yaz, cikti)

	def arkaplan_islem_pkexec(self):
		try:
			komut = ['pkexec', 'inxi', '-M', '-B', '-D', '-xx', '-c', '0', '-z']
			res = subprocess.run(komut, capture_output=True, text=True, env={'LC_ALL': 'tr_TR.UTF-8'})
			cikti = res.stdout
		except:
			cikti = "İşlem iptal edildi."
		GLib.idle_add(self.metni_formatli_yaz, cikti)

	def metni_formatli_yaz(self, metin):
		"""Metni analiz eder, başlıkları kalınlaştırır ve paragraflar ekler."""
		self.buffer.set_text("")
		
		satirlar = metin.split('\n')
		for satir in satirlar:
			if not satir.strip():
				continue
			
			# Satır başındaki başlıkları yakala (Örn: "CPU:", "Graphics:")
			match = re.match(r"^(\w+):", satir)
			iter = self.buffer.get_end_iter()
			
			if match:
				baslik = match.group(0)
				detay = satir[len(baslik):]
				# Başlığı mavi ve kalın yap, altına boşluk ekle
				self.buffer.insert_with_tags_by_name(iter, "\n" + baslik, "bold", "paragraf")
				iter = self.buffer.get_end_iter()
				self.buffer.insert(iter, detay + "\n")
			else:
				self.buffer.insert(iter, satir + "\n")
				
		self.spinner.stop()
		return False

if __name__ == "__main__":
	app = InxiSadePanel()
	app.run(None)

Sağ panel daha şık görünüyor böyle tabii, fakat sol tarafta seçili/etkin bölümün de vurgu renginde (ve koyu?) olmasında yarar var bence.
Claude ile çalışma sonucu çok dilli destek kazandırmayı da başardım. Bu son hali ile mevcut sistem dilini kullanır, bulamazsa İngilizce gösterir durumda.

import gi
import subprocess
import os
import threading
import gettext
import locale

gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, GLib

# Set up translation
try:
	locale.setlocale(locale.LC_ALL, '')
	lang = locale.getlocale()[0]
	if lang:
		lang = lang.split('_')[0]  # Get 'tr' from 'tr_TR'
except:
	lang = 'en'

# Try multiple locale directories
locale_dirs = [
	'/usr/share/locale',		   # System-wide
	'/usr/local/share/locale',	 # Local installations
	os.path.join(os.path.dirname(__file__), 'locale'),  # Relative to script
]

trans = None
for localedir in locale_dirs:
	try:
		trans = gettext.translation('inxigui', localedir=localedir, languages=[lang])
		break
	except FileNotFoundError:
		continue

if trans:
	_ = trans.gettext
else:
	# Fallback to English (no translation)
	_ = lambda s: s

class InxiSadePanel(Adw.Application):
	def __init__(self):
		super().__init__(application_id='com.debian.inxi.final')
		self.aktif_buton = None

	def do_activate(self):
		self.win = Adw.ApplicationWindow(application=self)
		self.win.set_title(_("System Information Center (inxi GUI)"))

		width, height = 1000, 700

		self.win.set_default_size(width, height)

		ana_kutu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		self.win.set_content(ana_kutu)

		# Üst Araç Çubuğu
		header = Adw.HeaderBar()
		self.spinner = Gtk.Spinner()
		header.pack_end(self.spinner)
		ana_kutu.append(header)

		# İçerik Düzeni
		paned = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		ana_kutu.append(paned)

		# SOL MENÜ
		self.sol_menu = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
		self.sol_menu.set_size_request(240, -1)
		self.sol_menu.set_margin_start(15)
		self.sol_menu.set_margin_end(15)
		self.sol_menu.set_margin_top(15)
		self.sol_menu.set_margin_bottom(15)

		paned.append(self.sol_menu)

		kategoriler = [
			("🏠 " + _("System Summary"), "-b"),
			("💻 " + _("Processor (CPU)"), "-C"),
			("🖼️ " + _("Graphics Card (GPU)"), "-G"),
			("💾 " + _("Memory (RAM)"), "-m"),
			("💽 " + _("Disk Information"), "-D"),
			("🌐 " + _("Network Cards"), "-N"),
			("📊 " + _("Audio System"), "-A"),
			("🌡️ " + _("Temperatures"), "-s"),
			("📦 " + _("Software Repositories"), "-r"),
			("🔋 " + _("Battery Status"), "-B"),
			("📄 " + _("Full Report"), "-F")
		]

		for isim, param in kategoriler:
			# Butonu oluştur
			btn = Gtk.Button(label=isim)
			btn.add_css_class("pill") # Yuvarlak stil

			# Metni sola yasla
			label = btn.get_child()
			if isinstance(label, Gtk.Label):
				label.set_xalign(0)
				label.set_margin_start(10)

			btn.param = param
			btn.connect('clicked', self.on_button_clicked)
			self.sol_menu.append(btn)

			# Başlangıçta "Sistem Özeti" butonunu seçili yap
			if param == "-b":
				self.vurgula_butonu(btn)

		# Ayırıcı ekle
		separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
		separator.set_margin_top(5)
		separator.set_margin_bottom(5)
		self.sol_menu.append(separator)

		# Seri Numaraları butonu (sudo gerektirir)
		btn_serial = Gtk.Button(label="🔑 " + _("Serial Numbers"))
		btn_serial.add_css_class("pill")

		label = btn_serial.get_child()
		if isinstance(label, Gtk.Label):
			label.set_xalign(0)
			label.set_margin_start(10)

		btn_serial.param = "serial"
		btn_serial.connect('clicked', self.on_serial_clicked)
		self.sol_menu.append(btn_serial)

		# SAĞ PANEL (Metin Çıktısı)
		vbox_sag = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		vbox_sag.set_hexpand(True)
		vbox_sag.set_margin_top(15)
		vbox_sag.set_margin_bottom(15)
		vbox_sag.set_margin_end(15)
		paned.append(vbox_sag)

		scrolled = Gtk.ScrolledWindow()
		scrolled.set_vexpand(True)
		scrolled.set_has_frame(True)

		self.metin_alani = Gtk.TextView(editable=False, monospace=True)
		self.metin_alani.set_left_margin(12)
		self.metin_alani.set_top_margin(12)
		self.metin_alani.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) # Uzun satırları böl

		scrolled.set_child(self.metin_alani)
		vbox_sag.append(scrolled)

		self.win.present()
		self.islem_baslat("-b")

	def vurgula_butonu(self, buton):
		"""Tıklanan butonu mavi yapar ve eski butonu normale döndürür."""
		if self.aktif_buton:
			# Önceki butonun vurgusunu kaldır
			self.aktif_buton.remove_css_class("suggested-action")

		# Yeni butonu vurgula
		self.aktif_buton = buton
		self.aktif_buton.add_css_class("suggested-action")

	def on_button_clicked(self, btn):
		self.vurgula_butonu(btn)
		self.islem_baslat(btn.param)

	def on_serial_clicked(self, btn):
		self.vurgula_butonu(btn)
		# Şifre dialogu göster
		self.sifre_iste()

	def sifre_iste(self):
		"""Kullanıcıdan sudo şifresi isteyip doğrulayan dialog"""
		dialog = Adw.MessageDialog.new(self.win)
		dialog.set_heading(_("Administrator Access Required"))
		dialog.set_body(_("Enter your sudo password to view serial numbers:"))

		# Şifre girişi için entry
		entry = Gtk.PasswordEntry()
		entry.set_show_peek_icon(True)
		entry.set_margin_top(10)
		entry.set_margin_bottom(10)
		entry.set_margin_start(10)
		entry.set_margin_end(10)

		# Entry'yi dialog'a ekle
		dialog.set_extra_child(entry)

		dialog.add_response("cancel", _("Cancel"))
		dialog.add_response("ok", _("OK"))
		dialog.set_response_appearance("ok", Adw.ResponseAppearance.SUGGESTED)
		dialog.set_default_response("ok")
		dialog.set_close_response("cancel")

		# Enter tuşu ile onaylama
		entry.connect("activate", lambda e: dialog.response("ok"))

		dialog.connect("response", self.sifre_dialog_cevap, entry)
		dialog.present()

	def sifre_dialog_cevap(self, dialog, response, entry):
		"""Şifre dialogundan gelen cevabı işle"""
		if response == "ok":
			sifre = entry.get_text()
			if sifre:
				self.spinner.start()
				thread = threading.Thread(target=self.arkaplan_islem_sudo, args=(sifre,))
				thread.daemon = True
				thread.start()
		dialog.close()

	def islem_baslat(self, param):
		self.spinner.start()

		thread = threading.Thread(target=self.arkaplan_islem, args=(param,))
		thread.daemon = True
		thread.start()

	def arkaplan_islem(self, param):
		# Türkçe karakter desteği ve renksiz çıktı (-c 0)
		env = os.environ.copy()
		env["LC_ALL"] = "tr_TR.UTF-8"
		env["LANG"] = "tr_TR.UTF-8"
		try:
			res = subprocess.run(['inxi', param, '-c', '0'], capture_output=True, text=True, env=env)
			cikti = res.stdout if res.stdout else _("No data found or this hardware is not available.")
		except Exception as e:
			cikti = _("Error: Is inxi installed?") + f"\n{_('Details')}: {str(e)}"

		GLib.idle_add(self.metni_yaz, cikti)

	def arkaplan_islem_sudo(self, sifre):
		"""Sudo ile inxi'yi çalıştır ve seri numaralarını ayıkla"""

		try:
			# Önce sudo -k ile timestamp'i sıfırla
			subprocess.run(['sudo', '-k'], check=False)

			# script komutu ile pseudo-terminal oluştur
			# Bu inxi'nin interactive mode'da çalıştığını düşünmesini sağlar
			process = subprocess.Popen(
				['sudo', '-S', '-p', '', 'script', '-qec',
				 'inxi -M -B -c 0', '/dev/null'],
				stdin=subprocess.PIPE,
				stdout=subprocess.PIPE,
				stderr=subprocess.PIPE,
				text=True,
				env={'LC_ALL': 'tr_TR.UTF-8', 'LANG': 'tr_TR.UTF-8'}
			)

			# Şifreyi gönder
			stdout, stderr = process.communicate(input=sifre + '\n', timeout=10)

			if process.returncode != 0:
				if 'incorrect password' in stderr.lower() or 'sorry' in stderr.lower():
					cikti = "❌ " + _("Incorrect password entered.")
				else:
					cikti = "❌ " + _("An error occurred:") + f"\n{stderr}"
			else:
				# Sadece seri numaralarını ayıkla
				cikti = self.seri_numaralari_ayikla(stdout)

		except subprocess.TimeoutExpired:
			process.kill()
			cikti = "❌ " + _("Operation timed out.")
		except Exception as e:
			cikti = "❌ " + _("Error:") + f" {str(e)}"

		GLib.idle_add(self.metni_yaz, cikti)

	def seri_numaralari_ayikla(self, tam_cikti):
		"""inxi çıktısından sadece gerçek seri numaralarını ayıklayan fonksiyon"""
		satirlar = tam_cikti.split('\n')
		seri_bilgileri = []

		for satir in satirlar:
			# "serial:" içeren ve <filter> OLMAYAN satırları bul
			if 'serial:' in satir.lower() and '<filter>' not in satir.lower():
				seri_bilgileri.append(satir.strip())

		if seri_bilgileri:
			return '\n'.join(seri_bilgileri)
		else:
			return "ℹ️  " + _("No displayable serial numbers found on this system.")

	def metni_yaz(self, metin):
		buffer = self.metin_alani.get_buffer()
		buffer.set_text(metin)
		self.spinner.stop()
		return False

if __name__ == "__main__":
	app = InxiSadePanel()
	app.run(None)

Bu durumda Türkçe dil desteği için ayrı bir dosya da oluşturuldu (msgfmt inxigui.po -o inxigui.mo komutuyla derlenip /usr/share/locale/tr/LC_MESSAGES/inxigui.mo konumuna konması yeterli oluyor.

# Turkish translation for inxigui
# Copyright (C) 2025
# This file is distributed under the same license as the inxigui package.
#
msgid ""
msgstr ""
"Project-Id-Version: inxigui 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-18 12:00+0000\n"
"PO-Revision-Date: 2025-01-18 12:00+0300\n"
"Last-Translator: \n"
"Language-Team: Turkish\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "System Information Center (inxi GUI)"
msgstr "Sistem Bilgi Merkezi (inxi grafik arayüzü)"

msgid "System Summary"
msgstr "Sistem Özeti"

msgid "Processor (CPU)"
msgstr "İşlemci (CPU)"

msgid "Graphics Card (GPU)"
msgstr "Ekran Kartı (GPU)"

msgid "Memory (RAM)"
msgstr "Bellek (RAM)"

msgid "Disk Information"
msgstr "Disk Bilgisi"

msgid "Network Cards"
msgstr "Ağ Kartları"

msgid "Audio System"
msgstr "Ses Sistemi"

msgid "Temperatures"
msgstr "Sıcaklıklar"

msgid "Software Repositories"
msgstr "Yazılım Depoları"

msgid "Battery Status"
msgstr "Pil Durumu"

msgid "Full Report"
msgstr "Tam Rapor"

msgid "Serial Numbers"
msgstr "Seri Numaraları"

msgid "Administrator Access Required"
msgstr "Yönetici Yetkisi Gerekli"

msgid "Enter your sudo password to view serial numbers:"
msgstr "Seri numaralarını görmek için sudo şifrenizi girin:"

msgid "Cancel"
msgstr "İptal"

msgid "OK"
msgstr "Tamam"

msgid "No data found or this hardware is not available."
msgstr "Veri bulunamadı veya bu donanım mevcut değil."

msgid "Error: Is inxi installed?"
msgstr "Hata: inxi yüklü mü?"

msgid "Details"
msgstr "Detay"

msgid "Incorrect password entered."
msgstr "Hatalı şifre girdiniz."

msgid "An error occurred:"
msgstr "Hata oluştu:"

msgid "Operation timed out."
msgstr "İşlem zaman aşımına uğradı."

msgid "Error:"
msgstr "Hata:"

msgid "No displayable serial numbers found on this system."
msgstr "Bu sistemde görüntülenebilir seri numarası bulunamadı."
1 Beğeni