用于计算modbus通信协议的CRC16校验值。
举例: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08得到校验值为0xB0CF(或0xCFB0)
计算方法,分两种,直接计算和查表计算。

直接计算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def calc_crc(string):
data = bytearray.fromhex(string)
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))


crc = calc_crc('0102030405060708')
print(crc)

查表计算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/env python3
table_crc_hi = (
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40)

table_crc_lo = (
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40)


def crc16(st):
crc_hi = crc_lo = 0xFF
L = []
st = st.strip().replace(' ', '')
if len(st) % 2:
return None
for j in range(0, len(st), 2):
L.append(st[j:(j + 2)])
i = 0
for ch in L:
ch = int(ch, 16)
i = crc_hi ^ ch
crc_hi = crc_lo ^ table_crc_hi[i]
crc_lo = table_crc_lo[i]
result = hex(crc_hi << 8 | crc_lo)
addition = 6 - len(result)
if addition > 0:
result = result.replace('x', 'x' + '0' * addition)
return result


if __name__ == '__main__':
print(crc16('01 02 03 04 05 06 07 08 '))

在上一篇里面,通过Python简单的爬虫在网上获取了天气实况数据,这次想办法用Python把数据发布在微博上面,微博提供了这样的接口,我们所做的就是用Python实现和微博公共接口的对接。
在这之前我们需要访问微博 微博开放平台 来注册一个应用,这个完全是免费的。注册成功之后获得AppKeyApp Secret,这是非常重要的数据,在后面会用到。

注册应用

通过上面的keySecret,我们可以从新浪微博服务器获取一个最最关键的参数,就是access_token,这个参数表明我们通过了服务器认证,也就是说微博认为我们是拥有发布微博的权限的。
获取的方法大概是:
1、使用get方法访问新浪的OAuth认证页,需要传递的参数是AppKey重定向地址,在通过验证之后,将重定向到我们提供的重定向地址,并提供给我们一个CODE参数的值。
2、发送post请求给微博服务器,包含以下参数:

1
2
fields = {'code': code, 'redirect_uri': self.redirect_uri, 'client_id': self.client_id,
'client_secret': self.client_secret, 'grant_type': 'authorization_code'}

收到返回值,一个就是access_token的值,另外一个expires是指access_token的超时时间,超时后需重新获取access_token
之后的工作就是调用接口发送数据了,当然,微博在这接口里面做出了很多限制,以前限制很少,现在用起来不那么舒服了:

限制

简易的代码如下:两个文件:weiboSDK.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__version__ = '1.0.0'
__author__ = 'aeropig@163.com'

'''
Python3 client SDK for sina weibo API using OAuth 2.
'''
# import gzip
import time
import requests
# import json
# import hmac
# import base64
# import hashlib
import logging
# import mimetypes
# import collections
# from io import StringIO
import webbrowser
default_redirect_uri = 'https://api.weibo.com/oauth2/default.html'
_HTTP_GET = 0
_HTTP_POST = 1
_HTTP_POST_UPLOAD = 2


def _encode_params(**kw):
args = []
for (k, v) in kw.items():
args.append('%s=%s' % (str(k), str(v)))
return '&'.join(args)


class APIError(BaseException):
'''
raise APIError
'''

def __init__(self, error_code, error, request):
self.error_code = error_code
self.error = error
self.request = request
BaseException.__init__(self, error)

def __str__(self):
return 'APIError: %s: %s, request: %s' % (self.error_code, self.error, self.request)


def _http_get(url, authorization=None, **kw):
logging.info('GET %s' % url)
return _http_call(url, _HTTP_GET, authorization, **kw)


def _http_post(url, authorization=None, **kw):
logging.info('POST %s' % url)
return _http_call(url, _HTTP_POST, authorization, **kw)


def _http_post_upload(url, authorization=None, **kw):
logging.info('MULTIPART POST %s' % url)
return _http_call(url, _HTTP_POST_UPLOAD, authorization, **kw)


def _http_call(the_url, method, authorization, **kw):
'''
send an http request and return a json object if no error occurred.
'''
if authorization:
kw['access_token'] = authorization
if method == _HTTP_GET:
r = requests.get(the_url, params=kw)
elif method == _HTTP_POST:
r = requests.post(the_url, data=kw)
elif method == _HTTP_POST_UPLOAD:
_files = {'pic': open(kw['pic'], 'rb')}
kw.pop('pic')
r = requests.post(the_url, data=kw, files=_files)
else:
pass
return r.json()


class APIClient(object):
'''
API client using synchronized invocation.
'''

def __init__(self, app_key, app_secret, redirect_uri=default_redirect_uri, domain='api.weibo.com', version='2'):
self.client_id = str(app_key)
self.client_secret = str(app_secret)
self.redirect_uri = redirect_uri
self.auth_url = 'https://%s/oauth2/' % domain
self.api_url = 'https://%s/%s/' % (domain, version)

def authorize(self):
if isinstance(self.access_token, str):
return
authorize_url = '%s%s?' % (self.auth_url, 'authorize')
fields = {'client_id': self.client_id,
'redirect_uri': self.redirect_uri}
params = _encode_params(**fields)
webbrowser.open(authorize_url + params)

def set_access_token(self, **token_dict):
self.access_token = token_dict['access_token']
self.uid = token_dict.get('uid', 0)
current = int(time.time())
self.expires = token_dict['expires_in'] + current
return (self.access_token, self.expires)

