#1 Le 28/09/2006, à 19:39
- foch
[Résolu] Bugs graphiques avec Python et PyGTK
Hello,
à la suite de ce post : http://forum.ubuntu-fr.org/viewtopic.php?id=61019
où je présente mon programme de téléchargement de musique sur RadioBlogClub, j'aimerais bien un peu d'aide pour m'aider à corriger les bugs graphiques !
J'ai remarqué que souvent, au deuxième téléchargement, le changement de bureau ou l'utilisation de l'ascenceur fait planter le bouzin.
#!/usr/bin/env python
###
#
# RBCD - RadioBlogClub Downloader - Download songs from RadioBlogClub.
# Copyright (C) 2006 Julien Faucher <faucherj AT gmail DOT com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
###
__version__ = "1.0-beta1b"
__date__ = "27/09/2006"
import urllib, re, sys, gobject, gtk, threading, os
# download folder
result_folder = "~/rbcd_result"
(COLUMN_NUMBER, COLUMN_DOWNLOAD, COLUMN_NAME) = range(3)
class UserInterface(gtk.Window):
def __init__(self, parent=None):
""" Create window and model """
# create window, etc
gtk.Window.__init__(self)
try:
self.set_screen(parent.get_screen())
except AttributeError:
self.connect('destroy', lambda *w: gtk.main_quit())
self.set_title('RadioBlogClub Downloader')
self.set_border_width(8)
self.set_default_size(300, 250)
vbox = gtk.VBox(False, 8)
self.add(vbox)
# top part of window
box_top = gtk.HBox(False, 5)
self.label = gtk.Label('Enter keywords :')
self.entry = gtk.Entry(max=0)
self.search = gtk.Button(stock=gtk.STOCK_FIND)
box_top.pack_start(self.label, False, False)
box_top.pack_start(self.entry, False, False)
box_top.pack_start(self.search, False, False)
self.search.connect("clicked", self.searchSongs, None)
self.entry.connect("activate", self.searchSongs, None)
vbox.pack_start(box_top, False, False)
# middle part of window
sw = gtk.ScrolledWindow()
sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
vbox.pack_start(sw)
# create tree model
self.model = gtk.ListStore(gobject.TYPE_INT,
gobject.TYPE_BOOLEAN,
gobject.TYPE_STRING)
# create tree view
self.treeview = gtk.TreeView(self.model)
self.treeview.set_rules_hint(True)
self.treeview.set_search_column(COLUMN_NAME)
sw.add(self.treeview)
# down part of window
box_down = gtk.HBox(False, 5)
self.state_label = gtk.Label('')
download = gtk.Button('Download')
exitb = gtk.Button(stock=gtk.STOCK_QUIT)
aboutb = gtk.Button(stock=gtk.STOCK_DIALOG_INFO)
box_down.pack_end(exitb, False, False)
box_down.pack_end(aboutb, False, False)
box_down.pack_end(download, False, False)
box_down.pack_start(self.state_label, False, False)
exitb.connect_object("clicked", gtk.Widget.destroy, self)
download.connect("clicked", self.download, None)
aboutb.connect("clicked", self.about, None)
vbox.pack_start(box_down, False, False)
# create columns
self.add_columns(self.treeview)
# change window size
self.resize(800, 500)
# print everything
self.show_all()
def add_columns(self, treeview):
""" Add the 3 columns """
# column for fixed toggles
renderer = gtk.CellRendererToggle()
renderer.connect('toggled', self.select_toggled, self.model)
column = gtk.TreeViewColumn('Select', renderer, active=COLUMN_DOWNLOAD)
# set this column to a fixed sizing(of 50 pixels)
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
column.set_fixed_width(50)
treeview.append_column(column)
# column for description
column = gtk.TreeViewColumn('#', gtk.CellRendererText(),
text=COLUMN_NUMBER)
column.set_sort_column_id(COLUMN_NUMBER)
treeview.append_column(column)
# column for description
column = gtk.TreeViewColumn('Song name', gtk.CellRendererText(),
text=COLUMN_NAME)
column.set_sort_column_id(COLUMN_NAME)
treeview.append_column(column)
def select_toggled(self, cell, path, model):
""" Change the select status """
# get toggled iter
iter = model.get_iter((int(path),))
download = model.get_value(iter, COLUMN_DOWNLOAD)
# do something with the value
download = not download
# set new value
model.set(iter, COLUMN_DOWNLOAD, download)
def searchSongs(self, widget, data=None):
""" Start searching songs according to the given key words """
self.setStateText("Starting research...")
print "Starting research..."
text = self.entry.get_text()
(self.songs, self.data) = Search.getListSongs(text.split(" "))
self.clearList()
self.createList()
self.setStateText("Research done.")
print "Research done."
def download(self, widget, data=None):
""" Start downloading the selected songs """
down = Download(self, self.songs, self.model)
down.start()
def createList(self):
""" Create the song list """
for item in self.data:
iter = self.model.append()
self.model.set(iter,
COLUMN_NUMBER, item[COLUMN_NUMBER],
COLUMN_DOWNLOAD, item[COLUMN_DOWNLOAD],
COLUMN_NAME, Search.shortName(item[COLUMN_NAME]))
def clearList(self):
""" Clear the song list """
self.model.clear()
def setStateText(self, text):
""" Change state text """
self.state_label.set_text(text)
def about(self, widget, data=None):
""" Print an about dialog window"""
dialog = gtk.AboutDialog()
dialog.set_name("RBCD")
dialog.set_comments("A program to download songs from RadioBlogClub")
dialog.set_copyright("\302\251 Copyright 2006 Julien Faucher")
dialog.set_website("http://www.dobitchu.info/blog")
dialog.set_license('GNU General Public License version 2')
dialog.set_version(__version__)
dialog.set_authors(['Julien Faucher <faucherj@gmail.com>'])
# close dialog on user response
dialog.connect("response", lambda d, r: d.destroy())
dialog.show()
def main(self):
""" Main function, one to rule them all """
# create download folder
if not os.path.isdir(os.path.expanduser(result_folder)):
os.mkdir (os.path.expanduser(result_folder))
gobject.threads_init()
gtk.main()
return 0
class Search:
@staticmethod
def changeRBSExtension(file):
""" Change the RBS extension to MP3 """
name = file.split("/")[-1].split(".")
# remove some extensions
name = [part for part in name
if not part.lower() in ("rbs", "mp3")]
# the file is a mp3, add .mp3 at the end of the name
name.append("mp3")
return ".".join(name)
@staticmethod
def searchURL(i, params):
""" Return the URL to search the MP3 """
url = "http://www.radioblogclub.com/search/" + str(i) + "/" + params
try:
return urllib.urlopen(url).read()
except IOError:
print "Error : could not connect"
sys.exit(1)
@staticmethod
def shortName(name):
""" Cut out too long name """
if len(name) > 80:
name = name[0:79] + "..."
return name
@staticmethod
def getListSongs(text):
""" Main function, one to rule them all """
# get the webpage data
data = Search.searchURL(0, "_".join(text))
lines = data.split('\n')
# regular expression to find the number of songs
reg1 = re.compile('^.*of about <b>(.*)</b> tracks for.*$')
n_songs = 0
# get the number of songs
for line in lines:
if reg1.match(line):
n_songs = int(reg1.search(line).groups()[0])
break
# if no song is found, exit
if n_songs == 0:
return ([], [])
# get the number of pages to browse
n_pages = n_songs / 50 + 1
# download the pages
pages = []
pages.append(lines)
numbers = [i * 50 for i in range(1, n_pages)]
for i in numbers:
pages.append(Search.searchURL(i, "_".join(text)).split('\n'))
# regular expression to find the song URLs
reg2 = re.compile('^.*<td><a href=\"(/open/.*)\">(.*)</a></td>.*$')
songs = []
data = []
i = 0
# parse the HTML data to find the songs and save in a list
for page in pages:
for line in page:
if reg2.match(line):
result = reg2.search(line).groups()
try:
url = unicode(result[0], "utf-8")
name = unicode(result[1], "utf-8")
songs.append((url, name))
data.append((i + 1, False, name))
i += 1
except UnicodeDecodeError:
# encoding error: skip song
print name + " : Encoding error. Skipping song."
return (songs, data)
class Download(threading.Thread):
def __init__(self, parent, songs, model):
""" Init the thread """
threading.Thread.__init__(self)
self.parent = parent
self.songs = songs
self.model = model
def run(self):
""" Thread main function """
gtk.threads_enter()
# clear state text
self.parent.setStateText("")
# init the counters
counter_ok = 0
counter_all = 0
# iter on all the paths
iter = self.model.get_iter_first()
while iter is not None:
number = self.model.get_value(iter, 0)
selected = self.model.get_value(iter, 1)
iter = self.model.iter_next(iter)
# if the song is selected for download...
if selected:
counter_all += 1
name = self.songs[number - 1][1]
name_search = name.replace('(', '\(').replace(')', '\)')\
.replace('[', '\[').replace(']', '\]')\
.replace('\'', '\\\\\'')
# change the extension to .mp3
name = Search.changeRBSExtension(name)
self.parent.setStateText("Downloading %s..." % (Search.shortName(name), ))
url = "http://www.radioblogclub.com" + self.songs[number - 1][0]\
.replace(' ', '%20')
print "Downloading %s" % (name, )
print "from %s" % (url, )
# get the HTML file with the RBS adress
playlist = urllib.urlopen(url).read().split('\n')
# get the RBS file URL
reg3 = re.compile('^.*blogThis\(\'(.*' + name_search + '\.rbs)\'.*$')
# find the URL
url = ""
for line in playlist:
if reg3.match(line):
url = reg3.search(line).groups()[0]\
.replace('\\', '').replace(' ', '%20')
break
# the RBS file was not found
if url == "":
self.parent.setStateText("Song not found. (Maybe the file was a SWF)")
print "Song not found. (Maybe the file was a SWF)"
continue
# download the file
try:
print "Downloading to %s..." % (result_folder, )
urllib.urlretrieve(url, os.path.join(os.path.expanduser(result_folder),
name))
### TODO : check out if the file is a valid mp3
counter_ok += 1
print "Download successful."
except IOError:
self.parent.setStateText("Download failed.")
print "Download failed."
print
result = "Downloaded %i song%sout of %i." % (counter_ok,
counter_ok > 1 and 's ' or ' ',
counter_all)
self.parent.setStateText(result)
print result
gtk.threads_leave()
# program entry point
if __name__ == "__main__":
GUI = UserInterface()
GUI.main()
Si une ame charitable pouvait bien me fournir quelques conseils pour améliorer mon code, je lui en serais très reconnaissant !
Merci
EDIT : Trouvé ! En fait il fallait lire la doc !
http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp
Dernière modification par foch (Le 01/10/2006, à 11:24)
De retour sous Ubuntu après quelques années sous ArchLinux.
Programme de téléchargement de musique qui ne marche plus vraiment.
Hors ligne