Tiling window manager——平铺式窗口管理器,不同于浮动式那样以层的形式管理窗口,平铺式将所有窗口平铺开来,窗口之间无缝对接,这有助于同时查看多个窗口的内容,对于程序员来说这尤其有用。
平铺式窗口管理器有很多,比较有名的像awesome,i3等等,不过awesome是以lua语言写的,我没用过,对此不做介绍。而qtile是以纯Python写的,作为一个Python迷(虽然海是菜鸟中的菜鸟),我自然选择qtile。
Qtile的图形对象(object graph)共七个部分:layouts、windows、screen、groups、bars、widgets还有一个root。
layouts:布局,以什么方式来分布窗口,包括Max(单窗口占据整个屏幕)、Tiling(按比例分配窗口)、Floationg(浮动窗口)、MonadTall(模拟XMonad的平铺式窗口)、Slice、Stack(将屏幕分成多个栈),TreeTab、Zoomy
groups:相当于其他桌面环境的工作区。
screens:物理屏幕
bar:栏,可以理解为任务栏
widget:小工具,组件,包括很多,像是时间,天气,邮件查看,任务列表,groupbox等等。。。
安装qtile的方法可以参考官网,不过我当时照着官网编译的时候提示错误,是一个ImportError,好像是说缺少某个模块,不过这个模块我没有找到。。。后来在google了一番后,找到如下的解决方法:pip install cffi
pip install xcffib
git clone -b xcb https://github.com/flacjacket/cairocffi.git
cd cairocffi && sudo python setup.py install
git clone git://github.com/qtile/qtile.git
cd qtile
sudo python setup.py install
貌似是作者把几个模块编译到了一起。。。
关于配置文件的写法官网上提供了很多,既有默认配置,也有其他用户写的配置,可以借鉴后使用。之后我会给出我的配置,不过就是并不好。
详细的内容可以看官方文档,不过官方文档并不全面,很多配置都未提到。尤其是关于windows(窗口对象)方面的,除了官方默认配置外,其他用户的配置都用到了windows方面的内容。我谷歌、度娘了半天,一无所获(也可能是我查的方式不对)。看来可能要直接看源代码了。。。
刚才传了一下,好像不怎么好上传,现直接贴出配置文件:#!/usr/bin/env python
#-*-coding:utf-8-*-
import os
from libqtile import layout,widget,bar,manager,hook
from libqtile.widget import base
from libqtile.manager import Screen,Drag
from libqtile.command import lazy
try:
from libqtile.manager import Key,Group
except ImportError:
from libqtile.config import Key,Group
sup='mod4'
alt='mod1'
#键位映射
keys=[
#Layout
Key([sup],'Down',lazy.layout.down()),
Key([sup],'Up',lazy.layout.up()),
Key([alt],'Tab',lazy.layout.next()),
Key([alt,'shift'],'Tab',lazy.layout.previous()),
Key([sup],'space',lazy.nextlayout()),
Key([sup],'k',lazy.layout.increase_ratio()),
Key([sup],'j',lazy.layout.decrease_ratio()),
Key([sup],'l',lazy.layout.increase_nmaster()),
Key([sup],'h',lazy.layout.decrease_nmaster()),
#Window
Key([alt],'F4',lazy.window.kill()),
Key([alt],'F10',lazy.window.toggle_maximize()),
#Group
Key([sup],'Left',lazy.group.prevgroup()),
Key([sup],'Right',lazy.group.nextgroup()),
#Application launchers
Key([sup],'Return',lazy.spawn('sakura')),
Key([sup],'f',lazy.spawn('firefox')),
Key([sup],'m',lazy.spawn('vlc')),
Key([sup],'v',lazy.spawn('virtualbox')),
Key([sup],'t',lazy.spawn('Thunar')),
Key([sup],'q',lazy.spawn('QQ')),
Key([sup],'z',lazy.spawn('zim')),
#Audio
Key([sup],'F8',lazy.spawn('amixer --quiet set Master mute')),
Key([sup],'F9',lazy.spawn('amixer --quiet set Master unmute')),
Key([alt],'minus',lazy.spawn('amixer --quiet set Master 2dB-')),
Key([alt,'shift'],'equal',lazy.spawn('amixer --quiet set Master 2dB+')),
#restart qtile
Key([sup],'r',lazy.restart()),
#shutdown
Key([sup,'shift'],'q',lazy.spawn('shutdown -h now')),
#interact with prompts:
Key([sup],'s',lazy.spawncmd()),
]
mouse=[
Drag([sup],"Button1",lazy.window.set_position_floating(),
start=lazy.window.get_position()),
Drag([sup],"Button3",lazy.window.set_size_floating(),
start=lazy.window.get_size()),
]
#建立groups
group_names=[
("code1",{'layout':'tile'}),
("code2",{"layout":'tile'}),
("web",{"layout":"max"}),
("vbox",{"layout":"max"}),
('music',{'layout':'max'}),
('doc',{'layout':'max'}),
('chat',{'layout':'max'})
]
groups=[Group(name,**kwargs) for name,kwargs in group_names]
for i , (name,kwargs) in enumerate(group_names,1):
keys.append(Key([sup],str(i),lazy.group[name].toscreen()))
keys.append(Key([sup,'shift'],str(i),lazy.window.togroup(name)))
#建立layouts
layouts=[
layout.Tile(border_focus='#196ff2',border_width=1),
layout.Max()
]
font='WenQuanYi Micro Hei'
fontsize=16
foreground='#FFFFFF'
background='#000000'
def humanize_bytes(value):
suff=['B','K','M','G','T']
while value > 1024. and len(suff)>1:
value/=1024.
suff.pop(0)
return "% 3s%s" %('%.3s'%value,suff[0])
#本来这个是用来显示CPU、Mem、Net的情况,后来不知为何无法显示
class Metrics(base._TextBox):
defaults=[
('font','Arial','Metrics font'),
('fontsize',None,'Metircs pixel size'),
('pading',None,'Metrics padding'),
('background','00000','background color'),
('foreground','ffffff','foreground color')
]
def __init__(self,**kwargs):
base._TextBox.__init__(self,**kwargs)
self.cpu_usage,self.cpu_total=self.get_cpu_stat()
self.interfaces={}
self.idle_ifaces={}
def _configure(self,qtile,bar):
base._TextBox._configure(self,qtile,bar)
self.timeout_add(0,self._update)
def get_cpu_stat(self):
stat=[int(i) for i in open('/proc/stat').readline().split()[1:]]
return sum(stat[:3]),sum(stat)
def get_cpu_usage(self):
new_cpu_usage,new_cput_total=self.get_cpu_stat()
cput_usage=new_cpu_usage-self.cpu_usage
cpu_total=new_cpu_total-self.cpu_total
self.cpu_usage=new_cpu_usage
self.cpu_tptal=new_cpu_total
return 'Cpu: %d%%' % (float(cpu_usage)/float(cpu_total)*100.)
def get_mem_usage(self):
info={}
for line in open('/proc/meminfo'):
key,val=line.split(':')
info[key]=int(val.spilt()[0])
mem=info['MemTotal']
mem-=info['MemFree']
mem-=info['Buffers']
mem-=info['Cached']
return 'Mem: %d%%' % (float(mem)/float(info['MemTotal'])*100)
def get_net_usage(self):
interfaces=[]
basedir='/sys/class/net'
for iface in os.listdir(basedir):
j=os.path.join
ifacedir=j(basedir,iface)
statdir=j(ifacedir,'statistics')
idle=iface in self.idle_ifaces
try:
if int(open(j(ifacedir, 'carrier')).read()):
rx = int(open(j(statdir, 'rx_bytes')).read())
tx = int(open(j(statdir, 'tx_bytes')).read())
if iface not in self.interfaces:
self.interfaces[iface] = (rx, tx)
old_rx, old_tx = self.interfaces[iface]
self.interfaces[iface] = (rx, tx)
rx = rx - old_rx
tx = tx - old_tx
if rx or tx:
idle = False
self.idle_ifaces[iface] = 0
rx = humanize_bytes(rx)
tx = humanize_bytes(tx)
interfaces.append('%s: %s / %s' % (iface, rx, tx))
except:
pass
if idle:
interfaces.append('%s: %-11s' % (iface, ("%ds idle" % self.idle_ifaces[iface]))
)
self.idle_ifaces[iface] += 1
if self.idle_ifaces[iface] > 30:
del self.idle_ifaces[iface]
return " | ".join(interfaces)
def _update(self):
self.update()
self.timeout_add(1, self.update)
return False
def update(self):
stat = [self.get_cpu_usage(), self.get_mem_usage()]
net = self.get_net_usage()
if net:
stat.append(net)
self.text = " | ".join(stat)
self.bar.draw()
return True
screens=[
Screen(top=bar.Bar([
widget.TextBox(text='◤ ',fontsize=40,foreground='#323335',padding=0),
widget.GroupBox(font=font,fontsize=fontsize,active=foreground,inactive="#808080",borderwidth=3),
widget.Prompt(font=font,fontsize=fontsize),
widget.CurrentLayout(font=font,foreground=foreground,fontsize=fontsize),
widget.Sep(foreground=background,linewidth=3),
widget.WindowName(font=font,fontsize=fontsize,foreground=foreground),
widget.Notify(font=font,fontsize=fontsize),
Metrics(font=font,fontsize=fontsize,foreground=foreground),
widget.Volume(foreground="#70ff70"),
widget.BatteryIcon(),
widget.Systray(icon_size=18),
widget.Clock(font=font,fontsize=fontsize,foreground=foreground,fmt='%Y-%m-%d %a %H:%M'),
],30))
]
@hook.subscribe.client_new
def dialogs(window):
if(window.window.get_wm_type() == 'dialog'
or window.window.get_wm_transient_for()):
window.floating = True