def request_access_token(self, code):
fields = {'code': code, 'redirect_uri': self.redirect_uri, 'client_id': self.client_id,
'client_secret': self.client_secret, 'grant_type': 'authorization_code'}
r = _http_post('%s%s' % (self.auth_url, 'access_token'), **fields)
return self.set_access_token(**r)

# def refresh_token(self, refresh_token):
# r = _http_post(req_str,
# client_id=self.client_id,
# client_secret=self.client_secret,
# refresh_token=refresh_token,
# grant_type='refresh_token')
# return self._parse_access_token(r)

def is_expires(self):
return not self.access_token or time.time() > self.expires

def __getattr__(self, attr):
if '__' in attr:
return getattr(self.get, attr)
return _Callable(self, attr)


class _Executable(object):

def __init__(self, client, method, path):
self._client = client
self._method = method
self._path = path

def __call__(self, **kw):
if self._method == _HTTP_POST and 'pic' in kw:
self._method = _HTTP_POST_UPLOAD
return _http_call('%s%s.json' % (self._client.api_url, self._path), self._method, self._client.access_token, **kw)

def __str__(self):
return '_Executable (%s %s)' % (self._method, self._path)

__repr__ = __str__


class _Callable(object):

def __init__(self, client, name):
self._client = client
self._name = name

def __getattr__(self, attr):
if attr == 'get':
return _Executable(self._client, _HTTP_GET, self._name)
if attr == 'post':
return _Executable(self._client, _HTTP_POST, self._name)
name = '%s/%s' % (self._name, attr)
return _Callable(self._client, name)

def __str__(self):
return '_Callable (%s)' % self._name

__repr__ = __str__


if __name__ == '__main__':
pass

第二个文件:weiboAPP.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import weiboSDK

uid = 'xxxxxxxxxxx'
client_id = 'xxxxxxxxxxxxxx'
client_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxx'
expires = 1672970161


if __name__ == '__main__':
weibo = weiboSDK.APIClient(client_id, client_secret)

if access_token is None:
weibo.authorize()
code = input("输入从浏览器获取的CODE:")
accessTuple = weibo.request_access_token(code)
print(accessTuple)
access_token = accessTuple[0]
expires = accessTuple[1]

weibo.set_access_token(access_token=access_token, expires_in=expires)

weibo.statuses.share.post(status='这条微博只是一个测试[doge]... https://www.jianshu.com/p/d55b71e85bd0 ')

执行效果如下:

发布成功

程序自动发送的微博如图:

需求示例

这里面的气象数据都是从别的网站上面获取,需要人工访问网站,查看每个小时的气温和降水量数据,然后计算出来,再编辑成微博发送,这样每天到时间都要进行人工操作显得很麻烦,有时候晚上忙就会忘掉。
于是我想到,这个工作其实可以全部交给电脑去自动完成,每天定时去获取网络数据,计算数值,并编辑要发送的内容,自动发送微博。

把这个需求分成两部分,一是生成要发送的微博的内容,主要是使用python3的requests库,获取网页文本,并用beautifulsoup库进行解析。二是如何使用python通过微博的验证,成功发布微博。
这里面需要注意的是,气象日里面的”今天“是指前一天的晚上20时到今天的晚上20时。 如下图红色方框区域就是程序要获取的数据。

数据

以下是代码和执行的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import requests
import time
from bs4 import BeautifulSoup
from datetime import datetime

tempCalcHours = ('02', '08', '14', '20')
baseUrl = 'http://q-weather.info/weather/'

hh = {('{:02d}'.format(i)) for i in range(24)}
dictData = dict.fromkeys(hh, ('0', '0'))


def getDateString():
return time.strftime('%Y-%m-%d', time.localtime())


# 从网站获取每个小时所对应的温度和降水量,放在字典中
def getUsefulValuesFromWeb(stationNum):
url = baseUrl + str(stationNum) + '/today/'
req = requests.get(url)
soup = BeautifulSoup(req.text, 'lxml')

judgeDataIntegrity = 0
today = getDateString()
for tr in soup.tbody.find_all('tr'):
strTd = tr.find('td').string
hour = strTd[-8:-6]

# 判断是否是今天的数据 假设今天是1月2日,今天的数据是指1月1日20时到1月2日20时,两种情况:
# 1、 1月2日 且 20时之前
# 2、 不是1月2日 且 20时之后
if strTd.startswith(today) == (hour <= '20'):
judgeDataIntegrity += 1
hourTuple = []
hourTuple.append(tr.find_all('td')[1].string) # 小时气温
hourTuple.append(tr.find_all('td')[-2].string) # 小时降水量
dictData[hour] = hourTuple
if judgeDataIntegrity != 24: # 不等于24说明数据不足24小时的
return None
return dictData


def combineData(dictData):
listData = sorted(dictData.items(), key=lambda d: d[0])
dailyData = []
totalTemp = 0.0
totalPrec = 0.0
for item in listData:
totalPrec += float(item[1][1])
if item[0] in tempCalcHours:
dailyData.append(item[1][0])
totalTemp += float(item[1][0])
if totalTemp > 0: # 保证四舍五入正确
avgTmp = totalTemp / 4.0 + 0.0001
else:
avgTmp = totalTemp / 4.0 - 0.0001
dailyData.append('{:.1f}'.format(avgTmp))
dailyData.append('{:.1f}'.format(totalPrec))
# print(dailyData)
return dailyData


