Komplette ?berarbeitung der Gtk-GUI mit Verbindungspanel, Konfiguration, Threads etc.

This commit is contained in:
gotik 2012-06-19 02:37:14 +02:00
parent a3215ef759
commit 1570b3b121

419
mcgGtk.py
View file

@ -9,57 +9,247 @@ import mcg
UI_INFO = """
<ui>
<toolbar name='ToolBar'>
<toolitem action='Connect' />
<toolitem action='Update' />
</toolbar>
</ui>
"""
class MCGGtk(Gtk.Window): class MCGGtk(Gtk.Window):
_default_cover_size = 128
def __init__(self): def __init__(self):
Gtk.Window.__init__(self, title="MPDCoverGridGTK") Gtk.Window.__init__(self, title="MPDCoverGridGTK")
self._mcg = mcg.MCGClient() self._mcg = mcg.MCGClient()
self._cover_pixbuf = None self._config = Configuration()
self.set_default_size(600, 400) self._quit = False
# Box # Box
_main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self._main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.add(_main_box) self.add(self._main_box)
# UIManager
action_group = Gtk.ActionGroup("toolbar")
ui_manager = Gtk.UIManager()
ui_manager.add_ui_from_string(UI_INFO)
accel_group = ui_manager.get_accel_group()
self.add_accel_group(accel_group)
ui_manager.insert_action_group(action_group)
self._action_connect = Gtk.Action("Connect", "_Connect", "Connect to server", Gtk.STOCK_DISCONNECT)
self._action_connect.connect("activate", self.toolbar_callback)
action_group.add_action_with_accel(self._action_connect, None)
self._action_update = Gtk.Action("Update", "_Update", "Update library", Gtk.STOCK_REFRESH)
self._action_update.connect("activate", self.toolbar_callback)
action_group.add_action_with_accel(self._action_update, None)
# Toolbar # Toolbar
toolbar = ui_manager.get_widget("/ToolBar") self._toolbar = Toolbar()
toolbar_context = toolbar.get_style_context() self._main_box.pack_start(self._toolbar, False, False, 0)
toolbar_context.add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR) # Connection Panel
_main_box.pack_start(toolbar, False, False, 0) self.set_default_size(600, 400)
# HPaned self._connection_panel = ConnectionPanel(self._config)
hpaned = Gtk.HPaned() self._main_box.pack_end(self._connection_panel, True, True, 0)
_main_box.pack_start(hpaned, True, True, 0) # Cover Panel
self._cover_panel = CoverPanel()
# Signals
self.connect("focus", self.focus)
self.connect("delete-event", self.destroy)
self._toolbar.connect_signal(Toolbar.SIGNAL_CONNECT, self._connect)
self._mcg.connect_signal(mcg.MCGClient.SIGNAL_CONNECT, self.connect_callback)
self._mcg.connect_signal(mcg.MCGClient.SIGNAL_UPDATE, self.update_callback)
self._mcg.connect_signal(mcg.MCGClient.SIGNAL_IDLE_PLAYER, self.idle_player_callback)
def focus(self, widget, state):
self._connect()
def destroy(self, widget, state):
self._mcg.close()
GObject.idle_add(Gtk.main_quit)
def _connect(self):
if self._mcg.is_connected():
self._cover_panel.stop_update()
self._mcg.disconnect()
else:
self._connection_panel.lock()
host = self._connection_panel.get_host()
port = self._connection_panel.get_port()
password = self._connection_panel.get_password()
self._mcg.connect(host, port, password)
def connect_callback(self, connected, message):
if connected:
GObject.idle_add(self._connect_connected)
else:
GObject.idle_add(self._connect_disconnected)
def _connect_connected(self):
self._toolbar.connected()
self._main_box.remove(self._connection_panel)
self._main_box.pack_start(self._cover_panel, True, True, 0)
self._main_box.show_all()
def _connect_disconnected(self):
self._main_box.remove(self._cover_panel)
self._main_box.pack_start(self._connection_panel, True, True, 0)
self._main_box.show_all()
self._connection_panel.unlock()
self._toolbar.disconnected()
def update_callback(self, albums):
self._cover_panel.update(albums)
def idle_player_callback(self, state, album):
GObject.idle_add(self._cover_panel.set_album, album.get_cover())
class Toolbar(Gtk.Toolbar):
SIGNAL_CONNECT = 'connect'
def __init__(self):
Gtk.Toolbar.__init__(self)
self._callbacks = {}
self.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
self._connection_button = Gtk.ToolButton(Gtk.STOCK_DISCONNECT)
self._connection_button.connect("clicked", self._callback)
self.add(self._connection_button)
def connect_signal(self, signal, callback):
self._callbacks[signal] = callback
def connected(self):
self._connection_button.set_stock_id(Gtk.STOCK_CONNECT)
def disconnected(self):
self._connection_button.set_stock_id(Gtk.STOCK_DISCONNECT)
def _callback(self, widget):
signal = None
if widget == self._connection_button:
signal = self.SIGNAL_CONNECT
if signal in self._callbacks:
callback = self._callbacks[signal]
callback()
class ConnectionPanel(Gtk.Box):
def __init__(self, config):
Gtk.HBox.__init__(self)
self._callbacks = {}
self._config = config
vbox = Gtk.VBox()
self.pack_start(vbox, True, False, 0)
self._table = Gtk.Table(3, 2, False)
vbox.pack_start(self._table, True, False, 0)
# Host
host_label = Gtk.Label("Host:")
host_label.set_alignment(0, 0.5)
self._table.attach(host_label, 0, 1, 0, 1)
self._host_entry = Gtk.Entry()
self._host_entry.set_text("localhost")
self._host_entry.connect("focus-out-event", self._lost_focus)
self._table.attach(self._host_entry, 1, 2, 0, 1)
# Port
port_label = Gtk.Label("Port:")
port_label.set_alignment(0, 0.5)
self._table.attach(port_label, 0, 1, 1, 2)
adjustment = Gtk.Adjustment(6600, 1024, 9999, 1, 10, 10)
self._port_spinner = Gtk.SpinButton()
self._port_spinner.set_adjustment(adjustment)
self._port_spinner.connect("focus-out-event", self._lost_focus)
self._table.attach(self._port_spinner, 1, 2, 1, 2)
# Passwort
password_label = Gtk.Label("Password:")
password_label.set_alignment(0, 0.5)
self._table.attach(password_label, 0, 1, 2, 3)
self._password_entry = Gtk.Entry()
self._password_entry.set_visibility(False)
self._password_entry.connect("focus-out-event", self._lost_focus)
self._table.attach(self._password_entry, 1, 2, 2, 3)
self._load_config()
def _lost_focus(self, widget, data):
self._save_config()
def _load_config(self):
self.set_host(self._config.host)
self.set_port(self._config.port)
self.set_password(self._config.password)
def _save_config(self):
self._config.host = self._host_entry.get_text()
self._config.port = self._port_spinner.get_value_as_int()
self._config.password = self._password_entry.get_text()
self._config.save()
def set_host(self, host):
self._host_entry.set_text(host)
def get_host(self):
return self._host_entry.get_text()
def set_port(self, port):
self._port_spinner.set_value(port)
def get_port(self):
return self._port_spinner.get_value_as_int()
def set_password(self, password):
if password is None:
password = ""
self._password_entry.set_text(password)
def get_password(self):
if self._password_entry.get_text() == "":
return None
else:
return self._password_entry.get_text()
def lock(self):
self._lock(False)
def unlock(self):
self._lock(True)
def _lock(self, sensitive):
self._table.set_sensitive(sensitive)
from threading import Thread
class CoverPanel(Gtk.HPaned):
_default_cover_size = 128
def __init__(self):
Gtk.HPaned.__init__(self)
# Image # Image
self._cover_pixbuf = None
self._cover_image = Gtk.Image() self._cover_image = Gtk.Image()
self._cover_image.connect('size-allocate', self.on_resize) self._cover_image.connect('size-allocate', self.on_resize)
# EventBox # EventBox
self._cover_box = Gtk.EventBox() self._cover_box = Gtk.EventBox()
self._cover_box.add(self._cover_image) self._cover_box.add(self._cover_image)
hpaned.pack1(self._cover_box, resize=True) self.pack1(self._cover_box, resize=True)
# GridModel # GridModel
self._cover_grid_model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str) self._cover_grid_model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str)
# GridView # GridView
@ -71,65 +261,42 @@ class MCGGtk(Gtk.Window):
self._cover_grid.set_margin(0) self._cover_grid.set_margin(0)
self._cover_grid.set_row_spacing(0) self._cover_grid.set_row_spacing(0)
self._cover_grid.set_column_spacing(0) self._cover_grid.set_column_spacing(0)
self._cover_grid.set_item_padding(0) self._cover_grid.set_item_padding(1)
self._cover_grid.set_reorderable(False) self._cover_grid.set_reorderable(False)
self._cover_grid.set_selection_mode(Gtk.SelectionMode.SINGLE) self._cover_grid.set_selection_mode(Gtk.SelectionMode.SINGLE)
#color = self.get_style_context().lookup_color('bg_color')[1]
#self._cover_grid.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(color.red, color.green, color.blue, 1))
# Scroll # Scroll
_cover_grid_scroll = Gtk.ScrolledWindow() self._cover_grid_scroll = Gtk.ScrolledWindow()
_cover_grid_scroll.add_with_viewport(self._cover_grid) self._cover_grid_scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
hpaned.pack2(_cover_grid_scroll, resize=False) self._cover_grid_scroll.add(self._cover_grid)
self.pack2(self._cover_grid_scroll, resize=False)
# Signals # Progress Bar
self.connect("focus", self.focus) self._progress_box = Gtk.VBox()
self.connect("delete-event", self.destroy) self._progress_bar = Gtk.ProgressBar()
#self.coverGrid.connect("selection-changed", self.coverGridShow) self._progress_box.pack_start(self._progress_bar, True, False, 0)
self._cover_grid.connect("item-activated", self._cover_grid_play)
self._mcg.connect_signal(mcg.MCGClient.SIGNAL_CONNECT, self.connect_callback)
self._mcg.connect_signal(mcg.MCGClient.SIGNAL_IDLE_PLAYER, self.idle_player_callback)
self._mcg.connect_signal(mcg.MCGClient.SIGNAL_UPDATE, self.update_callback)
def destroy(self, widget, state): def update(self, albums):
if self._mcg is not None: self._go = True
self._mcg.disconnect() Thread(target=self._update, args=(albums,)).start()
Gtk.main_quit()
def focus(self, widget, state): def stop_update(self):
self._update() self._go = False
def toolbar_callback(self, widget): def _update(self, albums):
if widget == self._action_connect: Gdk.threads_enter()
if self._mcg.is_connected(): self.remove(self._cover_grid_scroll)
self._mcg.disconnect() self._progress_bar.set_fraction(0.0)
else: self.pack2(self._progress_box, False)
self._mcg.connect() self.show_all()
elif widget == self._action_update:
self._update()
def on_resize(self, widget, allocation):
self._resize_image()
def connect_callback(self, connected, message):
if connected:
self._action_connect.set_stock_id(Gtk.STOCK_CONNECT)
else:
self._action_connect.set_stock_id(Gtk.STOCK_DISCONNECT)
def idle_player_callback(self, state, album):
self._set_album(album.get_cover())
def update_callback(self, albums):
self._cover_grid.set_model(None) self._cover_grid.set_model(None)
self._cover_grid.freeze_child_notify() self._cover_grid.freeze_child_notify()
self._cover_grid_model.clear() self._cover_grid_model.clear()
Gdk.threads_leave()
i = 0
n = len(albums)
for hash in albums.keys(): for hash in albums.keys():
album = albums[hash] album = albums[hash]
file = album.get_cover() file = album.get_cover()
@ -139,35 +306,44 @@ class MCGGtk(Gtk.Window):
if pixbuf is None: if pixbuf is None:
continue continue
self._cover_grid_model.append([pixbuf, album.get_title(), GObject.markup_escape_text("\n".join([album.get_title(), album.get_artist()]))]) self._cover_grid_model.append([pixbuf, album.get_title(), GObject.markup_escape_text("\n".join([album.get_title(), album.get_artist()]))])
i += 1
GObject.idle_add(self._progress_bar.set_fraction, i/n)
if not self._go:
self._cover_grid_model.clear()
break
Gdk.threads_enter()
self._cover_grid.set_model(self._cover_grid_model) self._cover_grid.set_model(self._cover_grid_model)
self._cover_grid.thaw_child_notify() self._cover_grid.thaw_child_notify()
self.remove(self._progress_box)
self.pack2(self._cover_grid_scroll, False)
Gdk.threads_leave()
def _update(self): def set_album(self, url):
if self._mcg is None or not self._mcg.is_connected(): # Check path
return
self._mcg.update()
def _set_album(self, url):
# Pfad überprüfen
if url is not None and url != "": if url is not None and url != "":
# Bild laden und zeichnen # Load image and draw it
self._cover_pixbuf = GdkPixbuf.Pixbuf.new_from_file(url) self._cover_pixbuf = GdkPixbuf.Pixbuf.new_from_file(url)
self._resize_image() self._resize_image()
else: else:
# Bild zurücksetzen # Reset image
self._cover_pixbuf = None self._cover_pixbuf = None
self._cover_image.clear() self._cover_image.clear()
def on_resize(self, widget, allocation):
self._resize_image()
def _resize_image(self): def _resize_image(self):
"""Diese Methode skaliert das geladene Bild aus dem Pixelpuffer """Diese Methode skaliert das geladene Bild aus dem Pixelpuffer
auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse
""" """
pixbuf = self._cover_pixbuf pixbuf = self._cover_pixbuf
size = self._cover_image.get_allocation() size = self._cover_image.get_allocation()
## Pixelpuffer überprüfen ## Check pixelbuffer
if pixbuf is None: if pixbuf is None:
return return
@ -184,15 +360,62 @@ class MCGGtk(Gtk.Window):
self._cover_image.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER)) self._cover_image.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
def _cover_grid_play(self, widget, item):
# TODO coverGridPlay()
pass import os
import configparser
class Configuration:
CONFIG_FILE = '~/.config/mcggtk.config'
def __init__(self):
self._config = configparser.RawConfigParser()
self.host = 'localhost'
self.port = 6600
self.password = ""
self.load()
def load(self):
if not os.path.isfile(self._get_filename()):
return
self._config.read(self._get_filename())
if self._config.has_section('connection'):
if self._config.has_option('connection', 'host'):
self.host = self._config.get('connection', 'host')
if self._config.has_option('connection', 'port'):
self.port = self._config.getint('connection', 'port')
if self._config.has_option('connection', 'password'):
self.password = self._config.get('connection', 'password')
def save(self):
if not self._config.has_section('connection'):
self._config.add_section('connection')
self._config.set('connection', 'host', self.host)
self._config.set('connection', 'port', self.port)
self._config.remove_option('connection', 'password')
if self.password is not "":
self._config.set('connection', 'password', self.password)
with open(self._get_filename(), 'w') as configfile:
self._config.write(configfile)
def _get_filename(self):
#return os.path.expanduser(self.CONFIG_FILE)
return 'config'
if __name__ == "__main__": if __name__ == "__main__":
GObject.threads_init() GObject.threads_init()
Gdk.threads_init()
mcgg = MCGGtk() mcgg = MCGGtk()
mcgg.show_all() mcgg.show_all()
try: try: