Python包管理

[[Rye 简单使用指南]]#

Rye 是一个由 Flask 作者 Armin Ronacher 开发的 Python 版本和依赖管理工具,类似于 Pyenv + Poetry 的组合。

安装 Rye#

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 安装cargo
cargo install --git https://github.com/mitsuhiko/rye rye # 安装rye

cargo的 .bashrc 相关:

略略略略略略

. "$HOME/.cargo/env"

基本命令#

初始化新项目#

rye init my_project
cd my_project

指定 Python 版本#

rye pin 3.9.10  # 指定使用 Python 3.9.10

添加依赖#

rye add flask==2.3.2
rye add black --dev  # 添加开发依赖

同步依赖#

rye sync

运行项目#

rye run python main.py

常用工具#

rye fmt    # 格式化代码
rye lint   # 代码检查
rye test   # 运行测试

优势特点#

  1. 内置 Python 版本管理,自动下载所需 Python 版本
  2. 使用标准的 pyproject.toml 文件
  3. 无需激活虚拟环境,自动感知项目环境
  4. 内置常用开发工具 (fmt/lint/test)

示例项目结构#

my_project/
├── .python-version
├── pyproject.toml
├── src/
│   └── ...
└── tests/
    └── ...

提示:Rye 适合小型到中型项目,对于大型项目可能需要评估是否满足需求。

Python编码规范

函数返回值定义#

多个返回值 - Tuple#

async def function_name(package_name: str) -> Tuple[ParamsPushLvl2Pool, bool]:
	pass

支持传入多类型 - Union#

ex: Union[ExpiryT, None] = None,

迭代#

def _yield_offer_info(_session, from_days_ago: int) -> Iterator[Offer]:

Python Redis 客户端

异步调用时自动关闭#

当事件循环正在运行时,会创建一个任务来关闭 redis 连接,而不会阻塞事件循环。如果事件循环没有运行,就可以像之前一样调用 run_until_complete

async def close_redis(self):
    if self.redis:
        await self.redis.close()

def __del__(self):
    loop = asyncio.get_event_loop()
    if loop.is_running():
        asyncio.create_task(self.close_redis())
    else:
        loop.run_until_complete(self.close_redis())

数据迁移(hash数据)#

脚本#

import os
import json
import redis
import argparse
from loguru import logger

home_path = os.environ.get('HOME')

parser = argparse.ArgumentParser(description='Copy Reten Config')
parser.add_argument('--mode', help='Pull or Push data from Redis')
parser.add_argument('-ds', '--datasource', help='retens or events config data from Redis')
args = parser.parse_args()

source_redis = redis.StrictRedis(host='redis-product.com', port=6379, db=0)
def scan_keys_with_pattern(pattern):
    # 初始游标
    cursor = '0'
    count_group = 0
    while True:
        # 使用 SCAN 命令进行迭代
        cursor, keys = source_redis.scan(cursor=cursor, match=pattern)

        # 游标为 '0' 表示迭代结束
        if cursor == b'0' or cursor == 0 or cursor == '0':
            break

        # 将找到的键添加到结果列表中
        count_group += 1
        logger.info(f"{count_group} - {len(keys)} = {cursor}")
        yield [key.decode('utf-8') for key in keys]

def append_2_file(data):
    # 将数据转换为字典
    decoded_data = {}
    for key, value in data.items():
        decoded_data[key.decode('utf-8')] = value.decode('utf-8')
    # 将数据写入文件
    with open(f'data_{args.datasource}.json', 'a') as file:
        file.write(json.dumps(decoded_data) + '\n')

def pull():
    # 连接到源 Redis 实例

    # 从 Redis 中读取数据
    all_keys = set()
    if args.datasource == 'retens':
        for keys in scan_keys_with_pattern('mmp:cfgs:*:retens'):
            for key in keys:
                if key not in all_keys:
                    all_keys.add(key)
                else:
                    logger.warning(f"重复一次: {key}")
                    continue
                data = source_redis.hgetall(key)
                append_2_file(data=data)
    elif args.datasource == 'events':
        for keys in scan_keys_with_pattern('mmp:cfgs:*:events'):
            for key in keys:
                if key not in all_keys:
                    all_keys.add(key)
                else:
                    logger.warning(f"重复一次: {key}")
                    continue
                data = source_redis.hgetall(key)
                append_2_file(data=data)
    else:
        logger.error(f"Invalid datasource: {args.datasource}")
        return

def push():
    # 连接到目标 Redis 实例
    target_redis = redis.StrictRedis(host='localhost', port=6379, db=1)

    # 读取文件内容并写入目标 Redis
    if not args.datasource in ['retens', 'events']:
        logger.error(f"Invalid datasource: {args.datasource}")
        return

    with open(os.path.join(home_path, f'data_{args.datasource}.json'), 'r') as file:
        for l in file:
            l = json.loads(l)
            for key, value in l.items():
                value_obj = json.loads(value)
                pkg_name = value_obj["pkg_name"]
                if args.datasource == 'retens':
                    ok = target_redis.hset(f'mmp:cfgs:{pkg_name}:retens', key, value)
                elif args.datasource == 'events':
                    ok = target_redis.hset(f'mmp:cfgs:{pkg_name}:events', key, value)
                else:
                    print(f"Invalid datasource: {args.datasource}")
                logger.info(f'Writing {key} {value} to Redis -> {ok}')

if __name__ == "__main__":
    if args.mode == 'pull':
        pull()
    elif args.mode == 'push':
        push()

使用#

python {thisscript}.py --mode=pull -ds=events
python {thisscript}.py --mode=push -ds=events

hash#

import redis

# 创建 Redis 连接
r = redis.Redis(host='localhost', port=6379, db=0)

# 写入数据到哈希
r.hset('my_hash', 'field1', 'value1')
r.hset('my_hash', mapping={'field2': 'value2', 'field3': 'value3'})

# 获取单个字段的值
value = r.hget('my_hash', 'field1')
print(f'field1: {value.decode("utf-8")}')

# 获取所有字段和值
all_fields = r.hgetall('my_hash')
print('All fields:')
for field, value in all_fields.items():
    print(f'{field.decode("utf-8")}: {value.decode("utf-8")}')

# 查看哈希中字段的数量
field_count = r.hlen('my_hash')
print(f'Number of fields in my_hash: {field_count}')

事务#

import asyncio
import aioredis