def combineWeiboStatus(dailyData, stationNum):
today = getDateString()
status = str(stationNum) + '站' + today + '数据: 02时气温' + dailyData[0] + '度,08时气温'\
+ dailyData[1] + '度,14时气温' + dailyData[2] + '度,20时气温' + dailyData[3] + '度,平均气温'\
+ dailyData[4] + '度,降水量' + dailyData[5] + 'mm。\n'
return status


def main(station):
data = getUsefulValuesFromWeb(str(station))
if data is None:
raise Exception("数据异常")
daily = combineData(data)
status = combineWeiboStatus(daily, station)
return status


if __name__ == '__main__':
t = datetime.now()
if (t.hour < 20) or (t.hour == 20 and t.minute < 20): # 20时气温更新时间一般在20时20分之后
print('今天的数据可能未更新,请稍后执行本程序...')
else:
totalStatus = main(57131) + main(57039)
print('#西安气象数据# ' + totalStatus)

执行结果:

1
2
#西安气象数据# 571312018-01-07数据: 02时气温-2.4度,08时气温-1.7度,14时气温-0.4度,20时气温-0.1度,平均气温-1.2度,降水量1.3mm。
570392018-01-07数据: 02时气温-3.2度,08时气温-1.5度,14时气温0.3度,20时气温0.0度,平均气温-1.1度,降水量0.7mm。

使用 PyQt5

方便电脑传输网址或者文字内容到手机上面,扫一下二维码就行了,短网址偶尔也能用上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env python3
from PyQt5.QtGui import QPixmap
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
import requests
from os.path import expanduser


class Ui_qrDlg():

def setupUi(self):
self.setFixedSize(650, 400)
self.setWindowTitle('生成二维码/短网址')
self.setWindowFlags(Qt.WindowCloseButtonHint)
self.center()

gBoxStyle = '''QGroupBox{border-width:1px;border-style:solid;border-color:grey;margin-top:0.85ex;}
QGroupBox::title{subcontrol-origin:margin;subcontrol-position:top left;left:10px;margin-left:0px;padding:0 1px;}'''
self.gBoxInfo = QtWidgets.QGroupBox(self, title='输入内容')
self.gBoxInfo.setGeometry(12, 10, 300, 380)
self.gBoxInfo.setStyleSheet(gBoxStyle)
self.textEdit = QtWidgets.QTextEdit(self.gBoxInfo)
self.textEdit.setGeometry(7, 30, 286, 300)
self.textEdit.setObjectName('textEdit')
self.urlShow = QtWidgets.QTextEdit(self)
self.urlShow.setGeometry(385, 12, 253, 30)
self.urlShow.setObjectName('urlShow')
self.urlShow.setReadOnly(True)
self._label = QtWidgets.QLabel(self, text='短网址:')
self._label.setGeometry(325, 12, 60, 30)
self._label.setAlignment(Qt.AlignCenter)
self.btnCreate = QtWidgets.QPushButton(self.gBoxInfo, text='生成')
self.btnCreate.setGeometry(7, 338, 286, 35)
self.btnCreate.setObjectName('btnCreate')
self.gBoxPic = QtWidgets.QGroupBox(self, title='二维码')
self.gBoxPic.setGeometry(325, 45, 313, 345)
self.gBoxPic.setStyleSheet(gBoxStyle)
self.label = QtWidgets.QLabel(self.gBoxPic)
self.label.setGeometry(0, 14, 313, 313)
self.label.setAlignment(Qt.AlignCenter)
self.btnSave = QtWidgets.QPushButton(self.gBoxPic, text='另存为')
self.btnSave.setGeometry(220, 300, 80, 40)
self.btnSave.setObjectName('btnSave')
self.btnSave.hide()

def center(self):
screen = QtWidgets.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)


class qrDlg(QtWidgets.QDialog, Ui_qrDlg):

def __init__(self, parent=None):
super(qrDlg, self).__init__(parent)
self.setupUi()
self.btnCreate.clicked.connect(self.on_btnCreate_clicked)
self.btnSave.clicked.connect(self.on_btnSave_clicked)

def on_btnCreate_clicked(self):
import qrcode
info = self.textEdit.toPlainText().strip()
if not info:
QtWidgets.QMessageBox.information(self, ('提示'), ('请填写信息'), QtWidgets.QMessageBox.Yes)
self.btnSave.hide()
self.label.clear()
else:
qr = qrcode.QRCode(version=12, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=3, border=4)
qr.add_data(info)
# qr.make(fit=True)
img = qr.make_image()
filename = expanduser('~') + '/.qr_code.png'
img.save(filename)
pic = QPixmap(filename)
self.label.setPixmap(pic)
self.btnSave.show()
if info.startswith('http://') or info.startswith('https://'):
self.urlShow.setText(get_short_url(info))
self.urlShow.selectAll()

def on_btnSave_clicked(self):
qrPic = self.label.pixmap()
if qrPic:
filename, extra = QtWidgets.QFileDialog.getSaveFileName(self, '另存为', expanduser('~') + '/Desktop', 'PNG Images (*.png)')
if filename:
qrPic.save(filename, 'png')


def get_short_url(url, data=None):
baseurl = 'http://api.t.sina.com.cn/short_url/shorten.json?source=3271760578&url_long='
url = baseurl + url
rep = requests.get(url, timeout=60)
return rep.json()[0]['url_short']


