RkBlog

Hardware, programming and astronomy tutorials and reviews.

QYolk III - List of updates

A new feature

In this tutorial we will add a tab with a list of upgradeable packages. To get such list we will use the following code:
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()
Note that this code will take few second to execute. We will have to use a cache system to prevent such slowdowns. I've added a new tab Updates named "updatesTab", and in the tab I've added QTreeWidget named "updatesList" with tree columns:
qyolk30
Here is updated 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
New code is a get_fresh_updates function, and modified tab_change slot-method:
		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])
When you click on the "Updates" tab it will take few seconds to show the updates (network connection required). We need to add a cache system. I'll use Pickle to store the list:
from os.path import expanduser
import cPickle
userpath = expanduser('~')
f = open(userpath + '/test', 'w')
cPickle.dump(['a', 'b', 'c'], f)
To read pickled data we will use:
f = open(userpath + '/test', 'r')
print cPickle.load(f)
So here is a get_fresh_updates with caching:
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 saves a list [Date, list of upgrades]. We check if cache file exists and if it isn't older than 24 hours. If the cache doesn't exist we execute the code and save the cache. Here is the updated 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_())
Adding list of updates I've moved to the __init__ - start of the application. When the cache doesn't exist the window will appear after few or more seconds, as the list must be created. When we have a valid cache there will be no problems. In the next tutorial we will use QSplashScreen to show proper info when the application is creating the list of updates.

Sources

Download Sources
RkBlog

14 July 2008;

Comment article