RkBlog

Hardware, programming and astronomy tutorials and reviews.

QYolk III - Pakiety do aktualizacji

Rozbudowujemy aplikację o listing pakietów Pythona oznaczonych do aktualizacji. W tym celu wykorzystamy biblioteki aplikacji Yolk pobierającej dane z serwisu pypi

Kolejnym elementem będzie dodanie listy pakietów z dostępnymi aktualizacjami (nowszymi wersjami). Poniższy kod zwróci nam listę z pakietami do aktualizacji (przeróbka funkcji z Yolk):
from yolk.cli import get_pkglist
from yolk.yolklib import get_highest_version, Distributions
from yolk.pypi import CheeseShop
import pkg_resources
def get_fresh_updates(package_name="", version=""):
	ret = []
	pypi = CheeseShop()
	dists = Distributions()
	for pkg in get_pkglist():
		for (dist, active) in dists.get_distributions("all", pkg,  dists.get_highest_installed(pkg)):
			(project_name, versions) = pypi.query_versions_pypi(dist.project_name, True)
			if versions:
				newest = get_highest_version(versions)
				if newest != dist.version:
					if pkg_resources.parse_version(dist.version) < pkg_resources.parse_version(newest):
						ret.append([project_name, dist.version, newest])
	return ret
print get_fresh_updates()
Zwróć uwagę że pobranie listy aktualizacji zajmuje sporo czasu. Tak więc w tym przypadku samo QTreeWidget nie wystarczy. Będziemy musieli dodać mechanizm keszowania. Ale najpierw zacznijmy od podstaw czyli dodania nowej zakładki z nowym widżetem QTreeWidget. A więc dodałem zakładkę Updates o nazwie "updatesTab", a w niej QTreeWidget o nazwie "updatesList" zawierającą trzy kolumny:
qyolk30
Oto zaktualizowany start.py:
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from qyolk import Ui_QYolk
from yolk import yolklib

class StartQT4(QtGui.QMainWindow):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_QYolk()
		self.ui.setupUi(self)
		# set the widths of the columns
		################
		# All packages
		################
		self.ui.allList.setColumnWidth(0,200)
		self.ui.allList.setColumnWidth(1,100)
		# generator which retuns list of installed packages
		packages = yolklib.Distributions()
		for pkg in packages.get_distributions('all'):
			a = QtGui.QTreeWidgetItem(self.ui.allList)
			pk = str(pkg[0]).split(' ')
			if pkg[1]:
				status = 'Active'
			else:
				status = 'Not Active'
				a.setTextColor(0, QtGui.QColor(128, 128, 128))
				a.setTextColor(1, QtGui.QColor(128, 128, 128))
				a.setTextColor(2, QtGui.QColor(128, 128, 128))
			a.setText(0, pk[0])
			a.setText(1, pk[1])
			a.setText(2, status)
		################
		# Active Packages
		################
		# set the widths of the columns
		self.ui.activeList.setColumnWidth(0,200)
		self.ui.activeList.setColumnWidth(1,100)
		# generator which retuns list of active packages
		for pkg in packages.get_distributions('active'):
			a = QtGui.QTreeWidgetItem(self.ui.activeList)
			pk = str(pkg[0]).split(' ')
			a.setText(0, pk[0])
			a.setText(1, pk[1])
			a.setText(2, 'Active')
		################
		# Not Active Packages
		################
		# set the widths of the columns
		self.ui.notActiveList.setColumnWidth(0,200)
		self.ui.notActiveList.setColumnWidth(1,100)
		# generator which retuns list of not-active packages
		for pkg in packages.get_distributions('nonactive'):
			a = QtGui.QTreeWidgetItem(self.ui.notActiveList)
			pk = str(pkg[0]).split(' ')
			a.setText(0, pk[0])
			a.setText(1, pk[1])
			a.setText(2, 'Not Active')
		
		# Signals
		QtCore.QObject.connect(self.ui.pkgTabs,QtCore.SIGNAL("currentChanged(int)"), self.tab_change)
	def tab_change(self, tab_id):
		if tab_id == 0:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing all installed cheeseshop packages')
		elif tab_id == 1:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing active packages')
		elif tab_id == 2:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing not active packages (older versions)')
		elif tab_id == 3:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing available updates')
			################
			# List Updates
			################
			# set the widths of the columns
			self.ui.updatesList.setColumnWidth(0,200)
			self.ui.updatesList.setColumnWidth(1,150)
			for pkg in get_fresh_updates():
				a = QtGui.QTreeWidgetItem(self.ui.updatesList)
				a.setText(0, pkg[0])
				a.setText(1, pkg[1])
				a.setText(2, pkg[2])