if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
Dlg = qrDlg()
Dlg.show()
sys.exit(app.exec_())

效果如下图:

vid参数

看了下 NOAAhttp://www.wpc.ncep.noaa.gov/html/heatindex.shtml 这个链接,计算体感温度的。看了下计算方法,是简单的公式,就是没有看公式是怎么得出来的。写成python大概这样子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import math
def calc_heat_index(T, RH):
'''NOAA计算体感温度 参数为气温(摄氏度)和相对湿度(0~100或者0~1)'''
if RH < 1:
RH *= 100
T = 1.8 * T + 32
HI = 0.5 * (T + 61 + (T - 68) * 1.2 + RH * 0.094)
if HI >= 80: # 如果不小于 80华氏度 则用完整公式重新计算
HI = -42.379 + 2.04901523 * T + 10.14333127 * RH - .22475541 * T * RH \
- .00683783 * T * T - .05481717 * RH * RH + .00122874 * T * T * RH \
+ .00085282 * T * RH * RH - .00000199 * T * T * RH * RH
if RH < 13 and 80 < T < 112:
ADJUSTMENT = (13 - RH) / 4 * math.sqrt((17 - abs(T - 95)) / 17)
HI -= ADJUSTMENT
elif RH > 85 and 80 < T < 87:
ADJUSTMENT = (RH - 85) * (87 - T) / 50
HI += ADJUSTMENT
return round((HI - 32) / 1.8, 2)

返回的是摄氏度,取了两位小数。
如果30摄氏度,相对湿度80%,计算得到体感温度是37.67摄氏度。

KMA天气图的链接地址是这样的:

http://www.weather.go.kr/repositary/image/cht/img/up50_2015120900.png

后面的up50是500hpa高度,20151209是日期,00是UTC时次,对应北京时间08时。

这个链接如果访问起来是不方便的,需要每次手动修改链接里的时间和高度,如果要看连着好几个时次的天气图变化更不方便。所以写了几行js代码,自动加载最新时次的天气图,可以点击上一张下一张切换时间,还可以手动选择日期时间高度(时间不能距离现在太久,太久之前的天气图服务器上没有存档)。

海平面气压:高度选择中选 000

有一点要注意,22时的时候20时次的图没有出来,22时过了一段时间后才更新出来,为了避免图片加载空白,设置的是更新时次3小时后才采用更新的图片地址。比如22时30分打开,仍然显示14时的图,23时打开,才会显示晚上20时的图。(也可以手动点击下一张。)

天气图更新需要时间,比如说早上9点之前看不到当天08时次的图,因为还没更新,只能看凌晨02时次的图。可以跨月,比如当前位于12月1日02时次的时候,点击上一张,则访问11月30日20时次的图。

Chrome图:

天气图

在线查看:https://quzhanghao.me/kma.html

也可以把下面的代码保存成 .html 文件(确保是.html后缀而不是.html.txt) 主流浏览器都可以使用。

(如果用某些浏览器打开,如果需要手动敲入日期,需要注意,月份和日期如果小于10,前面要加0,比如’2015-01-01’)

默认打开显示最近更新的500hpa形势图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>KMA天气图</title>
<style>#header a{padding-left:1%}#footer a{padding-right:1%}#footer{text-align:center}</style>
</head>
<body>
<div id='header'>
日期:<input type='date' id='date'/>
时次:<select id='hh'><option value='02'>02</option><option value='08'>08</option><option value='14'>14</option><option value='20'>20</option></select>
Hpa:
<select id='hpa'><option value='up50'>500</option><option value='up70'>700</option><option value='up85'>850</option>
<option value='up92'>925</option><option value='sfc3'>000</option><option value='up30'>300</option><option value='up20'>200</option><option value='up10'>100</option></select>
<a href='#' onclick='reloadPic()'>载入图片</a><a href='#' onclick='last_next(-1)'>上一张</a><a href='#' onclick='last_next(1)'>下一张</a>
<a href='#' onclick='startPic()' id='run'>从当前选择时间开始动画</a><a href='#' onclick='endPic()' id='stop' style='display: none'>结束动画</a>
</div><br/>
<img id='pic' alt='Weather Chart'/><hr/>
<script>function loadPic(){var d,e,f,g,h,a=new Date,b=a.getFullYear(),c=a.getMonth()+1;10>c&&(c='0'+c),d=a.getDate(),10>d&&(d='0'+d),e=b+'-'+c+'-'+d,f=a.getHours(),f>=23?f='20':f>=17?f='14':f>=11?f='08':f>=5?f='02':(f='20',g=chTime(e+'00',-4),e=g.substr(0,4)+'-'+g.substr(4,2)+'-'+g.substr(6,2)),document.getElementById('hh').value=f,document.getElementById('date').value=e,h=chTime(e+f,-8),document.getElementById('pic').src='http://www.weather.go.kr/repositary/image/cht/img/up50_'+h+'.png'}function reloadPic(){var a=document.getElementById('date').value,b=document.getElementById('hh').value,c=chTime(a+b,-8),d=document.getElementById('hpa').value;document.getElementById('pic').src='http://www.weather.go.kr/repositary/image/cht/img/'+d+'_'+c+'.png'}function last_next(a){var f,b=0>a?-6:6,c=document.getElementById('date').value,d=document.getElementById('hh').value,e=chTime(c+d,b);document.getElementById('date').value=e.substr(0,4)+'-'+e.substr(4,2)+'-'+e.substr(6,2),document.getElementById('hh').value=e.substr(8,2),f=document.getElementById('hpa').value,e=chTime(c+d,b-8),document.getElementById('pic').src='http://www.weather.go.kr/repositary/image/cht/img/'+f+'_'+e+'.png'}function chTime(a,b){var c=parseInt(a.substr(0,4)),d=parseInt(a.substr(5,2))-1,e=parseInt(a.substr(8,2)),f=parseInt(a.substr(10,2)),g=new Date(c,d,e,f+b);return c=g.getFullYear(),d=g.getMonth()+1,e=g.getDate(),f=g.getHours(),d=d>=10?d:'0'+d,e=e>=10?e:'0'+e,f=f>=10?f:'0'+f,c+''+d+e+f}function startPic(){reloadPic(),document.getElementById('run').style.display='none',document.getElementById('stop').style.display='inline',intPic=setInterval(function(){var a,b,c,d;return document.getElementById('run').href='#',a=document.getElementById('date').value.split('-'),b=document.getElementById('hh').value,c=(new Date).getTime(),d=new Date(a[0],a[1]-1,a[2],parseInt(b)).getTime(),324e5>c-d?(document.getElementById('run').style.display='inline',document.getElementById('stop').style.display='none',window.clearInterval(intPic),void 0):(last_next(1),void 0)},2e3)}function endPic(){document.getElementById('run').style.display='inline',document.getElementById('stop').style.display='none',clearInterval(intPic)}window.onload=loadPic</script>
</body>
</html>

