看这个标题一定很奇怪:难道我以前控制音乐播放不用鼠标么?这个文章的标题看起来像鼠标刚刚发明的推广期的文章……不过,当然不是这样的!
其实只是想:如何把我的小本合上放书架上,当作一个音乐播放器+功放,并用我的无线6键鼠当遥控器遥控控制之。
想想其实还是蛮有意义的功能,这样我做作业的时候可以不用戴耳机,不用用MP3,直接把本当播放器;同时,我不会看到屏幕上的东西,可以安心做作业~再看看我的6键无限鼠,那额外的功能键平时根本不用,也想不出能有什么用……这么好的东西就这样被我浪费了……(话说,拿本当音乐播放器是不是更浪费?)
说干就干!
首先提出构想:左键用于暂停和播放,滚轮调节音量,侧边的两个功能键用来切换上一首和下一首。至于右键和中键……再说吧,说不定以后可以扩展更多功能?说不定以后高兴了弄个鼠标手势什么的~嘿嘿
接下来查找资料。印象中我的 Audacious 是可以用 D-Bus 控制的。简单地查阅了一下相关资料,发现了一个叫做 MPRIS 的播放器控制接口。为此,我还专门学习了一下 python-dbus 的使用。
插一句话:python-dbus 怎么没有中文教程啊!英文教程看得还是蛮吃力的……
连接播放器的代码很简单:
import dbus bus = dbus.SessionBus() player = bus.get_object('org.mpris.audacious', '/Player')
其中 org.mpris.audacious
指的是 audacious,支持 MPRIS 的其他播放器还可以有如下:
org.mpris.bmp
org.mpris.vlc
org.mpris.xmms2
至于那个 /Player
指的是我们要获取的是一个叫 Player
的对象,用于控制播放器。在 MPRIS 中除了 Player
以外,还有根对象 (/
) 和 TrackList
对象 (/TrackList
) 两个,具体的用法其文档里详细说明。这里我只用到了 Player
对象……
看看 Player
对象的方法还挺多,这里我用了这几个:
Next()
- 切换到下一首歌曲Prev()
- 切换到上一首歌曲Pause()
- 暂停/继续Play()
- 播放歌曲VolumeGet()
- 获取当前音量VolumeSet()
- 设置音量
根据前面写的需求,这些已经足够了~
怎么用这些?很简单嘛!就像平时在 Python 里面调用类的方法一样~类型转换?不,那完全不需要你关心!
Python 程序拥有控制播放器的能力了,可是鼠标呢?如何捕获鼠标的动作呢?
想想……屏幕都关了……你还能指望他为你显示什么呢?干脆建立一个窗口把整个屏幕盖住算了!然后让他截获鼠标事件。
快速学习了一下 pygtk 的用法(其实就是照着 Hello World 打了一遍),差不多就知道怎么用了。窗口的建立代码大约如下:
window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.fullscreen() window.show() gtk.main()
这段代码很好理解了,第一行就是创建窗口对象,第二行令其全屏,第三行设置其显示,第四行开始 GTK 的主循环。
下面就是鼠标事件的问题了~添加事件用的是类似下面的代码:
window.connect('key-press-event', key_press) window.connect('destroy', lambda widget, data = None: gtk.main_quit())
第一个是注册了键盘按键的事件捕获,将 key-press-event
事件绑定上 key_press
这个函数,而把窗口的销毁事件 destroy
连接到退出 GTK 主循环。说一句废话,连接事件的语句要放在 gtk.main()
之前……key_press
主要用来实现当点击 escape 时退出:
def key_press(widget, event, data = None): if gdk.keyval_name(event.keyval) == 'Escape': window.destroy()
好了,下面是鼠标点击事件 button-press-event
:
def button_press(widget, event, data = None): print event window.connect('button-press-event', button_press)
试试?不行的!差了一番后,发现 Window 这个东西默认是不打开鼠标点击的事件捕获的,逼近大家很少会点窗口本身,都是点里面的按钮什么的……下面的代码要求窗口打开鼠标点击事件的捕获:
window.add_events(gtk.gdk.BUTTON_PRESS_MASK)
接下来就可以了~event.button
表示的是点击的键是哪个。检查了一下,左键是1,右键是3,中键是2,功能键1是8,功能键2是9。滚轮呢?
原来滚轮有自己的事件 scroll
。
def scroll(widget, event, data = None): print event window.connect('scroll-event', scroll)
直接可以用了。识别滚轮方向的是 event.direction
,应该是 gtk.gdk.SCROLL_UP
、SCROLL_DOWN
、SCROLL_LEFT
、SCROLL_RIGHT
其中的一个。
下面根据最初的设想,把他们都拼起来就形成了最初的版本:
#!/usr/bin/env python import pygtk pygtk.require('2.0') import gtk from gtk import gdk import dbus def button_press(widget, event, data = None): button = event.button if button == 1: if player.GetStatus() == 2: player.Play() else: player.Pause() elif button == 9: player.Prev() elif button == 8: player.Next() def scroll(widget, event, data = None): if event.direction == gdk.SCROLL_UP: player.VolumeSet(player.VolumeGet() + 3) elif event.direction == gdk.SCROLL_DOWN: player.VolumeSet(player.VolumeGet() - 3) def key_press(widget, event, data = None): if gdk.keyval_name(event.keyval) == 'Escape': window.destroy() bus = dbus.SessionBus() player = bus.get_object('org.mpris.audacious', '/Player') window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.connect('destroy', lambda widget, data = None: gtk.main_quit()) window.add_events(gdk.BUTTON_PRESS_MASK) window.connect('button-press-event', button_press) window.connect('key-press-event', key_press) window.connect('scroll-event', scroll) window.fullscreen() window.show() gtk.main()
OK 看起来不错了,也能控制了。可是,作为一个程序员,怎么能不考虑各种意外呢?试想,如果我们控制着控制着,突然,一个不明真相的窗口弹了出来怎么办呢?!这是一个问题……
最容易想到的方法:把这个窗口永久置顶!
事实上也确实找到了这么个方法,就是把窗体注册成 dock。我们知道 dock 是需要永久置顶的~
用如下方法可以让窗体变成 Dock:
window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
但是问题又来了:dock 似乎没法接受键盘输入。因为 dock 只是形式上覆盖,并且可以拦截一切在他上面发生的鼠标事件,可键盘焦点就未必在他那里了~
那么我们就不要键盘事件了吧~于是我就把键盘退出给删了,改成了双击退出~查了下资料,双击事件也通过 button-press-event
发送的,必须根据 event.type
来判断是单击、双击还是三击。此外,双击和三击中的每一次点击都会引发一次单击事件。
OK,于是我们把 button_press 函数改成下面这样:
def button_press(widget, event, data = None): if event.type == gdk._2BUTTON_PRESS: window.destroy() return elif event.type == gdk._3BUTTON_PRESS: return button = event.button if button == 1: if player.GetStatus() == 2: player.Play() else: player.Pause() elif button == 9: player.Prev() elif button == 8: player.Next()
然后删掉键盘处理,最后就成了这样:
#!/usr/bin/env python import pygtk pygtk.require('2.0') import gtk from gtk import gdk import dbus def button_press(widget, event, data = None): if event.type == gdk._2BUTTON_PRESS: window.destroy() return elif event.type == gdk._3BUTTON_PRESS: return button = event.button if button == 1: if player.GetStatus() == 2: player.Play() else: player.Pause() elif button == 9: player.Prev() elif button == 8: player.Next() def scroll(widget, event, data = None): if event.direction == gdk.SCROLL_UP: player.VolumeSet(player.VolumeGet() + 3) elif event.direction == gdk.SCROLL_DOWN: player.VolumeSet(player.VolumeGet() - 3) bus = dbus.SessionBus() player = bus.get_object('org.mpris.audacious', '/Player') window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.connect('destroy', lambda widget, data = None: gtk.main_quit()) window.add_events(gdk.BUTTON_PRESS_MASK) window.connect('button-press-event', button_press) window.connect('scroll-event', scroll) window.set_type_hint(gdk.WINDOW_TYPE_HINT_DOCK) window.fullscreen() window.show() gtk.main()
完成了!
以后就可以拿我的小本来当音乐播放器+功放啦~
参考资料:
Comments !