from yolk.cli import get_pkglist
from yolk.yolklib import get_highest_version, Distributions
from yolk.pypi import CheeseShop
import pkg_resources
def get_fresh_updates(package_name="", version=""):
	ret = []
	pypi = CheeseShop()
	dists = Distributions()
	for pkg in get_pkglist():
		for (dist, active) in dists.get_distributions("all", pkg,  dists.get_highest_installed(pkg)):
			(project_name, versions) = pypi.query_versions_pypi(dist.project_name, True)
			if versions:
				newest = get_highest_version(versions)
				if newest != dist.version:
					if pkg_resources.parse_version(dist.version) < pkg_resources.parse_version(newest):
						ret.append([project_name, dist.version, newest])
	return ret

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = StartQT4()
	myapp.show()
	sys.exit(app.exec_())
qyolk31
Nowy kod to funkcja get_fresh_updates, a w kodzie od strony PyQT4 zmodyfikowałem metodę-slot tab_change dodając zmianę tekstu etykiety dla nowej zakładki oraz wypełnienie QTreeWidget listą pakietów do aktualizacji:
		elif tab_id == 3:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing available updates')
			################
			# List Updates
			################
			# set the widths of the columns
			self.ui.updatesList.setColumnWidth(0,200)
			self.ui.updatesList.setColumnWidth(1,150)
			for pkg in get_fresh_updates():
				a = QtGui.QTreeWidgetItem(self.ui.updatesList)
				a.setText(0, pkg[0])
				a.setText(1, pkg[1])
				a.setText(2, pkg[2])
QTreeWidget można uzupełnić na starcie aplikacji, spróbuj kliknąć na zakładkę "Updates" - będziesz musiał poczekać kilkanaście sekund zanim lista aktualizacji zostanie pokazana. Musimy dodać mechanizm keszowania wyników by za każdym razem nie czekać na pobranie listy aktualizacji z sieci. get_fresh_updates zwraca listę, tak więc możemy za pomocą modułu Pickle zapisywać ją do pliku w katalogu użytkownika. Przykład:
from os.path import expanduser
import cPickle
userpath = expanduser('~')
f = open(userpath + '/test', 'w')
cPickle.dump(['a', 'b', 'c'], f)
Odczytanie danych:
f = open(userpath + '/test', 'r')
print cPickle.load(f)
Oto zmodyfikowana funkcja get_fresh_updates:
def get_fresh_updates(package_name="", version=""):
	userpath = expanduser('~')
	now = datetime.now()
	# do we have the cache
	if isfile(userpath + '/.qyolk'):
		f = open(userpath + '/.qyolk', 'r')
		cache = cPickle.load(f)
		check_time = now - timedelta(hours=24)
		if cache[0] > check_time:
			# fresh cache, use it
			return cache[1]
	
	# no cache, get updates and create the cace
	ret = []
	pypi = CheeseShop()
	dists = Distributions()
	for pkg in get_pkglist():
		for (dist, active) in dists.get_distributions("all", pkg,  dists.get_highest_installed(pkg)):
			(project_name, versions) = pypi.query_versions_pypi(dist.project_name, True)
			if versions:
				newest = get_highest_version(versions)
				if newest != dist.version:
					if pkg_resources.parse_version(dist.version) < pkg_resources.parse_version(newest):
						ret.append([project_name, dist.version, newest])
	f = open(userpath + '/.qyolk', 'w')
	cPickle.dump([now, ret], f)
	return ret
cPickle zapisuje listę postaci [Data, lista pakietów do aktualizacji]. W funkcji sprawdzamy czy plik cache istnieje i czy zapisana lista nie jest starsza niż 24 godziny. Jeżeli kesz nie istnieje lub jest za stary to pobieramy listę pakietów z aktualizacjami i generujemy kesz. Oto zaktualizowany start.py:
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
from qyolk import Ui_QYolk
from yolk import yolklib
from os.path import expanduser
import cPickle
from yolk.cli import get_pkglist
from yolk.yolklib import get_highest_version, Distributions
from yolk.pypi import CheeseShop
import pkg_resources
from os.path import isfile
from datetime import timedelta
from datetime import datetime

class StartQT4(QtGui.QMainWindow):
	def __init__(self, parent=None):
		QtGui.QWidget.__init__(self, parent)
		self.ui = Ui_QYolk()
		self.ui.setupUi(self)
		
		#splash = QtGui.QSplashScreen(self)
		#splash.drawContents()
		
		################
		# All packages
		################
		self.ui.allList.setColumnWidth(0,200)
		self.ui.allList.setColumnWidth(1,100)
		# generator which retuns list of installed packages
		packages = yolklib.Distributions()
		for pkg in packages.get_distributions('all'):
			a = QtGui.QTreeWidgetItem(self.ui.allList)
			pk = str(pkg[0]).split(' ')
			if pkg[1]:
				status = 'Active'
			else:
				status = 'Not Active'
				a.setTextColor(0, QtGui.QColor(128, 128, 128))
				a.setTextColor(1, QtGui.QColor(128, 128, 128))
				a.setTextColor(2, QtGui.QColor(128, 128, 128))
			a.setText(0, pk[0])
			a.setText(1, pk[1])
			a.setText(2, status)
		################
		# Active Packages
		################
		# set the widths of the columns
		self.ui.activeList.setColumnWidth(0,200)
		self.ui.activeList.setColumnWidth(1,100)
		# generator which retuns list of active packages
		for pkg in packages.get_distributions('active'):
			a = QtGui.QTreeWidgetItem(self.ui.activeList)
			pk = str(pkg[0]).split(' ')
			a.setText(0, pk[0])
			a.setText(1, pk[1])
			a.setText(2, 'Active')
		################
		# Not Active Packages
		################
		# set the widths of the columns
		self.ui.notActiveList.setColumnWidth(0,200)
		self.ui.notActiveList.setColumnWidth(1,100)
		# generator which retuns list of not-active packages
		for pkg in packages.get_distributions('nonactive'):
			a = QtGui.QTreeWidgetItem(self.ui.notActiveList)
			pk = str(pkg[0]).split(' ')
			a.setText(0, pk[0])
			a.setText(1, pk[1])
			a.setText(2, 'Not Active')
		################
		# List Updates
		################
		# set the widths of the columns
		self.ui.updatesList.setColumnWidth(0,200)
		self.ui.updatesList.setColumnWidth(1,150)
		for pkg in get_fresh_updates():
			a = QtGui.QTreeWidgetItem(self.ui.updatesList)
			a.setText(0, pkg[0])
			a.setText(1, pkg[1])
			a.setText(2, pkg[2])
		
		# Signals
		QtCore.QObject.connect(self.ui.pkgTabs,QtCore.SIGNAL("currentChanged(int)"), self.tab_change)
	def tab_change(self, tab_id):
		if tab_id == 0:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing all installed cheeseshop packages')
		elif tab_id == 1:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing active packages')
		elif tab_id == 2:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing not active packages (older versions)')
		elif tab_id == 3:
			self.ui.infoLabel.setText('<b>QYolk</b>: Browsing available updates')

def get_fresh_updates(package_name="", version=""):
	userpath = expanduser('~')
	now = datetime.now()
	# do we have the cache
	if isfile(userpath + '/.qyolk'):
		f = open(userpath + '/.qyolk', 'r')
		cache = cPickle.load(f)
		check_time = now - timedelta(hours=24)
		if cache[0] > check_time:
			# fresh cache, use it
			return cache[1]
	
	# no cache, get updates and create the cace
	ret = []
	pypi = CheeseShop()
	dists = Distributions()
	for pkg in get_pkglist():
		for (dist, active) in dists.get_distributions("all", pkg,  dists.get_highest_installed(pkg)):
			(project_name, versions) = pypi.query_versions_pypi(dist.project_name, True)
			if versions:
				newest = get_highest_version(versions)
				if newest != dist.version:
					if pkg_resources.parse_version(dist.version) < pkg_resources.parse_version(newest):
						ret.append([project_name, dist.version, newest])
	f = open(userpath + '/.qyolk', 'w')
	cPickle.dump([now, ret], f)
	return ret

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	myapp = StartQT4()
	myapp.show()
	sys.exit(app.exec_())
Pojawiła się zmodyfikowana wersja funkcji "get_fresh_updates" oraz listowanie pakietów do aktualizacji przeniosłem na start aplikacji. Zwróć uwagę że gdy kesz nie istnieje okno aplikacji pojawi się dopiero po jej pobraniu. Gdy kesz jest obecny aplikacja będzie działała bez opóźnień. W następnej wersji zastosujemy QSplashScreen by wyświetlić informację że aplikacja się ładuje i wymaga to chwili czasu (przy braku kesza)

Źródła

Pobierz źródła
RkBlog

14 July 2008;

Comment article