备份 Linux窗口管理器 Awesome 的配置文件

配置文件路径:~/.config/awesome/rc.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
-- 说明:Screen 指屏幕,awesome支持多屏幕。 Tag 指标签,一个标签对应一个虚拟桌面。 Client 指程序窗口。
-- {{{ 运行所需的库
-- 标准awesome库
local gears = require("gears")
local awful = require("awful")
awful.rules = require("awful.rules")
require("awful.autofocus")
-- 窗口和布局库
local wibox = require("wibox")
-- 主题控制库
local beautiful = require("beautiful")
-- 通知库
local naughty = require("naughty")
local menubar = require("menubar")
-- 导入右键程序菜单模块
require("debian.menu")
-- }}}

-- {{{ 错误处理
-- 检查awesome启动过程中是否发生错误
-- 如果发生错误则启用最近的正确配置,并通知用户
if awesome.startup_errors then
naughty.notify({ preset = naughty.config.presets.critical,
title = "Oops, there were errors during startup!",
text = awesome.startup_errors })
end

-- 处理awesome启动后的运行时错误
do
local in_error = false
awesome.connect_signal("debug::error", function (err)
-- Make sure we don't go into an endless error loop
if in_error then return end
in_error = true

naughty.notify({ preset = naughty.config.presets.critical,
title = "Oops, an error happened!",
text = err })
in_error = false
end)
end
-- }}}

-- {{{ 变量定义
-- 主题定义:颜色、图标、字体、壁纸
beautiful.init("/usr/share/awesome/themes/default/theme.lua") -- 默认主题配置文件所在路径

-- 设置默认的终端和文本编辑器
terminal = "mate-terminal"
editor = "pluma"
editor_cmd = terminal .. " -e " .. editor

-- 默认的modkey设置,用于快捷键。默认是Mod4,也就是Win键
-- 通常,Mod4键位于键盘左侧的Ctrl和Alt键之间
-- 如果你不喜欢这样的设置或者没有这个按键,
-- 建议你使用xmodmap或者别的工具把Mod4键映射到别的按键
modkey = "Mod4"

-- 默认的布局方式和顺序
-- 可以手动调整顺序,可以删除不需要的布局方式(建议注释掉即可,以免以后再想使用)
local layouts =
{
awful.layout.suit.tile, -- 平铺
awful.layout.suit.floating, -- 浮动
awful.layout.suit.tile.left,
awful.layout.suit.tile.bottom,
awful.layout.suit.tile.top,
awful.layout.suit.fair, -- 平铺,每个窗口大小一致
awful.layout.suit.max, -- 最大化
-- awful.layout.suit.fair.horizontal,
-- awful.layout.suit.max.fullscreen, -- 全屏
-- awful.layout.suit.spiral, -- 螺旋式
awful.layout.suit.magnifier -- 放大
}
-- }}}

-- {{{ 壁纸设置
if beautiful.wallpaper then
for s = 1, screen.count() do
gears.wallpaper.maximized("/home/td/Pictures/background.jpg", s, true) -- 替代引号中图片路径即可
end
end
-- }}}

-- {{{ 桌面标签
-- 定义一个包含所有虚拟桌面的标签列表
tags = {
names = {"Internet","Term","Code","Music",5,6}, -- 桌面标签的名称
layout = {layouts[1],layouts[1],layouts[7],layouts[1],layouts[2],layouts[1]} -- 标签默认布局,用索引表示,按照上面设置的layouts顺序
}
for s = 1, screen.count() do
tags[s] = awful.tag(tags.names, s, tags.layout)
end
-- }}}