async def run_transaction(redis: aioredis.Redis):
    async with redis.pipeline() as pipe:
        # 开始事务
        await pipe.multi()
        
        # 在事务中执行多个命令
        await pipe.set('key1', 'value1')
        await pipe.set('key2', 'value2')
        
        # 提交事务
        results = await pipe.execute()
        
        print('Transaction results:', results)

async def main():
    # 创建 Redis 连接
    redis = await aioredis.from_url("redis://localhost:6379", encoding="utf-8", decode_responses=True)
    
    # 运行事务
    await run_transaction(redis)
    
    # 关闭连接
    await redis.close()

# 运行异步主函数
if __name__ == "__main__":
    asyncio.run(main())

乐观锁#

详细解释

Python 多线程多进程

  • 一个协程示例:
import asyncio

async def my_coroutine():
    # 这里是协程的代码
    await asyncio.sleep(1)
    print("Coroutine is done")

async def main():
    # 创建一个任务对象
    task = asyncio.create_task(my_coroutine())
    # 等待任务完成
    await task

# 运行主协程
asyncio.run(main())
  • 一个线程示例:
import threading

# 定义一个函数,用于在线程中执行
def thread_function(name):
    print(f"Thread {name}: starting")
    # 在这里执行一些操作
    print(f"Thread {name}: finishing")

# 创建线程的实例
thread = threading.Thread(target=thread_function, args=(1,))
# 启动线程
thread.start()
# 等待线程结束
thread.join()
print("Main thread is done")
  • 一个loop tip:
self.loop = asyncio.get_event_loop()
self.queue.set_loop(loop=self.loop)
await self.af.set_loop(loop=self.loop)
await self.adjust.set_loop(loop=self.loop)

为什么这里使用同一个loop ?
---
在 Python 的 `asyncio` 框架中事件循环event loop是处理异步任务的核心每个异步应用程序通常只需要一个事件循环所有的异步任务都应该在这个事件循环中运行使用同一个事件循环的好处包括
1. **统一管理**所有异步任务都在同一个事件循环中运行这样可以更方便地管理和监控它们
2. **避免冲突**如果为不同的任务创建多个事件循环可能会导致资源竞争或冲突尤其是在共享资源或执行I/O操作时
3. **保持上下文一致性**使用同一个事件循环可以确保所有任务都在相同的上下文中运行这有助于维护状态的一致性

在你提供的代码片段中这样做的目的是确保所有这些组件都在同一个事件循环中运行以便它们可以正确地协同工作

如果这些组件是异步的并且需要与事件循环交互那么它们需要知道使用哪个事件循环通过这种方式设置事件循环可以确保组件在执行异步操作时能够正确地在事件循环中注册和处理

总之使用同一个事件循环有助于保持代码的整洁和协调避免潜在的问题并确保整个应用程序的异步操作能够顺利地进行

多进程#

多进程通信打日志#


from multiprocessing import Process, Queue
from loguru import logger
import sys
import time

# 配置日志处理函数
def configure_logger(queue):
    logger.remove()  # 移除默认日志处理
    logger.add(sys.stdout, format="{message}")
    logger.add("logfile.log", format="{message}", rotation="1 MB")
    
    while True:
        try:
            record = queue.get()
            if record is None:
                break  # None 表示日志记录结束
            # 使用 log 方法记录日志
            logger.log(record["level"], record["message"])
        except Exception:
            logger.exception("Failed to process log record")

# 配置工作进程
def worker_process(name, queue):
    logger.remove()  # 移除默认日志处理
    def enqueue_log_message(message):
        # print('message.record["level"].name:', message.record["level"].name)
        # print('message.record["function"]:', message.record["function"])
        # print('message.record["line"]:', message.record["line"])
        # print('message.record.keys:', message.record.keys())
        queue.put({
            "line": message.record["line"], "function": message.record["function"],
            "level": message.record["level"].name, "message": message.strip()
        })
    logger.add(enqueue_log_message)
    
    for i in range(5):
        time.sleep(0.01)
        logger.info(f"Worker {name} is logging iteration {i}")

if __name__ == "__main__":
    log_queue = Queue()
    
    # 启动日志处理进程
    log_process = Process(target=configure_logger, args=(log_queue,))
    log_process.start()
    
    # 启动工作进程
    processes = []
    for i in range(5):
        process = Process(target=worker_process, args=(f"Process-{i}", log_queue))
        processes.append(process)
        process.start()

    # 等待所有工作进程完成
    for process in processes:
        process.join()
    
    # 终止日志处理进程
    log_queue.put(None)
    log_process.join()

解决问题

python

PiP Not Found Issue#

使用pip安装某包时, 提示让更新, 按提示操作更新没效果没反应再用就提示ModuleNotFoundError: No module named 'pip' (ˉ▽ˉ;)…

ModuleNotFoundError: No module named ‘pip’#

升级PiP时出现问题可由下方命令修复#

python -m ensurepip
python -m pip install --upgrade pip

SSL校验#

安装EasyOCR时, reader = easyocr.Reader(['ch_sim','en']) 下载到接近90,结果报错了…. 估计是SSL问题加入以下两条,不知如何.

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

from http.client To revert to the previous, unverified, behavior ssl._create_unverified_context() can be passed to the context parameter.

使用国内镜像下载Python#

	pip install --index https://pypi.mirrors.ustc.edu.cn/simple/ dlib(numpy等包名)
    # 一键更新pip 包资源(利用管道grep传输查询到的需要更新包名,传输到install命令)
    pip3 freeze --local | grep -v '^\-e' | cut -d = -f 1  | xargs -n1 pip3 install -U
    # 权限不够的话就在`pip3 install` 之前加`sudo`反正我不习惯用`root`
安装错误?#

居然有pip3 install XX的错误…这也是因为有旧版pip3存留。需要

Py小工具和功能性方法

author:Ian

python

邮件#

中文附件#

发送邮件时携带附件 中文名称附件

