Windowsta yeni bir program kurduğumuzda kurmadan önce dosya adlarını taratıp
kurduktan sonra karşılaştırma yapabildiğimiz program vardı adını bulamadığımdan yazamadım.
Linux ve debian için benzer program var mı biliyor musunuz?
Windowsta yeni bir program kurduğumuzda kurmadan önce dosya adlarını taratıp
kurduktan sonra karşılaştırma yapabildiğimiz program vardı adını bulamadığımdan yazamadım.
Linux ve debian için benzer program var mı biliyor musunuz?
Hocam bahsettiğiniz türden bir yazılımı ilk defa duydum ve açıkçası tam anlamadım. YZ’ye Murat Taşkıran böyle soruyor (
) nedir bu anladın mı? dedim, şunları sıraladı:
Böyle birşey mi lazım linux’da size? Nereye ne kuruyor, kurduktan sonra sistemde ne değişiklik oluyor, öncesi-sonrası raporu yapacak bir program… Böyle mi?
Anlamadıysam tarif edin lütfen. Belki biraz çabayla bir alternatif üretebiliriz, kim bilir?..
Adımı söylemesen iyiymiş
Şaka bi yana doğru anlamış
ingilizce olarak bakıyorum ben de
yapmam gereken bi taşıma işlemi var hangi dosyaların oluştuğunu ve değiştiğini görüp kopyalayıp bi deneme yapayım diyorum
kapsamlı kaldırma için iyi oluyor winde bazı atık dosya bırakıyor programlar etiketlemek için
deneme versiyonlarını hep denemek için de kullanılabilir
Hocam sorularımı arttırdım ve sohbetin devamı şurada:
(https://gemini.google.com/share/223bcd4830f7)
İleride (muhtemelen yarın) bu kaynağı google hesabımdan silerim, kırık link oluşursa forumun SEO’suna zarar vermesin diye kod etiketi ile veriyorum (kırılırsa google farkedemez).
Ama google hesabınız varsa bu linki alıp sohbete devam et derseniz, sanki siz başlatmışsınız gibi sizin YZ sohbet geçmişinize geçer ve oradan devam edebilirsiniz.
Google hesabınız yoksa, kayda geçmeden sohbeti sürdürebilirs… öyle oluyor muydu?.. Onu tam bilmiyorum.
Yani bu linkte daha fazla programdan bahsetti bilginiz olsun.
Revo uninstaller’in portable versiyonu full gibi çalışıyor ve işini iyi yapıyor bilginiz olsun. Bereket versin linux tarafında çok fazla çöp oluşmuyor ve Pardus Paket Kurucu, kaldırma sonrasında tüm klasör ve dosyaları siliyor (denendi). Üstelik postuninst betiği oluşturmasam bile…
Gemini Snapper var dedi depodan kurdum gui olmadan arayüz açılmadı onu da kurdum deneyeyim bakayım
Timeshift de de varmış karşılaştırma o kadar kullandım görmedim
Snapper-gui nin bağımlılıkları varmış sayfasındaki ubuntu için olan bağımlılıkları da kurdum (synaptic kurmuyor) sonra snapper i kurdum hata vermedi ama istediğim gibi değil
timeshift te de karşılaştırma yok
sorunu gemini ye anlattım çözüm yollarını yazdı not aldım tahmin ettiğim dosyalar çıktı sistemi taşımam gerekirse işe yarar gibi.
Olası tüm sistem dizinlerinin içindekilerin listesini yapıp isteğe bağlı olarak bir önceki ile farklılıkları gösteren basit bir betik bu işi görmez mi?
Konuyu en başından ele almak istiyorum. Çünkü şu cümleyle aklıma birşey getirdiniz:
Yeni bir sistem kurarsam, şu anda kurulu programları tek komutla nasıl bir seferde kurabilirim gibi birşey sormuştum. Bana dedi ki aha şu komutu gir, çıktı dosyası oluşacak, yeni kurulum yaptığında da şu komutla o çıktı txt dosyasını göstert, tüm programların tek seferde kurulacak. Komutu verdim, şöyle bir çıktı dosyası oluştu:
accountsservice install
acl install
adduser install
adwaita-icon-theme install
alsa-topology-conf install
alsa-ucm-conf install
alsa-utils install
amd64-microcode install
ant install
ant-optional install
anydesk install
apparmor install
apt install
apt-file install
apt-listchanges install
apt-utils install
aspell install
aspell-en install
at-spi2-common install
at-spi2-core install
atmel-firmware install
audacious install
audacious-plugins:amd64 install
audacious-plugins-data install
audacity install
audacity-data install
avahi-daemon install
avahi-utils install
b43-fwcutter install
baobab install
base-files install
base-passwd install
bash install
bash-completion install
bc install
bind9-dnsutils install
bind9-host install
bind9-libs:amd64 install
binutils install
binutils-common:amd64 install
binutils-x86-64-linux-gnu install
blackbird-gtk-theme install
blt install
bluebird-gtk-theme install
blueman install
bluez install
bluez-firmware install
bluez-obexd install
bogofilter deinstall
bogofilter-bdb deinstall
bogofilter-common deinstall
brasero-common install
breeze install
breeze-cursor-theme install
breeze-gtk-theme install
breeze-icon-theme install
bsdextrautils install
bsdutils install
Devamı var ama sığmadı...
Devamı var ama sığmadı…
İkinci komutla da bu liste otomatik kurulacakmış öyle demişti. O konuşmadan sonra yüzlerce sohbet başlığı oluştuğundan şimdi nereye gitti o sohbet bilmiyorum.
Amacınız kurulum sonrası eski düzene dönmekse bu işe yarayabilir.
Yok, sadece yazılımların ne yaptığını incelemek istiyorum ise,
En mantıklı çözüm.
Yine de biraz uğraşarak bir install.deb yapılabilir.
Şu anda hala düşünüyorum… ![]()
Hangi yoldaki hangi dizine ne olmuş? Hangi dizinler oluşmuş? Hangi config dosyasının boyutu artmış? Rahatça görürsün.
Basit aslında. 1-2 gün uğraşmayla hallederim gibi geliyor ama elimde 2-3 proje var ve buraya ara vermek için giriyorum. Söz veremem.
Yine ben ![]()
Şöyle birşey yaptım @Murat_Taskiran :
Sağda gördüğün YENİ, SİLİNEN ve DEĞİŞEN işaretlilerden silinenleri ve yenileri google chrome yapıyor. Değişenler de, yine google chrome ve ferdium tarafından oluşturuluyor. Hiçbirşey yapmasam bile birkaç saniyede bir değişiklikler oluyor, yani bunlar açıkken ha bire birşey yazıp siliyorlar.
Bu program ne yapıyor: Kendisine belirtilmiş sistem dizinlerini tarayarak değişiklikleri kaydediyor. Yeni klasör var mı? Yeni dosya var mı? Var olan dosyalarda boyut değişikliği olmuş mu? Silinen var mı?
Soldaki panelde taranan dizinler var. Bunları koda müdahale ederek arttırabilirsin.
İlk tarama işleminde “/home/kullanıcı_adı/” dizininde compare.json dosyası oluşur. Böylece program kapatılıp açıldığında son tarama durumunu gösterir ve 2. butonla karşılaştırma yapabilirsin.
The source code:
import sys
import os
import json
import hashlib
from pathlib import Path
from datetime import datetime
import time # time modülü mtime'ı işlemek için gerekli
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QTextEdit, QTreeWidget, QTreeWidgetItem, QSplitter,
QLabel, QSizePolicy, QMessageBox, QHeaderView, QProgressBar, QFileDialog
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
# --- 1. Sabitler ve Yardımcı Fonksiyonlar ---
# Kontrol edilecek yolların listesi
DIRS_TO_CHECK = [
"/etc",
"/usr/bin",
"/usr/share",
"/usr/local/bin",
"~/.config",
"~/.local",
"/opt"
]
# Snapshot dosyasının kaydedileceği yol: /home/kullanıcı_adı/compare.json
SNAPSHOT_FILE = str(Path.home() / "compare.json")
# Belirtilen config uzantıları
CONFIG_EXTENSIONS = ['.conf', '.ini', '.json', '.yaml', '.xml', '.cfg']
def get_file_hash(filepath, block_size=65536):
"""Verilen dosyanın SHA256 özetini (hash) hesaplar."""
try:
hasher = hashlib.sha256()
with open(filepath, 'rb') as f:
while True:
buf = f.read(block_size)
if not buf:
break
hasher.update(buf)
return hasher.hexdigest()
except Exception:
return None
def take_snapshot(root_dirs):
"""
Belirtilen kök dizinleri özyinelemeli olarak tarar ve meta verileri toplar.
"""
snapshot_data = {}
for root_dir_str in root_dirs:
root_dir = Path(root_dir_str).expanduser()
if not root_dir.exists():
continue
for entry in root_dir.rglob('*'):
try:
if entry.is_symlink():
continue
stats = entry.stat()
data = {
"type": "dir" if entry.is_dir() else "file",
"size": stats.st_size,
"mtime": stats.st_mtime,
}
if entry.is_file() and any(entry.name.endswith(ext) for ext in CONFIG_EXTENSIONS):
data["hash"] = get_file_hash(entry)
snapshot_data[str(entry)] = data
except Exception:
continue
return snapshot_data
def compare_snapshots(snap1, snap2):
"""İki snapshot verisini karşılaştırır ve farkları döndürür."""
differences = {
"added": {},
"deleted": {},
"modified": {}
}
# 1. Silinenleri ve Değiştirilenleri bul (snap1'i temel alarak)
for path, data1 in snap1.items():
if path not in snap2:
differences["deleted"][path] = data1
else:
data2 = snap2[path]
is_modified = False
if data1["type"] == "file" and data2["type"] == "file":
if data1["size"] != data2["size"]:
is_modified = True
elif "hash" in data1 and data1.get("hash") != data2.get("hash"):
is_modified = True
if is_modified:
differences["modified"][path] = {"before": data1, "after": data2}
# 2. Eklenenleri bul (snap2'yi temel alarak)
for path, data2 in snap2.items():
if path not in snap1:
differences["added"][path] = data2
return differences
# --- 2. Arka Plan Çalışanı ---
class WorkerThread(QThread):
snapshot_finished = pyqtSignal(dict)
error_occurred = pyqtSignal(str)
def __init__(self, dirs_to_scan):
super().__init__()
self.dirs_to_scan = dirs_to_scan
def run(self):
try:
snapshot = take_snapshot(self.dirs_to_scan)
self.snapshot_finished.emit(snapshot)
except Exception as e:
self.error_occurred.emit(f"Snapshot sırasında hata oluştu: {e}")
# --- 3. PyQt5 Ana Pencere ---
class SystemMonitor(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Sistem Durumu İzleyici")
# Kullanıcının güncellediği pencere boyutları korunmuştur.
self.setGeometry(100, 100, 800, 500)
self.snapshot_before = None
self.snapshot_time = None
self.differences = None
self._setup_ui()
self._setup_worker()
# Program başlangıcında daha önce kaydedilmiş snapshot'ı yükle
self.load_initial_snapshot()
def _setup_ui(self):
central_widget = QWidget()
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(5)
main_layout.setContentsMargins(10, 10, 10, 10)
# 1. Butonlar için Yatay Düzen (En üst satır)
button_layout = QHBoxLayout()
self.snapshot_button = QPushButton("1. Başlangıç Snapshot'ı Al")
self.snapshot_button.clicked.connect(self.take_snapshot_before)
button_layout.addWidget(self.snapshot_button)
self.compare_button = QPushButton("2. Durum Karşılaştır")
self.compare_button.clicked.connect(self.compare_snapshots_after)
self.compare_button.setEnabled(False)
button_layout.addWidget(self.compare_button)
# 3. TXT Dosyasına Yazdır Butonu
self.save_button = QPushButton("3. TXT Dosyasına Yazdır")
self.save_button.clicked.connect(self.save_to_txt)
self.save_button.setEnabled(False)
button_layout.addWidget(self.save_button)
main_layout.addLayout(button_layout)
# 2. Durum Etiketi (Ayrı Satır)
self.status_label = QLabel("Durum: Başlangıç")
self.status_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
main_layout.addWidget(self.status_label)
# 3. İlerleme Çubuğu (Ayrı Satır, Tam Genişlik)
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 0)
self.progress_bar.setVisible(False)
self.progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
main_layout.addWidget(self.progress_bar)
# Splitter (Sol ve Sağ Paneller) - Yatay Bölücü
splitter = QSplitter(Qt.Horizontal)
splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# Sol Panel (Önceki Durum Bilgisi)
left_panel = QWidget()
left_layout = QVBoxLayout(left_panel)
left_layout.setContentsMargins(0, 0, 0, 0)
left_layout.setSpacing(5)
left_layout.addWidget(QLabel("Başlangıç Durumu"))
self.before_info = QTextEdit()
self.before_info.setReadOnly(True)
self.before_info.setPlainText("Başlangıç Snapshot'ı Alınmadı.")
left_layout.addWidget(self.before_info)
# Sağ Panel (Karşılaştırma Sonucu)
right_panel = QWidget()
right_layout = QVBoxLayout(right_panel)
right_layout.setContentsMargins(0, 0, 0, 0)
right_layout.setSpacing(5)
right_layout.addWidget(QLabel("Karşılaştırma Sonuçları (Farklar)"))
self.comparison_tree = QTreeWidget()
self.comparison_tree.setColumnCount(3)
self.comparison_tree.setHeaderLabels(["Tür", "Yol", "Açıklama"])
self.comparison_tree.header().setStretchLastSection(True)
# Başlıkları elle ayarlanabilir yapma
self.comparison_tree.header().setSectionResizeMode(0, QHeaderView.Interactive)
self.comparison_tree.header().setSectionResizeMode(1, QHeaderView.Interactive)
self.comparison_tree.header().setSectionResizeMode(2, QHeaderView.Interactive)
self.comparison_tree.setColumnWidth(0, 150)
right_layout.addWidget(self.comparison_tree)
splitter.addWidget(left_panel)
splitter.addWidget(right_panel)
# Kullanıcının güncellediği splitter boyutları korunmuştur.
splitter.setSizes([400, 750])
main_layout.addWidget(splitter)
self.setCentralWidget(central_widget)
def _setup_worker(self):
self.worker_thread = None
# --- YENİ: Başlangıçta Kayıtlı Snapshot Yükleme İşlevi ---
def load_initial_snapshot(self):
snapshot_path = Path(SNAPSHOT_FILE)
if snapshot_path.exists():
try:
with open(snapshot_path, 'r') as f:
self.snapshot_before = json.load(f)
# Dosyanın son değiştirilme zamanını (modification time) kullan
mtime = snapshot_path.stat().st_mtime
self.snapshot_time = datetime.fromtimestamp(mtime)
info_text = (
f"***Önceki Snapshot Dosyadan Yüklendi!***\n"
f"Yükleme Tarihi: {self.snapshot_time.strftime('%Y-%m-%d %H:%M:%S')}\n"
f"Toplam Öğe Sayısı: {len(self.snapshot_before)}\n"
f"JSON Dosyası: {SNAPSHOT_FILE}\n\n"
f"Taranan Dizinler:\n"
+ "\n".join(DIRS_TO_CHECK)
)
self.before_info.setText(info_text)
self.compare_button.setEnabled(True)
self.status_label.setText(f"Durum: Eski snapshot ({snapshot_path.name}) başarıyla yüklendi. 'Durum Karşılaştır'a basılabilir.")
except Exception as e:
self.snapshot_before = None
self.status_label.setText(f"Durum: HATA - Önceki snapshot yüklenemedi: {e}")
QMessageBox.warning(self, "Yükleme Hatası", f"Kaydedilmiş snapshot dosyası yüklenemedi:\n{e}")
# --- Güncellendi: Snapshot Alma İşlevi (Uyarı Eklendi) ---
def take_snapshot_before(self):
# Eğer önceden bir snapshot varsa (dosyadan yüklense de, bu oturumda alınsa da) uyarı ver.
if self.snapshot_before is not None:
reply = QMessageBox.question(self,
'Onay Gerekli',
"Daha önce alınmış bir snapshot kaydı mevcut. Yeni bir snapshot almak, eski kaydı silip (üzerine yazıp) güncelleyecek.\n\nDevam etmek istiyor musunuz?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.No:
self.status_label.setText("Durum: Yeni snapshot alma işlemi kullanıcı tarafından iptal edildi.")
return
self.status_label.setText("Durum: Başlangıç snapshot'ı alınıyor... Lütfen bekleyin.")
self._set_ui_busy(True)
if self.worker_thread:
self.worker_thread.quit()
self.worker_thread.wait()
self.worker_thread = WorkerThread(DIRS_TO_CHECK)
self.worker_thread.snapshot_finished.connect(self._handle_before_snapshot_result)
self.worker_thread.error_occurred.connect(self._handle_error)
self.worker_thread.start()
def _handle_before_snapshot_result(self, snapshot_data):
self._set_ui_busy(False)
self.snapshot_before = snapshot_data
self.snapshot_time = datetime.now()
try:
# Snapshot dosyası, SNAPSHOT_FILE sabitinde tanımlanan yeni yola kaydediliyor.
with open(SNAPSHOT_FILE, 'w') as f:
json.dump(self.snapshot_before, f, indent=4)
info_text = (
f"***Başlangıç Snapshot'ı Alındı!***\n"
f"Tarih/Saat: {self.snapshot_time.strftime('%Y-%m-%d %H:%M:%S')}\n"
f"Toplam Öğe Sayısı: {len(self.snapshot_before)}\n"
f"JSON Dosyası: {SNAPSHOT_FILE}\n\n" # Kayıt yolunu da gösteriyoruz
f"Taranan Dizinler:\n"
+ "\n".join(DIRS_TO_CHECK)
)
self.before_info.setText(info_text)
self.status_label.setText("Durum: Snapshot başarılı. Şimdi bir program kurup 'Durum Karşılaştır'a basın.")
self.compare_button.setEnabled(True)
except Exception as e:
self._set_ui_busy(False)
self._handle_error(f"Snapshot kaydedilirken hata oluştu: {e}")
def compare_snapshots_after(self):
if not self.snapshot_before:
QMessageBox.warning(self, "Uyarı", "Önce 'Başlangıç Snapshot'ı Al' düğmesine basmalısınız.")
return
self.status_label.setText("Durum: Yeni snapshot alınıyor ve karşılaştırma yapılıyor... Lütfen bekleyin.")
self._set_ui_busy(True)
self.worker_thread = WorkerThread(DIRS_TO_CHECK)
self.worker_thread.snapshot_finished.connect(self._handle_compare_snapshot_result)
self.worker_thread.error_occurred.connect(self._handle_error)
self.worker_thread.start()
def _handle_compare_snapshot_result(self, snapshot_after):
self._set_ui_busy(False)
try:
self.differences = compare_snapshots(self.snapshot_before, snapshot_after)
self.display_differences(self.differences)
total_diff_count = len(self.differences['added']) + len(self.differences['deleted']) + len(self.differences['modified'])
self.status_label.setText(f"Durum: Karşılaştırma tamamlandı. Fark sayısı: {total_diff_count}. Sonuçlar sağ panelde.")
self.save_button.setEnabled(True)
except Exception as e:
self._set_ui_busy(False)
self._handle_error(f"Karşılaştırma sırasında beklenmeyen hata oluştu: {e}")
# --- TXT Kaydetme İşlevi ---
def _format_differences_for_txt(self):
"""Karşılaştırma sonuçlarını TXT dosyası için formatlar."""
# Başlık ve Tarih Bilgisi
content = f"### Sistem Değişiklik Raporu ###\n"
content += f"Rapor Tarihi: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
content += f"Başlangıç Snapshot Tarihi: {self.snapshot_time.strftime('%Y-%m-%d %H:%M:%S')}\n"
content += "\n"
# Taranan Dizinler (En üstte)
content += "--- TARANAN DİZİNLER ---\n"
for d in DIRS_TO_CHECK:
content += f"- {d}\n"
content += "\n"
# Değişiklikler
# 1. Eklenenler
added = self.differences.get('added', {})
content += f"--- 1. YENİ EKLENEN ÖĞELER ({len(added)}) ---\n"
if added:
for path, data in added.items():
content += f"[YENİ] Yol: {path}, Tip: {data['type'].capitalize()}, Boyut: {data['size']} bayt\n"
else:
content += "Yeni öğe bulunamadı.\n"
content += "\n"
# 2. Silinenler
deleted = self.differences.get('deleted', {})
content += f"--- 2. SİLİNEN ÖĞELER ({len(deleted)}) ---\n"
if deleted:
for path, data in deleted.items():
content += f"[SİLİNDİ] Yol: {path}, Tip: {data['type'].capitalize()}, Boyut: {data['size']} bayt\n"
else:
content += "Silinen öğe bulunamadı.\n"
content += "\n"
# 3. Değiştirilenler
modified = self.differences.get('modified', {})
content += f"--- 3. DEĞİŞTİRİLEN ÖĞELER ({len(modified)}) ---\n"
if modified:
for path, data_pair in modified.items():
data1 = data_pair["before"]
data2 = data_pair["after"]
detail = []
if data1["size"] != data2["size"]:
size_diff = data2["size"] - data1["size"]
detail.append(f"Boyut Farkı: {size_diff} bayt ({data1['size']} -> {data2['size']})")
if "hash" in data1 and data1.get("hash") != data2.get("hash"):
detail.append("İçerik (Hash) Değişti")
content += f"[DEĞİŞTİ] Yol: {path}, Detay: {', '.join(detail)}\n"
else:
content += "Değiştirilen öğe bulunamadı.\n"
return content
def save_to_txt(self):
"""Sonuçları compare.txt dosyasına kaydeder."""
if self.differences is None:
QMessageBox.warning(self, "Uyarı", "Lütfen önce bir karşılaştırma yapın.")
return
# Kullanıcıdan kaydetme yolu al
filename, _ = QFileDialog.getSaveFileName(self,
"Karşılaştırma Sonuçlarını Kaydet",
"compare.txt",
"Metin Dosyaları (*.txt);;Tüm Dosyalar (*)")
if not filename:
return
# Dosya içeriğini oluştur
content = self._format_differences_for_txt()
# Windows satır sonu standardı ('\r\n') ile kaydet
try:
with open(filename, 'w', encoding='utf-8', newline='\r\n') as f:
f.write(content)
self.status_label.setText(f"Durum: Sonuçlar başarıyla kaydedildi: {filename}")
QMessageBox.information(self, "Başarılı", f"Karşılaştırma sonuçları başarıyla kaydedildi:\n{filename}")
except Exception as e:
self._handle_error(f"Dosya kaydedilirken hata oluştu: {e}")
# -----------------------------------
def _set_ui_busy(self, is_busy):
"""UI meşgul durumunu yönetir."""
self.snapshot_button.setEnabled(not is_busy)
self.compare_button.setEnabled(not is_busy and self.snapshot_before is not None)
self.save_button.setEnabled(not is_busy and self.differences is not None)
self.progress_bar.setVisible(is_busy)
def _handle_error(self, message):
"""Hata mesajlarını kullanıcıya gösterir ve GUI durumunu düzeltir."""
self._set_ui_busy(False)
self.status_label.setText(f"Durum: HATA - {message}")
QMessageBox.critical(self, "Hata", message)
# --- Farkları Görüntüleme ---
def display_differences(self, differences):
"""Farkları QTreeWidget içinde görüntüler."""
self.comparison_tree.clear()
# 1. Eklenenler (Added)
added_root = QTreeWidgetItem(self.comparison_tree, [f"YENİ ({len(differences['added'])})", "", ""])
added_root.setBackground(0, Qt.darkGreen)
added_root.setForeground(0, Qt.white)
for path, data in differences['added'].items():
detail = f"{data['type'].capitalize()}, Boyut: {data['size']} bayt"
QTreeWidgetItem(added_root, ["Yeni", path, detail])
# 2. Silinenler (Deleted)
deleted_root = QTreeWidgetItem(self.comparison_tree, [f"SİLİNEN ({len(differences['deleted'])})", "", ""])
deleted_root.setBackground(0, Qt.darkRed)
deleted_root.setForeground(0, Qt.white)
for path, data in differences['deleted'].items():
detail = f"{data['type'].capitalize()}, Boyut: {data['size']} bayt"
QTreeWidgetItem(deleted_root, ["Kaldırıldı", path, detail])
# 3. Değiştirilenler (Modified)
modified_root = QTreeWidgetItem(self.comparison_tree, [f"DEĞİŞEN ({len(differences['modified'])})", "", ""])
modified_root.setBackground(0, Qt.darkYellow)
modified_root.setForeground(0, Qt.black)
for path, data_pair in differences['modified'].items():
data1 = data_pair["before"]
data2 = data_pair["after"]
detail = ""
if data1["size"] != data2["size"]:
size_diff = data2["size"] - data1["size"]
detail += f"Boyut Farkı: {size_diff} bayt ({data1['size']} -> {data2['size']})"
if "hash" in data1 and data1.get("hash") != data2.get("hash"):
if detail: detail += " | "
detail += "İçerik (Hash) Değişti"
QTreeWidgetItem(modified_root, ["Değişti", path, detail])
self.comparison_tree.expandAll()
# --- 4. Programı Çalıştırma ---
if __name__ == '__main__':
final_dirs = [str(Path(d).expanduser()) for d in DIRS_TO_CHECK]
app = QApplication(sys.argv)
window = SystemMonitor()
window.show()
sys.exit(app.exec_())
Metni alıp örneğin compare.py adıyla bir yere kaydet.
Ardından o dizinde sağtık–>burada terminal aç de, şunu verip entırla:
python3 compare.py
Bu kadar.
Duruma göre ilerde geliştirebilirim de, belli olmaz. Şu haliyle çöp yazılım ama iyi bir raporlayıcı olabilir.
Harika!
Belki başka bazı klasörler de ekleyip ve ~/.config klasöründeki google-chrome vb. bazı yerleri tarama dışında tutmak gibi küçük bazı kolay değişiklikler yaparak kullanabilirim bunu şahsen.
Teşekkürler! ![]()
Aaaa doğru ya! ![]()
Zaman bulabilirsem onunla da biraz uğaşayım.
Yalnız bir sıkıntısı var; json dosyası çok büyük. Şu an elimdeki 35,6MB olmuş ve binlerce satırdan oluşuyor.
EDİT:
Özellik eklemesi yapıldı ve test edildi. Düzgün çalışıyor.
import sys
import os
import json
import hashlib
from pathlib import Path
from datetime import datetime
import time
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QTextEdit, QTreeWidget, QTreeWidgetItem, QSplitter,
QLabel, QSizePolicy, QMessageBox, QHeaderView, QProgressBar, QFileDialog,
QDialog, QLineEdit, QListWidget, QListWidgetItem
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDir # QDir eklendi
# --- 1. Sabitler ve Yardımcı Fonksiyonlar ---
# ... (Diğer sabitler ve fonksiyonlar aynı kalmıştır)
DIRS_TO_CHECK = [
"/etc",
"/usr/bin",
"/usr/share",
"/usr/local/bin",
"~/.config",
"~/.local",
"/opt"
]
SNAPSHOT_FILE = str(Path.home() / "compare.json")
EXCLUSION_FILE = Path.home() / ".config" / "system_monitor_exclude.json"
EXCLUSION_FILE.parent.mkdir(parents=True, exist_ok=True)
CONFIG_EXTENSIONS = ['.conf', '.ini', '.json', '.yaml', '.xml', '.cfg']
def get_file_hash(filepath, block_size=65536):
"""Verilen dosyanın SHA256 özetini (hash) hesaplar."""
try:
hasher = hashlib.sha256()
with open(filepath, 'rb') as f:
while True:
buf = f.read(block_size)
if not buf:
break
hasher.update(buf)
return hasher.hexdigest()
except Exception:
return None
def is_excluded(path_str, excluded_paths):
"""
Verilen yolun hariç tutulan yollardan herhangi birinin altında olup olmadığını kontrol eder.
"""
path = Path(path_str)
for excluded in excluded_paths:
if path == excluded or excluded in path.parents:
return True
return False
def take_snapshot(root_dirs, excluded_paths_str):
"""
Belirtilen kök dizinleri özyinelemeli olarak tarar ve meta verileri toplar.
Hariç tutulan yolları atlar.
"""
snapshot_data = {}
excluded_paths = [Path(d).expanduser() for d in excluded_paths_str]
for root_dir_str in root_dirs:
root_dir = Path(root_dir_str).expanduser()
if not root_dir.exists():
continue
for entry in root_dir.rglob('*'):
try:
if is_excluded(str(entry), excluded_paths):
continue
if entry.is_symlink():
continue
stats = entry.stat()
data = {
"type": "dir" if entry.is_dir() else "file",
"size": stats.st_size,
"mtime": stats.st_mtime,
}
if entry.is_file() and any(entry.name.endswith(ext) for ext in CONFIG_EXTENSIONS):
data["hash"] = get_file_hash(entry)
snapshot_data[str(entry)] = data
except Exception:
continue
return snapshot_data
def compare_snapshots(snap1, snap2):
"""İki snapshot verisini karşılaştırır ve farkları döndürür."""
differences = {
"added": {},
"deleted": {},
"modified": {}
}
for path, data1 in snap1.items():
if path not in snap2:
differences["deleted"][path] = data1
else:
data2 = snap2[path]
is_modified = False
if data1["type"] == "file" and data2["type"] == "file":
if data1["size"] != data2["size"]:
is_modified = True
elif "hash" in data1 and data1.get("hash") != data2.get("hash"):
is_modified = True
if is_modified:
differences["modified"][path] = {"before": data1, "after": data2}
for path, data2 in snap2.items():
if path not in snap1:
differences["added"][path] = data2
return differences
# --- Hariç Tutma Penceresi (Exclusion Dialog) ---
class ExclusionDialog(QDialog):
def __init__(self, parent=None, initial_exclusions=None):
super().__init__(parent)
self.setWindowTitle("Hariç Tutulacak Yolları Yönet")
self.setGeometry(200, 200, 500, 400)
self.exclusions = initial_exclusions if initial_exclusions is not None else []
self._setup_ui()
def _setup_ui(self):
main_layout = QVBoxLayout(self)
main_layout.addWidget(QLabel("Hariç Tutulan Yollar:"))
self.list_widget = QListWidget()
self.list_widget.addItems(self.exclusions)
main_layout.addWidget(self.list_widget)
button_layout = QHBoxLayout()
self.add_button = QPushButton("Yol Ekle...")
self.add_button.clicked.connect(self._add_path)
self.remove_button = QPushButton("Seçileni Kaldır")
self.remove_button.clicked.connect(self._remove_path)
button_layout.addWidget(self.add_button)
button_layout.addWidget(self.remove_button)
main_layout.addLayout(button_layout)
dialog_buttons = QHBoxLayout()
self.save_button = QPushButton("Kaydet ve Kapat")
self.save_button.clicked.connect(self.accept)
self.cancel_button = QPushButton("İptal")
self.cancel_button.clicked.connect(self.reject)
dialog_buttons.addWidget(self.save_button)
dialog_buttons.addWidget(self.cancel_button)
main_layout.addLayout(dialog_buttons)
def _add_path(self):
# YENİ YAKLAŞIM: QFileDialog.getExistingDirectory yerine QFileDialog nesnesi kullanılıyor.
# Bu, gizli klasörleri göstermede daha güvenilir ve adres satırı girişini iyileştirir.
dialog = QFileDialog(self, "Hariç Tutulacak Dizin Seç", str(Path.home()))
# 1. Sadece Dizinleri Göster modunu ayarla
dialog.setFileMode(QFileDialog.DirectoryOnly)
# 2. Gizli dizinleri de dahil etmek için QDir filtre bayrağını ayarla
# QDir.Dirs: Yalnızca dizinleri listele
# QDir.Hidden: Gizli dosya ve dizinleri dahil et
# QDir.NoDotAndDotDot: . ve .. dizinlerini listeden kaldır (tercih edilebilir)
dialog.setFilter(QDir.Dirs | QDir.Hidden | QDir.NoDotAndDotDot)
# 3. İstenen bayrakları (ShowDirsOnly ve DontResolveSymlinks) setOption ile ayarla
# QFileDialog.ShowDirsOnly, QFileDialog.DirectoryOnly ile zaten ayarlanmış olsa da,
# adres çubuğundaki kısıtlamaları kaldırmak için DirectoryOnly kullanıldı.
# DontResolveSymlinks sembolik bağları çözmemeyi sağlar.
dialog.setOption(QFileDialog.DontResolveSymlinks, True)
if dialog.exec_() == QDialog.Accepted:
# Seçilen yolu al
path = dialog.selectedFiles()[0]
if path and path not in self.exclusions:
try:
# Yol '~' ile başlayan bir göreli yola çevrilir (Daha temiz kayıt için)
relative_path = Path(path).relative_to(Path.home())
path_to_save = str(Path('~') / relative_path)
except ValueError:
# Eğer yol Home dizininin dışında ise, tam yolu kaydet
path_to_save = path
if path_to_save not in self.exclusions:
self.exclusions.append(path_to_save)
self.exclusions.sort()
self.list_widget.clear()
self.list_widget.addItems(self.exclusions)
def _remove_path(self):
current_row = self.list_widget.currentRow()
if current_row >= 0:
item_text = self.list_widget.item(current_row).text()
self.list_widget.takeItem(current_row)
if item_text in self.exclusions:
self.exclusions.remove(item_text)
def get_exclusions(self):
"""Kaydedilecek nihai listeyi döndürür."""
return self.exclusions
# --- 2. Arka Plan Çalışanı ---
# ... (WorkerThread aynı kaldı)
class WorkerThread(QThread):
snapshot_finished = pyqtSignal(dict)
error_occurred = pyqtSignal(str)
def __init__(self, dirs_to_scan, excluded_paths):
super().__init__()
self.dirs_to_scan = dirs_to_scan
self.excluded_paths = excluded_paths
def run(self):
try:
snapshot = take_snapshot(self.dirs_to_scan, self.excluded_paths)
self.snapshot_finished.emit(snapshot)
except Exception as e:
self.error_occurred.emit(f"Snapshot sırasında hata oluştu: {e}")
# --- 3. PyQt5 Ana Pencere ---
# ... (SystemMonitor sınıfının geri kalanı aynı kalmıştır)
class SystemMonitor(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Sistem Durumu İzleyici")
self.setGeometry(100, 100, 900, 600)
self.snapshot_before = None
self.snapshot_time = None
self.differences = None
self.excluded_paths = self._load_exclusions()
self._setup_ui()
self._setup_worker()
self.update_before_info_panel()
self.load_initial_snapshot()
def _load_exclusions(self):
if EXCLUSION_FILE.exists():
try:
with open(EXCLUSION_FILE, 'r') as f:
return json.load(f)
except Exception:
return []
return []
def _save_exclusions(self):
try:
with open(EXCLUSION_FILE, 'w') as f:
json.dump(self.excluded_paths, f, indent=4)
return True
except Exception as e:
QMessageBox.critical(self, "Kayıt Hatası", f"Hariç tutma listesi kaydedilemedi: {e}")
return False
def open_exclusion_dialog(self):
dialog = ExclusionDialog(self, initial_exclusions=list(self.excluded_paths))
if dialog.exec_() == QDialog.Accepted:
self.excluded_paths = dialog.get_exclusions()
if self._save_exclusions():
QMessageBox.information(self, "Başarılı", "Hariç tutma listesi başarıyla güncellendi ve kaydedildi.")
self.update_before_info_panel()
if self.snapshot_before is not None:
QMessageBox.warning(self, "Uyarı", "Hariç tutma listesi değişti. Yeni ayarlarla doğru karşılaştırma yapmak için lütfen **yeniden 'Başlangıç Snapshot'ı Al'** düğmesine basın.")
def _setup_ui(self):
central_widget = QWidget()
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(5)
main_layout.setContentsMargins(10, 10, 10, 10)
button_layout = QHBoxLayout()
self.snapshot_button = QPushButton("1. Başlangıç Snapshot'ı Al")
self.snapshot_button.clicked.connect(self.take_snapshot_before)
button_layout.addWidget(self.snapshot_button)
self.compare_button = QPushButton("2. Durum Karşılaştır")
self.compare_button.clicked.connect(self.compare_snapshots_after)
self.compare_button.setEnabled(False)
button_layout.addWidget(self.compare_button)
self.save_button = QPushButton("3. TXT Dosyasına Yazdır")
self.save_button.clicked.connect(self.save_to_txt)
self.save_button.setEnabled(False)
button_layout.addWidget(self.save_button)
self.exclude_button = QPushButton("4. Hariç Tutulacak Yolları Yönet")
self.exclude_button.clicked.connect(self.open_exclusion_dialog)
button_layout.addWidget(self.exclude_button)
main_layout.addLayout(button_layout)
self.status_label = QLabel("Durum: Başlangıç")
self.status_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
main_layout.addWidget(self.status_label)
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 0)
self.progress_bar.setVisible(False)
self.progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
main_layout.addWidget(self.progress_bar)
splitter = QSplitter(Qt.Horizontal)
splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
left_panel = QWidget()
left_layout = QVBoxLayout(left_panel)
left_layout.setContentsMargins(0, 0, 0, 0)
left_layout.setSpacing(5)
left_layout.addWidget(QLabel("Başlangıç Durumu"))
self.before_info = QTextEdit()
self.before_info.setReadOnly(True)
self.before_info.setPlainText("Başlangıç Snapshot'ı Alınmadı.")
left_layout.addWidget(self.before_info)
right_panel = QWidget()
right_layout = QVBoxLayout(right_panel)
right_layout.setContentsMargins(0, 0, 0, 0)
right_layout.setSpacing(5)
right_layout.addWidget(QLabel("Karşılaştırma Sonuçları (Farklar)"))
self.comparison_tree = QTreeWidget()
self.comparison_tree.setColumnCount(3)
self.comparison_tree.setHeaderLabels(["Tür", "Yol", "Açıklama"])
self.comparison_tree.header().setStretchLastSection(True)
self.comparison_tree.header().setSectionResizeMode(0, QHeaderView.Interactive)
self.comparison_tree.header().setSectionResizeMode(1, QHeaderView.Interactive)
self.comparison_tree.header().setSectionResizeMode(2, QHeaderView.Interactive)
self.comparison_tree.setColumnWidth(0, 150)
right_layout.addWidget(self.comparison_tree)
splitter.addWidget(left_panel)
splitter.addWidget(right_panel)
splitter.setSizes([400, 750])
main_layout.addWidget(splitter)
self.setCentralWidget(central_widget)
def _setup_worker(self):
self.worker_thread = None
def update_before_info_panel(self, is_loaded=False):
info_parts = []
if self.snapshot_before is not None and self.snapshot_time is not None:
status = "***Önceki Snapshot Dosyadan Yüklendi!***" if is_loaded else "***Başlangıç Snapshot'ı Alındı!***"
info_parts.append(status)
info_parts.append(f"Tarih/Saat: {self.snapshot_time.strftime('%Y-%m-%d %H:%M:%S')}")
info_parts.append(f"Toplam Öğe Sayısı: {len(self.snapshot_before)}")
info_parts.append(f"JSON Dosyası: {SNAPSHOT_FILE}")
else:
info_parts.append("Başlangıç Snapshot'ı Alınmadı.")
info_parts.append("\n" + "-"*30 + "\n")
info_parts.append("Taranan Kök Dizinler:")
info_parts.extend([f" - {d}" for d in DIRS_TO_CHECK])
info_parts.append("\n")
info_parts.append(f"Hariç Tutulan Dizinler ({len(self.excluded_paths)}):")
if self.excluded_paths:
info_parts.extend([f" - {d}" for d in self.excluded_paths])
else:
info_parts.append(" - (Yok)")
self.before_info.setText('\n'.join(info_parts))
self.compare_button.setEnabled(self.snapshot_before is not None and not self.progress_bar.isVisible())
def load_initial_snapshot(self):
snapshot_path = Path(SNAPSHOT_FILE)
if snapshot_path.exists():
try:
with open(snapshot_path, 'r') as f:
self.snapshot_before = json.load(f)
mtime = snapshot_path.stat().st_mtime
self.snapshot_time = datetime.fromtimestamp(mtime)
self.update_before_info_panel(is_loaded=True)
self.compare_button.setEnabled(True)
self.status_label.setText(f"Durum: Eski snapshot ({snapshot_path.name}) başarıyla yüklendi. 'Durum Karşılaştır'a basılabilir.")
except Exception as e:
self.snapshot_before = None
self.status_label.setText(f"Durum: HATA - Önceki snapshot yüklenemedi: {e}")
QMessageBox.warning(self, "Yükleme Hatası", f"Kaydedilmiş snapshot dosyası yüklenemedi:\n{e}")
def take_snapshot_before(self):
if self.snapshot_before is not None:
reply = QMessageBox.question(self,
'Onay Gerekli',
"Daha önce alınmış bir snapshot kaydı mevcut. Yeni bir snapshot almak, eski kaydı silip (üzerine yazıp) güncelleyecek.\n\nDevam etmek istiyor musunuz?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.No:
self.status_label.setText("Durum: Yeni snapshot alma işlemi kullanıcı tarafından iptal edildi.")
return
self.status_label.setText("Durum: Başlangıç snapshot'ı alınıyor... Lütfen bekleyin. (Hariç tutulan yollar atlanacak)")
self._set_ui_busy(True)
if self.worker_thread:
self.worker_thread.quit()
self.worker_thread.wait()
self.worker_thread = WorkerThread(DIRS_TO_CHECK, self.excluded_paths)
self.worker_thread.snapshot_finished.connect(self._handle_before_snapshot_result)
self.worker_thread.error_occurred.connect(self._handle_error)
self.worker_thread.start()
def _handle_before_snapshot_result(self, snapshot_data):
self._set_ui_busy(False)
self.snapshot_before = snapshot_data
self.snapshot_time = datetime.now()
try:
with open(SNAPSHOT_FILE, 'w') as f:
json.dump(self.snapshot_before, f, indent=4)
self.update_before_info_panel()
self.status_label.setText("Durum: Snapshot başarılı. Şimdi bir program kurup 'Durum Karşılaştır'a basın.")
self.compare_button.setEnabled(True)
except Exception as e:
self._set_ui_busy(False)
self._handle_error(f"Snapshot kaydedilirken hata oluştu: {e}")
def compare_snapshots_after(self):
if not self.snapshot_before:
QMessageBox.warning(self, "Uyarı", "Önce 'Başlangıç Snapshot'ı Al' düğmesine basmalısınız.")
return
self.status_label.setText("Durum: Yeni snapshot alınıyor ve karşılaştırma yapılıyor... Lütfen bekleyin. (Hariç tutulan yollar atlanacak)")
self._set_ui_busy(True)
self.worker_thread = WorkerThread(DIRS_TO_CHECK, self.excluded_paths)
self.worker_thread.snapshot_finished.connect(self._handle_compare_snapshot_result)
self.worker_thread.error_occurred.connect(self._handle_error)
self.worker_thread.start()
def _handle_compare_snapshot_result(self, snapshot_after):
self._set_ui_busy(False)
try:
self.differences = compare_snapshots(self.snapshot_before, snapshot_after)
self.display_differences(self.differences)
total_diff_count = len(self.differences['added']) + len(self.differences['deleted']) + len(self.differences['modified'])
self.status_label.setText(f"Durum: Karşılaştırma tamamlandı. Fark sayısı: {total_diff_count}. Sonuçlar sağ panelde.")
self.save_button.setEnabled(True)
except Exception as e:
self._set_ui_busy(False)
self._handle_error(f"Karşılaştırma sırasında beklenmeyen hata oluştu: {e}")
def _format_differences_for_txt(self):
content = f"### Sistem Değişiklik Raporu ###\n"
content += f"Rapor Tarihi: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
content += f"Başlangıç Snapshot Tarihi: {self.snapshot_time.strftime('%Y-%m-%d %H:%M:%S')}\n"
content += "\n"
content += "--- TARANAN KÖK DİZİNLER ---\n"
for d in DIRS_TO_CHECK:
content += f"- {d}\n"
content += "\n"
content += "--- HARİÇ TUTULAN DİZİNLER ---\n"
if self.excluded_paths:
for d in self.excluded_paths:
content += f"- {d}\n"
else:
content += "(Yok)\n"
content += "\n"
added = self.differences.get('added', {})
content += f"--- 1. YENİ EKLENEN ÖĞELER ({len(added)}) ---\n"
if added:
for path, data in added.items():
content += f"[YENİ] Yol: {path}, Tip: {data['type'].capitalize()}, Boyut: {data['size']} bayt\n"
else:
content += "Yeni öğe bulunamadı.\n"
content += "\n"
deleted = self.differences.get('deleted', {})
content += f"--- 2. SİLİNEN ÖĞELER ({len(deleted)}) ---\n"
if deleted:
for path, data in deleted.items():
content += f"[SİLİNDİ] Yol: {path}, Tip: {data['type'].capitalize()}, Boyut: {data['size']} bayt\n"
else:
content += "Silinen öğe bulunamadı.\n"
content += "\n"
modified = self.differences.get('modified', {})
content += f"--- 3. DEĞİŞTİRİLEN ÖĞELER ({len(modified)}) ---\n"
if modified:
for path, data_pair in modified.items():
data1 = data_pair["before"]
data2 = data_pair["after"]
detail = []
if data1["size"] != data2["size"]:
size_diff = data2["size"] - data1["size"]
detail.append(f"Boyut Farkı: {size_diff} bayt ({data1['size']} -> {data2['size']})")
if "hash" in data1 and data1.get("hash") != data2.get("hash"):
if detail: detail += " | "
detail.append("İçerik (Hash) Değişti")
content += f"[DEĞİŞTİ] Yol: {path}, Detay: {', '.join(detail)}\n"
else:
content += "Değiştirilen öğe bulunamadı.\n"
return content
def save_to_txt(self):
if self.differences is None:
QMessageBox.warning(self, "Uyarı", "Lütfen önce bir karşılaştırma yapın.")
return
filename, _ = QFileDialog.getSaveFileName(self,
"Karşılaştırma Sonuçlarını Kaydet",
"compare.txt",
"Metin Dosyaları (*.txt);;Tüm Dosyalar (*)")
if not filename:
return
content = self._format_differences_for_txt()
try:
with open(filename, 'w', encoding='utf-8', newline='\r\n') as f:
f.write(content)
self.status_label.setText(f"Durum: Sonuçlar başarıyla kaydedildi: {filename}")
QMessageBox.information(self, "Başarılı", f"Karşılaştırma sonuçları başarıyla kaydedildi:\n{filename}")
except Exception as e:
self._handle_error(f"Dosya kaydedilirken hata oluştu: {e}")
def _set_ui_busy(self, is_busy):
self.snapshot_button.setEnabled(not is_busy)
self.exclude_button.setEnabled(not is_busy)
self.compare_button.setEnabled(not is_busy and self.snapshot_before is not None)
self.save_button.setEnabled(not is_busy and self.differences is not None)
self.progress_bar.setVisible(is_busy)
def _handle_error(self, message):
self._set_ui_busy(False)
self.status_label.setText(f"Durum: HATA - {message}")
QMessageBox.critical(self, "Hata", message)
def display_differences(self, differences):
self.comparison_tree.clear()
added_root = QTreeWidgetItem(self.comparison_tree, [f"YENİ ({len(differences['added'])})", "", ""])
added_root.setBackground(0, Qt.darkGreen)
added_root.setForeground(0, Qt.white)
for path, data in differences['added'].items():
detail = f"{data['type'].capitalize()}, Boyut: {data['size']} bayt"
QTreeWidgetItem(added_root, ["Yeni", path, detail])
deleted_root = QTreeWidgetItem(self.comparison_tree, [f"SİLİNEN ({len(differences['deleted'])})", "", ""])
deleted_root.setBackground(0, Qt.darkRed)
deleted_root.setForeground(0, Qt.white)
for path, data in differences['deleted'].items():
detail = f"{data['type'].capitalize()}, Boyut: {data['size']} bayt"
QTreeWidgetItem(deleted_root, ["Kaldırıldı", path, detail])
modified_root = QTreeWidgetItem(self.comparison_tree, [f"DEĞİŞEN ({len(differences['modified'])})", "", ""])
modified_root.setBackground(0, Qt.darkYellow)
modified_root.setForeground(0, Qt.black)
for path, data_pair in differences['modified'].items():
data1 = data_pair["before"]
data2 = data_pair["after"]
detail = ""
if data1["size"] != data2["size"]:
size_diff = data2["size"] - data1["size"]
detail += f"Boyut Farkı: {size_diff} bayt ({data1['size']} -> {data2['size']})"
if "hash" in data1 and data1.get("hash") != data2.get("hash"):
if detail: detail += " | "
detail += "İçerik (Hash) Değişti"
QTreeWidgetItem(modified_root, ["Değişti", path, detail])
self.comparison_tree.expandAll()
# --- 4. Programı Çalıştırma ---
if __name__ == '__main__':
app = QApplication(sys.argv)
window = SystemMonitor()
window.show()
sys.exit(app.exec_())
Bir test edin.
manifest json bende 1.3 kb
home klasörünü de ekledim koda oldu
teşekkürler