-- {{{ 菜单
-- myawesomemenu是mymainmenu主菜单的一项,这里包含3个子菜单,可以手动修改子菜单
-- 还可以仿照myawesomemenu向主菜单添加新项,添加后务必加入下面的mymainmenu中
myawesomemenu = {
{ "edit config", "pluma /home/td/.config/awesome/rc.lua" }, -- 这个子菜单用于修改awesome配置文件,pluma是一个简单的文本编辑器
{ "restart", awesome.restart },
{ "quit", awesome.quit }
}
-- mymainmenu主菜单
mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon }, -- 这里的awesome项添加上面的myawesomemenu
{ "应用程序", debian.menu.Debian_menu.Debian }, -- 这是debian.menu模块自动生成的应用程序子菜单
{ "文件管理器", "caja /home/td/" }, -- 手动添加的项
{ "网络浏览器", "chromium-browser %u" }, -- 手动添加的项
{ "open terminal", terminal },
{ "重启电脑", "gksu reboot now" }, -- 手动添加的项
{ "关闭系统", "gksu 'shutdown -h now'" }, -- 手动添加的项
}
})

mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
menu = mymainmenu })

menubar.utils.terminal = terminal -- 应用终端,见上文默认终端设置
-- }}}

-- {{{ 窗口和布局
-- 创建一个时钟部件
mytextclock = awful.widget.textclock("%X",1)

-- 为每个屏幕创建一个wibox
mywibox = {}
mypromptbox = {}
mylayoutbox = {}
mytaglist = {}
mytaglist.buttons = awful.util.table.join(
awful.button({ }, 1, awful.tag.viewonly),
awful.button({ modkey }, 1, awful.client.movetotag),
awful.button({ }, 3, awful.tag.viewtoggle),
awful.button({ modkey }, 3, awful.client.toggletag),
awful.button({ }, 4, function(t) awful.tag.viewnext(awful.tag.getscreen(t)) end),
awful.button({ }, 5, function(t) awful.tag.viewprev(awful.tag.getscreen(t)) end)
)
mytasklist = {}
mytasklist.buttons = awful.util.table.join(
awful.button({ }, 1, function (c)
if c == client.focus then
c.minimized = true
else
-- Without this, the following
-- :isvisible() makes no sense
c.minimized = false
if not c:isvisible() then
awful.tag.viewonly(c:tags()[1])
end
-- This will also un-minimize
-- the client, if needed
client.focus = c
c:raise()
end
end),
awful.button({ }, 3, function ()
if instance then
instance:hide()
instance = nil
else
instance = awful.menu.clients({
theme = { width = 250 }
})
end
end),
awful.button({ }, 4, function ()
awful.client.focus.byidx(1)
if client.focus then client.focus:raise() end
end),
awful.button({ }, 5, function ()
awful.client.focus.byidx(-1)
if client.focus then client.focus:raise() end
end))

for s = 1, screen.count() do
-- 为每个屏幕创建一个信息框
mypromptbox[s] = awful.widget.prompt()
-- 在屏幕右上角显示目前正在使用的布局样式
mylayoutbox[s] = awful.widget.layoutbox(s)
mylayoutbox[s]:buttons(awful.util.table.join(
awful.button({ }, 1, function () awful.layout.inc(layouts, 1) end),
awful.button({ }, 3, function () awful.layout.inc(layouts, -1) end),
awful.button({ }, 4, function () awful.layout.inc(layouts, 1) end),
awful.button({ }, 5, function () awful.layout.inc(layouts, -1) end)))
-- 创建屏幕左上方的tag列表
mytaglist[s] = awful.widget.taglist(s, awful.widget.taglist.filter.all, mytaglist.buttons)

-- Create a tasklist widget
mytasklist[s] = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons)

-- Create the wibox
mywibox[s] = awful.wibox({ position = "top", screen = s })

-- 左对齐的部件
local left_layout = wibox.layout.fixed.horizontal()
left_layout:add(mylauncher)
left_layout:add(mytaglist[s])
left_layout:add(mypromptbox[s])

-- 右对齐的部件
local right_layout = wibox.layout.fixed.horizontal()
if s == 1 then right_layout:add(wibox.widget.systray()) end
right_layout:add(mytextclock)
right_layout:add(mylayoutbox[s])

--
local layout = wibox.layout.align.horizontal()
layout:set_left(left_layout)
layout:set_middle(mytasklist[s])
layout:set_right(right_layout)

mywibox[s]:set_widget(layout)
end
-- }}}

-- {{{ 鼠标绑定
root.buttons(awful.util.table.join(
awful.button({ }, 3, function () mymainmenu:toggle() end),
awful.button({ }, 4, awful.tag.viewnext),
awful.button({ }, 5, awful.tag.viewprev)
))
-- }}}

-- {{{ 快捷键
globalkeys = awful.util.table.join(
awful.key({ modkey, }, "Left", awful.tag.viewprev ), -- 向左一个标签
awful.key({ modkey, }, "Right", awful.tag.viewnext ), -- 向右一个标签
awful.key({ modkey, }, "Escape", awful.tag.history.restore), -- 之前使用的标签

awful.key({ modkey, }, "j", -- 切换下一个窗口
function ()
awful.client.focus.byidx( 1)
if client.focus then client.focus:raise() end
end),
awful.key({ modkey, }, "k", -- 切换上一个窗口
function ()
awful.client.focus.byidx(-1)
if client.focus then client.focus:raise() end
end),
awful.key({ modkey, }, "w", function () mymainmenu:show() end), -- 显示主菜单,鼠标右键关闭

-- Layout manipulation
awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end), -- 当前窗口和前一个窗口互换位置
awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end), -- 当前窗口和后一个窗口互换位置
awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end), -- 切换到下一个屏幕
awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end), -- 切换到上一个屏幕
-- awful.key({ modkey, }, "u", awful.client.urgent.jumpto),
awful.key({ modkey, }, "Tab", -- 切换到之前使用的窗口
function ()
awful.client.focus.history.previous()
if client.focus then
client.focus:raise()
end
end),