def send_email_week_report(htm, recipients, copys, file_name, file_path):
    port = 465
    # str_today = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    str_today = datetime.now().strftime("%Y-%m-%d")
    mail_host = "mail.{demo}.com"
    mail_user = "{user name}"
    mail_pass = "{passwd}"
    sender = '{name}@{demo}.com'
    mail_from = "{name} <{name}@{demo}.com>"
    msg_Bcc = '{name}@{demo}.com'
    subject = f"{title}-{str_today}"  # 标题
    msg = MIMEMultipart()
    msg.attach(MIMEText(htm, 'html', 'utf-8'))  # 这里可以传html内容 也可以传普通文字

    # 创建附件
    with open(file_path, 'rb') as attachment:
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(attachment.read())
        encoders.encode_base64(part)
        part.add_header(
            'Content-Disposition',
            'attachment',
            filename=("utf-8", "", file_name),
        )
        msg.attach(part)

    for root, _, files in os.walk(os.path.join(BASE_DIR, '{path}/images')):
        for f in files:
            if f in htm:
                with open(os.path.join(root, f), 'rb') as fp:
                    image = MIMEImage(fp.read())
                    image.add_header('Content-ID', f'<{f}>')
                    msg.attach(image)

    msg['From'] = mail_from  # 发件人
    msg['To'] = ";".join(recipients)  # 收件人多个逗号分隔
    msg["Cc"] = ";".join(copys)
    msg["Bcc"] = msg_Bcc
    subject = subject
    msg['Subject'] = Header(subject)
    try:
        smtpObj = smtplib.SMTP_SSL(mail_host)
        if is_partner_node == 1:
            smtpObj = smtplib.SMTP(mail_host)
        smtpObj.connect(mail_host, port)
        smtpObj.login(mail_user, mail_pass)
        smtpObj.sendmail(sender, recipients + copys + [sender], msg.as_string())
        return True
    except smtplib.SMTPException as e:
        return False

有限制的附件#

这种内容插入附件会有文件名称长度限制(19好像或者16)

机器学习库

Python 机器学习库 👽#

Plotly#

与matplotlib 都是绘图工具,不过效果炫一些,我也没画过,所以只放链接,不放实例了 Plotly Python Library : https://plot.ly/python/

matplotlib#

import matplotlib.pyplot as plt

参数等太多,链接最可靠#

pyplot参数

还是粘一些常用的: marker 属性(下面写在分号里呦) o . v ^ < > 1 2 3 4 8 s p * h H + x D d | _ 之类

画出一些“花儿”

绘图#

plt.plot(x, y)
# 在y之后可添加参数,例如常用的label = ‘IamLabel’之类
# 线的样式、颜色 :b: blue  g: green    r: red  c: cyan m: magenta
y: yellow   k: black    w: white
'-' : solid , '--' : dashed, '-.' : dashdot ':' : dotted    '   '', ' '   ': None
# 粗细 lw=3 更改数字
# 数值折点显示样式 marker = ‘o’   

plot.show()

绘制图表#

1#

plt.figure(1)
绘图
plt.figure(2)
绘图

2(未测试)#

plt.figure(1)   # 创建图表1
plt.figure(2)   # 创建图表2
hi1 = plt.subplot(211)  # 在图表2中创建子图1
hi2 = plt.subplot(212)  # 在图表2中创建子图2

一表多图#

pCapital = plt.subplot(4, 1, 1)
    pCapital.set_ylabel("capital")
    pCapital.plot(d['capitalList'], color='r', lw=0.8)
plt.show()

标注点#

eg1#

for w, m in enumerate(self.lowestPrice):
        if w % 120*10 == 0:
            plt.plot([w, w], [m, self.highestPrice[w]], linestyle = '--')
        #plt.scatter(self.dealPoints,color = 'c')
for i in self.dealPoints:           
    plt.scatter([i[0]], [i[1]], color = 'c')    
    for ii in self.ydealPoints:
    plt.scatter([ii[0]], [ii[1]], color = 'm')
    
plt.title('Tick & TradePoint')
plt.legend()
plt.show()

plt.legend() # show() 之前不加这句是不会显示出标注的呦

Python 常用库

python

pydantic#

来自类#

from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int

    class Config:
        from_attributes = True

# 假设我们有一个普通的 Python 对象,它具有与模型字段相同的属性名
class OrdinaryObject:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建一个 OrdinaryObject 实例
ordinary_object = OrdinaryObject(name='Alice', age=30)

# 现在我们可以直接将 ordinary_object 传递给 User 模型的构造函数
user_model = User(ordinary_object)

# 输出模型的字段值
print(user_model.dict())  # 输出: {'name': 'Alice', 'age': 30}

自定义字段#

class User(BaseModel):
	id: int
	is_active: bool
	wx_infos: List[str] = []

	class Config:
		arbitrary_types_allowed = True # 允许任意类型

Tuple定义问题#

from typing import List, Tuple
class Task(BaseModel):
	launch_count_today: int
	launch_groups: List[Tuple[str, ...]]
	@property
	def launch_groups_count(self) -> int:
		return len(self.launch_groups)

wrong tuple length, expected 1 (type=value_error.tuple.length; actual_length=5; expected_length=1)#

from typing import List, Tuple
class Task(BaseModel):
	launch_count_today: int
	launch_groups: List[Tuple[str]] # 这样定义就会tuple限制长度
	@property
	def launch_groups_count(self) -> int:
		return len(self.launch_groups)

random#

有权重的随机#

import random
# 假设有一个字典
my_dict = {'a': 1, 'b': 2, 'c': 3}

# 将字典的键和值分别保存在两个列表中
keys = list(my_dict.keys())
weights = list(my_dict.values())

# 使用 random.choices() 函数进行带有权重的随机选择 取12个值
random_choice = random.choices(keys, weights, k=12)

print("随机选择的键:", random_choice)

loguru#

from multiprocessing import Process, Queue
from loguru import logger
import sys
import time

# 配置日志处理函数
def configure_logger(queue):
    logger.remove()  # 移除默认日志处理
    logger.add(sys.stdout, format="{message}")
    logger.add("logfile.log", format="{message}", rotation="1 MB")
    
    while True:
        try:
            record = queue.get()
            if record is None:
                break  # None 表示日志记录结束
            # 使用 log 方法记录日志
            logger.log(record["level"], record["message"])
        except Exception:
            logger.exception("Failed to process log record")

# 配置工作进程
def worker_process(name, queue):
    logger.remove()  # 移除默认日志处理
    def enqueue_log_message(message):
        # print('message.record["level"].name:', message.record["level"].name)
        # print('message.record["function"]:', message.record["function"])
        # print('message.record["line"]:', message.record["line"])
        # print('message.record.keys:', message.record.keys())
        queue.put({
            "line": message.record["line"], "function": message.record["function"],
            "level": message.record["level"].name, "message": message.strip()
        })
    logger.add(enqueue_log_message)
    
    for i in range(5):
        time.sleep(0.01)
        logger.info(f"Worker {name} is logging iteration {i}")