-- 标准程序
awful.key({ modkey, }, "Return", function () awful.util.spawn(terminal) end),
awful.key({ modkey, "Control" }, "r", awesome.restart), -- 重启awesome
awful.key({ modkey, "Shift" }, "q", awesome.quit), -- 退出awesome

awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end), -- 增加主区域宽度
awful.key({ modkey, }, "h", function () awful.tag.incmwfact(-0.05) end), -- 减少主区域宽度
awful.key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster( 1) end), -- 增加主区域窗口数目
awful.key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1) end), -- 减少主区域窗口数目
awful.key({ modkey, "Control" }, "h", function () awful.tag.incncol( 1) end),
awful.key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1) end),
awful.key({ modkey, }, "space", function () awful.layout.inc(layouts, 1) end), -- 按顺序切换布局样式
awful.key({ modkey, "Shift" }, "space", function () awful.layout.inc(layouts, -1) end), -- 反向切换布局样式

awful.key({ modkey, "Control" }, "n", awful.client.restore),

-- 启动器
awful.key({ modkey }, "r", function () mypromptbox[mouse.screen]:run() end), -- 默认,程序启动器
-- awful.key({ modkey }, "r", function () awful.util.spawn( "dmenu_run" ) end), -- dmenu,程序启动器,需额外安装
-- 包含显式选项的启动器
awful.key({ modkey }, "p", function() menubar.show() end)
)

clientkeys = awful.util.table.join(
awful.key({ modkey, }, "f", function (c) c.fullscreen = not c.fullscreen end), -- 切换全屏/非全屏
awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end),
awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ),
awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end),
awful.key({ modkey, }, "o", awful.client.movetoscreen ),
awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end), -- 标记当前窗口
awful.key({ modkey, }, "n", -- 最小化窗口
function (c)
-- The client currently has the input focus, so it cannot be
-- minimized, since minimized clients can't have the focus.
c.minimized = true
end),
awful.key({ modkey, }, "m", -- 最大化窗口
function (c)
c.maximized_horizontal = not c.maximized_horizontal
c.maximized_vertical = not c.maximized_vertical
end)
)

-- 把数字键1到9绑定到对应的标签
for i = 1, 9 do
globalkeys = awful.util.table.join(globalkeys,
-- 转到指定窗口
awful.key({ modkey }, "#" .. i + 9,
function ()
local screen = mouse.screen
local tag = awful.tag.gettags(screen)[i]
if tag then
awful.tag.viewonly(tag)
end
end),

awful.key({ modkey, "Control" }, "#" .. i + 9,
function ()
local screen = mouse.screen
local tag = awful.tag.gettags(screen)[i]
if tag then
awful.tag.viewtoggle(tag)
end
end),
-- 把窗口移动到指定标签
awful.key({ modkey, "Shift" }, "#" .. i + 9, -- 快捷键,注意需要先用 Mod4 + t 标记当前窗口
function ()
if client.focus then
local tag = awful.tag.gettags(client.focus.screen)[i]
if tag then
awful.client.movetotag(tag)
end
end
end),

awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
function ()
if client.focus then
local tag = awful.tag.gettags(client.focus.screen)[i]
if tag then
awful.client.toggletag(tag)
end
end
end))
end

clientbuttons = awful.util.table.join(
awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
awful.button({ modkey }, 1, awful.mouse.client.move),
awful.button({ modkey }, 3, awful.mouse.client.resize))

-- 应用快捷键
root.keys(globalkeys)
-- }}}

-- {{{ 规则
-- 设置部分程序启动时默认所在的屏幕和虚拟桌面,以及是否浮动(支持多屏幕,默认为屏幕1)
awful.rules.rules = {
{ rule = { },
properties = { border_width = beautiful.border_width,
border_color = beautiful.border_normal,
focus = awful.client.focus.filter,
raise = true,
keys = clientkeys,
buttons = clientbuttons } },
{ rule = { class = "MPlayer" },
properties = { floating = true } }, -- 设置mplayer启动时默认为浮动状态
{ rule = { class = "pinentry" },
properties = { floating = true } },
{ rule = { class = "gimp" },
properties = { floating = true } },
-- 设置chromium浏览器启动时默认位于第一个屏幕的第一个标签
{ rule = { class = "Chromium-browser" },
properties = { tag = tags[1][1] } },
-- { rule = { class = "Firefox" },
-- properties = { tag = tags[1][1] } },
}
-- }}}