if __name__ == "__main__":
    log_queue = Queue()
    
    # 启动日志处理进程
    log_process = Process(target=configure_logger, args=(log_queue,))
    log_process.start()
    
    # 启动工作进程
    processes = []
    for i in range(5):
        process = Process(target=worker_process, args=(f"Process-{i}", log_queue))
        processes.append(process)
        process.start()

    # 等待所有工作进程完成
    for process in processes:
        process.join()
    
    # 终止日志处理进程
    log_queue.put(None)
    log_process.join()

Python 笔记

python

pip cache 目录#

echo $PIP_CACHE_DIR

特殊函数调用 property#

使用字段的方式调用函数

from datetime import datetime
from pydantic import BaseModel
class InstallCountInfo(BaseModel):
	count: int
	total: int
	@property
	def done_rate(self):
		return round(self.count / self.total, 4)
class InstallCount(BaseModel):
	pass_day: int
	install_day: datetime
	info: InstallCountInfo

彻底摆脱to_dictfrom_dict#

使用 pydantic#

BaseModel类型支持:

  • b = BattleAxiePositionInfo.parse_obj(DICT_DATA)
  • b.json()
  • b.dict()
  • parse_file
  • parse_raw
from pydantic import BaseModel
class PositionInfo(BaseModel):
    error: int = -1 # 收集错误
    none: int = 0 # 还没开始
    clicked: int = 1 # 在client 赋此值
    done: int = 2 # 在server 赋此值
    xy: List[int] = [0, 0]
    status: int = 0 # clicked or done or none or error
class BattleAxiePositionInfo(BaseModel):
    our: List[PositionInfo] = [PositionInfo(), PositionInfo(), PositionInfo(), PositionInfo(), PositionInfo(), PositionInfo()]
    enemy: List[PositionInfo] = [PositionInfo(), PositionInfo(), PositionInfo(), PositionInfo(), PositionInfo(), PositionInfo()]

pp = BattleAxiePositionInfo()
print(f"pp json: {pp.json()}")
dict_pp = pp.dict()
pp = BattleAxiePositionInfo.parse_obj(dict_pp)

pp json: {“our”: [{“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}], “enemy”: [{“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}, {“error”: -1, “none”: 0, “clicked”: 1, “done”: 2, “xy”: [0, 0], “status”: 0}]}

图形化界面 (Python Gui)

author:Ian

Python GUI 💽#

pynput#

在 pynput 模块中,Win键被称为“特殊键”(Special keys),需要使用特殊的名称来表示。

以下是可以使用的特殊键名称列表:

因此,如果你想要在热键设置中使用 Win键+空格 这个热键,可以将它们分别替换为 cmd 和 space,如下所示:

from pynput import keyboard

def on_activate():
    print('Hotkey activated')

def on_exit():
    print('Hotkey exited')
    return False

with keyboard.GlobalHotKeys({'<cmd>+<space>': on_activate}) as h:
    h.join(on_exit)```
在这个例子中我们使用 <cmd>+<space> 来表示 Win键+空格 热键因为在Mac中Command键cmd可以起到类似于Win键的作用
## PyQt
![qt](https://tse4-mm.cn.bing.net/th/id/OIP.J4_Nqrcc0x7slHHUFwKLSQHaI6?pid=ImgDet&rs=1 "tmp")

官方说明文档<http://pyqt.sourceforge.net/Docs/PyQt4/index.html>
照例先贴网址 <http://www.qaulau.com/books/PyQt4_Tutorial/index.html>

## 画界面
    #PyQt4使用designer.exe
    import os 
    for root, dirs, files in os.walk('.'): 
        for file in files: 
            if file.endswith('.ui'): 
                os.system('pyuic4 -o ui_%s.py %s' % (file.rsplit('.', 1)[0], file))
            elif file.endswith('.qrc'): 
                os.system('pyrcc4 -o %s_rc.py %s' % (file.rsplit('.', 1)[0], file))
    # 注:在Win中调用pyrcc4 可能无法识别该命令,即使添加到环境变量也不行,而是
    #pyrcc.exe才能调用简直……
#### 窗口设置
    resize(8, 8)
    setWindowTitle(u'标题')
#### 获取图标
    def getIcon(filename):
    """ 获取图标 """
        fileInfo = Qt.QFileInfo(filename) 
        fileIcon = Qt.QFileIconProvider() 
        icon = QtGui.QIcon(fileIcon.icon(fileInfo)) 
        return icon  
    使用时
    self.setWindowIcon(getIcon('../hi/app.ico'))

---

##### Dock
    widgetTestM, dockTestM = self.createDock(AllMarketMonitor, vtText.dock标题, QtCore.Qt.RightDockWidgetArea)
    # 方向有: `RightDockWidgetArea`,`BottomDockWidgetArea`,`LeftDockWidgetArea`
    # 创建dock窗口
    # 可利用
    self.tabifyDockWidget(dockMarketM, dockAllDataM)
    来合并同一个方向上的dock
    
    # 此下还没看… 
    dockTradeM.raise_()
    dockPositionM.raise_()
    
    # 连接组件之间的信号
    widgetPositionM.itemDoubleClicked.connect(widgetTradingW.closePosition)
        
    # 保存默认设置
    self.saveWindowSettings('default')

其实现函数为
"""创建停靠组件"""

    def createDock(self, widgetClass, widgetName, widgetArea):
        widget = widgetClass(self.mainEngine, self.eventEngine) 
        dock = QtGui.QDockWidget(widgetName)
        dock.setWidget(widget)
        dock.setObjectName(widgetName)
        dock.setFeatures(dock.DockWidgetFloatable|dock.DockWidgetMovable)
        self.addDockWidget(widgetArea, dock)
        return widget, dock
> 再本质一点的东西为

    widget1 = Ha(self)
    dock = QtGui.QDockWidget('haha')
    dock.setObjectName('ha1')
    dock.setWidget(widget1)
    dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)
    self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, dock)

#### 动作
    exit = QtGui.QAction(QtGui.QIcon('hello.ico'), 'exit', self)
    exit.setShortcut('Ctrl+Q')
    exit.setStatusTip('Exit application')
    # 图标、文字、快捷键、提示信息
    
    menubar = self.menuBar()
    file = menubar.addMenu('&File')
    file.addAction(exit)
    # 创建目录和工具栏,将动作添加进去。工具栏同理

### 定位布局
#### 绝对定位
    label1 = QtGui.QLabel(u'绝对定位', self)
    label1.move(15, 60)
    # 创建、移动到显示位置

#### 框布局 及 布局元素平均分布
    okButton = QtGui.QPushButton("OK")
    cancelButton = QtGui.QPushButton("Cancel")
    # 创建按钮

    hbox = QtGui.QHBoxLayout()
    hbox.addStretch(1)
    hbox.addWidget(okButton)        # 增加组件
    hbox.addWidget(cancelButton)
    # 创建水平栏
    hbox.addStretch()   # 平均分布
    
    vbox = QtGui.QVBoxLayout()
    vbox.addStretch(1)
    # 创建竖列

    vbox.addLayout(hbox)
    # 将水平栏插入竖列
    self.setLayout(vbox)     
    # 显示最终竖列
注意一下add时选择对类型就好了

### QTableWidget
<p><textarea cols="50" rows="15" name="code" class="python">#!/usr/bin/env python
#coding=utf-8
from PyQt4.QtGui  import *
from PyQt4.QtCore import *  
class MyDialog(QDialog):
    def __init__(self, parent=None):
        super(MyDialog, self).__init__(parent)
        self.MyTable = QTableWidget(4,3)
        self.MyTable.setHorizontalHeaderLabels(['姓名','身高','体重'])
        
        newItem = QTableWidgetItem(&quot;松鼠&quot;)
        self.MyTable.setItem(0, 0, newItem)
        
        newItem = QTableWidgetItem(&quot;10cm&quot;)
        self.MyTable.setItem(0, 1, newItem)
        
        newItem = QTableWidgetItem(&quot;60g&quot;)
        self.MyTable.setItem(0, 2, newItem) 
      
        layout = QHBoxLayout()
        layout.addWidget(self.MyTable)
        self.setLayout(layout)    
        
        
if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    myWindow = MyDialog()
    myWindow.show()
    sys.exit(app.exec_())       </textarea>
 </p>
<p>&nbsp;</p>
其中
<p>self.MyTable = QTableWidget(4,3)&nbsp; 构造了一个QTableWidget的对象并且设置为4行3</p><p>self.MyTable.setHorizontalHeaderLabels(['姓名','身高','体重']) 则设置表格的表头</p><p>newItem = QTableWidgetItem(&quot;松鼠&quot;)&nbsp;&nbsp; 则是生成了一个QTableWidgetItem的对象并让其名为&ldquo;松鼠&rdquo;</p><p>self.MyTable.setItem(0, 0, newItem)&nbsp;&nbsp;&nbsp; 将刚才生成的Item加载到第0行0列处
<p>&nbsp;</p>
<p>这样一个简单的QTableWidget就构造完成了</p>
<p><img src="http://hi.csdn.net/attachment/201103/1/0_1298961912c0mN.gif" alt="" />
</p>

感谢[来源](view-source:blog.csdn.net/vah101/article/details/6215066)


### 以上。  之后的设置字体,底色,之类用的的时候再说。
其中 
##### 合并单元格效果的实现:
    self.MyTable.setSpan(0, 0, 3, 1) 
    # 其参数为: 要改变单元格的 1行数  2列数     要合并的 3行数  4列数

##### 表格表头的显示与隐藏
    self.MyTable.verticalHeader().setVisible(False)
    self.MyTable.horizontalHeader().setVisible(False)
    # 默认显示 且标号为: 1,2,3…
#### 空位填补
    .addStretch()
    # 不过比较鸡肋,还是下面的调整窗口显示比例比较好玩
##### 调整各部分显示比例
    QVBoxLayout*  layout = new QVBoxLayout;
    QPushButton*      btn = new QPushButton;
    QTableWidget*     tableWidget = new QTableWidget;   
    QHBoxLayout*     h_layout = new QHBoxLayout;
    layout.addWidget(btn);
    layout.addWidget(tableWidget);
    layout.addLayout(h_layout)
    layout->setStretchFactor(btn, 1);
    layout->setStretchFactor(tableWidget, 2);
    layout->setStretchFactor(h_layout, 2);
上面是C++  所以下面的是俺的Python
    hbox1[i].addWidget(self.buttonBox[i])
        hbox1[i].addWidget(self.paramMonitor[i])  
        hbox1[i].addWidget(self.varMonitor[i])
        # 设置显示比例
        hbox1[i].setStretchFactor(self.buttonBox[i], 3)
        hbox1[i].setStretchFactor(self.paramMonitor[i], 3)
        hbox1[i].setStretchFactor(self.varMonitor[i], 4)

### 列表内添加按钮
<p>1定义添加按钮的方法</p>
<div class="cnblogs_code">
<pre><span style="color: #008080"> 1</span> <span style="color: #008000">#</span><span style="color: #008000"> 列表内添加按钮</span>
<span style="color: #008080"> 2</span>     <span style="color: #0000ff">def</span><span style="color: #000000"> buttonForRow(self,id):
</span><span style="color: #008080"> 3</span>         widget=<span style="color: #000000">QWidget()
</span><span style="color: #008080"> 4</span>         <span style="color: #008000">#</span><span style="color: #008000"> 修改</span>
<span style="color: #008080"> 5</span>         updateBtn = QPushButton(<span style="color: #800000">'</span><span style="color: #800000">修改</span><span style="color: #800000">'</span><span style="color: #000000">)
</span><span style="color: #008080"> 6</span>         updateBtn.setStyleSheet(<span style="color: #800000">'''</span><span style="color: #800000"> text-align : center;
</span><span style="color: #008080"> 7</span> <span style="color: #800000">                                          background-color : NavajoWhite;
</span><span style="color: #008080"> 8</span> <span style="color: #800000">                                          height : 30px;
</span><span style="color: #008080"> 9</span> <span style="color: #800000">                                          border-style: outset;
</span><span style="color: #008080">10</span> <span style="color: #800000">                                          font : 13px  </span><span style="color: #800000">'''</span><span style="color: #000000">)
</span><span style="color: #008080">11</span> 
<span style="color: #008080">12</span>         updateBtn.clicked.connect(<span style="color: #0000ff">lambda</span><span style="color: #000000">:self.updateTable(id))
</span><span style="color: #008080">13</span> 
<span style="color: #008080">14</span>         <span style="color: #008000">#</span><span style="color: #008000"> 查看</span>
<span style="color: #008080">15</span>         viewBtn = QPushButton(<span style="color: #800000">'</span><span style="color: #800000">查看</span><span style="color: #800000">'</span><span style="color: #000000">)
</span><span style="color: #008080">16</span>         viewBtn.setStyleSheet(<span style="color: #800000">'''</span><span style="color: #800000"> text-align : center;
</span><span style="color: #008080">17</span> <span style="color: #800000">                                  background-color : DarkSeaGreen;
</span><span style="color: #008080">18</span> <span style="color: #800000">                                  height : 30px;
</span><span style="color: #008080">19</span> <span style="color: #800000">                                  border-style: outset;
</span><span style="color: #008080">20</span> <span style="color: #800000">                                  font : 13px; </span><span style="color: #800000">'''</span><span style="color: #000000">)
</span><span style="color: #008080">21</span> 
<span style="color: #008080">22</span>         viewBtn.clicked.connect(<span style="color: #0000ff">lambda</span><span style="color: #000000">: self.viewTable(id))
</span><span style="color: #008080">23</span> 
<span style="color: #008080">24</span>         <span style="color: #008000">#</span><span style="color: #008000"> 删除</span>
<span style="color: #008080">25</span>         deleteBtn = QPushButton(<span style="color: #800000">'</span><span style="color: #800000">删除</span><span style="color: #800000">'</span><span style="color: #000000">)
</span><span style="color: #008080">26</span>         deleteBtn.setStyleSheet(<span style="color: #800000">'''</span><span style="color: #800000"> text-align : center;
</span><span style="color: #008080">27</span> <span style="color: #800000">                                    background-color : LightCoral;
</span><span style="color: #008080">28</span> <span style="color: #800000">                                    height : 30px;
</span><span style="color: #008080">29</span> <span style="color: #800000">                                    border-style: outset;
</span><span style="color: #008080">30</span> <span style="color: #800000">                                    font : 13px; </span><span style="color: #800000">'''</span><span style="color: #000000">)
</span><span style="color: #008080">31</span> 
<span style="color: #008080">32</span> 
<span style="color: #008080">33</span>         hLayout =<span style="color: #000000"> QHBoxLayout()
</span><span style="color: #008080">34</span> <span style="color: #000000">        hLayout.addWidget(updateBtn)
</span><span style="color: #008080">35</span> <span style="color: #000000">        hLayout.addWidget(viewBtn)
</span><span style="color: #008080">36</span> <span style="color: #000000">        hLayout.addWidget(deleteBtn)
</span><span style="color: #008080">37</span>         hLayout.setContentsMargins(5,2,5,2<span style="color: #000000">)
</span><span style="color: #008080">38</span> <span style="color: #000000">        widget.setLayout(hLayout)
</span><span style="color: #008080">39</span>         <span style="color: #0000ff">return</span> widget</pre>
</div>
<p>2在向tableWidget里添加数据时插入即可</p>
<div class="cnblogs_code">
<pre><span style="color: #008080">1</span> <span style="color: #0000ff">for</span> row_number, row_data <span style="color: #0000ff">in</span><span style="color: #000000"> enumerate(rsdata):
</span><span style="color: #008080">2</span> <span style="color: #000000">    self.ui.tableWidget.insertRow(row_number)
</span><span style="color: #008080">3</span>     <span style="color: #0000ff">for</span> i <span style="color: #0000ff">in</span> range(len(row_data)+1<span style="color: #000000">):
</span><span style="color: #008080">4</span>         <span style="color: #0000ff">if</span> i&lt;<span style="color: #000000">len(row_data):
</span><span style="color: #008080">5</span> <span style="color: #000000">            self.ui.tableWidget.setItem(row_number, i, QtWidgets.QTableWidgetItem(str(row_data[i])))
</span><span style="color: #008080">6</span>         <span style="color: #008000">#</span><span style="color: #008000"> 添加按钮</span>
<span style="color: #008080">7</span>         <span style="color: #0000ff">if</span> i==<span style="color: #000000">len(row_data):
</span><span style="color: #008080">8</span>             <span style="color: #008000">#</span><span style="color: #008000"> 传入当前id</span>
<span style="color: #008080">9</span>             self.ui.tableWidget.setCellWidget(row_number, i,self.buttonForRow(str(row_data[0])))</pre>
</div>
<p>效果图</p>
<p><img src="http://images2017.cnblogs.com/blog/896442/201709/896442-20170907222902741-300228307.png" alt=""></p>

就像以上显示的那样[来源](https://www.cnblogs.com/yuanlipu/p/7492260.html)

#### 值得注意的是,再排布的时候必须只有一个表格对其进行放置(也就是说指挥它去哪儿的只能有一个人),之前我为了测试,两个hbox 对其进行排版,简直……

        labelSymbol = QtGui.QLabel(self.CtaEngine.strategyTickDict[self.name])
        buttonInit[i] = QtGui.QPushButton(u'初始化')
        buttonStart[i] = QtGui.QPushButton(u'启动')
        buttonStop[i] = QtGui.QPushButton(u'停止')
        buttonUpdate[i] = QtGui.QPushButton(u'更新参数')
        buttonSave[i] = QtGui.QPushButton(u'保存参数')

        buttonInit[i].clicked.connect(partial(self.init, i))
        buttonStart[i].clicked.connect(partial(self.start, i))
        buttonStop[i].clicked.connect(partial(self.stop, i))
        buttonUpdate[i].clicked.connect(partial(self.updateParams,i))
        buttonSave[i].clicked.connect(partial(self.saveParams, i))
    
    hbox1[i] = QtGui.QHBoxLayout()
        ##参数设置显示
        hbox2[i] = QtGui.QHBoxLayout()
            #hbox1[i].addWidget(labelSymbol)
            #hbox1[i].addWidget(buttonInit[i])
            #hbox1[i].addWidget(buttonStart[i])
            #hbox1[i].addWidget(buttonStop[i])
            #hbox1[i].addWidget(buttonUpdate[i])
            #hbox1[i].addWidget(buttonSave[i])

        self.buttonBox[i].setCellWidget(0, 0, buttonInit[i])
        self.buttonBox[i].setCellWidget(0, 1, buttonStart[i])
        self.buttonBox[i].setCellWidget(0, 2, buttonStop[i])


---

---

---

#### 组件之间连接信号
    # classA(QtGui.QTableWidget):
    #   pass
    classA.itemDoubleClicked.connect(classB.actionFunction)
    # 这样单纯调用还是可以的,但是数据传输… 就得继续研究一下了。
##### 关闭事件退出提示
    def closeEvent(self, event):
        reply = QtGui.QMessageBox.question(self, 'Message',"Are you sure to quit?",

            QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
    if reply == QtGui.QMessageBox.Yes:
        event.accept()
    else:
        event.ignore()
函数放置位置就是主窗口类下就好

### QTableWidget 清除数据
    def updateData(self, data): #(缩进没问题吧…🙏)
            """将数据更新到表格中"""
        
        if self.name == u'RiskAlarmWidget':
            pass

        if self.updateBeginAction:
            data = self.updateBeginAction(data)
            if not data:
               return

        if self.updateAction:
            self.updateAction(data)
        else:
            if isinstance(data, unicode) and self.dataKey:
                # 删除模式
                key = data
                if key in self.dataDict:
                    d = self.dataDict[key]
                    row = d[self.headerList[0]].row()
                    self.removeRow(row)
                    del self.dataDict[key]
            else:
                # 如果设置了dataKey,则采用存量更新模式
                if self.InsertMode:
                    # 如果允许了排序功能,则插入数据前必须关闭,否则插入新的数据会变乱
                    if self._sorting:
                        self.setSortingEnabled(False)

                    newRow = 0 if self.InsertTopRow else self.rowCount()
                    self.insertRow(newRow)

                    for n, header in enumerate(self.headerList):

                        content = self.getContents(data, header)
                        cellType = self.headerDict[header]['cellType']
                        cell = cellType(content)
                
                        if self.font:
                            cell.setFont(self.font)

                        if self._saveData:            
                            cell.data = data                

                        self.setItem(newRow, n, cell)  
        
                    # 重新打开排序
                    if self._sorting:
                        self.setSortingEnabled(True)
          
                else:            
                    key = data.__getattribute__(self.dataKey)
                    if key in self.dataDict:
                        # 已存在数据字典中,则更新相关单元格
                        d = self.dataDict[key]
                        for header in self.headerList:

                            content = self.getContents(data, header)
                            cell = d[header]
                            cell.setContent(content)

                            if self._saveData:            
                # 如果设置了保存数据对象,则进行对象保存
                                cell.data = data                    
                    else:
                        # 如果允许了排序功能,则插入数据前必须关闭,
            #否则插入新的数据会变乱
                        if self._sorting:
                            self.setSortingEnabled(False)
                        # 不存在数据字典中,插入新行,创建此行单元格
                        newRow = 0 if self.InsertTopRow else self.rowCount()
                        self.insertRow(newRow)
 
                        d = {}
                        for n, header in enumerate(self.headerList):
                        
                            content = self.getContents(data, header)
                            cellType = self.headerDict[header]['cellType']
                            cell = cellType(content)
                    
                            if self.font:
                                cell.setFont(self.font)  
                # 如果设置了特殊字体,则进行单元格设置
                    
                            if self._saveData:            
                # 如果设置了保存数据对象,则进行对象保存
                                cell.data = data
                        
                            self.setItem(newRow, n, cell)
                            d[header] = cell
                        self.dataDict[key] = d
        
                        # 重新打开排序
                        if self._sorting:
                            self.setSortingEnabled(True)

        if self.updateAfterAction:
            self.updateAfterAction(data)            

插入后数据需要清理只需`.clearContentsText()`,这样是清理数据但保留表格方便更新内容。`.clear()`则清理数据以及表格方便<font color=75362109>重新</font>填入新的内容

---

#### 调用Windows程序
    import win32api
        path = 'D:/.../dockerTrader/gateway/ctpGateway/CTP_connect.json'
        win32api.ShellExecute(0, 'open', 'notepad.exe', path, '', 1)
    # 使用记事本打开此文件

---

#### Json
    with open('D:/.../dockerTrader/gateway/ctpGateway/CTP_connect.json', 'r') as f:
    setting = json.load(f)
    self.userID = str(setting['userID'])
    self.password = str(setting['password'])

---

---

### 信号槽(传输额外参数)
一般来说比如一个按钮吧 在链接点击信号与槽时`buttonInit[i].clicked.connect(partial(self.init, i))`就完事了然而当循环创建按钮对应同样的槽函数只是需要执行的变量有区别时就需要传输额外的参数
这时方法有二

环境

python2.7.8

pyqt 4.11.1
### 一: 使用lambda表达式
<pre class="prettyprint"><code class="language-python hljs "><span class="hljs-keyword">from</span> PyQt4.QtCore <span class="hljs-keyword">import</span> *  
<span class="hljs-keyword">from</span> PyQt4.QtGui <span class="hljs-keyword">import</span> *  

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyForm</span><span class="hljs-params">(QMainWindow)</span>:</span>  
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self, parent=None)</span>:</span>  
        super(MyForm, self).__init__(parent)  
        button1 = QPushButton(<span class="hljs-string">'Button 1'</span>)  
        button2 = QPushButton(<span class="hljs-string">'Button 1'</span>)  
        button1.clicked.connect(<span class="hljs-keyword">lambda</span>: self.on_button(<span class="hljs-number">1</span>))  
        button2.clicked.connect(<span class="hljs-keyword">lambda</span>: self.on_button(<span class="hljs-number">2</span>))  

        layout = QHBoxLayout()  
        layout.addWidget(button1)  
        layout.addWidget(button2)  

        main_frame = QWidget()  
        main_frame.setLayout(layout)  

        self.setCentralWidget(main_frame)  

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_button</span><span class="hljs-params">(self, n)</span>:</span>  
        print(<span class="hljs-string">'Button {0} clicked'</span>.format(n))  

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:  
    <span class="hljs-keyword">import</span> sys  
    app = QApplication(sys.argv)  
    form = MyForm()  
    form.show()  
    app.exec_()  </code></pre>

<p>解释一下on_button是怎样处理从两个按钮传来的信号我们使用lambda传递按钮数字给槽也可以传递任何其他东西甚至是按钮组件本身假如槽打算把传递信号的按钮修改为不可用</p>

### 二: 使用functools里的partial函数
<pre class="prettyprint"><code class="language-python hljs ">button1.clicked.connect(partial(self.on_button, <span class="hljs-number">1</span>))  
button2.clicked.connect(partial(self.on_button, <span class="hljs-number">2</span>))  </code></pre>

<p>Rapid GUI Program with Python and QT P143例子</p>

<pre class="prettyprint"><code class="language-python hljs "><span class="hljs-keyword">from</span> PyQt4.QtCore <span class="hljs-keyword">import</span> *  
<span class="hljs-keyword">from</span> PyQt4.QtGui <span class="hljs-keyword">import</span> *  
<span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> partial  
<span class="hljs-keyword">import</span> sys  

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bu1</span><span class="hljs-params">(QWidget)</span>:</span>  

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self, parent=None)</span>:</span>  
        super(Bu1, self).__init__(parent)  
        <span class="hljs-comment">#水平盒式布局  </span>
        layout = QHBoxLayout()  
        <span class="hljs-comment">#显示  </span>
        self.lbl = QLabel(<span class="hljs-string">'no button is pressed'</span>)  
        <span class="hljs-comment">#循环5个按钮  </span>
        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">5</span>):  
            but = QPushButton(str(i))  
            layout.addWidget(but)  
            <span class="hljs-comment">#信号和槽连接  </span>
            but.clicked.connect(self.cliked)  

        <span class="hljs-comment">#使用封装,lambda  </span>
        but = QPushButton(<span class="hljs-string">'5'</span>)  
        layout.addWidget(but)  
        but.clicked.connect(<span class="hljs-keyword">lambda</span>: self.on_click(<span class="hljs-string">'5'</span>))  
        <span class="hljs-comment">#使用个who变量,结果不正常,显示 False is pressed  </span>
        <span class="hljs-comment">#but.clicked.connect(lambda who="5": self.on_click(who))  </span>

        <span class="hljs-comment">#使用封装,partial函数  </span>
        but = QPushButton(<span class="hljs-string">'6'</span>)  
        layout.addWidget(but)  
        but.clicked.connect(partial(self.on_click, <span class="hljs-string">'6'</span>))  

        layout.addWidget(self.lbl)  
        <span class="hljs-comment">#设置布局  </span>
        self.setLayout(layout)  

    <span class="hljs-comment">#传递额外参数     </span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">cliked</span><span class="hljs-params">(self)</span>:</span>  
        bu = self.sender()  
        <span class="hljs-keyword">if</span> isinstance(bu, QPushButton):  
            self.lbl.setText(<span class="hljs-string">'%s is pressed'</span> % bu.text())  
        <span class="hljs-keyword">else</span>:  
            self.lbl.setText(<span class="hljs-string">'no effect'</span>)  
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_click</span><span class="hljs-params">(self, n)</span>:</span>  
        self.lbl.setText(<span class="hljs-string">'%s is pressed'</span> % n)  

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:          
    app = QApplication(sys.argv)  
    bu =Bu1()  
    bu.show()  
    app.exec_()   </code></pre>

感谢[来源](http://blog.csdn.net/fengyu09/article/details/39498777) 

---

###


## PyQt + echarts图表
    echarts绘制图标会在本地保存一个`html`文件所以使用`PyQt`将之作为一个网页页面加载即可
    from PyQt4.QtWebKit import *
    from PyQt4.QtGui  import *
    from PyQt4.QtCore import * 
    from pyecharts import Kline, Page
    def creat_charts():
        page = Page()
        chart_init = {
                "width": 1600,
                "height": 900,
            }
        chart = Kline("K 线图", **chart_init)
        chart.add("日K", ["2017/7/{}".format(i + 1) for i in range(31)], v1)
        page.add(chart)
        return page
    creat_charts()_render()     # 生成`render.html`文件
    self.view = QWebView()
    self.create_charts().render()
    url = QUrl("render.html")   # 需要加载`QUrl`使用字符串转化
    self.view.load(url)


## addWidget
在窗口布局的时候这玩意至关重要因为要先创建一个框架再往里面塞东西而塞的方法就是`addWidget`。所以简单记录一下小年这一天经常用到的环套

    hbox = QtGui.QHBoxLayout()
        vbox = QtGui.QVBoxLayout()
    可以通过`addWidget`来塞入`QtGui.QWidget`, `QtGui.QGroupBox`, 
`QtGui.QTableWidget`  ,`QScrollArea`    还可以`addWidget`诸如`QtGui.QPushButton`, `QtGui.QLabel`, 
`QtGui.QComboBox`, `QtGui.QLineEdit`等小部件

    `hbox``vbox`可以通过`addLayout`互相添加这两个是 框架即页面上的骨头”。来摆设肌肉->Widget”、"Box"等的
    当然最后要显示谁需要`self.setLayout(hbox)` 而支持`setLayout`少不了有
`QWidget`,`QScrollArea`, 所以它们好像能够无限互相套下去

### 小部件的方法
其实哪些方法也不用记打开`QtDesigner`直接搜索拖拽然后看右边的清单就行了但这几个还是记录下来吧在网上找太累了~
    
`QtGui.QComboBox`: `addItems`, '''addItem`, `currentText`
`QtGui.QSpinBox`: `setValue`, 