-- {{{ Signals
-- 以下功能仅在新窗口出现时生效
client.connect_signal("manage", function (c, startup)
-- Enable sloppy focus
c:connect_signal("mouse::enter", function(c)
if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
and awful.client.focus.filter(c) then
client.focus = c
end
end)

if not startup then
-- Set the windows at the slave,
-- i.e. put it at the end of others instead of setting it master.
-- awful.client.setslave(c)

-- Put windows in a smart way, only if they does not set an initial position.
if not c.size_hints.user_position and not c.size_hints.program_position then
awful.placement.no_overlap(c)
awful.placement.no_offscreen(c)
end
elseif not c.size_hints.user_position and not c.size_hints.program_position then
-- Prevent clients from being unreachable after screen count change
awful.placement.no_offscreen(c)
end

local titlebars_enabled = false
if titlebars_enabled and (c.type == "normal" or c.type == "dialog") then
-- 标题栏的按钮
local buttons = awful.util.table.join(
awful.button({ }, 1, function()
client.focus = c
c:raise()
awful.mouse.client.move(c)
end),
awful.button({ }, 3, function()
client.focus = c
c:raise()
awful.mouse.client.resize(c)
end)
)

-- 左对齐的部件
local left_layout = wibox.layout.fixed.horizontal()
left_layout:add(awful.titlebar.widget.iconwidget(c))
left_layout:buttons(buttons)

-- 右对齐的部件
local right_layout = wibox.layout.fixed.horizontal()
right_layout:add(awful.titlebar.widget.floatingbutton(c))
right_layout:add(awful.titlebar.widget.maximizedbutton(c))
right_layout:add(awful.titlebar.widget.stickybutton(c))
right_layout:add(awful.titlebar.widget.ontopbutton(c))
right_layout:add(awful.titlebar.widget.closebutton(c))

--
local middle_layout = wibox.layout.flex.horizontal()
local title = awful.titlebar.widget.titlewidget(c)
title:set_align("center")
middle_layout:add(title)
middle_layout:buttons(buttons)

-- Now bring it all together
local layout = wibox.layout.align.horizontal()
layout:set_left(left_layout)
layout:set_right(right_layout)
layout:set_middle(middle_layout)

awful.titlebar(c):set_widget(layout)
end
end)

client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
-- }}}

0.安装之前

操作系统:Ubuntu 14.04 / LinuxMint17

1.安装

1
2
3
sudo add-apt-repository ppa:klaus-vormweg/awesome
sudo apt-get update
sudo apt-get install awesome awesome-extra feh xcompmgr

2.配置文件

默认配置文件位于:/etc/xdg/awesome/
awesome 文件夹复制到$HOME/.config/目录下。
修改配置时,修改$HOME/.config/awesome/目录下的配置文件即可。

3.修改配置

主要是 rc.lua 文件。

修改壁纸:搜索:**”Wallpaper”**,修改图片路径。

1
2
3
for s = 1, screen.count() do
gears.wallpaper.maximized("/home/td/Pictures/background.jpg", s, true)
end

修改 awesome 主题,内容包括桌面字体,配色等。
默认主题文件:/usr/share/awesome/themes/default/theme.lua
这里只改了显示的字体。

1
theme.font = "sans 8"

修改 tag 数量及名称: 默认为桌面左上角显示的 1-9 ,每个 tag 可以理解为一个工作区。
搜索 Tags,修改默认的配置,以下是我修改后的结果。

1
2
3
4
5
tags = {}
for s = 1, screen.count() do
-- Each screen has its own tag table.
tags[s] = awful.tag({ '[Internet]', '[Term]', '[Code]', '[Music]', 5, 6 }, s, layouts[1])
end

修改右键菜单:

搜索 mymainmenu
lua 语法不熟悉,照猫画虎。

添加关机功能:

items = { }大括号中添加一项:{ "关闭系统", "gksu 'shutdown -h now'" }

4.启动设置

有一些需要添加的启动项。
在用户主目录下创建文件.xprofile。在文件中添加内容:(后面4行是使用 fcitx 输入法需要的。)

1
2
3
4
5
6
7
8
mate-settings-daemon &
xcompmgr &
mate-power-manager &
nm-applet &
export XMODIFIERS="@im=fcitx"
export QT_IM_MODULE=fcitx
export GTK_IM_MODULE=fcitx
fcitx-autostart &

5.修改应用程序菜单

修改配置目录下的/awesome/debian/menu.lua文件。
默认的是由debian.menu模块自动生成的。
有很多项目,每一项类似于:

1
2
3
4
Debian_menu["Debian_应用程序_Shell"] = {
{"bash", "x-terminal-emulator -e ".."/bin/bash"},
{"zsh", "x-terminal-emulator -e ".."/usr/bin/zsh"},
}

6.常用快捷键

以上是最基本的配置文件修改。

快捷键可以在rc.lua中搜索Key bindings修改。

Mod4键默认为Win键

Mod4 + Enter打开终端

Mod4 + r执行命令或程序

Mod4 + 1~6切换到指定Tag

Mod4 + Space切换桌面布局

Mod4 + Shift + Space当前布局切换为前一个布局

Mod4 + Ctrl + r重启awesome

Mod4 + w打开awesome主菜单

Ctrl + Q许多程序默认用来关闭的快捷键

Mod4 + Shift + C关闭当前窗口/程序

Mod4 + Left/Right左右切换tag

Mod4 + h/l调整主区域宽度

Mod4 + j/k切换窗口

Mod4 + t标记窗口

Mod4 + m/n最大化/最小化窗口

Mod4 + Esc切换到上一个桌面

Mod4 + Control + space切换当前窗口是否浮动

Mod4 + Shift + j当前窗口和前一个/后一个窗口切换位置

Mod4 + Shift + 1~6把标记的窗口移动到 tag 1~6

附图一张。

screenshot

0%