### QString2String
    def QString2PyString(self, qStr):   
        #QString,如果内容是中文,则直接使用会有问题,要转换成 python
        #string
        return unicode(qStr.toUtf8(), 'utf-8', 'ignore')

## QLineEdit
之所以放这么后面是因为我觉得这东西没什么好说的,也就是一个输入框嘛,但因为出现在后面所以说是现在发现了其中有趣的东西。那就是最近在做输入密码的时候想要限制,但在程序中去判断显然并不是什么有意思的事情。然后想起来输入的时候可以直接限制住啊。于是采用了`textChanged`去`connect`一个函数,来动态监测输入。但在调试中打开错了窗口所以以为没有用就找了其他的方法。那就是在初始化的时候直接卡死,结果没有找到设置长度的.set***但找到了现用的`setValidator`其配合正则表达式的食用方法如下:
    
    import re
        a = QtCore.QRegExp("[A-Za-z]{0,5}[1-9][0-9]{0,5}")
        a = QtCore.QRegExp("[a-zA-Z0-9]{0,12}$")
        self.txtPasswd.setValidator(QtGui.QRegExpValidator(a))
    # emm,很显然,把a替换成limitRule就要顺眼一些了。

不过<font color=#ff3bffl9>值得注意的是:</font>如果用`setText`将其它字符写进这个编辑小框框中的话,那么这个框框的规则就会被打破然后随便写。但要是`setText`的内容没有超出所规定的形式之外的话,那么规则照旧,依然执行。

# PyQtGraph:
[Link](http://www.pyqtgraph.org/)

    import pyqtgraph.examples  
    pyqtgraph.examples.run()