-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
229 lines (229 loc) · 247 KB
/
search.xml
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[事件处理]]></title>
<url>%2F2020%2F02%2F22%2F%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%2F</url>
<content type="text"><![CDATA[一个简单的安卓按钮点击事件案例带你了解java/Android事件处理的几种方式 处理事件 首先让我们回顾一下java中处理事件模式: 事件源: 能够产生事件的对象都可以成为事件源 . 监视器:事件源通过调用相应的方法将某个对象注册为自己的监视器。对于java,这个方法是:addXXXXListener(监视器);对于Android,这个方法是setXXXXListener(监视器)。 处理事件接口:监视器负责处理事件源发生的事件。监视器是一个对象,为了处理事件源发生的事件,监视器这个对象会自动调用接口中一个方法来处理事件。 如图所示: 事件描述 很简单,在安卓模拟器界面上点击按钮,TextView的内容由”这里是TextView”变成”按钮被点击!”。 事件监听的五种方法 自身类,也就是MainActivity实现OnClickListener接口。 外部类,这里使用 BtnEventListener 继承OnClickListener接口。 内部类 匿名内部类 Lambda表达式(本质上就是匿名类的另一种表达) 自身类 代码如下1234567891011121314151617181920212223242526272829package com.example.workhandling;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener { //自身类继承OnClickListener接口 Button myBtn1; //创建按钮对象 TextView textView;//创建文本视图对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.buttonevent); //绑定相应ID 注意findViewById返回View对象,需要强制类型转换 myBtn1 = (Button) findViewById(R.id.btn1); textView=(TextView)findViewById(R.id.textview); //事件源添加监视器(该类本身) myBtn1.setOnClickListener(this); } //注意 public 访问权限 public void onClick(View view){ textView.setText("Button被点击!"); }} 外部类123456789101112131415161718192021222324252627282930313233343536373839package com.example.workhandling;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity { Button myBtn1; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.buttonevent); myBtn1 = (Button) findViewById(R.id.btn1); textView = (TextView)findViewById(R.id.textview); //实现监视器与安卓界面的textView绑定 BtnEventListener BtnListener = new BtnEventListener(textView); //事件源设置监视器(外部类对象) myBtn1.setOnClickListener(BtnListener); }}//外部类继承OnClickListener接口class BtnEventListener implements View.OnClickListener{ TextView textView; //因为onClick函数中要使用textView对象,所以需要构造函数初始化 BtnEventListener(TextView textView){ this.textView=textView; } @Override public void onClick(View v) { textView.setText("(外部类)Button被点击!"); }} 内部类1234567891011121314151617181920212223242526272829303132package com.example.workhandling;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity { Button myBtn1; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.buttonevent); myBtn1 = (Button) findViewById(R.id.btn1); textView = (TextView)findViewById(R.id.textview); BtnEventListener BtnListener = new BtnEventListener(); myBtn1.setOnClickListener(BtnListener); } //内部类 class BtnEventListener implements View.OnClickListener{ @Override public void onClick(View v) { textView.setText("(内部类)Button被点击!"); } }} 匿名内部类123456789101112131415161718192021222324252627282930package com.example.workhandling;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity { Button myBtn1; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.buttonevent); myBtn1 = (Button) findViewById(R.id.btn1); //绑定 textView = (TextView)findViewById(R.id.textview); myBtn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { textView.setText("(匿名内部类)按钮被点击!"); } }); }} Lambda表达式Lambda表达式配置 参考博客: lambda表达式在Android中的妙用 在安卓中使用lambda报错(Lambda expressions are not supported at language level ‘7’)解决办法 ① 在Android视图下的build.gradle(Project:xxx(你的项目名))添加配置代码:123456789android {//注意是在android这里面.... compileOptions { sourceCompatibility = 1.8 targetCompatibility = 1.8 sourceCompatibility JavaVersion.VERSION_1_8//添加代码 targetCompatibility JavaVersion.VERSION_1_8//添加代码 }} ② 打开 File 点击Project structure,左侧选项栏选择SDK Location,在JDK location:选项框中选择找到1.8版本及以上的jdk路径.左侧选择栏选择Modules,在Source Compatibility和Target Compatibility中选择java1.8版本。重新构建项目。 Android中点击事件 使用 Lambda表达式123456789101112131415161718192021222324 package com.example.workhandling;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity { Button myBtn1; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.buttonevent); myBtn1 = (Button) findViewById(R.id.btn1); textView = (TextView)findViewById(R.id.textview); myBtn1.setOnClickListener(v->{ textView.setText("(Lambda表达式)按钮被点击!"); }); }} 总结 事件处理中应多使用匿名内部类和Lambda表达式!]]></content>
<categories>
<category>java</category>
<category>android</category>
</categories>
<tags>
<tag>事件监听</tag>
</tags>
</entry>
<entry>
<title><![CDATA[java(类与对象、子类与继承)]]></title>
<url>%2F2019%2F10%2F30%2Fjava%EF%BC%88%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1%E3%80%81%E5%AD%90%E7%B1%BB%E4%B8%8E%E7%BB%A7%E6%89%BF%EF%BC%89%2F</url>
<content type="text"><![CDATA[在java课程学习之中对类与对象、子类与继承有一些更深的理解,记录在这篇博客中。 类与对象 (1)一个java文件里面只能有一个主类,主类即类前面加一个public修饰。 (2)实例方法既能操作实例变量和类变量(前面加static修饰),也能调用实例方法和类方法。类方法只能操作类变量和调用类方法。类变量在主函数第一次使用该类时就为它分配了内存空间,防止堆里面,该类创建的所有对象共同使用该类的类变量。 (3) 包语句:如果你导入(import)了两个包且他们的类名相同。方法有两种:①两个类创建对象的时候都加上前缀包目录,就不需要写import导入包语句了。②一个类使用import导入包语句,创建对象就可以直接使用类名(不用加目录前缀),如果你要用另一个创建对象则需要写上前缀包目录。 (4)构造方法匹配问题:如果你用类定义一个对象,但该类有多个构造方法,则根据参数个数和参数类型匹配。还有下列情况:①如果你定义了有参构造函数,编译器就不会给你生成无参构造函数,用该类创建对象时是不允许无实参的,如果要创建无参对象,就要自己在该类里创建无参构造函数。② 如果你在类中定义了参数的优先级高构造函数,在创建对象时就不能传优先级低的参数。③ 如果你创建的对象参数类型在类中的构造函数没有,则他会找到参数优先级比他高的构造函数。优先级如下:从左到右增高 byte short char int long float double (5)类中的某一个构造函数调用其他构造函数,可以用 this(参数);函数语句,根据参数调用匹配的构造函数。但是不能用向优先级高的类型转换规则,会作递归调用处理。 子类与继承(1)访问权限表(Y表示能访问,N表示不能访问) 内部类 本包 子类 外部包 public Y Y Y Y protected Y Y Y N friendly Y Y N N private Y N N N 注意分析表的各个维度,例如:子类是可以访问不在同一包里的父类的protected变量或方法的。 (2)子类一定继承一个带无参构造函数的父类,没有无参构造函数的父类是不能被继承的,子类创建对象一定是先调用父类的无参构造函数再执行子类的构造函数。 (3) instanceof 用来判断左操作元对象是不是右操作元类创建的。子类创建的对象也是父类创建的。但是父类创建的对象,不是子类的对象。 (4)子类继承父类,则一定要满足下面条件中任意一条:①父类既没有无参构造方法也没有有参构造方法;②父类有无参构造方法,也要写无参构造方法;③父类有有参构造函数而没有无参构造函数,则子类的无参或有参构造函数的第一个语句一定是 super(参数); 这条函数语句。 (5)对象的上转型对象:父类创建的对象赋值给子类创建的对象(父类的引用指向子类的对象)。操作权限如图所示: (6)final类:不能被继承;final方法:不予许子类重写,可以被继承;常量:放在堆里(变量放在栈中),一定要赋初值,不允许被修改。 abstract 类 和abstract方法 (1)abstract修饰符在修饰类时必须放在类名前。abstract抽象类,不能创建实例对象,列如动物这一类. (2)abstract修饰方法,会使这个方法变成抽象方法,也就是只有声明(定义)而没有实现,实现部分以”;”代替。abstract修饰方法就是要求其子类覆盖(实现)这个方法。调用时可以以多态方式调用子类覆盖(实现)后的方法,也就是说抽象方法必须在其子类中实现,除非子类本身也是抽象类。父类是抽象类,其中有抽象方法,那么子类继承父类,并把父类中的所有抽象方法都实现(覆盖)了,子类才有创建对象的实例的能力,否则子类也必须是抽象类 (3)有抽象方法的类一定是抽象类。但是抽象类中不一定都是抽象方法,也可以全是具体方法。]]></content>
<categories>
<category>java</category>
</categories>
<tags>
<tag>类与对象</tag>
<tag>子类与继承</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hello_java]]></title>
<url>%2F2019%2F09%2F06%2Fhello-java%2F</url>
<content type="text"><![CDATA[本篇博客包括安装java运行环境(JDK)并在MS-DOS命令窗口(也就是常说的cmd命令)上编译运行java程序和安装java 强大的IDE:Intellij IDEA 并编写第一个hello java程序 JDK安装 java SE(java 标准平台)提供了JDK,下载地址:jdk下载 根据自己电脑版本和位数选择相应的JDK安装,具体安装步骤和环境变量配置如下:jdk安装 这里有配置环境变量经验的小伙伴可能会有“javac不是内部命令…”的错误,因为新建系统变量path的时候,会自动加一个分号,所以并不需要你多加一个分号。(如果没有上述问题请忽略此段文字) 解决办法链接:javac不是内部命令解决办法 java的平台无关 java最大的优势就是编写的软件能在执行码上兼容,在所有计算机上运行: MS-DOS运行第一个java程序 编写源文件,用记事本新建一个txt文件,打开文件编写一个java程序,保存,把文件名改为Hello.java,这里提供一份源码: 1234567891011121314151617package begin.demo;public class Hello { public static void main(String args[]){ System.out.println("大家好!"); System.out.println("Nice to meet you"); Student stu = new Student(); stu.speak("we are students"); float foo =-1; }}class Student{ public void speak(String s){ System.out.println(s); }} 编译:cmd命令cd到你的源文件所在的目录下,执行命令: 1javac Hello.java 你会发现目录下多了俩个.class(Hello.class、Student.class)文件,这也说明了java程序的基本单位是类。 运行:继续执行命令: 1java Hello 得到运行结果: 123大家好!Nice to meet youwe are students Intelij IDEA安装及运行一个强大的软件安装公众号:软件安装管家 Intelij IDEA安装 在intellij idea上编写第一个程序时,发现右键不能没有java class选项,解决办法:intellij idea右键不能创建java class怎么办? 运行错误: Error:java: 无效的目标发行版解决办法:Error:java: 无效的目标发行版]]></content>
<categories>
<category>java</category>
</categories>
<tags>
<tag>java</tag>
<tag>安装</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python爬虫(5)]]></title>
<url>%2F2019%2F08%2F05%2FPython%E7%88%AC%E8%99%AB%EF%BC%885%EF%BC%89%2F</url>
<content type="text"><![CDATA[Python爬虫之scrapy框架 Scrapy是Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。 安装及介绍安装 通过cmd命令执行 pip install scrapy即可安装。 注意: 在ubuntu 上安装scrapy之前,需要先安装以下依赖: sudo apt-It install python-dev python-pip libxml2-dev libxslt1-dev zliblg-dev libffi-dev libssl-dev ,然后再通过pip install scrapy 安装。 如果在windows 系统下运行时,提示这个错误ModuleNotFoundError: No module named ‘win32api’ ,那么使用以下命令可以解决: pip install pypiwin32 ,一般做法,就提前执行这个命令 文档 Scrapy官方文档: Scrapy中文文档: 介绍Scrapy架构图如图所示: Scrapy架构 Scrapy Engine(引擎):负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。 Scheduler(调度器):它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。 Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理。 Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)。 Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。 Downloader Middlewares(下载中间件):一个可以自定义扩展下载功能的组件。 Spider Middlewares(Spider中间件):一个可以自定扩展和操作引擎和Spider中间通信的功能组件。 项目创建 创建项目:cmd命令进入你要创建项目的目录下,执行命令: scrapy startproject [项目名字] 创建爬虫:进入项目所在的路径,执行命令: scrapy genspider [爬虫名字] [爬虫的域名] 注意:爬虫名字不能和项目名字一致。 生成目录如下: items.py: 用来存放爬虫爬取下来数据的模型。eg:content = scrapy.Field() middlewares.py: 用来存放各种中间件的文件.下载器中间件可以用来设置代理ip、user-agent来反反爬虫,需要在配置文件里配置SPIDER_MIDDLEWARES,不然就不会启动中间件。 pipelines.py:用来将items的模型 存储到本地磁盘中。里面存放pipline类,类中包括有init函数(用来打开存储数据的文件,例如json文件,或者连接数据库)、open_spider()(和init函数一样,但一般使用init函数)、process_item()(写入数据到文件中、或者执行数据库插入命令)、close_spider()(关闭文件).也是要去setting.py文件中取消ITEM_PIPELINES 注释,你要使用哪个pipline类就取消哪一个的注释,也可以在后面设置优先级。 settings.py: 本爬虫的一些配置信息(比如请求头、是否遵从机器人协议、下载延迟等)。 scrapy.cfg: 项目的配置文件。 spiders包: 以后所有的爬虫,都是存放到这个里面。做解析页面的操作,最后yeild返回 items数据。 Scrapy shell终端Scrapy shell是一个交互式终端,可以用来检测你用XPatn或者CSS表达式解析网页是否错误。避免了每次修改都要运行一次爬虫。 用法在cmd终端执行如下命令: 1scrapy shell [url] [url]即你打算解析的网页的网址 然后输入你的XPath表达式,判断结果是否真确。 实战糗事百科Scrapy爬虫笔记: response是一个‘scrapy. http.response.html.HtmlResponse’对象。可以执行‘xpath’和‘css’语法来提取数据。 2.提取出来的数据,是一个‘Selector’ 或者是一个‘SelectorList’对象。如果想要获取其中的字符串。那么应该执行‘getall()’或者‘get()’方法。 getall方法:获取‘Selector’中的所有文本。返回的是一一个列表。 get方法:获取的是‘Selector’中的第 一个文本。返回的是一个str类型。 5.如果数据解析回来,要传给pipline处理。那么可以使用yield来返回。或者是收集所有的item,最后统一使用return返回。 item: 建议在‘items.py’中定义好模型。以后就不要使用字典。 pipeline: 这个是专门用来保存数据的。其中有三个方法是会经常用的。 open spider(self,spider): 当爬虫被打开的时候执行。 process_ item(self, item, spider): 当爬虫有item传过来的时候会被调用。 close_ spider(self, spider): 当爬虫关闭的时候会被调用。 要激活piplilne,应该在settings.py中,设置ITEM PIPELINES。 JsonItemExporter和JsonLinesItemExporter保存json数据的时候,可以使用这两个类,让操作变得得更简单。 1.、JsonItemExporter:这个是每次把数据添加到内存中。最后统一写入到磁盘中。好处是,存储的数据是一个满足json规则的数据。坏处是如果数据量比较大,那么比较耗内存。 2.、JsonL inesItemExporter:这个是每次调用export_ item的时候就把这个item存储到硬盘中。坏处是每一个字典是一行,整个文件不是一个满足json格式的文件。好处是每次处理数据的时候就直接存储到了硬盘中,这样不会耗内存,数据也比较安全。]]></content>
<categories>
<category>Python爬虫</category>
<category>框架</category>
</categories>
<tags>
<tag>scrapy框架</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python爬虫(4)]]></title>
<url>%2F2019%2F07%2F14%2FPython%E7%88%AC%E8%99%AB%EF%BC%884%EF%BC%89%2F</url>
<content type="text"><![CDATA[python爬虫进阶 多线程多线程介绍 多线程是为了同步完成多项任务,通过提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。最简单的比喻多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理火车也可以有多节车厢。多线程的出现就是为了提高效率。同时它的出现也带来了-些问题。 threading模块 threading模块是python中专门提供用来做多线程编程的模块。threading 模块中最常用的类是Thread 。threading的一些功能函数有: 查看线程数:使用threading . enumerate()函数便可以看到当前线程的数量。 查看当前线程的名字:使用threading.current _thread() 可以看到当前线程的信息。 例示代码如下:1234567891011121314151617181920212223242526#encoding: utf-8import timeimport threadingdef coding(): for x in range(3): print('正在写代码%s'%threading.current_thread()) time.sleep(1)def drawing(): for x in range(3): print('正在画图%s' % threading.current_thread()) time.sleep(1)def main(): t1 = threading.Thread(target=coding) t2 = threading.Thread(target=drawing) t1.start() t2.start() print(threading.enumerate())if __name__ == '__main__': main() 运行结果:123456789正在写代码<Thread(Thread-1, started 1648)>正在画图<Thread(Thread-2, started 10252)>[<_MainThread(MainThread, started 13940)>, <Thread(Thread-1, started 1648)>, <Thread(Thread-2, started 10252)>]正在写代码<Thread(Thread-1, started 1648)>正在画图<Thread(Thread-2, started 10252)>正在画图<Thread(Thread-2, started 10252)>正在写代码<Thread(Thread-1, started 1648)>Process finished with exit code 0 由运行结果可看出:总共有三个线程,两个子线程执行先后顺序是不固定的。 Thread类: 为了让线程代码更好的封装。可以使用thdeading 模块下的Thread类,继承自这个类,然后实现run 方法,线程就会自动运行run方法中的代码。例示代码如下: 12345678910111213141516171819202122232425262728#encoding: utf-8import threadingimport time# 继承自threading.Threadclass CodingThread(threading.Thread): def run(self):# run方法 for x in range(3): print('正在写代码%s' % threading.current_thread()) time.sleep(1)class DrawingThread(threading.Thread): def run(self): for x in range(3): print('正在画图%s' % threading.current_thread()) time.sleep(1)def main(): t1 = CodingThread() t2 = DrawingThread() t1.start() t2.start()if __name__ == '__main__': main() 多线程共享全局变量问题及解决办法:锁机制 多线程都是在同一个进程中运行的。因此在进程中的全局变量所有线程都是可共享的。这就造成了一个问题,因为线程执行的顺序是无序的。全局变量变换也是无规则的,有可能会造成数据错误。示例代码如下: 1234567891011121314151617181920212223#encoding: utf-8import threadingVALUE = 0 #全局变量# gLock = threading.Lock() # 定义一个锁对象def add_value(): global VALUE # 为了使函数体内可以访问这个全局 # gLock.acquire() #上锁 # 其他线程等待 for x in range(10000000): VALUE += 1 # gLock.release() # 去锁 print('value:%d'%VALUE)def main(): for x in range(2): t = threading.Thread(target=add_value) t.start()if __name__ == '__main__': main() 运行结果: 1234567没上锁前:value:11535753value:12040114上锁后:value:10000000value:20000000 注意:如果没有全局变量或者只是单纯的访问全局变量,不要用锁机制 多线程之生产者和消费者Lock版 生产者和消费者模式是多线程开发中经常见到的一种模式。生产者的线程专门]用来生产一些数据,然后存放到一个中间的变量中。消费者再从这个中间的变量中取出数据进行消费。但是因为要使用中间变量,中间变量经常是一些全局变量,因此需要使用锁来保证数据完整性。 例示代码如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354import timegMoney = 1000gLock = threading.Lock() # 生产者消费者共用一把锁gTotalTimes = 10 # 生产者总生产10次gTimes = 0 # 记录生产者生产次数class Producer(threading.Thread): def run(self): global gMoney # 改变全局变量的值 global gTimes while True: money = random.randint(100,1000) # 需要导入random包 gLock.acquire() # 获取这把锁 if gTimes >= gTotalTimes: # 次数达到10次 gLock.release() # 跳出循环,也要记得开锁 break gMoney += money print('%s生产了%d元钱,剩余%d元钱'%(threading.current_thread(),money,gMoney)) gTimes += 1 gLock.release() time.sleep(1)class Consumer(threading.Thread): def run(self): global gMoney while True: money = random.randint(100,1000) gLock.acquire() if gMoney >= money: gMoney -= money print('%s消费者消费了%d元钱,剩余%d元钱' % (threading.current_thread(),money,gMoney)) else: if gTimes >= gTotalTimes: gLock.release() break print('%s消费者准备消费%d元钱,剩余%d元钱,不足!'%(threading.current_thread(),money,gMoney)) gLock.release() time.sleep(1)def main(): for x in range(3): # 三个消费者 t = Consumer(name='消费者线程%d'%x) # 可以直接命名 t.start() for x in range(5): # 五个生产者 t = Producer(name="生产者线程%d"%x) t.start()if __name__ == '__main__': main() Condition版 Lock版本的生产者与消费者模式可以正常的运行。但是存在一个不足,在消费者中,总是通过while True死循环并且上锁的方式去判断钱够不够。上锁是一个很耗费CPU资源的行为。 因此这种方式不是最好的。还有一种更好的方式便是使用threading.Condition来实现。threading. Condition可以在没有数据的时候处于阻塞等待状态。一旦有合适的数据了,还可以使用notify 相关的函数来通知其他处于等待状态的线程。这样就可以不用做一-些无用的上锁和解锁的操作。可以提高程序的性能。 threading.Condition 类似threading.Lock ,可以在修改全局数据的时候进行上锁,也可以在修改完毕后进行解锁。以下将一些常用的函数做个简单的介绍: acquire :上锁。 release :解锁。 wait :将当前线程处于等待状态,并且会释放锁。可以被其他线程使用notify和notify_ all函数唤醒。被唤醒后会立马上锁,继续执行下面的代码。 notify :通知某个正在等待的线程,默认是第1个等待的线程。需要在release之前调用。 notify. all :通知所有正在等待的线程。 notify和notify. al1不会释放锁。并且需要在release之前调用。 Condition版的生产者与消费者模式示例代码如下:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859#encoding: utf-8import threadingimport randomimport timegMoney = 1000gCondition = threading.Condition()gTotalTimes = 10gTimes = 0class Producer(threading.Thread): def run(self): global gMoney global gTimes while True: money = random.randint(100,1000) gCondition.acquire() if gTimes >= gTotalTimes: gCondition.release() break gMoney += money print('%s生产了%d元钱,剩余%d元钱'%(threading.current_thread(),money,gMoney)) gTimes += 1 gCondition.notify_all() # 提醒所有等待的线程 gCondition.release() time.sleep(0.5)class Consumer(threading.Thread): def run(self): global gMoney while True: money = random.randint(100,1000) gCondition.acquire() while gMoney < money: # 被提醒不需等待后仍要排队,所以还是可能会出现生产的不够消费的情况,所以还是需要判断。 if gTimes >= gTotalTimes: gCondition.release() # 返回整个函数之前还是要记得释放锁 return # break只能跳出最近的while循环 print('%s准备消费%d元钱,剩余%d元钱,不足!' % (threading.current_thread(),money,gMoney)) gCondition.wait() # 等待 结束等待后继续执行下面的代码 gMoney -= money print('%s消费了%d元钱,剩余%d元钱' % (threading.current_thread(),money,gMoney)) gCondition.release() time.sleep(0.5)def main(): for x in range(3): t = Consumer(name='消费者线程%d'%x) t.start() for x in range(5): t = Producer(name="生产者线程%d"%x) t.start()if __name__ == '__main__': main() Queue线程安全队列 在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列中,那么Python内置了一个线程安全的模块叫做queue 模块。Python中 的queue模块中提供了同步的、线程安全的队列类,包括FIFO (先进先出)队列Queue, LIFO (后入先出)队列LifoQueue.这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么都做完) ,能够在多线程中直接使用。可以使用队列来实现线程间的同步。相关的函数如下: 初始化Queue(maxsize):创建一个先进先出的队列。 qsize(): 返回队列的大小。 empty():判断队列是否为空。 full():判断队列是否满了。 get():从队列中取最后一个数据。 put():将一个数据放到队列中。 例示代码如下:12345678910111213141516171819202122232425262728#encoding: utf-8from queue import Queueimport timeimport threadingdef set_value(q): # 带有参数 队列q index = 0 while True: q.put(index) index += 1 time.sleep(3)def get_value(q): while True: print(q.get())def main(): q = Queue(4) t1 = threading.Thread(target=set_value,args=[q]) t2 = threading.Thread(target=get_value,args=[q]) t1.start() t2.start()if __name__ == '__main__': main() GIL全局解释器锁 Python自带的解释器是CPython 。CPython 解释器的多线程实际上是一个假的多线程(在多核CPU中,只能利用一核,不能利用多核,但是cpu在不断地切换线程,给我们多线程的感觉 )。同一时刻只有一个线程在执行,为了保证同一时刻只有一个线程在执行,在CPython 解释器中有一个东西叫做GIL (GlobalIntepreter Lock) , 叫做全局解释器锁。这个解释器锁是有必要的。因为CPython解释器的内存管理不是线程安全的。当然除了CPython解释器,还有其他的解释器,有些解释器是没有GIL 锁的,见下面: Jython :用Java实现的Python解释器。不存在GIL锁。 IronPython :用.net 实现的Python解释器。不存在GIL锁。 PyPy: 用Python实现的Python解释器。存在GIL锁。 GIL虽然是一个假的多线程。但是在处理一些IO操作 (比如文件读写和网络请求)还是可以在很大程度上提高效率的。在I0操作上建议使用多线程提高效率。在一些CPU计算操作上不建议使用多线程,而建议使用多进程。 动态网页数据抓取 AJAX什么是AJAX AJAX ( Asynchronouse JavaScript And XML)异步JavaScript和XML。AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用Ajax) 如果需要更新内容,必须重载整个网页页面。因为传统的在传输数据格式方面使用的是XML 语法。因此叫做AJAX ,其实现在数据交互基本上都是使用JSON。使用AJAX加载的数据,即使使用了JS,将数据渲染到了浏览器中,在(右键->查有网页源代码)还是不能看到通过ajax加载的数据,只能看到使用这个url加载的html代码。 获取ajax数据的方式: 1.直接分析ajax调用的接口。然后通过代码请求这个接口。 2.使用Selenium+chromedriver模拟浏览器行为获取数据。 方式 优点 缺点 分析接口 直接可以请求到数据。不需要做一些解析工作。代码量少,性能高。 分析接口比较负责,特别是一些通过js 混淆的接口,要有一定的js功底。容易被发现是爬虫。 selenium 直接模拟浏览器的行为。浏览器能请求到的,使用selenium也能请求到。爬虫更稳定。 代码量多。性能低。 Selenium+chromedriver获取动态数据: Selenium相当于是一个机器人。可以模拟人类在浏览器上的-些行为,自动处理浏览器上的一些行为,比如点击,填充数据,删除cookie等。Selenium最初是用来做自动化测试的,只是后来被发现可以运用在爬虫中。针对不同的浏览器有不同的driver。以下是两个浏览器及其对应的driver: Chrome的Chromedriver· : http://sites.google.com/a/chromium.org/chromedriver/downloads Firefox的geckodriver: https://github.com/mozilla/geckodriver/releases 安装Selenium和Firefox的geckodriver1.安装Selenium : Selenium 有很多语言的版本,有java、ruby、 python等。 我们下载python版本的就可以了。 1pip install selenium 2.安装geckodriver :下载完成后,放到不需要权限的纯英文目录下就可以了。 Selenium操作打开和关闭关闭页面: driver.clkse() :关闭当前页面。 driver.quit() :退出整个浏览器。 例示代码:1234567891011121314#encoding: utf-8from selenium import webdriverimport timedriver_path = r"D:\FireFoxDriver\geckodriver.exe"driver = webdriver.Firefox(executable_path=driver_path)driver.get('https://www.baidu.com/')print(driver.page_source) # 打印网页源代码time.sleep(5)# driver.close()driver.quit() 定位元素 find_ element_ by _id :根据id来查找某个元素。 12submitTag = driver. find_ element_ by_ id('su')submitTagl = driver. find_ element(By.ID, 'su') find_ element_by_class_name :根据类名查找元素。 12submitTagI driiver. find_ element by_ class_ name('su')submitTag1元driver . find_ element(By.CLASS_ NAME, 'su') find _element _by _name :根据name属性的值来查找元素。 12submitTag = driver. find_ element_by_name( ' email' )submitTagl = driver. find_ element(By.NAME,'email') find element_ by_ tag_ name :根据标签名来查找元素。 12submitTag = driver.find_ element _by_tag. name('div')submitTag1 = driver.find_ element(By.TAG_ NAME,'div') find_ element _by_xpath :根据xpath语法来获取元素。 12submitTag = driver.find_ element_ by_xpath('//div')submitTag1 = driver.find_ element(By. XPATH, '//div') find element_ by_ .css_ selector :根据css选择器选择元素。 12submitTag。driver. find_ element_ by_ Css_ selector('//div')submitTag1。driver . find_ element(By.CSS_ SELECTOR,'//div') 操作表单元素 操作输入框: 分为两步。第一步:找到这个元素。第二步:使用send, _keys(value) ,将数据填充进去。示例代码如下: 12inputTag = driver. find element_ by_id('kw' )inputTag.send.keys('python') 使用Clear方法可以清除输入框中的内容。示例代码如下:1InputTag. clear() 操作checkbox:因为要选中checkbox 标签,在网页中是通过鼠标点击的。因此想要选中checkbox 标签,那么先选中这个标签,然后执行click事件。示例代码如下: 12rememberTag = driver.find element_by_name("rememberMe")rememberTag.click() 选择select: select元素不能直接点击。因为点击后还需要选中元素。这时候selenium就专门为select标签提供了一个类selenium. webdriver . suport.ui .Select。将获取到的元素当成参数传到这个类中,创建这个对象。以后就可以使用这个对象进行选择了。示例代码如下: 12345678910111213141516from selenium. webdriver. support.ui import Select#选中这个标签,然后使川Select创建对象selectTag = Select(driver.find_ element_by_ name( "jumpNenu" ))# 1. 根据索引选择selectTag.select_ by_ index(1)# 2. 根据值选择 selectTag.select_by_ value("http://ww.95yueba.com")# 3.根据可视的文本选择selectTag.select_ by_visible_text("95秀客户蹦")#取消选中所有选项selectTag.deselect_all() 操作按钮:操作按鈕有很多种方式。比如単击、右击、双去等。这里讲一个最常用的。就是点击。直接調用click函数就可以了。示例代码如下: 12inputTag = driver.find_element_by_id('su')inputTag.click() 行为链 有时候在页面中的操作可能要有很多步,那么这时候可以使用鼠标行为链类ActionChains来完成。比如现在要将鼠标移动到某个元素上并执行点击事件。那么示例代码如下: 12345678910111213141516171819#encoding: utf-8from selenium import webdriverfrom selenium.webdriver.common.action_chains import ActionChainsimport timedriver_path = r"D:\ProgramApp\chromedriver\chromedriver.exe"driver = webdriver.Chrome(executable_path=driver_path)driver.get('https://www.baidu.com/')inputTag = driver.find element_by_id('kw')submitTag = driver.find_ element_by_id('su')actions = ActionChains(driver) # 行为对象actions.move_to_element(inputTag) # 移动到输入框actions.send_keys_to_element(inputTag,'python') # 输入值actions .move_to_element(submitTag) # 移动到提交按钮actions.click(submitTag) # 点击提交按钮actions.perform() # 整个行为表演一遍 还有更多的鼠标相关的操作。 click and_hold(element): 点击但不松开鼠标。 context_click(element): 右键点击. double_click(element): 双击。 更多方法请参考: selenium 操作cookieCookie操作:1.获取所有的cookie : 12for cookie in driver.get_cookies():print(cookie) 2.根据cookie的key获取value: 1value.driver.get_cookie(key) 3.删除所有的cookie: 1driver.delete_all_cookies() 4.删除某个cookie : 1driver.delete_cookie(key) 例示代码如下: 123456789101112131415161718192021#encoding: utf-8from selenium import webdriverfrom selenium.webdriver.common.action_chains import ActionChainsimport timedriver_path = r"D:\FireFoxDriver\geckodriver.exe"driver = webdriver.Firefox(executable_path=driver_path)driver.get('https://www.baidu.com/')for cookie in driver.get_cookies():# 获取所有的cookies print(cookie)print('='*30)print(driver.get_cookie("PSTM"))# 根据cookie的key获取valuedriver.delete_cookie("PSTM") #根据cookie名删除print('='*30)print(driver.get_cookie('PSTM'))driver.delete_all_cookies()# 删除所有的·cookie 页面等待: 现在的网页越来越多采用了Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果实际页面等待时间过长导致某个dom元素还没出来,但是你的代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。为了解决这个问题。所以Selenium提供了两种等待方式: 一种是隐式等待、一种是显式等待。 隐式等待:调用driver.implicitly_wait。那么在获取不可用的元素之前,会先等待传入的时间。示例代码如下: 123456789#encoding: utf-8from selenium import webdriverdriver_path = r"D:\FireFoxDriver\geckodriver.exe"driver = webdriver.Firefox(executable_path=driver_path)driver.implicitly_wait(10)driver.get('https://www.douban.com/')driver.find_element_by_id("dsjkafhaafh") 显示等待:显示等待是表明某个条件成立后才执行获取元素的操作。也可以在等待的时候指定一个最大的时间,如果还没超过这个时间就加载出来了就不再等待,如果超过这个时间那么就抛出一个异常。显示等待应该使用selenium. webdriver. support.excepted_conditions 期望的条件和selenium.webdriver.support .ui .WebDriverWait来配合完成。示例代码如下: 1234567891011121314151617# 显式等待#encoding: utf-8from selenium import webdriverfrom selenium.webdriver.support.ui import WebDriverWait # 显示等待from selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Bydriver_path = r"D:\FireFoxDriver\geckodriver.exe"driver = webdriver.Firefox(executable_path=driver_path)driver.get('https://www.douban.com/')element = WebDriverWait(driver,10).until( EC.presence_of_element_located((By.ID,'fdgfst34')))print(element) 一些其他的等待条件: presence_of_element_located: 某个元素已经加载完毕了。 presence_of_all_emement_located: 网页中所有满足条件的元素都载完毕了。 element_to_be_cliable:某个元素是可以点击了。更多条件请参考: 切换页面 有时候窗口中有很多子tab页面。这时候肯定是需要进行切换的。selenium 提供了一个叫做switch_to.window 来进行切换,具体切换到哪个页面,可以从driver.window_handles 中找到。示例代码如下: 1234567891011121314151617181920212223242526272829#encoding: utf-8from selenium import webdriverimport timedriver_path = r"D:\FireFoxDriver\geckodriver.exe"driver = webdriver.Firefox(executable_path=driver_path)driver.get('https://www.baidu.com/')print(driver.current_url) # 打印当前url# print(driver.page_source)# 打印当前页面代码js = "window.open('https://www.douban.com/')"driver.execute_script(js)# 打开新的页面print(driver.current_window_handle)handles = driver.window_handlesprint(handles)time.sleep(2) # 设置一个睡眠时间否则会,因为执行速度太快还没加载完第二个页面就直接跳转driver.switch_to.window(handles[1])print(driver.current_url)# print(driver.page_source)# 虽然在窗口中切换到了新的页面。但是driver中还没有切换。# 如果想要在代码中切换到新的页面,并且做一些爬虫。# 那么应该使用driver.switch_to_window来切换到指定的窗口# 从driver.window_handlers中取出具体第几个窗口# driver.window_handlers是一个列表,里面装的都是窗口句柄。# 他会按照打开页面的顺序来存储窗口的句柄。 运行结果:1234https://www.baidu.com/6442450945['6442450945', '6442450949']https://www.douban.com/ selenium设置代理ip: 有时候频繁爬取一些网页。服务器发现你是爬虫后会封掉你的ip地址。这时候我们可以更改代理ip。更改代理ip,不同的浏览器有不同的实现方式。这里以Chrome 浏览器为例来讲解: 1234567891011#encoding: utf-8from selenium import webdriverdriver_path =r"D:\FireFoxDriver\geckodriver.exe"options = webdriver.FirefoxOptions()options.add_argument("--proxy-server=http://116.208.55.75:9999")driver = webdriver.Firefox(firefox_options=options,executable_path=driver_path)driver.get("https://www.whatismyip.com/") 结果说明:执行该代码发现ip还是本机ip(笑哭),找了很久的解决方案也没找到。。。。 WebElement元素:from selenium . webdriver.remote.webelement import WebElement 类是每个获取出来的元素的所属类。有一些常用的属性: get_attribute:这个标签的某个属性的值。 screentshot:获取当前页面的截图。这个方法只能在driver上使用。 driver的对象类,也是继承自WebElement 。更多请阅读相关源代码。(ctrl + b) 例示代码如下: 1234567891011121314#encoding: utf-8from selenium import webdriverdriver_path = r"D:\FireFoxDriver\geckodriver.exe"driver = webdriver.Firefox(executable_path=driver_path)driver.get('https://www.baidu.com/')submitBtn = driver.find_element_by_id('su')print(type(submitBtn))print(submitBtn.get_attribute("value"))driver.save_screenshot('baidu.png') # 当前路径会生成当前网页截屏文件 运行结果:12<class 'selenium.webdriver.firefox.webelement.FirefoxWebElement'>百度一下 Tesseract 图片识别安装及配置 安装tesseract下载地址: 我这里下载的是 tesseract-ocr-setup-3.05.02-20180621.exe双击安装,然后在如图所示Additional Language data项选择你要识别图片里包含的语言:一般勾选英语、简单汉语和Math,默认c盘路径不要更改。 配置环境变量此电脑-高级设置-环境配置-系统变量:① 新建,变量名TESSDATA_PREFIX ,变量值:C:\Program Files (x86)\Tesseract-OCR\tessdata。② 想要在cmd下能够使用tesseract命令,那么需要把tesseract.exe所在的目录放到PATH 环境变量中,找到变量名为path,新添加路径:C:\Program Files (x86)\Tesseract-OCR。 配置完成后在命令行输入tesseract -v验证是否配置成功 用命令tesseract –list-langs来查看Tesseract-OCR支持语言。 在终端下识别图片 cmd命令cd到你要识别图片的路径下,输入命令 1234 tesseract 图片路径 文件路径eg: tesseract a.png D:\python\a那么就会识别出a.png中的图片,并且把文字写入到a.txt 中。如果不想写入文件,直接想显示在终端,那么不要加文件名就可以了。 python代码识别图片 在Python代码中操作tesseract 。需要安装-个库, 叫做pytesseract 。通过pip的方式即可安装: 1pip install pytesseract 并且,需要读取图片,需要借助一个第三方库叫做PIL 。通过pip list看下是否安装。如果没有安装,通过pip的方式安装: 1pip install PIL 例示代码:1234567891011121314#encoding: utf-8import pytesseractfrom PIL import Image# 指定 tesseract.exe所在的路径pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe"# 打开图片image = Image.open('a.png')# 调用image_to_string将图片转化为文字。text = pytesseract.image_to_string(image,lang='eng')print(text)]]></content>
<categories>
<category>Python爬虫</category>
</categories>
<tags>
<tag>多线程</tag>
<tag>ajax</tag>
<tag>selenium</tag>
<tag>tesseract图片识别</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python爬虫(3)]]></title>
<url>%2F2019%2F07%2F13%2FPython%E7%88%AC%E8%99%AB%EF%BC%883%EF%BC%89%2F</url>
<content type="text"><![CDATA[Python爬虫之数据处理 json基本介绍JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。一些好用的json转换网站:www.json.cn JSON支持数据格式: 对象(字典)。使用花括号。 数组(列表)。使用方括号。 整形、浮点型、布尔类型还有null类型。 字符串类型(字符串必须要用双引号,不能用单引号)。 多个数据之间使用逗号分开。注意: json本质上就是一个字符串。 将python对象转换为json字符串dump和dumps举例代码如下:123456789101112131415161718192021222324252627282930313233343536373839#encoding: utf-8import json# 将python对象转换为json字符串persons = [ { 'username':"张三", 'age': 18, 'country': 'china' }, { 'username': '李赛', 'age': 20, 'country': 'china' }]# 方法一# json_str = json.dumps(persons) # dumps注意有s# with open('person.json','w') as fp:# fp.write(json_str)# 方法二# 因为json在dump的吋候,只能存放ascii的字符,因此会将中文迸行转义,这吋候我们可以使用ensure_ ascii=False 关闭这个特性。with open('person.json','w',encoding='utf-8') as fp: json.dump(persons,fp,ensure_ascii=False) # 第二个参数是为了中文不会变成ASCII码形式,dump注意没有s# 在Python中。只有基本数据类型オ能转换成json格式的字符串。也即: int 、float、list、dict 、tuple o# class Person(object):# 非基本数据类型不能被转化为json.# country = 'china'## a = {# 'person': Person()# }# json.dumps(a) 运行结果:生成一个person.json文件里面的内容是: 1[{"username": "张三", "age": 18, "country": "china"}, {"username": "李赛", "age": 20, "country": "china"}] 将一个json字符串转化成python对象load 和loads12345678910111213141516#encoding: utf-8import json# 方法一json_str = '[{"username": "张三", "age": 18, "country": "china"}, {"username": "李赛", "age": 20, "country": "china"}]'persons = json.loads(json_str)print(type(persons))for person in persons: print(person)# 方法二# with open('person.json','r',encoding='utf-8') as fp:# persons = json.load(fp)# print(type(persons))# for person in persons:# print(person) 运行结果: 123<class 'list'>{'username': '张三', 'age': 18, 'country': 'china'}{'username': '李赛', 'age': 20, 'country': 'china'} CSV基本介绍 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。 纯文本,使用某个字符集,比如ASCII、Unicode、EBCDIC或GB2312; 由记录组成(典型的是每行一条记录); 每条记录被分隔符分隔为字段(典型分隔符有逗号、分号或制表符;有时分隔符可以包括可选的空格); 每条记录都有同样的字段序列。 读取csv文件reader()返回列表下标读取123456789101112131415161718#encoding: utf-8import csv# 通过下标获取def read_csv_demo1(): with open('stock.csv', 'r') as fp: # reader是一个迭代器 reader = csv.reader(fp) # 列表 next(reader) # 向下面移动一行 ,跳过标题数据 for x in reader: name = x[3] volumn = x[-1] print({'name': name, 'volumn': volumn})if __name__ == '__main__': read_csv_demo1() DictReader()返回字典key读取1234567891011121314151617#encoding: utf-8import csv# 通过key获取def read_csv_demo2(): with open('stock.csv','r') as fp: # 使用DictReader创建的reader对象 # 不会包含标题那行的数据 # reader是一个迭代器,遍历这个迭代器,返回来的是一个字典。 reader = csv.DictReader(fp) #字典 for x in reader: value = {"name":x['secShortName'],'volumn':x['turnoverVol']} print(value)if __name__ == '__main__': read_csv_demo2() 写入csv文件writer()123456789101112131415161718192021#encoding: utf-8import csvdef write_csv_demo1(): headers = ['username', 'age', 'height'] values = [ ('張三', 18, 180), ('李四', 19, 190), ('王五', 20, 160) ] with open('classroom.csv', 'w', encoding='utf-8', newline='') as fp: # 解决乱码 ,第三个参数是每行结束符, # 默认为一行空行,这里换成空。 writer = csv.writer(fp) writer.writerow(headers) writer.writerows(values)if __name__ == '__main__': write_csv_demo1() DictWriter()1234567891011121314151617181920#encoding: utf-8import csvdef write_csv_demo2(): headers = ['username', 'age', 'height'] values = [ {'username':'张三','age':18,'height':180}, {'username':'李四','age':19,'height':190}, {'username':'王五','age':20,'height':160} ] with open('classroo1.csv','w',encoding='utf-8',newline='') as fp: writer = csv.DictWriter(fp,headers) # 表头是列表,不会自动写入的 # 写入表头数据的时候,需要调用writeheader方法 writer.writeheader() writer.writerows(values)if __name__ == '__main__': write_csv_demo2()]]></content>
<categories>
<category>Python爬虫</category>
</categories>
<tags>
<tag>json</tag>
<tag>csv</tag>
<tag>excel</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python爬虫(2)]]></title>
<url>%2F2019%2F06%2F09%2FPython%E7%88%AC%E8%99%AB%EF%BC%882%EF%BC%89%2F</url>
<content type="text"><![CDATA[网络爬虫之数据提取数据提取的解析器有lxml、Beautifulsoup和正则表达式三种,对比如下: 解析工具 解析速度 使用难度 lxml 快 简单 BeautifulSoup 最慢 最简单 正则表达式 最快 最难 Xpath xpath (XML Path Language)是- -门 ]在XML和HTML文档中查找信息的语信,可用来在XML和HTML文档中对元素和属性进行遍历。 XPath开发工具 Chrome插件XPath Helper。 Firefox插件XPath Checker。xpath语法 使用方式:使用//获取整个页面当中的元素,然后写标签名,然后再写谓词进行提取。比如:1//div[@class='abc'] 需要注意的知识点: /和//的区别:/代表只获取直接子节点。//获取子孙节点。一般//用得比较多。当然也要视情况而定。 contains:有时候某个属性中包含了多个值,那么可以使用contains函数。示例代码如下: 1//div[contains(@class,'job_detail')] 谓词中的下标是从1开始的,不是从0开始的。 lxml解析器解析HTML代码安装lxml1pip install lxml 使用 解析html字符串:使用lxml.etree.HTML进行解析。示例代码如下: 1234from lxml import etreehtmlElement = etree.HTML(text)print(etree.tostring(htmlElement,encoding='utf-8').decode("utf-8")) 解析html文件:使用lxml.etree.parse进行解析。示例代码如下: 12htmlElement = etree.parse("tencent.html")print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8')) 这个函数默认使用的是XML解析器,所以如果碰到一些不规范的HTML代码的时候就会解析错误,这时候就要自己创建HTML解析器。 123parser = etree.HTMLParser(encoding='utf-8')htmlElement = etree.parse("lagou.html",parser=parser)print(etree.tostring(htmlElement, encoding='utf-8').decode('utf-8')) lxml结合xpath 使用xpath语法。应该使用Element.xpath方法。来执行xpath的选择。示例代码如下:1trs = html.xpath("//tr[position()>1]") xpath函数返回来的永远是一个列表。 获取某个标签的属性: 12href = html.xpath("//a/@href")# 获取a标签的href属性对应的值 获取文本,是通过xpath中的text()函数。示例代码如下: 1address = tr.xpath("./td[4]/text()")[0] 在某个标签下,再执行xpath函数,获取这个标签下的子孙元素,那么应该在斜杠之前加一个点,代表是在当前元素下获取。示例代码如下: 1address = tr.xpath("./td[4]/text()")[0] 综合示例如下: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#encoding: utf-8from lxml import etreeparser = etree.HTMLParser(encoding='utf-8')html = etree.parse("tencent.html",parser=parser)# 1. 获取所有tr标签# //tr# xpath函数返回的是一个列表trs = html.xpath("//tr")for tr in trs: print(etree.tostring(tr,encoding='utf-8').decode("utf-8"))# 2. 获取第2个tr标签tr = html.xpath("//tr[2]")[0]print(etree.tostring(tr,encoding='utf-8').decode("utf-8"))# 3. 获取所有class等于even的tr标签trs = html.xpath("//tr[@class='even']")for tr in trs: print(etree.tostring(tr,encoding='utf-8').decode("utf-8"))# 4. 获取所有a标签的href属性aList = html.xpath("//a/@href")for a in aList: print("http://hr.tencent.com/"+a)# 5. 获取所有的职位信息(纯文本)trs = html.xpath("//tr[position()>1]")positions = []for tr in trs: # 在某个标签下,再执行xpath函数,获取这个标签下的子孙元素 # 那么应该在//之前加一个点,代表是在当前元素下获取 href = tr.xpath(".//a/@href")[0] fullurl = 'http://hr.tencent.com/' + href title = tr.xpath("./td[1]//text()")[0] category = tr.xpath("./td[2]/text()")[0] nums = tr.xpath("./td[3]/text()")[0] address = tr.xpath("./td[4]/text()")[0] pubtime = tr.xpath("./td[5]/text()")[0] position = { 'url': fullurl, 'title': title, 'category': category, 'nums': nums, 'address': address, 'pubtime': pubtime } positions.append(position)print(positions) 下面是两个实战例子,爬取豆瓣电影top250网页内容和爬取电影天堂近期电影网页内容,已上传到码云,地址如下。 BeautifulSoup安装1pip install bs4 用BeautifulSoup解析的时候需要指定一些解析器,一些常用的解析器: 各种解析器的优势和劣势在图中可以很明显看出,这里多说一句:html5lib是一个类似浏览器的解析器,功能很强大,如果遇到那些很古怪的网页代码就可以考虑用这个解析器,直接用命令:pip install html5lib 安装即可。 使用12345678910#encoding: utf-8from bs4 import BeautifulSouphtml = """这里省略htmL格式字符串内容,你可以自己去网站审查元素复制过来"""# pip install lxml 安装lxml解析器bs = BeautifulSoup(html,"lxml")print(bs.prettify()) # 美化 find_all与findfind_all的使用: 在提取标签的时候,第一个参数是标签的名字。然后如果在提取标签的时候想要使用标签属性进行过滤,那么可以在这个方法中通过关键字参数的形式,将属性的名字以及对应的值传进去。或者是使用attrs属性,将所有的属性以及对应的值放在一个字典中传给attrs属性。 有些时候,在提取标签的时候,不想提取那么多,那么可以使用limit参数。限制提取多少个。 find与find_all的区别: find:找到第一个满足条件的标签就返回。说白了,就是只会返回一个元素。 find_all:将所有满足条件的标签都返回。说白了,会返回很多标签(以列表的形式)。 使用find和find_all的过滤条件: 关键字参数:将属性的名字作为关键字参数的名字,以及属性的值作为关键字参数的值进行过滤。 attrs参数:将属性条件放到一个字典中,传给attrs参数。 获取标签的属性: 通过下标获取:通过标签的下标的方式。 1href = a['href'] 通过attrs属性获取:示例代码: 1href = a.attrs['href'] string和strings、stripped_strings属性以及get_text方法: string:获取某个标签下的非标签字符串(他自己的)。返回来的是个字符串。如果这个标签下有多行字符,那么就不能获取到了。 strings:获取某个标签下的子孙非标签字符串。返回来的是个生成器。 stripped_strings:获取某个标签下的子孙非标签字符串,会去掉空白字符。返回来的是个生成器。 get_text:获取某个标签下的子孙非标签字符串。不是以列表的形式返回,是以普通字符串返回。 综合示例代码如下:12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273#encoding: utf-8from bs4 import BeautifulSouphtml = """省略 """soup = BeautifulSoup(html,'lxml')# 1. 获取所有tr标签trs = soup.find_all('tr')for tr in trs: print(tr) print('='*30)# 2. 获取第2个tr标签tr = soup.find_all('tr',limit=2)[1]第二个参数limit用来限制获取的数量,防止太多print(tr)# 3. 获取所有class等于even的tr标签trs = soup.find_all('tr', class_ = "even")# trs = soup.find_all('tr',attrs={'class':"even"})# atrribute属性for tr in trs: print(tr) print('='*30)# 4. 将所有id等于test,class也等于test的a标签提取出来。# aList = soup.find_all('a',id='test',class_='test')aList = soup.find_all('a',attrs={"id":"test","class":"test"})for a in aList: print(a)# 5. 获取所有a标签的href属性aList = soup.find_all('a')for a in aList: # 1. 通过下标操作的方式 # href = a['href'] # print(href) # 2. 通过attrs属性的方式 href = a.attrs['href'] print(href)# 6. 获取所有的职位信息(纯文本)trs = soup.find_all('tr')[1:] #第0个tr标签不需要movies = []for tr in trs: movie = {}# 方式1 # tds = tr.find_all("td") # title = tds[0].string # 去掉多余的标签 # category = tds[1].string # nums = tds[2].string # city = tds[3].string # pubtime = tds[4].string # movie['title'] = title # movie['category'] = category # movie['nums'] = nums # movie['city'] = city # movie['pubtime'] = pubtime # movies.append(movie)# 方式2 #2.1 infos = list(tr.strings) #这种方法换行符等空白字符也会被提取出来 print(infos) #2.2 infos = list(tr.stripped_strings)#stripp可以过滤空白字符 movie['title'] = infos[0] movie['category'] = infos[1] movie['nums'] = infos[2] movie['city'] = infos[3] movie['pubtime'] = infos[4] movies.append(movie)print(movies) 有关匿名函数(lambda表达式)和map函数的用法。map函数lambda表达式 正则表达式匹配规则特殊匹配字符 匹配一个字符 ‘字符串’:匹配某个字符串。 点:匹配任意的字符。但是不能匹配\n,可以添加参数:re.DOTALL解决 \d:匹配任意的数字(0-9)。 \D:匹配任意的非数字。 \s:匹配空白字符(\n,\t,\r,空格)\r回车。 \w:匹配的是a-z,A-Z,数字和下划线。 \W:与\w相反。 []组合的方式,只要满足中括号中的字符,就可以匹配, 中括号的形式代替\d:[0-9];中括号的形式代替\D:[^0-9];中括号的形式代替\w:[a-zA-Z0-9_];中括号的形式代替\W:[^a-zA-Z0-9_] 匹配多个字符 *:可以匹配0或者任意多个字符 +:匹配1个或者多个字符 , 如果第一个不是,就返回错误 ?:匹配一个或者0个(要么没有,要么就只有一个) {m}:匹配m个字符。 {m,n}:匹配m-n个字符 ,匹配到最多的字符(不超过n)返回 小案例3.1. 验证手机号码: 123text = "12578900980" # 以1开头,第二位是34578中的一位,后面九个字符任意ret = re.match('1[34578]\d{9}',text)print(ret.group()) 3.2. 验证邮箱: 123text = "hynever12_@163com"ret = re.match('\w+@[a-z0-9]+\.[a-z]+',text) # 任意数字字母下划线+@+数字或字母(163、qq)+.+字母(com、net)print(ret.group()) 3.3. 验证URL 123text = "https://baike.baidu.com/item/Python/407313?fr=aladdin"ret = re.match('(http|https|ftp)://[^\s]+',text)print(ret.group()) 3.4. 验证身份证: 123text = "31131118908123230a"ret = re.match('\d{17}[\dxX]',text)print(ret.group()) 特殊字符 ^(脱字号): # 表示以…开始,match函数本身就从第一个开始匹配,匹配不成功就返回错误,所以不需要脱字符 $:表示以…结尾:将正常字符(非正则表达式特殊字符)规定到结尾的规则中来 |:匹配多个字符串或者表达式中的一个。 贪婪模式与非贪婪模式:贪婪模式匹配最长的满足条件的字符,非贪婪模式在后面加个?就只匹配满足条件的最短的结果 举例:4.1.123text = "<h1>标题</h1>"ret = re.match('<.+?>',text) #没有?匹配到<h1>标题</h1>,有?匹配到<h1>print(ret.group()) 4.2. 匹配0-100之间的数字, 即不可以出现类似:09,101 1001123text = "01"ret = re.match('[1-9]\d?$|100$',text)print(ret.group()) 转移字符和原生字符串 12345678910111213141516171819202122#encoding: utf-8import re# text = "apple price is $299"# ret = re.search("\$\d+",text) #转义# print(ret.group())# r = raw = 原生的# text = r'\n'# print(text)text = "\\n" #= '\n'# 1.转义# python:'\\n' = \n# \\\\n -> \\n# 正则表达式中:# \\n -> \n# ret = re.match('\\\\n',text)# 2. 原生ret = re.match(r'\\n',text)print(ret.group()) 匹配函数match 和 search 函数 match:从开始的位置进行匹配。如果开始的位置没有匹配到。就直接失败了。如果想要匹配空白字符的数据,那么就要传入一个flag=re.DOTALL ,就可以匹配换行符了。(re.S=re.DOTALL)示例代码如下: 1234import retext = "abc\nabc"ret = re.match('abc.*abc', text,re.DOTALL)print(ret.group()) search:在字符串中找满足条件的字符。如果找到,就返回。说白了,就是只会找到第一个满足条件的。 group分组 在正则表达式中,可以对过滤到的字符串进行分组。分组使用圆括号的方式。 group :和group(e)是等价的,返回的是整个满足条件的字符串。 groups :返回的是里面的子组。索引从1开始。 group(1) :返回的是第一 个子组,可以传入多个。示例代码如下: 1234567891011text = "apple's price $99,orange's price is $10"ret = re.search('.*(\$\d+).*(\$\d+)',text)print(ret.group(0)) # 结果:apple's price $99,orange's price is $10# ret.group(0) = ret.group()print(ret.group(1)) # 结果:$99print(ret.group(2)) # 结果:$10# 把第一个第二个分组拿出来 ,以括号分组print(ret.group(1,2)) # 结果: ('$99', '$10')# 所有的子分组都拿出来,注意有sprint(ret.groups()) # # 结果: ('$99', '$10') re模块常用函数 findall函数:找出所有满足条件的,返回的是一个列表。 123text = "apple's price $99,orange's price is $10"ret = re.findall('\$\d+',text)print(ret) # 结果:['$99','$10'] sub函数:用来替换字符串,将匹配到字符串替换为其他字符。 123text = "apple's price $99,orange's price is $10"ret = re.sub('\$\d+',"0",text) # sub函数的参数分别是(正则表达式,替换后的字符串,整个字符串,要代替几个(没有指明的话将代替所有满足条件的))print(ret) # 结果:apple's price 0,orange's price is 0 sub函数可以用作替换html标签,提取文本信息,例如拉勾网的职位招聘信息。1ret = re.sub('<.+?>',"",html) # ?非贪婪模式 split函数:使用正则表达式来分割字符串。 123text = "hello&world ni hao"ret = re.split('[^a-zA-Z]',text)print(ret) # 结果:['hello', 'world', 'ni', 'hao'] compile函数:对于一些经常要用到的正则表达式,可以使用compile 进行编译,后期再使用的时候可以直接拿过来用,执行效率会更快。而且compile 还可以指定flag=re.VERBOSE,在写正则表达式的时候可以做好注释。 123456789text = "the number is 20.50"# r = re.compile('\d+\.?\d*')r = re.compile(r""" \d+ # 小数点前面的数字 \.? # 小数点本身 \d* # 小数点后面的数字""",re.VERBOSE) # 方便分段写注释ret = re.search(r,text)print(ret.group()) # 结果:20.50]]></content>
<categories>
<category>Python爬虫</category>
</categories>
<tags>
<tag>beautifulsoup</tag>
<tag>xpath</tag>
<tag>正则表达式</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python爬虫]]></title>
<url>%2F2019%2F06%2F08%2FPython%E7%88%AC%E8%99%AB%EF%BC%881%EF%BC%89%2F</url>
<content type="text"><![CDATA[网络爬虫之网络请求 说明:此博客有关爬虫的内容均学习自B站:up主:神奇的老黄。视频链接: urllib库安装urllib库 pip命令pip install urllib pycharm->菜单栏的file->setting->Project Interpreter->点击”+”号->搜索urllib库安装即可 使用 urlopen打开网址并读取内容 123456789101112131415#encoding: utf-8from urllib import request,parse# 打开地址req=request.urlopen("http://www.baidu.com")# 读取页面所有内容代码print(req.read())# 读取一行# print(req.readline())# 读取多行# print(req.readlines())# 读取状态码# print(req.getcode()) urlretrieve函数保存数据到文件 123# 保存文件ret = request.urlretrieve("http://www.baidu.com","baidu.html") 编码与解码 12345678910111213# 编码与解码params={'name':'张三','age':'18','greet':'hallo word'}#字典# 编码qu = parse.urlencode(params)print(qu)url = "http://www.baidu.com/s"url = url+"?"+quprint(url)qs = parse.parse_qs(url)print(qs)# 解码使用 urllib.parse.unquote() 运行结果:123name=%E5%BC%A0%E4%B8%89&age=18&greet=hallo+wordhttp://www.baidu.com/s?name=%E5%BC%A0%E4%B8%89&age=18&greet=hallo+word{'http://www.baidu.com/s?name': ['张三'], 'age': ['18'], 'greet': ['hallo word']} urlparse和urlsplit函数分割网址 1234567# 网址分割url = 'http://www.baidu.com/s?wd=python&username=abc#1'result1 = parse.urlparse(url)print(result1)result2 = parse.urlsplit(url)print(result2) 运行结果: 12ParseResult(scheme='http', netloc='www.baidu.com', path='/s', params='', query='wd=python&username=abc', fragment='1')SplitResult(scheme='http', netloc='www.baidu.com', path='/s', query='wd=python&username=abc', fragment='1') 让你的爬虫更加人性化这里要从urllib库中导入request 添加headers 标头 (header) 是服务器以HTTP协议传HTML资料到浏览器前所送出的字串 Referer也是包括在Request Headers里面的信息,添加此选项,可以让你的爬虫更人性化 添加data数据,GET请求的请求参数是直接包含在URL中了,而POST请求的请求参数则不会出现在URL中,而是要经过单独的封装处理,这就用到data了。 以下是包括上述的一个爬取电驴登录界面的例子代码如下: 123456789101112131415161718192021222324#encoding: utf-8from urllib import request,parseurl = 'http://secure.verycd.com/signin'headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5944.400 LBBROWSER/10.1.3378.400','Referer':'http://secure.verycd.com/signin'}data = {'username':'11111','password':'1e312r','continue':'http://www.verycd.com','fk':'','save_cookie':'1','login_submit':'登录'}req = request.Request(url,headers=headers, data = parse.urlencode(data).encode('utf-8'),method = 'POST')# 也可以通过Request.add_header()来添加resp = request.urlopen(req)print(resp.read().decode('utf-8')) ProxyHandler处理器(代理): 代理的原理:在请求目的网站之前,我们先请求代理服务器,然后让代理服务器去请求目的网站,代理服务器拿到目的网站的数据后,再转发给我们。 一些好用的网站: http://httpbin.org:这个网站可以方便的查看http请求的一些参数。 西刺免费代理IP: http://www.xicidaili.com/ 快代理: http://www. kuaidaili.com/ 代理云: http://www.dailiyun.com/ 在代码中使用代理: 使用urllib.request.ProxyHandler,传入一个代理,这个代理是一个字典,字典的key依赖于代理服务器能够接收的类型,一般是http或者https,值是ip:port。 使用上一步创建的handler,以及request.build_opener创建一个opener对象。 使用上一步创建的opener,调用open函数,发起请求。示例代码如下:1234567891011121314from urllib import request# 未使用代理url = 'http://httpbin.org/ip'req = request.urlopen(url)print(req.read())# 使用代理url = 'http://httpbin.org/ip'#下面的代理ip是通过我们网站得到的一些免费代理iphandler = request.ProxyHandler({"http":"114.113.220.192:80"})opener = request.build_opener(handler)req = opener.open(url)print(req.read()) 你用的免费代理可能不会成功访问,所以要多试几个哦 CookieCookie模拟登录 Cookie,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。cookie 的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie) 给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie 数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie 存储的数据量有限,不同的浏览器有不同的存储大小,但-般不超过4KB。因此使用cookie只能存储一些小量的数据。 cookie的格式: 1Set-Cookie: NAME=VALUE; Expires /Max-age=DATE: Path=PATH; Domain=DOMAIN NAME: SECURE 参数意义: - NAME: cookie的名字。 - VALUE: cookie的值。 - Expires : cookie的过期时间。 - Path: cookie作用的路径。 - Domain: cookie作用的域名。 - SECURE: 是否只在https协议下起作用。 比如说有些网站需要登录后才能访问某个页面,在登录之前,你想抓取某个页面内容是不允许的,他会给你自动跳转到登录或注册页面了。那么我们可以利用Urllib2库保存我们登录的Cookie,然后再抓取其他页面就达到目的了。 第一种解决方案:在headers里添加key:Cookie成员,value:也是包括在Request Headers里面的。这里用cookie爬取某一微博博主界面1234567891011121314# coding:utf-8from urllib import requesturl = 'https://weibo.com/seize?refer_flag=0000015010_&from=feed&loc=avatar&is_all=1'headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5944.400 LBBROWSER/10.1.3378.400','Cookie':'数据太多省略'}req = request.Request(url = url,headers= headers)resp = request.urlopen(req)with open('weibo.html','w',encoding='utf-8') as fp: fp.write(resp.read().decode('utf-8','ignore')) 虽然最后结果是返回给你的是一个假的代码,但方法就是这样。还要说明一点:这里的write函数写入的必须是str类型(read()读出来是一个bytes类型),bytes通过decode变成str,str通过encode变成bytes类型 第二种方法:http.cookiejar模板 http.cookiejar模块: 该模块主要的类有CookieJary FileCookieJar、 MoillaCookieJar、 LWPCookieJar。这四个类的作用分别如下: CookieJar:管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失。 FileCookieJar (flename,delayload=None,policy=None):从CookieJar派生而来,用来创建FileCookieJar实例,检索cookie信息并将cookie存储到文件中。filename是存储cookie的文件名。delayload为True时支持延迟访问访问文件,即只有在需要时才读取文件或在文件中存储数据。 MoillaCookieJar (filename,delayload=None,policy=None):从FileCookieJa派生而来,创建与Mozilla浏览器cookies.txt兼容的FileCookieJar实例。 LWPCookieJar (filename,delayload=None,policy=None):从FileCookieJar派生 而来,创建与libwww-perl标准的Set-Cookie3文件格式兼容的FileCookieJar实例。 下面利用http.cookiejar和request .HTTPCookieProcessor登录人人网,代码如下:12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849#encoding: utf-8from urllib import requestfrom urllib import parsefrom http.cookiejar import CookieJar#全局headers = { 'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"}def get_opener(): # 1. 登录 # 1.1 创建一个cookiejar对象 cookiejar = CookieJar() # 1.2 使用cookiejar创建一个HTTPCookieProcess对象 handler = request.HTTPCookieProcessor(cookiejar) # 1.3 使用上一步创建的handler创建一个opener opener = request.build_opener(handler) return openerdef login_renren(opener): # 1.4 使用opener发送登录的请求(人人网的邮箱和密码) # 把这些cookie数据写到opener里面 data = { 'email':"[email protected]", 'password': "pythonspider" } login_url = "http://www.renren.com/PLogin.do" req = request.Request(login_url,data=parse.urlencode(data).encode('utf-8'),headers=headers) opener.open(req)def visit_profile(opener): # 2. 访问个人主页 dapeng_url = "http://www.renren.com/880151247/profile" # 获取个人主页的页面的时候,不要新建一个opener # 而应该使用之前的那个opener,因为之前的那个opener已经包含了登录所需要的cookie信息 req = request.Request(dapeng_url,headers=headers) resp = opener.open(req) with open('renren.html','w',encoding='utf-8') as fp: fp.write(resp.read().decode('utf-8'))if __name__ == '__main__': opener = get_opener() login_renren(opener) visit_profile(opener) 保存cookie到本地1234567891011121314151617#encoding: utf-8from urllib import requestfrom http.cookiejar import MozillaCookieJarcookiejar = MozillaCookieJar('cookie.txt') # 建立对象handler = request.HTTPCookieProcessor(cookiejar) # opener = request.build_opener(handler)resp = opener.open('http://httpbin.org/cookies/set?course=anzhusen')# # 浏览器会话结束cookie信息就会删掉,到达过期时间了cookiejar.save()#运行结果为空# 解决办法:# 保存cookie即将过期的cookie信息# cookiejar.save(ignore_discard=True)# 运行结果# httpbin.org FALSE / FALSE course anzhusen 加载本地的cookie信息并使用1234567891011121314151617#encoding: utf-8from urllib import requestfrom http.cookiejar import MozillaCookieJarcookiejar = MozillaCookieJar('cookie.txt')# 把本地的cookie信息(以前用过的cookie信息),加载到cookiejar对象中。cookiejar.load(ignore_discard=True) # 参数表示把已经过期的cookie信息也加载进来handler = request.HTTPCookieProcessor(cookiejar)opener = request.build_opener(handler)resp = opener.open('http://httpbin.org/cookies')for cookie in cookiejar: print(cookie)# 运行结果:# <Cookie course=anzhusen for httpbin.org/> Requests库发送get请求:发送get请求,直接调用requests.get就可以了。想要发送什么类型的请求,就调用什么方法。1response = requests.get("https://www.baidu.com/") response的一些属性:1234567891011121314151617181920212223import requestskw = {'wd':'中国'}headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()response = requests.get("http://www.baidu.com/s", params = kw, headers = headers)# 查看响应内容,response.text 返回的是Unicode格式的数据print(response.text)# 查看响应内容,response.content,返回的字节流数据print(response.content)# 查看完整url地址print(response.url)# 查看响应头部字符编码print(response.encoding)# 查看响应码print(response.status_code) response.text和response.content的区别: response.content:这个是直接从网络上面抓取的数据。没有经过任何解码。所以是一个bytes类型。其实在硬盘上和在网络上传输的字符串都是bytes类型。 response.text:这个是str的数据类型,是requests库将response.content进行解码的字符串。解码需要指定一个编码方式,requests会根据自己的猜测来判断编码的方式。所以有时候可能会猜测错误,就会导致解码产生乱码。这时候就应该使用response.content.decode('utf-8')进行手动解码。这里不一定是’utf-8’,你可以检查元素网页的编码格式 发送post请求:发送post请求非常简单。直接调用requests.post()方法就可以了。如果返回的是json数据。那么可以调用response.json()来将json字符串转换为字典或者列表。 使用代理:在请求方法中,传递proxies参数就可以了。例示代码如下:12345678910111213import requestsurl = 'http://httpbin.org/get'headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5944.400 LBBROWSER/10.1.3378.400'}proxy = { 'http':'163.204.243.182:9999'}resp = requests.get(url ,headers = headers,proxies = proxy)print(resp.text) 处理cookie:如果想要在多次请求中共享cookie。那么应该使用session。示例代码如下:1234567891011121314import requestsurl = "http://www.renren.com/PLogin.do"data = {"email":"[email protected]",'password':"pythonspider"}headers = { 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"}session = requests.Session()# 访问登录页面,并把信息存储在seesion里面session.post(url,data=data,headers=headers)# 用这个session去获取需要登录后才能访问的界面response = session.get('http://www.renren.com/880151247/profile')with open('renren.html','w',encoding='utf-8') as fp: fp.write(response.text) 处理没有授权的https协议:对于那些已经不被信任的SSL整数的网站,添加一个verify参数,示例代码如下:12resp = requests . get( http://www.12386. cn/mormhweb/' ,verify=False)print(resp. content . decode("utf-8'))]]></content>
<categories>
<category>Python爬虫</category>
</categories>
<tags>
<tag>爬虫</tag>
<tag>urllib</tag>
<tag>requests</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Qt实战(3)]]></title>
<url>%2F2019%2F05%2F26%2FQt%E5%AE%9E%E6%88%98%EF%BC%883%EF%BC%89%2F</url>
<content type="text"><![CDATA[Qt 操作MySql数据库 配置问题:Qt连接MySQL的时候提示“QSqlDatabase: QMYSQL driver not loaded”, 解决办法: 你的Qt Creator与MySQL位数统一的情况下,从 你的MySQL\lib目录下中将 libmysql.dll 文件复制到C:\Qt\Qt5.6.1\5.6\mingw49_32\bin中。运行程序。 如果你的Qt Creator与MySQL位数不统一,下载一个位数与你的Qt相同的Mysql,找到里面的 libmysql.dll文件复制到目录下。 总之,必须保证你拿到libmysql.dll这个文件对应的mysql的位数必须与QT的位数相同。 数据库基本操作对数据库基本操作就是增、删、改、查。 新建 Qt Widgets 应用, 项目名称为 database, 类名为Mywidget, 基类选择QWidget。 完成后在 database.pro文件中添加如下代码: 1QT += sql 连接数据库,在构造函数中添加以下代码 12345678910111213141516171819202122232425262728293031#include<QSqlDatabase>#include<QDebug>#include<QMessageBox>#include<QSqlError>Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); 打印QT支持的数据库驱动 qDebug()<<QSqlDatabase::drivers(); //添加mysql数据库 QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); //连接数据库 db.setHostName("127.0.0.1"); db.setUserName("root"); db.setPassword("an1234"); db.setDatabaseName("test_db"); //打开数据库是否成功 if(!db.open()) { QMessageBox::warning(this,"错误",db.lastError().text()); return; } } QT支持的数据库驱动有:1("QSQLITE", "QMYSQL", "QMYSQL3", "QPSQL", "QPSQL7") 创建数据表格,继续向后添加代码 123456#include<QSqlQuery>//一组数据//创建数据表格 QSqlQuery query; query.exec("create table student(id int primary key auto_increment ,name varchar(255),age int,score int);"); 此条语句运行一次后要注释,不能多次创建同名的数据表格 数据库的另一种操作 12345678910111213141516171819 //添加mysql数据库 a是连接名,用于区分QSqlDatabase db1 = QSqlDatabase::addDatabase("QMYSQL","a");//连接数据库db1.setHostName("127.0.0.1");db1.setUserName("root");db1.setPassword("an1234");db1.setDatabaseName("testdb");//打开数据库是否成功if(!db1.open()){ QMessageBox::warning(this,"错误",db.lastError().text()); return;}QSqlQuery query1(db1);//有区分时,一定要加上数据库名query1.exec("create table student(id int primary key auto_increment ,name varchar(255),age int,score int);"); 插入(增) ① 插入一条数据 1234//插入数据 QSqlQuery query; query.exec("insert into student (id, name, age, score)values(2,'lucy',22,59);"); ② 批量插入两种方法:obdc风格 123456789101112131415161718192021#include<QVariantList>//放任何类型,列表 //obdc风格插入 //预处理语句 //?相当于占位符 query.prepare("insert into student (name, age, score)values(?,?,?);"); //给字段设置内容 QVariantList namelist; namelist << "xiaoming" << "xiaohua" << "xiaoliao"; QVariantList agelist; agelist << 19 << 20 << 21; QVariantList scorelist; scorelist << 98 << 88 << 99; //给字段绑定相应的值,按顺序绑定 query.addBindValue(namelist); query.addBindValue(agelist); query.addBindValue(scorelist); //执行预处理命令 query.execBatch(); oracle风格123456789101112131415161718//oracle风格//占位符 : + 自定义名字query.prepare("insert into student (name, age, score)values(:name, :age, :score);");//给字段设置内容QVariantList namelist;namelist << "xiaoa" << "xiaob" << "xiaoc";QVariantList agelist;agelist << 19 << 20 << 21;QVariantList scorelist;scorelist << 98 << 88 << 99;//给字段绑定 可以不按照顺序query.bindValue(":name",namelist);query.bindValue(":score",scorelist);query.bindValue(":age",agelist);//执行预处理命令query.execBatch(); 更新数据(改) 12QSqlQuery query;query.exec("update student set score = 72 where id = 3"); 查找并打印(查) 12345678910QSqlQuery query;query.exec("select * from student where name = 'xiaob' ");//过滤while(query.next())//一行一行的查找,一直到没有返回false{ qDebug()<<query.value(0).toInt() <<query.value(1).toString() <<query.value("age").toInt() <<query.value(3).toInt();} 在界面上执行删除操作(删) ①设计页面如图所示 ②右击按钮转到槽 123456789101112131415161718192021222324252627void Widget::on_buttondelete_clicked(){ //获取行编辑器内容 QString name = ui->lineEdit->text(); QString sql = QString("delete from student where name ='%1'").arg(name); //开启一个事务 QSqlDatabase::database()获取你操作的数据库 //事务是数据库的一个重要功能,所谓事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。 //qDebug()<< QSqlDatabase::database().transaction();//有的版本不支持这种用法 QSqlQuery query; query.exec("START TRANSACTION"); query.exec(sql);}void Widget::on_buttonsure_clicked(){ //确认删除 QSqlDatabase::database().commit();}void Widget::on_buttoncancel_clicked(){ //回滚,撤销 qDebug()<<"hahahhahh"; QSqlDatabase::database().rollback();} 本地数据库Sqlite 新建 Qt Widgets 应用, 项目名称为 Sqlite, 类名为Mywidget, 基类选择QWidget。 在你的Sqlite相同目录下创建文件info.db 在.pro文件中添加 1QT+= sql 操作,同mysql的操作基本一样,因为是本地数据库,所以没有连接数据库的操作 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960#include<QSqlDatabase>#include<QDebug>#include<QMessageBox>#include<QSqlError>//#include<QSqlQuery>//一组数据#include<QVariantList>//放任何类型,列表MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget){ ui->setupUi(this); //打印QT支持的数据库驱动 qDebug()<<QSqlDatabase::drivers(); //添加qsqlite数据库 本地数据库 //不是很大型的数据库 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("../info.db"); //打开数据库是否成功 if(!db.open()) { QMessageBox::warning(this,"错误",db.lastError().text()); return; } //创建数据表格 QSqlQuery query;//不支持自动添加id功能 query.exec("create table student(id int primary key,name varchar(255),age int,score int);"); query.prepare("insert into student (name, age, score)values(?,?,?);"); //给字段设置内容 QVariantList namelist; namelist << "xiaoming" << "xiaohua" << "xiaoliao"; QVariantList agelist; agelist << 19 << 20 << 21; QVariantList scorelist; scorelist << 98 << 88 << 99; //给字段绑定相应的值,按顺序绑定 query.addBindValue(namelist); query.addBindValue(agelist); query.addBindValue(scorelist); query.execBatch(); //查找并打印 query.exec("select * from student"); while(query.next())//一行一行的查找,一直到没有返回false { qDebug()<<query.value(0).toInt() <<query.value(1).toString() <<query.value("age").toInt() <<query.value(3).toInt(); }} QTableModel类实现对数据库的可视化操作QSqlTableModel,该类提供了一个可读写单张SQL表的可编辑数据模型。下面实际操作一下 新建 Qt Widgets 应用, 项目名称为sqlModel, 类名为Mywidget, 基类选择QWidget。 在.pro文件中添加 1QT+= sql 设计界面如图所示 注意:和里面的显示数据库的框用的是Item Views(Model-Based)里的TableView。 在头文件中添加库和对象 123456#include<QSqlTableModel>private: Ui::MyWidget *ui; QSqlTableModel *model; 实现可视化操作 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152#include<QSqlDatabase>#include<QDebug>#include<QMessageBox>#include<QSqlError>#include<QSqlTableModel>#include<QSqlRecord>//数据库操作记录MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget){ ui->setupUi(this); //添加mysql数据库 QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); //连接数据库 db.setHostName("127.0.0.1"); db.setUserName("root"); db.setPassword("an1234"); db.setDatabaseName("test_db"); //打开数据库是否成功 if(!db.open()) { QMessageBox::warning(this,"错误",db.lastError().text()); return; } //设置模型 model = new QSqlTableModel(this); model->setTable("student");//指定使用那个表 //把model放在view ui->tableView->setModel(model); //显示model里的数据 model->select(); //字段设置为指定中文名 第0个字段即id model->setHeaderData(0,Qt::Horizontal,"学号"); model->setHeaderData(1,Qt::Horizontal,"姓名"); model->setHeaderData(2,Qt::Horizontal,"年龄"); model->setHeaderData(3,Qt::Horizontal,"分数"); //设置model的编辑模式 为手动提交 model->setEditStrategy(QSqlTableModel::OnManualSubmit); //设置view中的数据库为不能修改 //ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);} 按钮槽函数添加代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051//改//修改操作可以直接在视图框中操作//增void MyWidget::on_buttonadd_clicked(){ //添加空记录 QSqlRecord record = model->record(); //获取行号 int row = model->rowCount(); model->insertRecord(row,record);}//删void MyWidget::on_buttondelete_clicked(){ //获取选中的模型 QItemSelectionModel *sModel=ui->tableView->selectionModel(); //取出选中模型中的索引(哪几行) QModelIndexList list = sModel->selectedRows(); //删除所有选中的行 for(int i=0 ;i<list.size();i++) { model->removeRow(list.at(i).row()); //取出所有索引中的i行的索引,再根据这个索引选中并删除这一行 }}void MyWidget::on_buttonsure_clicked(){ model->submitAll();//提交动作}void MyWidget::on_buttoncancel_clicked(){ model->revertAll();//取消动作 model->submitAll();//提交动作(取消也是一个动作)}//查void MyWidget::on_buttonsure_2_clicked(){ //获取行编辑器内容 QString name = ui->lineEdit->text(); //过滤条件 QString str = QString("name='%1'").arg(name); model->setFilter(str); model->select();//重新显示model里的数据}]]></content>
<categories>
<category>QT界面</category>
</categories>
<tags>
<tag>Qtcreator</tag>
<tag>MySql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Qt实战(2)]]></title>
<url>%2F2019%2F05%2F19%2FQt%E5%AE%9E%E6%88%98%EF%BC%882%EF%BC%89%2F</url>
<content type="text"><![CDATA[TCP 和 UDP 通信 TCP 先通过一张图片了解TCP通信过程,QT的TCP Socket(套接字)通信仍然有服务端、客户端之分。图中左为客户端,右为服务端。服务端通过监听某个端口来监听是否有客户端连接到来,如果有连接到来,则建立新的SOCKET连接;客户端通过IP和PORT(端口)连接服务端,当成功建立连接之后,就可进行数据的接收和发送了。数据的收发是通过read()和write()来进行的。Socket,也就是常说的“套接字”。Socket简单地说,就是一个IP地址加一个port端口 TCP通信原理图 先来看运行结果: 服务端 新建项目,选择Qt Widgets应用,项目名为TCP_connect,类名为ServerWidget。 在 TCP_connect.pro内联网,添加以下代码,添加后先编译不运行。(之后的所有有关UDP和CDP,都要加上这句,以后不再多说。) 123QT += core gui networkCONFIG += C++11 //之后可能会用到c++中的Lambdas表达式,所以把这句也加上 设计ui界面如生成结果中的服务端 在ServerWidget.h头文件中,添加头文件并,创建监听套接字和通信套接字对象。 123456#include<QTcpServer> //监听套接字#include<QTcpSocket> //通信套接字private: QTcpServer *tcpServer;//监听套接字 QTcpSocket *tcpSocket;//通信套接字 在ServerWidget.h源文件构造函数添加以下 1234567891011121314151617181920212223242526272829303132 setWindowTitle("服务器:8888"); tcpServer=NULL; tcpSocket=NULL; //监听套接字分配空间,父窗口自动回收空间 tcpServer=new QTcpServer(this); //监听并绑定 tcpServer->listen(QHostAddress::Any,8888); //触发新连接 connect(tcpServer,&QTcpServer::newConnection, [=](){ //取出建立好连接的套接字并赋值给通信套接字 tcpSocket =tcpServer->nextPendingConnection();//取出下一个连接,即当前最近的一个连接 //获取对方的ip和端口(peer表示对方) QString ip=tcpSocket->peerAddress().toString();//.toIPv4Address() qint16 port = tcpSocket->peerPort();//连接成功 QString temp=QString("[%1:%2]:成功连接").arg(ip).arg(port); ui->textEditRead->setText(temp);//显示到只读窗口 //接收数据 connect(tcpSocket,&QTcpSocket::readyRead, [=](){ //从通信套接字中取出内容 QByteArray array= tcpSocket->readAll(); ui->textEditRead->append(array); }); }); 发送消息(文本)按钮和关闭连接按钮,分别右击转到槽。添加代码 1234567891011121314151617181920212223void ServerWidget::on_buttonsend_clicked(){ if(tcpSocket==NULL){//有套接字连接的情况下,才能进行此操作 return; } //获取write编辑区内容 QString str=ui->textEditWrite->toPlainText(); //给客服端发送数据,使用tcpSocket套接字 tcpSocket->write(str.toUtf8().data());}void ServerWidget::on_buttonclose_clicked(){ if(tcpSocket==NULL){ return; } //主动和客户端断开连接 tcpSocket->disconnectFromHost();//从服务器取消这个连接到它的服务器 tcpSocket->close(); tcpSocket=NULL;//断开连接后,通信套接字里面就没有内容了} 客户端1.右击项目,添加c++class类,类名为clientwidget。设计ui界面如生成结果。2.在clientwidget.h头文件中添加代码,通信套接字头文件和对象。 1234#include<QTcpSocket> //通信套接字private: QTcpSocket *tcpsocket;//通信套接字 3.在clientwidget.cpp文件构造函数中添加代码, 12345678910111213141516171819setWindowTitle("客户端");tcpsocket=NULL;//分配空间,指定父对象tcpsocket=new QTcpSocket(this);//连接到服务器就会触发connected转到槽connect(tcpsocket,&QTcpSocket::connected, [=](){ ui->textEditRead->setText("成功和服务器建立好连接"); });connect(tcpsocket,&QTcpSocket::readyRead, [=](){ //获取对方发送的数据 QByteArray array=tcpsocket->readAll(); //显示到read编辑框中 ui->textEditRead->append(array);}); 三个按钮分别右击转到槽 1234567891011121314151617181920212223242526272829#include<QHostAddress>//之后的操作要添加此头文件void ClientWidget::on_bottonSend_clicked(){ //获取write编辑框内容 QString str=ui->textEditWrite->toPlainText(); //发送数据 tcpsocket->write(str.toUtf8().data());}void ClientWidget::on_buttonConnect_clicked(){ //从输入行编辑器获取服务器的ip和端口 QString ip=ui->lineEditIp->text(); qint16 port=ui->lineEditPort->text().toInt();//qint16代表16位的无符号整形。 //主动和服务器建立连接 tcpsocket->connectToHost(QHostAddress(ip),port);}void ClientWidget::on_bottonClose_clicked(){ //主动和对方断开连接 tcpsocket->disconnectFromHost(); tcpsocket->close();} UDP UDP(User Datagram Protocol即用户数据报协议)是一个轻量级的,不可靠的,面向数据报的无连接协议。就像我们现在使用的QQ,其聊天时就是使用UDP协议进行消息发送的。像QQ那样,当有很多用户,发送的大部分都是短消息,要求能及时响应,并且对安全性要求不是很高的情况下使用UDP协议。即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。 在Qt中提供了QUdpSocket 类来进行UDP数据报(datagrams)的发送(writeDatagram)和接收(readDatagram)。 先来看一下UDP通信过程 现在我们来创造下面如图所示的一个服务器:新建项目,选择Qt Widgets应用,项目名为udpsocket,类名为Widget。 先根据上图设计界面 在头文件中添加通信套接字声明,再定义一个对象 123#include<QUdpSocket>QUdpSocket *udpsocket; 分配空间和绑定端口,连接通信套接字自动触发readyRead()信号和处理的槽函数 先在头文件声明公有槽函数:1void dealMag();//槽函数,处理对方发送过来的信号。 在构造函数添加:1234567891011121314151617181920212223242526272829303132333435363738394041Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); setWindowTitle("服务端口为:8888"); //分配空间,指定父对象 udpsocket= new QUdpSocket(this); //绑定端口 udpsocket->bind(8888); //udpsocket->bind(QHostAddress::AnyIPv4,8888); //加入某个组播 //组播地址是D类地址 //udpsocket->joinMulticastGroup(QHostAddress("244.0.0.2")); //udpsocket->leaveMulticastGroup(1);//退出组播 //当对方成功发送数据过来 //自动触发readyRead()信号 connect(udpsocket,&QUdpSocket::readyRead,this,&Widget::dealMag);}void Widget::dealMag(){ //读取对方发送的内容 char buf[1024]={0}; QHostAddress clientAddr; //对方地址 quint16 clientPort; //对方端口 qint64 len=udpsocket->readDatagram(buf,sizeof(buf),&clientAddr,&clientPort); if(len>0) { //接收内容格式化 QString str = QString("[%1:%2] %3") .arg(clientAddr.toString()) .arg(clientPort) .arg(buf); ui->textEdit->setText(str); }} 两个按钮转到槽 12345678910111213141516171819202122#include<QHostAddress>//记得添加这个头文件void Widget::on_bottonSend_clicked(){ //获取对方ip和端口 QString ip=ui->lineEditIP->text(); qint16 port=ui->lineEditPort->text().toInt(); //获取编辑区数据 QString str=ui->textEdit->toPlainText(); //给指定的ip发送数据 udpsocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);}void Widget::on_bottonClose_clicked(){ //主动和对方断开连接 udpsocket->disconnectFromHost(); udpsocket->close();} 根据以上方法就可以创建很多服务端(客户端),通过绑定的端口就可以相互通信了。 UDP组播问题 因为在实际项目中,用户有N个电脑预览实时视频,如果同时有N多个终端去连接服务器,服务器的压力发送数据带宽的压力很大,所以给提出采用组播的方式去解决此类的问题。就像QQ群一样,限定人数,避免广播风暴。组播的原理大致就是服务器往某一组播地址和端口发数据,之后客户端从指定的组播地址和端口去取数据,好处就是减轻了服务器发送的压力 开启多个服务器,用客户端发送数据,所有服务器端(复制的过程)都会收到客户端发送的数据。 组播实现在上个例子代码中注释部分有,需要注意: 发送端既可以加入组播,也可以不加入组播; -吧服务端绑定的ip地址必须是ipv4地址; 组播地址必须是D类地址 TCP传输文件 先来看过传输文件程图: 运行结果: 首先看到运行结果如上图所示新建项目,选择Qt Widgets应用,项目名为tcpfile,类名为ServerWidget。 服务端 根据运行结果设计界面. ServerWidget.h 1234567891011121314151617181920212223242526272829303132333435363738394041#ifndef SERVERWIDGET_H#define SERVERWIDGET_H#include <QWidget>#include<QTcpServer>//监听套接字#include<QTcpSocket>//通信套接字#include<QFile>#include<QTimer>namespace Ui {class ServerWidget;}class ServerWidget : public QWidget{ Q_OBJECTpublic: explicit ServerWidget(QWidget *parent = 0); ~ServerWidget(); void sendData();private slots: void on_buttonfile_clicked();//这是转到槽自动添加的 void on_buttonsend_clicked();//这是转到槽自动添加的private: Ui::ServerWidget *ui; QTcpServer *tcpserver;//通信套接字和 QTcpSocket *tcpsocket;//监听套接字 QFile file;//文件对象 QString filename;//文件名字 qint64 filesize;//文件大小 qint64 sendsize;//已经发送文件大小 QTimer timer;//定时器};#endif // SERVERWIDGET_H ServerWidget.cpp中添加代码 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182#include "serverwidget.h"#include "ui_serverwidget.h"#include<QFileDialog>//文件对话框#include<QDebug>#include<QFileInfo>ServerWidget::ServerWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ServerWidget){ ui->setupUi(this); setWindowTitle("服务器端口:8888"); //分配空间 tcpserver = new QTcpServer(this); //监听 tcpserver->listen(QHostAddress::Any,8888); //没建立连接前是不能对进行操作的 ui->buttonfile->setEnabled(false); ui->buttonsend->setEnabled(false); //自动触发newconnection connect(tcpserver,&QTcpServer::newConnection, [=]() { //取出建立好的连接套接字,获取ip和端口 tcpsocket = tcpserver->nextPendingConnection(); QString ip = tcpsocket->peerAddress().toString(); quint16 port = tcpsocket->peerPort();//无符号的 //格式化并显示在文本编辑框中 QString str = QString("[%1;%2] 连接成功!").arg(ip).arg(port); ui->textEdit->setText(str); //连接成功后,才能发送文件 ui->buttonfile->setEnabled(true); }); connect(&timer,&QTimer::timeout, [=]() { //关闭定时器 timer.stop(); //发送文件 sendData(); });}void ServerWidget::sendData(){ qint64 len=0; do { //每次发送数据的大小 char buf[4*1024]={0};//每次发4k len=0; //往文件中读数据 len = file.read(buf,sizeof(buf)); //发送数据,读多少发多少 len = tcpsocket->write(buf,len);//数据和最大大小 //发送数据需要累加 sendsize += len; }while(len > 0); //是否文件发送完毕 if(sendsize == filesize) { ui->textEdit->append("文件发送完毕"); file.close(); //把客户端断开 tcpsocket->disconnectFromHost(); tcpsocket->close(); }} 按钮转到槽函数 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263void ServerWidget::on_buttonfile_clicked(){ //获取打开文件路径 QString filePath = QFileDialog::getOpenFileName(this,"open","../"); if(false == filePath.isEmpty())//选择路径有效 { filename.clear(); filesize = 0; //通过路径获取文件信息(文件名和文件大小) QFileInfo info(filePath);//构造对象 filename = info.fileName(); filesize = info.size();//qint64 //已经发送大小还为0 sendsize=0; //只读方式打开文件 //指定文件名字 file.setFileName(filePath); //打开文件 bool isok = file.open(QIODevice::ReadOnly); if(false == isok) { qDebug()<<"只读方式打开文件失败 98"; } //提示打开的文件路径 ui->textEdit->append(filePath); //打开文件后,按钮的状态 ui->buttonfile->setEnabled(false); ui->buttonsend->setEnabled(true); } else { qDebug()<<"选择文件路径出错"; }}void ServerWidget::on_buttonsend_clicked(){ //先发送文件头信息 文件名##文件大小 QString head = QString("%1##%2").arg(filename).arg(filesize); //发送头部信息 qint64 len = tcpsocket->write(head.toUtf8()); if(len > 0)//头部信息发送成功 { //发送真正的文件信息 //防止tcp黏包文件 //需要通过定时器 timer.start(20);//定时器开始就会自动触发timeout信号 } else { qDebug()<<"头部信息发送失败"; file.close(); ui->buttonfile->setEnabled(true); ui->buttonsend->setEnabled(false); }} 客户端 右击添加,class c++新文件,类名为clientwidget.设计界面 clientwidget.h中添加代码 1234567891011121314151617181920212223242526272829303132333435#include<QFile>#include<QTimer>namespace Ui {class ServerWidget;}class ServerWidget : public QWidget{ Q_OBJECTpublic: explicit ServerWidget(QWidget *parent = 0); ~ServerWidget(); void sendData();private slots: void on_buttonfile_clicked();//这是转到槽自动添加的 void on_buttonsend_clicked();//这是转到槽自动添加的private: Ui::ServerWidget *ui; QTcpServer *tcpserver;//通信套接字和 QTcpSocket *tcpsocket;//监听套接字 QFile file;//文件对象 QString filename;//文件名字 qint64 filesize;//文件大小 qint64 sendsize;//已经发送文件大小 QTimer timer;//定时器};#endif // SERVERWIDGET_H clientwidget.cpp中添加代码 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667#include "clientwidget.h"#include "ui_clientwidget.h"#include<QDebug>#include<QMessageBox>#include<QHostAddress>clientWidget::clientWidget(QWidget *parent) : QWidget(parent), ui(new Ui::clientWidget){ ui->setupUi(this); setWindowTitle("客户端"); tcpsocket = new QTcpSocket(this); //ui->progressBar->setValue(0);//当前值 isstart = true;//用来判断接受的是不是头部 //连接成功准备接收文件 connect(tcpsocket,&QTcpSocket::readyRead, [=]() { //取出接受的内容 QByteArray buf = tcpsocket->readAll(); if(true == isstart)//第一次发的头,接收头 { isstart = false; //取出接收的内容(拆包) filename = QString(buf).section("##",0,0); filesize = QString(buf).section("##",1,1).toInt(); //开始收到的文件大小为0 receivesize=0; //打开文件 file.setFileName(filename); bool isok=file.open(QIODevice::WriteOnly); if(false == isok) { qDebug()<<"writeonly error"; } } else//取出文件数据 { qint64 len = file.write(buf); receivesize += len; if(receivesize==filesize)//文件接受完成 { //给服务器发送接收完成信息 tcpsocket->write("file done"); QMessageBox::information(this,"完成","文件接受完成!"); tcpsocket->disconnectFromHost();//断开连接 tcpsocket->close(); } } });} 按钮转到槽 12345678void clientWidget::on_bottonconnect_clicked(){ //获取服务器的ip和端口 QString ip=ui->lineEditip->text(); quint16 port = ui->lineEditport->text().toInt(); tcpsocket->connectToHost(QHostAddress(ip),port);} 主函数添加为: 123456789101112131415#include "serverwidget.h"#include <QApplication>#include"clientwidget.h"int main(int argc, char *argv[]){ QApplication a(argc, argv); ServerWidget w; w.show(); clientWidget w2; w2.show(); return a.exec();} 心得 在学完上面的四个知识后,我写了一个实现两个pc机之间互相通信的程序,一个简单的聊天室,运行结果如下图,另一端改变绑定端口即可,使用的UDP协议,可以实现发文字消息和传输文件(文件对方接收后可以直接创建,但只能写一部分进去,我一直以为是文件写入有问题,后来才找出是我发文件的这一端发送文件大小小于实际文件大小,然后拖着拖着就难得去改了(优秀)),里面最大的问题是判断你接受的是文件还是文字问题,然后我用了分片(section)加关键字(file 和 text)区分。之后我会把我的代码上传到码云,这里不再说明过程。]]></content>
<categories>
<category>QT界面</category>
</categories>
<tags>
<tag>Qtcreator</tag>
<tag>TCP</tag>
<tag>UDP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Qt实战(1)]]></title>
<url>%2F2019%2F05%2F06%2FQt%E5%AE%9E%E6%88%98%EF%BC%881%EF%BC%89%2F</url>
<content type="text"><![CDATA[Qt实战 通过两个独立窗口的切换来学习信号和槽创建项目 新建Qt Widgets应用,项目名称SingelAndSlot ,类名默认为mainWidget,基类默认为QWidget,不勾选创建界面。 先来认识SingelAndSlot.pro文件: 1234567891011121314151617#添加模块QT += core gui#高于4版本,添加QT += widgets,为了兼容Qt4greaterThan(QT_MAJOR_VERSION, 4): QT += widgets#应用程序的名字TARGET = SingelAndSlot#指定SingelAndSlot的类型为appTEMPLATE = app#源文件 .cpp文件SOURCES += main.cpp\ mainwidget.cpp#头文件 .h文件HEADERS += mainwidget.h 创建两个按钮:mainwidget.h中添加代码: 123private: QPushButton b1; QPushButton *b2; mainwidget.cpp中添加:123456b1.setParent(this);b1.setText(("close");b1.move(100,100);b2=new QPushButton(this);//b2是指针,要分配存储空间b2->setText("abc"); connect函数用法:connect( 信号发出者, &发出者类型::信号名字,接受者(this),&接受者类名::槽函数名字)说明: 信号发送者和接受者均为指针类型(非指针类型添加“&”符号) 槽函数,即信号处理函数 如果是自定义的槽函数,需要注意以下几点: 可以是任意的成员函数,普通全局函数,静态函数; 槽函数需要和信号一致(返回值,参数); 由于信号都是没有返回值的,所以,槽函数一定没有返回值; 添加b1的槽函数MainWidget.cpp中的构造函数中继续添加: 1connect(&b1,&QPushButton::pressed,this,&MainWidget::close); 代码实现点击b1按钮,主窗口关闭。 添加b2的槽函数在mainwidget.h中添加自定义公有槽函数成员: 1void myslot(); MainWidget.cpp中的构造函数中添加:123//b2本来就是指针(地址),不需添加地址符connect(b2,&QPushButton::released,this,&MainWidget::myslot);connect(b2,&QPushButton::released,&b1,&QPushButton::hide); MainWidget.cpp中添加函数定义:123void MainWidget::myslot(){ b2->setText("123");} 以上代码实现释放b2按钮,主窗口实现自定义槽函数,修改按钮b2的内容,b1按钮实现按钮隐藏。 创建新窗口 头文件右击添加新文件,选择c++ class,类名为subwidget,基类为Widget。 在主窗口的头文件中添加按钮a3,源文件中添加代码: 12345b3.setParent(this);b3.setText("切换到父窗口");b3.move(50,50); resize(300,400);//窗口大小设置 在子窗口文件头文件添加按钮成员b,源文件中添加代码:12345this->setWindowTitle("我是小弟");b.setParent(this);b.setText("切换到主窗口");resize(300,400); 切换槽函数 主窗口切换为子窗口。 ① 头文件中添加公有函数成员:1void changeWin();//切换窗口 ② 添加槽函数连接和定义:1234567891011//构造函数中添加 connect(&b3,&QPushButton::released,this,&MainWidget::changeWin); //函数定义void MainWidget::changeWin(){ //子窗口显示 w.show(); //主窗口隐藏 this->hide();} 子窗口切换为主窗口 子窗口发送信号 信号必须有signals关键字 信号没有返回值,但可以有参数 信号就是函数的声明,只需声明,无需定义 使用 emit mysignel(); 信号可以重载 ① 在子窗口头文件中添加公有信号处理函数和信号成员 1234void sendSlot();signals: void mysignal(); ② 在子窗口源文件中添加连接及其信号处理函数:1234567//子窗口构造函数中添加 connect(&b,&QPushButton::clicked,this,&subWidget::sendSlot);void subWidget::sendSlot(){ emit mysignal();//发送信号} ③ 在主窗口中添加处理信号函数: 1void dealsubSignal(); ④ 在主窗口的源文件中创建信号和槽函数的连接 和处理函数的定义123456789 //处理主窗口信号 connect(&w,&subWidget::mysignal,this,&MainWidget::dealsubSignal); void MainWidget::dealsubSignal(){ //子窗口隐藏 w.hide(); //主窗口显示 this->show();} 这里解释一下:其实这里不是在子窗口中响应子窗口按钮发出的信号,而是通过主窗口来处理信号,只是他的信号发送方是子窗口,但要通过按钮实现,所以按下子窗口的按钮后的调用函数,函数执行的事件是发送一个信号给主窗口,这个信号属于子窗口。主窗口在对信号进行处理。 带参数的信号 在子窗口继续添加一个带参信号: 123signals: void mysignal(); void mysignal(int,QString); 在前面按钮按下而发送信号的函数里继续添加发送信号: 1234void subWidget::sendSlot(){ emit mysignal(); emit mysignal(250,"我是子窗口");} 在主窗口里添加信号处理函数 1void dealSlot(int,QString); 信号连接和处理函数定义: 123456 connect(&w,&subWidget::mysignal,this,&MainWidget::dealSlot); //信号处理函数 void MainWidget::dealSlot(int a, QString str){ qDebug()<<a<<str.toUtf8().data();} 运行程序,发现报错,这是为什么呢?因为connect里的参数&subWidget::mysignal不知道是含参信号函数还是不含参信号函数啊 解决办法有两种:① 函数指针 12345void (subWidget::*withnotcansignal)()=&subWidget::mysignal; connect(&w,withnotcansignal,this,&MainWidget::dealsubSignal); void (subWidget::*withcansignal)(int,QString)=&subWidget::mysignal; connect(&w,withcansignal,this,&MainWidget::dealSlot); 注意: 加上作用域 ② SIGNAL和SLOT(这是qt4的信号连接,安全性不好) 123//Qt4connect(&w,SIGNAL(mysignal()),this,SLOT(dealsubSignal()));connect(&w,SIGNAL(mysignal(int,QString)),this,SLOT(dealSlot(int,QString))); Lamda表达式直接处理信号 在.pro项目文件中添加 1CONFIG +=C++11 在主窗口的源文件中继续添加 123456789101112131415QPushButton *b4=new QPushButton(this); b4->setText("lambda表达式"); b4->move(150,150); //[]里面放你要对外部那些变量进行操作的变量,否则无法捕捉到, //一般最好用“=”表示外面所有局部变量、类中所有成员以值传递方式(就像拷贝一样) //()传参,如果这个函数要处理的信号有参数,那括号里需要包含所传参数 //mutable允许对传入的成员进行操作,否则只读 int a=2; connect(b4,&QPushButton::released, [=]()mutable { b4->setText("123"); a=11; }); Lamda表达式可以创造一个对信号直接处理的函数,不需要重声明和定义一个函数 创建一个主窗口创建项目 新建Qt Widgets应用,项目名称QWindow ,类名默认MainWindow,基类默认为QMainWindow,不勾选创建界面。 mainwindow.cpp代码如下,根据代码领会,不在解释: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157#include "mainwindow.h"#include<QMenuBar>#include<QMenu>#include<QAction>#include<QDebug>#include<QToolBar>#include<QPushButton>#include<QStatusBar>#include<QLabel>#include<QTextEdit>#include<QDockWidget>#include<QDialog>#include<QMessageBox>#include<QFileDialog>MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){ //创建菜单栏 QMenuBar *mybar=menuBar(); // 添加菜单 QMenu *pFile=mybar->addMenu("文件"); // 添加菜单项,添加动作 QAction *pNew=pFile->addAction("新建"); connect(pNew,&QAction::triggered, [=]() { qDebug()<<"新建被按下"; }); pFile->addSeparator();//添加分割线 QAction *pOpen= pFile->addAction("打开"); connect(pOpen,&QAction::triggered, [=]() { qDebug()<<"打开被按下"; }); //工具栏,菜单项的快捷方式 QToolBar *toolbar=addToolBar("ToolBar"); //工具栏添加快捷键 toolbar->addAction(pNew); //工具栏添加按钮控件 QPushButton *b=new QPushButton(this); b->setText("工具按钮"); toolbar->addWidget(b); connect(b,&QPushButton::clicked, [=]() { b->setText("哈哈哈"); }); //状态栏 QStatusBar *sBar=statusBar(); QLabel *label=new QLabel(this); label->setText("程序正在运行"); sBar->addWidget(label); //addWidget从左往右添加 sBar->addWidget(new QLabel("2",this)); //从右向左添加 sBar->addPermanentWidget(new QLabel("3",this)); //核心控件 QTextEdit *textedit=new QTextEdit(this); setCentralWidget(textedit); //浮动窗口 QDockWidget *dockwidget=new QDockWidget(this); addDockWidget(Qt::RightDockWidgetArea,dockwidget); // 设置浮动窗口内容 QTextEdit *textedit1=new QTextEdit(this); dockwidget->setWidget(textedit1); //模态与非模态对话框 setMenuBar(mybar); QMenu *menu1=mybar->addMenu("对话框"); QAction *p1=menu1->addAction("模态对话框"); connect(p1,&QAction::triggered, [=]() { QDialog dlg; dlg.exec();//一直运行,直到用户操作 }); QAction *p2=menu1->addAction("非模态对话框"); connect(p2,&QAction::triggered, [=]() { //这种用法导致,点击非模态对话框,对话框闪现后就被释放// QDialog dlg;// dlg.show(); //运行结束时才会释放,所以你点击多少次,就会创建多少个程序, //你关闭这个对话框时他分配到空间也不会释放// QDialog *dlg1=new QDialog(this);// dlg1->show(); //每次点击关闭内存空间就会释放 QDialog *dlg2=new QDialog(this); dlg2->setAttribute(Qt::WA_DeleteOnClose); dlg2->show(); }); mybar->addSeparator();//添加分割线 QAction *p3=menu1->addAction("关于对话框"); connect(p3,&QAction::triggered, [=]() { QMessageBox::about(this,"about","关于Qt"); }); QAction *p4=menu1->addAction("问题对话框"); connect(p4,&QAction::triggered, [=]() { int ret=QMessageBox::question(this,"question", "Are you ok?", QMessageBox::Ok|QMessageBox::Cancel); switch(ret) { case QMessageBox::Ok: qDebug()<<"i am ok"; break; case QMessageBox::Cancel: qDebug()<<"i am bad"; break; default: break; } }); QAction *p5=menu1->addAction("文件对话框"); connect(p5,&QAction::triggered, [=]() { QString path=QFileDialog::getOpenFileName( this, "open", "../", "souce(*.cpp .h);;" "Text(*.text);;" "all(*.*)"); qDebug()<<path; });}MainWindow::~MainWindow(){} 运行结果如图: Qt样式表Qt样式表Qt样式表样式大全]]></content>
<categories>
<category>QT界面</category>
</categories>
<tags>
<tag>Qtcreator</tag>
<tag>实战</tag>
</tags>
</entry>
<entry>
<title><![CDATA[QtCreator学习(3)]]></title>
<url>%2F2019%2F05%2F04%2FQtCreator%E5%AD%A6%E4%B9%A0%EF%BC%883%EF%BC%89%2F</url>
<content type="text"><![CDATA[应用程序主窗口 应用程序主窗口关系图: 主窗口框架 主窗口为建立应用程序用户界面提供了一个框架,Qt提供了QMainWindow和其他一些相关类共同完成主窗口的管理。QMainWindow类拥有自己的布局,包含以下部件: 菜单栏(QMenuBar)。菜单栏包含了一个下拉菜单项的列表,这些菜单项由QAction动作类实现。菜单栏位于主窗口的顶部,一个主窗口只能有一个菜单栏。 工具栏(QToolBar)。工具栏一般用于显示一一些常用的菜单项目,也可以插入其他窗口部件,并且是可以移动的。一个主窗口可以拥有多个工具栏。 中心部件(Central Widget)。在主窗口的中心区域可以放入一个窗口部件作为中心部件,是应用程序的主要功能实现区域。一个主窗口只能拥有一个中心部件。 Dock部件(QDockWidget)。Dock 部件常被称为停靠窗口,因为可以停靠在中心部件的四周,用来放置- -些部件来实现- -些功能,就像个工具箱一样。一个主窗口可以拥有多个Dock部件。 状态栏(QStatusBar)。状态栏用于显示程序的一.些状态信息,在主窗口的最底部。一个主窗口只能拥有一个状态栏。 本节知识可以在帮助索引中通过Application Main Window关键字查看。 Qt资源系统、菜单栏和工具栏 新建Qt Widgets应用,项目名称mymainwindow ,类名默认为MainWindow,基类默认为QMainWindow,不改动。建立好项目后,在文件列表中双击mainwindow. ui文件进人设计模式,这时在设计区域出现的便是主窗口界面。下面来添加菜单,双击左上角的“在这里输人”,修改为“文件(&F)”,这里要使用英文半角的括号,“&F”被称为加速键,表明程序运行时可以按下Alt+F键来激活该菜单。修改完成后按下回车键,并在弹出的下拉菜单中将第一项改为“新建文件(&N)”,并按下回车键(由于版本原因,如果这里无法直接输人中文,则可以通过复制粘贴完成),这时可以看到,下面的Action编辑器中已经有了“新建文件”动作。选中该动作并将其拖入菜单栏下面的工具栏中。运行程序 为菜单添加图标: 使用资源 这里将使用Qt的资源系统来存储图片,就可以嵌人到可执行文件之中了。避免图片位置发生变化,程序将无法显示图片这个问题。步骤如下: 添加Qt资源文件。往项目中添加新文件,选择Qt分类中的Qt ResourceFile,文件名称改为myimages,其他选项默认即可。 添加资源。建立好资源文件后会默认进人资源管理界面,打开myimages.qrc文件。现在先到项目文件夹mymainwindow中新建一个名为images的文件夹,并在其中放人两张图标图片,比如放人了一个new. png和一个open. png图片。(注意:Qt资源系统要求资源文件必须放在与qre文件同级或子级目录下,如果放在其他地方,则添加资源时会提示将文件复制到有效的位置。) 回到QtCreator中,在资源管理界面单击“添加”按钮,选择“添加前缀”,然后将属性栏中的前缀改为“/image”,再单击“添加”按钮选择添加文件”,在弹出的对话框中进人到前面新建的images文件夹中,选中那两张图片,单击“打开”即可。这时myimages. qre文件中就出现了添加的图片列表。最后,按下Ctrl+S快捷键保存对文件的修改(注意:这一点很重要,如果没有保存,下面使用图片时将看不到图片)。 使用图片。先使用Ctrl+ Tab快捷键转到mainwindow. ui文件.回到设计模式(如果先前没有打开过mainwindow. ui文件,那么该快捷键无法切换,需要直接双击该文件进行打开)。在Action编辑器中双击“新建文件”动作,这时会弹出编辑动作对话框。将对象名称改为action_New,工具提示ToolTip改为“新建文件”,然后按下“图标”后面的按钮进人选择资源界面。第一次进入该界面时,如果没有显示可用的资源,则可以单击左上角的重新加载绿色箭头图标 ,这时图片资源就显示出来了。这里选择new.png图片并单击OK按钮。最后在快捷键Shortcut后面的输入栏上单击并按下Ctrl+N组合键.就可以将它设为这个动作的快捷键了。这样就为动作添加了图标和快捷键,单击OK按钮关闭对话框。因为设置了工具提示,所以运行程序时可以将鼠标停在工具栏的一个动作上来显示提示信息。 编写代码方式添加菜单 下面使用代码来添加一个菜单,在mainwindow. cpp文件的MainWin-dow类构造函数中添加代码: 12345678QMenu *editMenu=ui->menuBar->addMenu(tr("编辑(&E)")); //添加编辑菜单QAction *action_Open=editMenu->addAction( QIcon(":/image/images/open.png"),tr("打开文件(&O)")); //添加打开菜单QAction *action_Open2=editMenu->addAction( QIcon(":/image/images/open.png"),tr("删除文件(&D)"));action_Open->setShortcut(QKeySequence("ctrl+O")); //设置快捷键ui->mainToolBar->addAction(action_Open); //在工具栏中添加动作ui->mainToolBar->addAction(action_Open2); 这里使用ui→menuBar来获取QMainWindow的菜单栏,使用ui→mainToolBar来获取QMainWindow的工具栏,然后分别使用相应的函数添加菜单和动作 菜单栏 QMenu中还提供了间隔器.可以在设计器中向添加菜单那样直接添加间隔器,或者在代码中使用addSeparator()函数来添加,它是一条水平线,可以将菜单进行分组,使得布局很整齐。应用程序中很多普通的命令都是通过菜单来实现的,而我们也希望能将这些菜单命令放到工具栏中以方便使用。QAction 就是这样一种命令动作,可以同时放在菜单和工具栏中。一个QAction动作包含了图标、菜单显示文本、快捷键、状态栏显示文本、“What’s This?”显示文本和工具提示文本。这些都可以在构建QAction类对象时在构造函数中指定。另外,还可以设置QAction的checkable属性,如果指定这个动作的checkable为true,那么选中这个菜单时就会在它的前面显示小圆点之类的表示选中状态的符号;如果该菜单有图标.就会用线框将图标围住.用来表示该动作被选中了。 下面再介绍一个动作组QActionGroup类。它可以包含一组动作QAction,可以设置这组动作中是否只能有一个动作处于选中状态,这对于互斥型动作很有用。在前面程序的MainWindow类构造函数中继续添加如下代码: 123456789101112QActionGroup *group=new QActionGroup(this); //建立动作组QAction *action_L=group->addAction(tr("左对齐(&L)")); //向动作组中添加动作action_L->setCheckable(true); //设置动作有选中和未选中状态QAction *action_R=group->addAction(tr("右对齐(&R)"));action_R->setCheckable(true);QAction *action_C=group->addAction(tr("居中对齐(&C)"));action_C->setCheckable(true);action_L->setChecked(true); //指定初始action_L为默认选中状态editMenu->addSeparator(); //向菜单中添加间隔器editMenu->addAction(action_L); //向编辑菜单中添加动作editMenu->addAction(action_R);editMenu->addAction(action_C); 工具栏 工具栏QToolBar类提供了一-个包含了一组控件的、可以移动的面板。前面已经看到可以将QAction对象添加到工具栏中,默认只是显示一个动作的图标,可以在QToolBar的属性栏中进行更改。在设计器中查看QToolBar的属性栏: toolButtonStyle属性用来设置图标和相应文本的显示及其相对位置; movabel属性用来设置状态栏是否可以移动; allowedArea用来设置允许停靠的位置; iconsize属性用来设置图标的大小; floatable属性用来设置是否可以悬浮。 工具栏中除了可以添加动作外,还可以添加其他的窗口部件,下面在前面的程序中的mainwindow.cpp文件中添加头文件: 12#include<QToolButton>#include<QSpinBox> 在构造函数中继续添加代码 12345678910QToolButton *toolBtn=new QToolButton(this); //创建QToolButtontoolBtn->setText(tr("颜色"));QMenu *colorMenu=new QMenu(this); //创建一个颜色菜单colorMenu->addAction(tr("红色"));colorMenu->addAction(tr("绿色"));toolBtn->setMenu(colorMenu); //为工具栏添加菜单toolBtn->setPopupMode(QToolButton::MenuButtonPopup); //设置弹出模式ui->mainToolBar->addWidget(toolBtn); //向工具栏中添加QToolButton按钮QSpinBox *spinBox=new QSpinBox(this); //创建QSpinBoxui->mainToolBar->addWidget(spinBox); //向工具栏中添加QSpinBox部件 这里创建了一个QToolButton类对象,并为它添加了一个弹出菜单,设置了弹出方式是在按钮旁边有一个向下的小箭头,可以按下这个箭头弹出菜单(默认的弹出方式是按下按钮一段时间才弹出菜单)。最后将它添加到了工具栏中。下面又在工具栏中添加了一个QSpinBox部件,可以看到,往工具栏中添加部件可以使用addWidget()函数。 这里还要再说明一下QToolButton类。其实,当往工具栏中添加一个QAction类对象时就会自动创建了一个QToolButton实例,所以说工具栏上的动作就是一个QToolButton,这就是属性栏中会有toolButtonStyle的原因。 以上运行结果如图所示: 中心部件 主窗口的中心区域可以放置一个中心部件,它一般是一个编辑器或者浏览器。这里支持单文档部件,也支持多文档部件。一般的,我们会在这里放置一个部件,然后使用布局管理器使其充满整个中心区域,并可以随着窗口的大小变化而变化。下面在前面的程序中添加中心部件。在设计模式中,往中心区域拖人一个Text Edit,然后单击界面,按下Ctrl+G,使其处于一个栅格布局中。现在可以运行程序查看效果。 QTextEdit是一个高级的WYSIWYG(所见即所得)浏览器和编辑器,支持富文本的处理,为用户提供了强大的文本编辑功能。 与QTextEdit对应的是QPlainTextEdit类,它提供了一个纯文本编辑器,这个类与QTextEdit类的很多功能都很相似,只不过无法处理富文本。 还有一个QTextBrowser类,它是一一个富文本浏览器,可以看作是QTextE-dit的只读模式。 这3个类的用法大同小异。 中心区域还可以使用多文档部件。Qt中的QMdiArea部件就是用来提供一个可以显示MDI( Multiple Document Interface)多文档界面的区域,从而有效地管理多个窗口。QMdiArea中的子窗口由QMdiSubWindow类提供,这个类有自己的布局,包含一个标题栏和一个中心区域,可以向它的中心区域添加部件。 下面更改前面的程序,在设计模式将前面添加的TextEdit部件删除,然后拖入一个MDI Area部件。在Action编辑器中的“新建文件”动作上右击,在弹出的级联菜单中选择“转到槽”,然后在弹出的对话框中选择triggered()触发信号,单击OK按钮后便转到mainwindow. cpp文件中的该信号的槽的定义处,更改如下: 123456789void MainWindow::on_action_New_triggered(){ //新建文本编辑器 QTextEdit *edit=new QTextEdit(this); //使用QMdiArea类的addSubWindow()函数创建子窗口,以文本编辑器为中心部件。 QMdiSubWindow *child=ui->mdiArea->addSubWindow(edit); child->setWindowTitle(tr("多文档编辑器子窗口")); child->show();} 这里需要先添加井include < QTextEdit>和井include < QMdiSubWindow>头文件在新建文件菜单动作的触发信号槽on_action_New_trigered()中创建了多文档区域的子窗口。这时运行程序,然后按下工具栏上的新建文件动作图标,每按下一次,就会生成一个子窗口,如图所示。 Dock部件 QDockWidget类提供了这样一个部件,可以停靠在QMainWindow中,也可以悬浮起来作为桌面顶级窗口,称为Dock部件或者停靠窗口。Dock部件一般用于存放一些其他部件来实现特殊功能,就像一个工具箱。在主窗口中可以停靠在中心部件的四周,也可以悬浮起来被拖动到任意的地方,还可以被关闭或隐藏起来。一个Dock部件包含一个标题栏和一个内容区域,可以向Dock部件中放人任何部件。 在设计模式中向中心区域拖入一个Dock Widget 部件,然后再向Dock中随意拖人几个部件,比如这里拖入一个Push Button和一个Font Combo Box。在dock Widget的属性栏中更改其windowTitle为“工具箱”,另外还可以设置它的features属性,包含是否可以关闭、移动和悬浮等;还有allowedArea属性,用来设置可以停靠的区域。 下面在文件菜单中添加“显示Dock” 菜单项,然后在Action 编辑器中转到“显示Dock”动作的触发信号triggered()的槽函数,更改如下:1234void MainWindow::on_action_Dock_triggered(){ ui->dockWidget->show();} 运行程序,关闭Dock部件后,按下显示Dock这个菜单项,就可以从新显示Dock了,运行结果如图: 状态栏 QStatusBar类提供了一个水平条部件,用来显示状态信息。QMainWindow 中默认提供了一个状态栏。状态信息可以被分为3类: 临时信息,如一般的提示信息; 正常信息,如显示页数和行号; 永久信息,如显示版本号或者日期。 显示函数: 使用showMessage()函数显示一个临时消息,它会出现在状态栏的最左边。 用addWidget()函数添加一个QLabel到状态栏上,用于显示正常信息,它会生成到状态栏的最左边,可能被临时消息掩盖。 显示永久信息,则要使用addPermanentWidget()函数来添加一.个如QLabel–样的可以显示信息的部件,它会生成在状态栏的最右端,不会被临时消息掩盖。 状态栏的最右端还有一个QSizeGrip部件,用来调整窗口的大小,可以使用set-SizeGripEnabled()函数来禁用它。因为目前的设计器还不支持直接向状态栏中拖放部件,所以需要使用代码来生成。向mainwindow.cpp文件中的构造函数里继续添加代码: 12345678#include <QLabel> //显示临时消息,时长为20000毫秒 ui->statusBar->showMessage(tr("欢迎使用多文档编辑器"),20000); //创建标签,设置标签样式和显示信息,将其以永久部件形式添加到状态栏 QLabel *permanent=new QLabel(this); permanent->setFrameStyle(QFrame::Box|QFrame::Sunken); permanent->setText("www.qter.org"); ui->statusBar->addPermanentWidget(permanent); 此时运行程序可以发现“欢迎使用多文档编辑器”字符串在显示一会儿后就自动消失了,而“www. qter. org”一直显示在状态栏最右端。如图所示: 自定义菜单 Qt中的QWidgetAction类就提供了在菜单中使用其他部件功能。为了实现自定义菜单,需要新建一个类,它继承自QWidgetAction 类,并且在其中重新实现create-Widget()函数。下面的例子中实现了这样一个菜单项:包含一个标签和一个行编辑器,可以在行编辑器中输人字符串,按下回车键就可以自动将字符串输入到中心部件文本编辑器中。 新建Qt Widgets应用,项目名称为myaction,类名默认为MainWindow ,基类默认为QMainWindow不改动。建好项目后往项目中添加新文件模板选择C++ Class, 类名设置为MyAction,基类设置为QWidgetAction。 myaction.h中添加代码: 12345678910111213141516171819202122232425#ifndef MYACTION_H#define MYACTION_H#include<QWidgetAction>class QLineEdit; //前置声明class MyAction : public QWidgetAction{ Q_OBJECTpublic: explicit MyAction(QObject *parent=0);protected: //声明函数,该函数是QWidgetAction类中的虚函数 QWidget *createWidget(QWidget *parent);signals: //新建信号,用于在按下回车键时,将行编辑器中的内容发射出去 void getText(const QString &string);private slots: //新建槽,它用来与行编辑器的按下回车键信号关联 void sendText();private: //声明行编辑器对象的指针 QLineEdit *lineEdit;};#endif // MYACTION_H myaction.cpp中添加代码: 1234567891011121314151617181920212223242526272829303132333435#include "myaction.h"#include<QLineEdit>#include<QSplitter>#include<QLabel>MyAction::MyAction(QObject *parent): QWidgetAction(parent){ //创建行编辑器 lineEdit=new QLineEdit; //将行编辑器按下回车键信号与发送文本槽关联 connect(lineEdit,&QLineEdit::returnPressed,this,&MyAction::sendText);}QWidget *MyAction::createWidget(QWidget *parent){ //这里使用inherits()函数判断父部件(是否继承)是否是菜单或工具栏 //如果是,则创建该父部件的子部件,并返回子部件 //如果不是,则直接返回0 if(parent->inherits("QMenu")||parent->inherits("QToolBar")){ QSplitter *splitter = new QSplitter(parent);// 创建分裂器 QLabel *label=new QLabel; label->setText(tr("插入文本:")); splitter->addWidget(label); splitter->addWidget(lineEdit); return splitter; } return 0;}void MyAction::sendText(){ emit getText(lineEdit->text());//发射信号,将行编辑器中的内容发射出去 lineEdit->clear(); //清空行编辑器中的内容} 双击mainwindow.ui文件进入设计模式,向中心区域拖入一个Text Edit部件,并使用ctrl+G使其处于一个栅格布局中。 在mainwindow.h中添加代码: 1234567891011121314151617181920212223#ifndef MAINWINDOW_H#define MAINWINDOW_H#include <QMainWindow>namespace Ui {class MainWindow;}class MainWindow : public QMainWindow{ Q_OBJECTpublic: explicit MainWindow(QWidget *parent = 0); ~MainWindow();private: Ui::MainWindow *ui;private slots: void setText(const QString &string);};#endif // MAINWINDOW_H 在mainwindow.cpp中添加代码: 1234567891011121314151617181920212223#include "mainwindow.h"#include "ui_mainwindow.h"#include "myaction.h"MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){ ui->setupUi(this); MyAction *action =new MyAction; QMenu *editMenu=ui->menuBar->addMenu(tr("编辑(&E)")); editMenu->addAction(action); connect(action,SIGNAL(getText(QString)),this,SLOT(setText(QString)));}MainWindow::~MainWindow(){ delete ui;}void MainWindow::setText(const QString &string){ ui->textEdit->setText(string);//将获取的文本添加到编辑器中} 现在运行程序,在编辑菜单中单击自定义的菜单动作,然后输人字符并按下回车键,可以看到输人的字符自动添加到了文本编辑器中。这个例子中设计了自己的信号和槽,整个过程是这样的:在行编辑器中输人文本,然后按下回车键,这时行编辑就会发射returnPressed()信号,而这时就调用了我们的sendText()槽,在sendText()槽中又发射了getText()信号,信号中包含了行编辑器中的文本,接着又会调用setText()槽,在setText()槽中将getText()信号发来的文本输人到文本编辑器中。这样就完成了按下回车键将行编辑器中的文本输人到中心部件的文本编辑器中的操作。 富文本处理 富文本(Rich Text)或者叫富文本格式,简单来说就是在文档中可以使用多种格式,比如字体颜色、图片和表格等。它是与纯文本(Plain Text)相对而言的,比如Windows.上的记事本就是纯文本编辑器,而Word就是富文本编辑器。Qt中提供了对富文本处理的支持,可以在帮助中通过Rich Text Processing关键字查看。 富文本文档结构富文本处理: 编辑操作->基于光标的一些接口函数->textCursor()函数->基于QTextCursor类 QTextFrameFormat QTextBlockFormat QTextTableFormat QTextListFormat 只读操作->基于文档结构,使用了只读的分层次的接口函数->document()函数->基于QTextDocument类 QTextFrame QTextBlock QTextTable QTextList文本块表格、列表与图片查找功能语法高亮与HTML 拖放操作使用拖放打开文件自定义拖放操作打印文档为什么后面没内容了呢?发现看电子档太无聊了啊,然后开始看视频了。。。。]]></content>
<categories>
<category>QT界面</category>
</categories>
<tags>
<tag>Qt Creator</tag>
</tags>
</entry>
<entry>
<title><![CDATA[QtCreator学习(2)]]></title>
<url>%2F2019%2F05%2F02%2FQtCreator%E5%AD%A6%E4%B9%A0%EF%BC%882%EF%BC%89%2F</url>
<content type="text"><![CDATA[布局管理Qt主要提供了QLayout类及其子类作为布局管理器。 布局管理系统一旦一个部件上设置了布局管理器,那么它会完成以下几种任务: 定位子部件; 感知窗口默认大小;➢感知窗口最小大小; 窗口大小变化时进行处理;➢当内容改变时自动更新: 字体大小、文本或 子部件的其他内容随之改变; 隐藏或显示子部件; 移除一个子 部件。 布局管理器 QLayout类是布局管理器的基类,是一个抽象基类,継承自QObject 和QLayoutItem类, Qlayoutltem类提供了一个供Qlayout操作的抽象項目。Qlayout 和QLayoutItem都是在设计自己的布局管理器吋オ使用的,一般只需要使用QLayout的几个子类即可,它们分別是QBoxlayout(基本布局管理器)、QGridLayout(柵格布局管理器)、QFormLayout(窗体布局管理器)和QStackedlayout(桟布局管理器)。这里的;QStackedlayout与(1)中述的桟部件QStackedWidget用法相同,不再螯述。 打开Qt Creator ,新建Qt Widgets应用,项目名称为mylayout,基类选择QWidget,类名设为MyWidget。完成后打开mywidget. ui文件,在设计模式中向界面上拖入一个字体选择框Font Combo Box和一个文本编辑器Text Edit部件。然后单击主界面并按下Ctrl+L快捷键,或者单击设计器上部边栏中的 “目” 图标来对主界面进行垂直布局管理。也可以在主界面上右击,在弹出的级联菜单中选择“布局→垂直布局”。这样便设置了顶层布局管理器(因为是对整个窗口设置的布局管理器,所以叫顶层布局管理器),可以看到两个部件已经填满了整个界面。这时运行程序,然后拉伸窗口,两个部件会随着窗口的大小变化而变化,运行结果如图4所示。这就是布局管理器的作用。 基本布局管理器 基本布局管理器QBoxLayout类可以使子部件在水平方向或者垂直方向排成一列,它将所有的空间分成一行盒子,然后将每个部件放人一个盒子中。它有两个子类QHBoxLayout水平布局管理器和QVBoxLayout垂直布局管理器,编程中经常用到。再回到设计模式中看看布局管理器的属性。先单击主界面,查看它的属性栏,最后面的部分是其使用的布局管理器的属性,如下表所列。 下面打破已有的布局,使用代码实现水平布局。在界面上右击,然后在弹出的级联菜单中选择“打破布局”,或者单击设计器上方边栏中的打破布局图标。在MyWidget类的构造函数中添加如下代码: 123456789# include < QHBoxLayout> QHBoxLayout *layout=new QHBoxLayout; //新建布局管理器 layout->addWidget(ui->fontComboBox); //向布局管理器中添加部件 layout->addWidget(ui->textEdit); layout->setSpacing(50); //设置部件间的间隔 layout->setContentsMargins(0,0,50,100);//设置布局管理器到边界的距离 //4个参数顺序是左上右下 setLayout(layout); //将这个布局设置为MyWidget的布局 说明:除了使用addWidget()函数向布局管理器的末尾添加部件外,还可以使用insertWidget()函数向任意位置添加部件。 栅格布局管理器(QGridLayout) 栅格布局管理器QGridLayout类使部件在网格中进行布局,它将所有的空间分隔成一些行和列,行和列的交叉处形成了单元格,然后将部件放人一个确定的单元格中。先往界面上拖放一个Push Button, 然后在mywidget. cpp中添加头文件,再注释掉前面添加的关于水平布局管理器的代码,添加的如下代码:12345678# include<QGridLayout> QGridLayout *layout =new QGridLayout; //添加部件从0行0列开始占据一行两列 layout->addWidget(ui->fontComboBox,0,0,1,2); layout->addWidget(ui->pushButton,0,2,1,1); layout->addWidget(ui->textEdit,1,0,1,3); setLayout(layout); 窗口布局管理器(QFormLayout) 窗体布局管理器QFormLayout类用来管理表单的输人部件以及与它们相关的标签。窗体布局管理器将它的子部件分为两列,左边是一些标签,右边是一些输入部件,比如行编辑器或者数字选择框等。 先将前面在MyWidget类的构造函数中自己添加的代码全部注释掉,然后进人设计模式,这里使用另外一种方法来使用布局管理器。从部件栏中找到Form Layout,将其拖人到界面上,然后双击或者在它上面右击并在弹出级联菜单中选择“添加窗体布局行”。在弹出的“添加表单布局行”对话框中填入标签文字“姓名(&N):”,这样下面便自动填写了“标签名称”、“字段类型”和“字段名称”等,并且设置了伙伴关系。这里使用了QLineEdit行编辑器,当然也可以选择其他部件。填写的标签文字中的“(&N)”必须是英语半角的括号,表明它的快捷键是Alt+N。设置伙伴关系表示按下Alt+N时,光标自动跳转到标签后面对应的行编辑器中。单击“确定”键.则会在布局管理器中添加一个标签和一个行编辑器。按照这种方法,再添加3行:性别(&S),使用QCo-moBox;年龄(&A),使用QSpinBox;邮箱(&M),使用QLineEdit。完成后运行程序,可以按下快捷键Alt+N,这样光标就可以定位到“姓名”标签后的行编辑器中。 上面添加表单行是在设计器中完成的,其实也可以在代码中使用addRow()函数来完成。 综合使用布局管理器 现在将前面的界面再进行设计:按下Ctrl键的同时选中界面上的字体选择框fontComboBox和按钮pushButton,然后按下Ctrl+H快捷键将它们放人一个水平布局管理器中(其实;也可以从部件栏中拖人一个Horizontal Layout, 然后再将这两个部件放进去,效果是:一样的)。然后再从部件栏中拖入一个Vertical Spacer垂直分隔符,用来在部件间产生间隔,将它放在窗体布局管理器与水平布局管理器之间。最后单击主界面并按下Ctrl+L快捷键,让整个界面处于一个垂直布局管理器中。这时可以在右上角的对象列表中选择分隔符Spacer,然后在属性栏中设置它的高度为100,这时运行程序可以看到,分隔符是不显示的。如图所示: 设置部件大小 讲解之前要先了解两个概念:大小提示(sizeHint)和最小大小提示(minimumSize-Hint)。凡是继承自QWidget的类都有这两个属性: sizeHint属性保存了部件的建议大小,对于不同的部件,默认拥有不同的sizeHint; minimumSizeHint保存了一个建议的最小大小提示。 可以在程序中使用sizeHint()函数来获取sizeHint的值,使用minimumSizeHint( )函数获取minimumSizeHint的值 需要说明的是,如果使用setMinimumSize()函数设置了部件的最小大小,那么最小大小提示将会被忽略。 QWidget类的属性: 大小策略(sizePolicy)属性,这个属性保存了部件的默认布局行为,在水平和垂直两个方向分别起作用,控制着部件在布局管 下面再来看一下大小策略(sizePolicy)属性,它也是QWidget类的属性。这个属性保存了部件的默认布局行为,在水平和垂直两个方向分别起作用,控制着部件在布局管理器中的大小变化行为。 sizePolicy在Qt Creator里的位置及取值: 高度与宽度属性是现在界面的大小;下面的sizePolicy属性可以设置大小策略以及伸缩因子;minimumSize属性用来设置最小值 ;maximumSize属性设置最大值; sizeIncrement属性和baseSize属性是设置窗口改变大小的,一般不用设置。 布局管理器的属性: 水平布局管理器的layoutStretch属性设置为“2,1”,这样这个水平布局管理器中的两个部件的宽度就是2:1的比例了。如果要在代码中进行设置,则可以在使用布局管理器的addWidget()函数添加部件的同时,在第二个参数中指定伸缩因子。 窗口布局管理器的属性如表所示: LayoutSizeConstraint属它是用来约束窗口大小的,也就是说,这个只对顶级布局管理器有用,因为它只对窗口有用,对其他子部件没有效果。 可扩展窗口 一个窗口可 能有很多选项是扩充的,只有在必要的时候才显示出来,这时就可以使用一个按钮来隐藏或者显示多余的内容,就是所谓的可扩展窗口。要实现可扩展窗口,就要得力于布局管理器的特性,那就是当子部件隐藏时,布局管理器自动缩小,当子部件重新显示时,布局管理器再次放大。下面看一个具体的例子。 依然在前面的程序中进行更改。首先将界面上的pushButton显示文本更改为“显示可扩展窗口”,并在其属性栏选中checkable选项。然后转到它的toggled( bool)信号的槽,更改如下: 123456void MyWidget::on_pushButton_toggled(bool checked)//隐藏窗口按钮{ ui->textEdit->setVisible(checked);//设置文本编辑器的扩展和隐藏true or false if(checked)ui->pushButton->setText(tr("隐藏可扩展的窗口")); else ui->pushButton->setText(tr("显示可扩展的窗口"));} 这里使用按钮的按下与否两种状态来设置文本编辑器是否显示,并且相应地更改按钮的文本。为了让文本编辑器在一开始是隐藏的,还要在MyWidget类的构造函数中添加一行代码:1ui ->textEdit ->hide();1让文本编辑器隐藏,也可以使用setVisible( false)函数 分裂器(QSplitter) 分裂器QSplitter类提供了一个分裂器部件。和QBoxLayout类似,可以完成布局管理器的功能,但是包含在它里面的部件,默认是可以随着分裂器的大小变化而变化的。比如一个按钮放在布局管理器中,它的垂直方向默认是不会被拉伸的,但是放到分裂器中就可以被拉伸。还有一点不同就是,布局管理器继承自QObject类,而分裂器却是继承自QFrame类,QFrame类又继承自QWidget类,也就是说,分裂器拥有QWid-get类的特性,它是可见的,而且可以像QFrame一样设置边框。 新建Qt Widgets应用,项目名称为mysplitter,基类选择QWidget,类名设为MyWidget。建好项目后打开mywid-get, ui文件,然后往界面上拖人4个Push Button,同时选中这4个按钮,右击并在弹出的级联菜单中选择“布局→使用分裂器水平布局”,将这4个按钮放到一个分裂器中。将分裂器拉大点,并在属性栏中设置其frameShape为Box, frameShadow为Raised,lineWidth为5。运行程序,效果如图所示: 设置伙伴(buddy) 前面讲述窗体布局管理器时提到了设置一个标签和一个部件的伙伴关系。其实,伙伴( buddy)是在QLabel类中提出的一个概念。因为一个标签经常用作一个交互式部件的说明,就像在讲窗体布局管理器时看到的那样,一个lineEdit部件前面有一个标签说明这个lineEdit的作用。为了方便定位,QLabel提供了一个有用的机制,那就是提供了助记符来定位键盘焦点到对应的部件上,而这个部件就叫这个QLabel的伙伴。其中,助记符就是我们所说的加速键。使用英文标签时,在字符串的一一个字母前面添加“&”符号,就可以指定这个标签的加速键是Alt加上这个字母;对于中文,需要在小括号中指定加速键字母,这个前面已经见过多次了。Qt设计器中也提供了伙伴设计模式,下面看一个例子。 新建Qt Widgets应用,项目名称为mybuddy,基类选择QWidget,类名设为MyWidget。完成后打开mywidget,ui文件,往界面上拖放4个标签Label, 再在标签后面依次放上PushButton、CheckBox、LineEdit和SpinBox。然后将PushButton前面的标签文本改为“&Button:”, Check-Box前面的标签文本改为“C&heckBox:”,LineEdit前面的标签文本改为“行编辑器(&L):”, SpinBox前面的标签文本改为“数字选择框(&N):”。单击设计器上方边栏中的编辑伙伴图标进入伙伴设计模式,分别将各个标签与它们后面的部件关联起来。然后按下F3键回到正常编辑模式,可以看到所有的&符号都不显示了。 现在运行程序,按下Alt+ B组合键,则可以看到按钮被按下了,而字母下面多了一个横杠,表示这个标签的加速键就是Alt加这个字母。如果要在代码中设置伙伴关系,则只需要使用QLabel的setBuddy()函数就可以了。本小节内容可以在帮助索引中通过Qt Designer’s Buddy Editing Mode关键字查看。 设置Tab键顺序 对于一个应用程序,有时总希望使用Tab键将焦点从一个部件移动到下一个部件。在设计模式中,设计器提供了Tab键的设置功能。在前面程序的设计模式中,按下,上方边栏的编辑Tab顺序按钮进人编辑Tab键顺序模式,这时已经显示出了各个部件的Tab键顺序,只需要单击这些数字就可以更改顺序。设置好之后,可以运行一下程序测试效果。需要说明,当程序启动时,焦点会在Tab键顺序为1的部件上。这里进行的设置等价于在MyWidget类的构造函数中使用如下代码: 123setTabOrder(ui -> lineEdit, ui ->spinBox); //lineEdit 在spinBox前面 setTabOrder(ui -> spinBox, ui ->pushButton); // spinBox 在pushButton前面 setTabOrder(ui -> pushButton, ui ->checkBox); // pushButton在checkBox前面 关于在设计器中设置Tab键顺序,可以在帮助索引中通过Qt Designer’s TabOrder Editing Mode关键字查看。 Qt Creator中的定位器 定位器,它位于主界面的左下方。使用定位器可以很方便地打开指定文件、定位到文档的指定行、打开一个特定的帮助文档、进行项目中函数的查找等。更多的功能可以在帮助索引中通过Searching With the Locator关键字查看。 定位器中提供了多个过滤器来实现不同的功能,按下Ctrl+K快捷键就会在定位器中显示各个过滤器的前缀及其功能,如图所示。使用方法是“前缀符号+空格+要定位的内容”。 在Qt Creator中,按下Ctrl+K快捷键打开定位器,这时输人“18”(英文字母1和一个空格,然后是数字8),按下Enter回车键,就会跳转到编辑模式的当前打开文档的第8行。再次按下Ctrl + K快捷键,输入“? qla” ,这时已经查找到了QLabel, 按下回车键,就会跳转到帮助模式中,并打开QLabel类的帮助文档。]]></content>
<categories>
<category>QT界面</category>
</categories>
<tags>
<tag>Qt</tag>
<tag>布局</tag>
</tags>
</entry>
<entry>
<title><![CDATA[QtCreator学习(1)]]></title>
<url>%2F2019%2F04%2F28%2FQtCreator%E5%AD%A6%E4%B9%A0%EF%BC%881%EF%BC%89%2F</url>
<content type="text"><![CDATA[Qt是一个跨平台的c++图形用户界面应用程序框架。 窗口部件 基础窗口部件QWidget 窗口、子部件以及窗口类型QWidget类是所有用户界面对象的基类 打开Qt Creator,新建空的qmake项目,项目名为mywidget1,完成后在mywidget1.pro中添加1QT+=widgets 然后在项目中添加C++源文件main.cpp,添加以下代码:123456789101112131415161718192021222324252627282930//包含QApplication,QWidget等#include<QtWidgets>int main(int argc,char *argv[]){ QApplication a(argc,argv); //新建Qwidget类对象,默认parent参数是0,所以他是一个窗口 QWidget *widget=new QWidget(); //设置窗口标题 widget->setWindowTitle(QObject::tr("我是widget")); //新建Qlabel对象,也是一个窗口 QLabel *label=new QLabel(); label->setWindowTitle(QObject::tr("我是label")); //设置显示文本 label->setText(QObject::tr("label:我是一个窗口")); //改变部件大小,以便能显示出完整的内容 label->resize(380,200); //指定了父窗口为widget,所以不是窗口 QLabel *label2= new QLabel(widget); label2->setText(QObject::tr("label2:我不是独立窗口,只是widget的子部件")); label2->resize(450,200); //在屏幕上显示 label->show(); widget->show(); int ret =a.exec(); delete label; //QT销毁父对象时会自动销毁子对象 delete widget; return ret;} 运行结果: 注意:这里使用new操作符为label2分配了空间,但是并没有使用delete进行释放,这是应为在Qt中销毁父对象的时候会自动销毁子对象,这里label2指定了parent为widget QWidget的构造函数有两个参数:1234//parent表示父窗口部件,默认为0,表示没有父窗口,后面的参数是 Qt::WindowType枚举类型//用来指定各种窗口系统属性,f=0表示窗口类型值为Qt::Widget,这种类型的部件如果有父窗口,//那么他就是子部件,否则就是独立窗口.QWidget(QWidget*parent = 0,Qt::WindowFlags f=0) 修改创建对象的两行代码为,运行程序查看效果:12QWidget *widget=new QWidget(0,Qt::Dialog);QLabel *label=new QLabel(0,Qt::SplashScreen); 再次更改:123QWidget *widget=new QWidget(0,Qt::Dialog|Qt::FramelessWindowHint);//label窗口一直在“图层”最上面QLabel *label=new QLabel(0,Qt::SplashScreen|Qt::WindowStaysOnTopHint); 说明:更多的f参数的使用,可以在帮助中索引Qt::WindowFlags关键字 窗口几何布局窗口的大小和位置,根据是否包含边框和标题栏两种情况,要用不同的函数来获取。可以在帮助索引中查看 Window and Dialog Widgets 关键字 这里的函数分为两类: 包含框架:x()、y()、frameGeometry()、pos()、move()等函数 不包含框架:geometry()、width()、height()、rect()、size()等函数 程序调试这部分内容可以在帮助索引中通过Interacting with the Debugger 和Debugging a C++Example Application关键字查看 设置断点创建mywidght2项目,主函数内容为:12345678910111213141516#include<QApplication>#include<QWidget>int main(int argc,char *argv[]){ QApplication a(argc,argv); QWidget widget; int x=widget.x(); int y=widget.y(); //QRect类型返回数据为:(x,y,宽,高) QRect geometry =widget.geometry(); QRect frame =widget.frameGeometry(); return a.exec();} 在一行代码前面单击来设置断点,取消再单击一下,如图: 单步调试–>快捷键F11。 使用qDebug()函数更改上面的程序:1234567891011121314151617181920212223242526#include<QApplication>#include<QWidget>//使用qDebug()函数要添加#include<QDebug>头文件#include<QDebug>int main(int argc,char *argv[]){ QApplication a(argc,argv); QWidget widget; widget.resize(400,300); //窗口大小 widget.move(200,100); //窗口位置 widget.show(); int x=widget.x(); qDebug("x:%d",x); int y=widget.y(); qDebug("y:%d",y); QRect geometry =widget.geometry(); QRect frame =widget.frameGeometry(); qDebug()<<"geometry:"<<geometry<<"frame:"<<frame; qDebug()<<"pos:"<<widget.pos()<<endl<<"rect:"<<widget.rect() <<endl<<"size:"<<widget.size()<<endl<<"width:"<<widget.width() <<endl<<"height:"<<widget.height(); return a.exec();} 运行结果:123456789Starting D:\QT\03\3-02\build-mywidget2-Desktop_Qt_5_6_0_MinGW_32bit-Debug\debug\mywidget2.exe...x:200y:100geometry: QRect(211,145 400x300) frame: QRect(200,100 422x356)pos: QPoint(200,100) rect: QRect(0,0 400x300) size: QSize(400, 300) width: 400 height: 300 对话框QDialog模态和非模态对话框索引关键字QDialog 和Dialog Windows对话框分为两类: 模态对话框:就是在没有关闭它之前不能再与同一个应用程序的其他窗口进行交互。 非模态对话框:既可以与它交互也可以与同一程序中的其他窗口交互。 新建Qt Widgets应用,项目名称为mydialog1,基类选择QWidget,类名为MyWidget,然后在mywidget.cpp文件中添加一下代码:123456789101112#include"mywidget.h"#include"ui_mywidget.h"#include<QDialog>MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget){ ui->setupUi(this); QDialog dialog(this); //父窗口为MyWidget类对象 dialog.show();} 运行程序发现dialog窗口一闪而过,只显示myWidget窗口了,是因为,dialog只在mywidget这个构造函数中有用,这个构造函数执行完了,dialog也就消失了。解决办法: 非模态对话框 12QDialog *dialog =new QDialog(this); //创建一个对象,用new开辟内存空间dialog->show(); 模态 12QDialog dialog(this); dialog.exec(); 运行程序,对话框弹出来,但MyWidget窗口并没有弹出,当关闭对话框后MyWidget才弹出来 模态 123QDialog *dialog =new QDialog(this); //创建一个对象,用new开辟内存空间dialog->setModal(true);dialog->show(); 运行程序,两个对话框都显示出来,但只有在关闭对话框之后才能操作MyWidget窗口。 setModal()函数默认设置是Qt::ApplicationModal. setWindowModality()函数,它有一个参数来设置模态对话框要阻塞的窗口类型,可以是: Qt::NonModal(不阻塞任何窗口,就是非模态)、 Qt:: WindowModal(阻塞它的父窗口、所有祖先窗口以及它们的子窗ロ) Qt: ApplicationModal(阻塞整个应用程序的所有窗口)。 多窗口切换信号和槽新建Qt Widgets应用,项目名称为mydialog2,继承QWidget,双击mywidget. ui文件,在设计模式中往界面添加一个Label和一个PushButton,在属性栏中将PushButton的objectName改为showChildButton,然后更改Label 的显示文本为“我是主界面!”,更改按钮的显示文本为“显示子窗口”。然后回到编辑模式打开mywidget. h文件,在MyWidget类声明的最后添加槽的声明:一、 手动关联 定义槽 12public slots: void showChildDialog(); 到源文件中编写这个槽的实现代码 Qt Creator设计了一个快速添加定义的方法:单击showChildDialog()槽,同时按下Alt+ Enter键(也可以在函数上右击,在弹出的级联菜单中选择Refactor 菜单项),会弹出“在mywidget. cpp添加定义”选项,再次按下回车键Enter,编辑器便会转到mywidget. cpp文件中,并且自动创建showChildDialog()槽的定义,只需要在其中添加代码即可。这种方法也适用于先在源文件中添加定义,然后自动在头文件中添加声明的情况。 添加代码:12345678910111213141516171819202122232425#include "mywidget.h"#include "ui_mywidget.h"#include<QDialog>MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget){ ui->setupUi(this); //添加代码 connect(ui->showChildButton,&QPushButton::clicked, this,&MyWidget::showChildDialog);}MyWidget::~MyWidget(){ delete ui;}void MyWidget::showChildDialog(){ //添加代码 QDialog *dialog = new QDialog(this); dialog->show();} 这里使用了connect( )函数将按钮的单击信号clicked() 与新建的槽进行关联。clicked( )信号在QPushButton类中定义,而connect()是QObject类中的函数,因为MyWidget类继承自QObject,所以可以直接使用它。connect()函数中的4个参数分别是发射信号的对象、发射的信号、接收信号的对象和要执行的槽。 运行程序,然后单击主界面上的按钮就会弹出一个对话框。 二、自动关联。 自动关联就是将关联函数整合到槽命名中,比如前面的槽可以重命名为on_show-ChildButton_ clicked(), 就是由字符on、发射信号的部件对象名和信号名组成。这样就可以去掉connect()关联函数了,具体做法: 打开mywidget. cpp文件,在MyWidget类的构造函数中删除connect()函数,然后更改showChildDialog()槽的名字,QtCreator中提供了一个快捷方式来更改所有该函数出现的地方,从而不再需要逐- -更改函数名。先在showChildDialog上右击,在弹出的级联菜单中选择Refactor- Rename Symbol Under Cursor,或者直接使用Ctrl+ Shift+ R快捷键,在出现的替换栏中输入on_ showChildButton_ clicked ,再单击Replace就可以了。这时源文件和头文件中相应的函数名都进行了更改。现在运行程序,和前面的效果是一样的。 源文件代码如下:12345678910111213141516171819202122#include "mywidget.h"#include "ui_mywidget.h"#include<QDialog>MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget){ ui->setupUi(this); //on_showChildButton_clicked();}MyWidget::~MyWidget(){ delete ui;}void MyWidget::on_showChildButton_clicked(){ QDialog *dialog = new QDialog(this); dialog->show();} 自定义对话框新建Qt Widgets应用,项目名称为mydialog3,继承QWidget. 添加自定义对话类框。首先向该项目中添加Qt设计师界面类。界面模板选择Dialog without Buttons, 类名改为MyDialog。然后在设计模式中向窗口添加两个Push Button,并且分别更改其显示文本为“进入主界面”和“退出程序”。 设计信号和槽。 使用设计器来实现“退出程序”按钮的信号和槽的关联。 单击设计器上方的Edit Signals/ Slots图标,或者按下快捷键F4,于是进入了部件的信号和槽的编辑模式。在“退出程序”按钮上按住鼠标左键,然后拖动到窗口界面上,这时松开鼠标左键。在弹出的配置连接对话框中选择“显示从QWidget继承的信号和槽”选项,然后在左边的QPushButton栏中选择信号clicked(),在右边的QDialog栏中选择对应的槽close(),完成后单击OK按钮. 要想取消这个关联,只须在信号和槽编辑模式中选择这个关联;当它变为红色时,按下Delete 键,或者右击选择“删除”。也可以在设计器下方的信号和槽编辑器中看到设置好的关联。 设置好关联后按下F3键,或者单击“编辑控件”图标,则回到部件编辑模式。 关于设计器中信号和槽的详细使用,可以在帮助索引中通过QtDesigner’sSignalsandSlotsEd-iting Mode关键字查看。 现在设置“进人主界面”按钮的信号和槽的关联。 在该按钮上右击,在弹出的级联菜单中选择“转到槽”,然后在弹出的对话框中选择clicked()信号,并单击OK按钮。这时便会进人代码编辑模式,并且定位到自动生成的on_pushButton_ clicked()槽中 。在其中添加代码: 1234void MyDialog::on_pushButton_clicked(){ accept();} 说明:这个acceptO)函数是QDialog类中的一个槽,対于一个使用exec()函数实现的模态対话框,执行了这个槽就会隠藏这个模态対活框,并返回QDialog::Accepted值,这里里就是要使用这个值来判断是哪个按钮被按下了。与其对应的还有一个reject()槽,它可以返回一个QDialog::Rejected值,前面的“退出程序”按钮也可以关联这个槽。 在主界面中使用自定义的对话框 更改main.cpp函数内容如下 12345678910111213141516#include "mywidget.h"#include <QApplication>#include"mydialog.h"int main(int argc, char *argv[]){ QApplication a(argc, argv); MyWidget w; MyDialog dialog; if(dialog.exec()==QDialog::Accepted){ //判断dialog执行结果 w.show(); //如果按下了“进入主界面”按钮,则显示主页面 return a.exec(); } else return 0;} 主函数中建立MyDialog对象,然后判断其exec()函数的返回值,如果按下了“进人主界面”按钮,返回值应该是QDialog:: Accepted,则显示主界面,并且正常执行程序;如果不是,则直接退出程序。 运行程序后可以发现,已经实现了从登录对话框到主界面,再从主界面显示一个对话框的应用了。再来实现可以从主界面重新进入登录界面的功能。双击mywidget. ui文件,在设计模式中再向界面添加两个Push Button,分别更改它们的显示文本为“重新登录”和“退出”。然后使用信号和槽模式将“退出”按钮的clicked()信号和MyWidget界面的close()槽关联。完成后再转到“重新登录”按钮的clicked()信号的槽,并更,改如下: 1234567891011void MyWidget::on_pushButton_clicked(){ //先关闭主界面,其实它是隐藏起来了,并没有退出 close(); //新建MyDialog对象 MyDialog dlg; //如果按下了“进入主窗口“按钮,则再次显示主界面 //否则,因为现在已经没有显示的界面了,所以程序将退出 if(dlg.exec()==QDialog::Accepted) show();} 说明:那个close()槽,它不一定使程序退出,只有当只剩下最后一个主界面了(就是没有父窗口的界面),这时调用close()槽,程序才会退出;而其他情况下界面只是隐藏起来了,并没有被销毁。这里还需要包含MyDialog类的头文件#include“mydialog. h” ,然后运行程序查看效果。 标准对话框新建Qt Widgets应用,项目名称为mydialog5,继承QWidget.本节帮助索引Standard Dialogs关键字。添加如图所示按钮: 颜色对话框 在mywidget.cpp文件中添加: 12#include<QDebug>#include<QColorDialog> 进入“颜色对话框”按钮的clicked()信号槽,用静态函数直接直接显示颜色对话框: 12345678void MyWidget::on_pushButton_clicked(){//参数分别是:设置初始颜色、指定父窗口、设置对话框标题 QColor color = QColorDialog::getColor(Qt::red,this,tr("颜色对话框")); //,QColorDialog::ShowAlphaChannel); //添加透明度选项 1.0表示完全不透明,0.0表示完全透明 qDebug()<<"color:"<<color;} 或者,创建一个对象: 123456789void MyWidget::on_pushButton_clicked(){ QColorDialog dialog(Qt::red,this); dialog.setOption(QColorDialog::ShowAlphaChannel);//显示透明度选项 //1.0表示完全不透明,0.0表示完全透明 dialog.exec(); //模态方式运行对话框 QColor color = dialog.currentColor(); //获取当前颜色 qDebug()<<"color:"<<color;} 运行结果: 文件对话框12345678910111213void MyWidget::on_pushButton_2_clicked(){ //参数分别是:指定父窗口、设置对话框标题、默认打开目录路径和设置文件类型过滤器 //如果不指定过滤器则默认选择所有类型文件 //同文件类型不同格式要用空格隔开,不同类型用“;;”隔开。// QString fileName=QFileDialog::getOpenFileName(this,tr("文件对话框"),// "D:",tr("图片文件(*png *jpg);;文本文件(*txt)"));// qDebug()<<"fileName:"<<fileName;//同时选择多个文件 QStringList fileNames=QFileDialog::getOpenFileNames(this,tr("文件对话框"), "D:",tr("图片文件(*png *jpg)")); qDebug()<<"fileNames:"<<fileNames;} 其他函数: getSaveFileName()实现保存文件对话框和文件另存对话框 getExistingDirectory()获取一个已存在的文件夹路径 运行结果: 字体对话框12345678void MyWidget::on_pushButton_3_clicked(){ //ok用于标记是否单击了ok按钮 bool ok; QFont font =QFontDialog::getFont(&ok,this); if(ok) ui->pushButton_3->setFont(font); else qDebug()<<tr("没有选择字体!");} 运行结果: 输入对话框12345678910111213141516171819202122232425262728void MyWidget::on_pushButton_4_clicked(){ bool ok; //指定父窗口、设置窗口标题、设置对话框中的标签显示文本、设置输入字符串的 //显示模式(例如密码可以显示成小黑点,这里选择了显示用户输人的实际内容)、 //设置输入框中的默认字符串和设置获取按下按钮信息的bool变量; QString string =QInputDialog::getText(this,tr("输入字符串对话框"), tr("请输入用户名:"),QLineEdit::Normal,tr("admin"),&ok); if(ok)qDebug()<<"string:"<<string; //其中的参数100表示默认的数值是100,一1000表示可输人的最小值是一1 000, //1000表示可输人的最大值是1000,10表示使用箭头按钮,数值每次变化10 int value1=QInputDialog::getInt(this,tr("输入整数对话框"), tr("请输入-1000到1000之间的数值"),100,-1000,1000,10,&ok); if(ok)qDebug()<<"value1:"<<value1; //参数2表示小数的位数为2 double value2=QInputDialog::getDouble(this,tr("输入浮点数对话框"), tr("请输入-1000到1000之间的数值"),0.00,-1000,1000,2,&ok); if(ok)qDebug()<<"value2:"<<value2; //参数0表示默认显示列表中的第0个条目(0就是第一个),参数true设置条目是否可以被更改.true就是可以被更改。 QStringList items; items<<tr("条目1")<<tr("条目2"); QString item=QInputDialog::getItem(this,tr("输入条目对话框"), tr("请选择或输入一个条目"),items,0,true,&ok); if(ok)qDebug()<<"item:"<<item;} 消息对话框123456789101112131415161718192021222324void MyWidget::on_pushButton_5_clicked(){ //分别拥有不同的图标和提示音 //参数:父窗口、标题栏、显示信息、拥有的按钮 int ret1=QMessageBox::question(this,tr("问题对话框"), tr("你了解Qt吗?"),QMessageBox::Yes,QMessageBox::No); if(ret1==QMessageBox::Yes)qDebug()<<tr("问题!"); int ret2=QMessageBox::information(this,tr("提示对话框"), tr("这是Qt书籍!"),QMessageBox::Ok); if(ret2==QMessageBox::Ok)qDebug()<<tr("提示!"); int ret3=QMessageBox::warning(this,tr("警告对话框"), tr("不能提前结束?"),QMessageBox::Abort); if(ret3==QMessageBox::Abort)qDebug()<<tr("警告!"); int ret4=QMessageBox::critical(this,tr("严重错误对话框"), tr("发现一个严重错误!现在要关闭所有文件!"),QMessageBox::YesAll); if(ret4==QMessageBox::YesAll)qDebug()<<tr("错误!"); QMessageBox::about(this,tr("关于对话框"), tr("Qt及QtCreator")); QMessageBox::aboutQt(this,tr("关于qt"));} 进度条对话框123456789101112131415void MyWidget::on_pushButton_6_clicked(){ //参数:设置对话框的标签内容、取消按钮的显示文本、最小值、最大值和和父窗口 QProgressDialog dialog(tr("文件复制进度"),tr("取消"),0,50000,this); dialog.setWindowTitle(tr("进度对话框")); dialog.setWindowModality(Qt::WindowModal); //将对话框设置为模态 dialog.show(); for(int i=0;i<50000;i++){ //演示进度条 dialog.setValue(i); //设置进度条当前值 QCoreApplication::processEvents(); //避免界面冻结 if(dialog.wasCanceled())break; //按下取消按钮则中断 } dialog.setValue(50000); //这样才能显示100%,因为for循环中少加一个数 qDebug()<<tr("复制结束!");} 错误信息对话框在mywidget.h文件中 添加类前置声明 1class QErrorMessage; 添加私有对象: 1QErrorMessage *errordlg 在mywidget.cpp中 在构造函数中添加: 1errordlg = new QErrorMessage(this); 添加槽信号 12345void MyWidget::on_pushButton_7_clicked(){ errordlg->setWindowTitle(tr("错误信息对话框")); errordlg->showMessage(tr("这里是出错信息!"));} 向导对话框在mywidget.h文件中 添加头文件 1#include<QWizard> 在MyWidget类中添加private类型函数声明 1234567private: Ui::MyWidget *ui; QErrorMessage *errordlg; QWizardPage *createPage1(); QWizardPage *createPage2(); QWizardPage *createPage3();}; 在mywidget.cpp中添加:123456789101112131415161718192021222324252627QWizardPage *MyWidget::createPage1(){ QWizardPage *page =new QWizardPage; page->setTitle(tr("介绍")); return page;}QWizardPage *MyWidget::createPage2(){ QWizardPage *page =new QWizardPage; page->setTitle(tr("用户选择信息")); return page;}QWizardPage *MyWidget::createPage3(){ QWizardPage *page =new QWizardPage; page->setTitle(tr("结束")); return page;}//右击设置槽函数void MyWidget::on_pushButton_8_clicked(){ QWizard wizard(this); wizard.setWindowTitle(tr("向导对话框")); wizard.addPage(createPage1()); wizard.addPage(createPage2()); wizard.addPage(createPage3()); wizard.exec();} 运行结果: 其他窗口部件QFrame类 QFrame类是带有边框的部件的基类。它的子类包括最常用的标签部件QLabel,另外还有QLCDNumber、QSplitter、QStackedWidget, QToolBox和QAbstractScrol-lArea类。QAbstractScrollArea类是所有带有滚动区域的部件类的抽象基类,这里需要说明,Qt中凡是带有Abstract 字样的类都是抽象基类。抽象基类是不能直接使用的,但是可以继承该类实现自己的类,或者使用它提供的子类。 QFrame边框形状和阴影 新建Qt Widgets应用,项目名称为mydialog5,继承QWidget.在Qt设计器中从部件列表里拖入一个Frame到界面上,然后在右下方的属性栏中更改其frameShape为Box, frameShadow为Sunken , lineWidth为5,midLineWidth为10。在属性栏中设置部件的属性,这和在源码中用代码实现是等效的,其实也可以直接在mywidget.cpp文件中的MyWidget构造函数里使用代码。 QLabel 标签QLabel部件用来显示文本或者图片。在设计器中向界面拖人一个Label,然后将其拖大点,并在属性栏中设置对其方式alignment的属性,水平的改为AlignH-Center ,垂直的改为AlignVCenter,这样QLabel中的文本就会在正中间显示。font 属性可以对字体进行设置,也可以通过代码进行设置,下面打开mywidget. cpp文件,在构造函数中添加如下代码: 1234567//设置字体样式 QFont font; font.setFamily("华文行楷"); font.setPointSize(20); font.setBold(true); font.setItalic(true); ui->label->setFont(font); QLabel属性栏中的wordWrap属性可以实现文本的自动换行。QFontMetrics类实现自动省略 继续在构造函数例添加代码:12345//实现文本自动换行 QString string = tr("标题太长,需要进行省略!"); //参数分别是:要省略的文本、省略的模式(就是省略号出现的位置)、文本的长度 QString str=ui->label->fontMetrics().elidedText(string,Qt::ElideRight,180); ui->label->setText(str); 显示图片:12345678910 #include<QPixmap> //标签中添加图片,设计器中scaledContents属性选中可以显示整个图片 ui->label->setPixmap(QPixmap("D:/QT/pig.jpg")); #include<QMovie> //显示GIF动态图片 QMovie *movie=new QMovie("D:/A/img/cat.gif"); ui->label->setMovie(movie); movie->start();} QLCDNumber QLCDNumber部件可以让数码字符显示类似液晶数字一样的效果。从部件栏中拖入一个LCD Number部件到界面上,然后更改其属性: 选中smallDecimalPoint项,这样可以显示小数点; digitCount的作用是设置显示的数字的个数,设置为7,表示要显示7个数字; mode选Dec表示显示十进制数值,这里还可以设置显示为十六进制(Hex)、八进制(Oct)和二进制( Bin)数值; segmentStyle用来设置数码的显示样式,这里提供了3种样式,选择Filled; value设置为456. 123,这就是要显示的数值; 也可以在代码中使用display()函数来设置要显示的数值。 在QLCDNumber中可以显示的数码有0/O.1.2.3.4.5/S.6.7.8.9/g、负号、小数点、A、B、C、D、E、F、h、H、L、o、P、r、u、U、Y、冒号、度符号(输人时使用单引号来代替)和空格。 QStackedWidget QStackedWidget类提供了- -个部件栈,可以有多个界面(称为页面),每个界面可以拥有自己的部件,不过每次只能显示一个界面。这个部件需要使用QComboBox或者QListWidget来选择它的各个页面。 在设计模式中向界面上拖入一个List Widget和一个StackedWidget。 在ListWidget.上右击,在弹出的级联菜单中选择“编辑项目”项,然后在“编辑列表窗口部件”对话框中按下左下角的加号添加两项,并更该名称为“第一页”和“第二页”。 然后在Stacked Widget 上拖人一个Label,更改文本为“第一页”,再单击Stacked Widget右上角的小箭头进入下一页,再拖入一个标签,更改文本为“第二页”。 然后再将Stacked Widget 部件的frameShape属性更改为StyledPanel。 最后,在信号和槽设计模式将listWidget 部件的currentRowChanged( )信号和stackedWidget的setCurrentIndex()槽关联。 设置完成后运行程序可以看到,现在可以单击listWidget中的项目来选择stackedWidget的页面了。 QToolBox QToolBox类提供了一.列层叠窗口部件,就像常用的聊天工具QQ中的抽屉效果。 从部件栏中选择ToolBox拖人到界面上,右击并在弹出的级联菜单中选择“插人页→在当前页之后”项来新插入一页。 然后更改其frameShape属性为Box,更改currentIndex即为第几个”抽屉“,更改其对应cucurrenttItemText分别为“好友”、“黑名单”、“陌生人”、“特别讨厌”、”特别关心“。 最后所有的运行结果如图所示: ) 按钮部件 QAbstractButton类是按钮部件的抽象基类,提供了按钮的通用功能。它的子类包括复选框QCheckBox、标准按钮QPushButton.单选框按钮QRadioButton和工具按钮QToolButton。 新建Qt Widgets应用,项目名称mybutton,基类选择QWidget,类名设为MyWidget。完成后在项目文件夹中新建images文件夹,并且放人几张图标图片,供下面编写程序时使用。 QPushButton QPushButton提供- - 个标准按钮。在项目中打开mywidget. ui文件,拖人3个PushButton到界面上.然后将它们的objectName依次更改为pushBtnl、pushBtn2和pushBtn3。下面选中pushBtnl的checkable属性,使得它可以拥有“选中”和“未选中”两种状态;再选中pushBtn2的flat 属性,可以不显示该按钮的边框。然后转到push-Btnl的toggled( bool)信号的槽.更改如下:123456#include<QDebug>void MyWidget::on_pushBtn1_toggled(bool checked)//按钮是否处于被按下状态{ qDebug()<<tr("按钮是否按下:")<<checked;//按下checked为true,否则为false} 在MyWidget的构造函数里添加代码: 12345678910include<QMenu> ui->pushBtn1->setText(tr("&nihao")); //Alt+n为加速键 ui->pushBtn2->setText(tr("帮助(&H)")); //给按钮添加图标 ui->pushBtn2->setIcon(QIcon("D:/QT/03/3-07/mybutton/images/loading7.png")); ui->pushBtn3->setText(tr("z&oom")); QMenu *menu =new QMenu(this); menu->addAction(QIcon("D:/QT/03/3-07/mybutton/images/selected.png"),tr("放大")); ui->pushBtn3->setMenu(menu); QcheckBox、QRadioButton和QGroupBox 对于调查表之类的应用,往往提供多个选项供选择,有些是可以选择多项的,有些只能选择其中-一项。复选框QCheckBox类提供了同时选择多项的功能,而QRadioButton提供了只能选择- -项的功能,一般要把-一组按钮放到一个QGroupBox中来管理。 在设计模式时可往界面上拖人两个Group Box,将它们的标题分别改为“复选框”和“单选框”。然后往复选框中拖人3个Check Box,分别更改显示内容为“跑步”、“踢球”和“游泳”。再往单选框中拖入3个Radio Button,分别更改其显示内容为“很好”、“一般”和“不好”。这里还可以选中CheckBox的tristate属性,让它拥有不改变状态、选中状态和未选中状态3种状态。对于选择按钮后的操作,可以关联它们的state-Changed(>信号和自定义的槽,也可以使用isChecked()函数查看一个按钮是否被选中。除了Group Box,还可以使用QButtonGroup类来管理多个按钮。 运行结果: QLineEdit 行编辑器QLineEdit部件是-一个单行的文本编辑器,它允许用户输入和编辑单行的纯文本内容,而且提供了一系列有用的功能,包括撤销与恢复、剪切和拖放等操作。 新建Qt Widgets应用,项目名称mylineedit,基类QWidget,类名MyWidget。在设计模式时可往界面上拖人几个标签和Line Edit,设计界面运行结果所示。然后将4个Line Edit从上到下依次更改其objectName为lineEdit1 JlineEdit2 . lineEdit3和lineEdit4。 显示模式 行编辑器QLineEdit有4种显示模式(echoMode),可以在echoMode属性中更改它们,分别是: Normal正常显示输人的信息;NoEcho不显示任何输人,这样可以保证不泄露输人的字符位数; Password显示为密码样式,就是以小黑点或星号之类的字符代替输人的字符; PasswordEchoOnEdit在编辑时显示正常字符,其他情况下显示为密码样式。 这里设置lineEdit1的echoMode为Password. 输入掩码 QLineEdit提供了输人掩码( inputMask)来限制输入的内容。可以使用一些特殊的字符来设置输入的格式和内容.这些字符中有的起限制作用且必须要输入一一个字符,有的只是起限制作用,但可以不输人字符而是以空格代替。先来看一下这些特殊字符的含义,如下表所列。 下面将lineEdit2 的inputMask属性设置为“> AA-90-bb-! aa# H; “,其含义为: “>”号表明后面输人的字母自动转为大写; “AA”表明开始必须输人两个字母,因为有前面的“>”号的作用,所以输人的这两个字母会自动变为大写; “一”号为分隔符,;直接显示,该位不可输入; “9”表示必须输人一个数字;“0”表示输入一个数字,或者留;空; “bb”表示这两位可以留空,或者输人两个二进制字符,即0或1; “!”表明停止大小写转换,就是在最开始的“>”号不再起作用; “aa”表示可以留空,或者输人两个字母; “#”表示将“#”号作为分隔符,因为“#”号在这里有特殊含义,所以前面要加上“\”号; “H”表明必须输入一个十六进制的字符; “; ”表示用 “ ”号来填充空格。 另外,也可以使用setInputMask()函数在代码中来设置输人掩码。 在lineEdit2上右击,然后转到它的returnPressed()回车键按下信号的槽中。更改代码如下: 123456void MyWidget::on_lineEdit2_returnPressed() //回车键按下信号的槽{ ui->lineEdit3->setFocus(); //让lineEdit3获得焦点 qDebug()<<ui->lineEdit2->text(); //输入lineEdit2的内容 qDebug()<<ui->lineEdit2->displayText(); //输出lineEdit2显示的内容} 输入验证 在QLineEdit中可以使用验证器(validator)来对输入进行约束在mywidget.cpp的构造函数里添加代码:1234//新建验证器,指定范围为100~999 QValidator *validator=new QIntValidator(100,999,this); //在行编辑器中使用验证器 ui->lineEdit3->setValidator(validator); 在代码中为lineEdit3添加了验证器,那么它现在只能输人100~999之间的数字。再进人lineEdit3的回车键按下信号的槽,输出lineEdit3 的内容。然后运行程序会发现,其他的字符无法输入,而输人小于100的数字时,按下回车键也是没有效果的。QValidator中还提供了QDoubleValidator,可以用它来设置浮点数。如果想设置更强大的字符约束.就要使用正则表达式了 自动补全 QLineEdit提供强大的自动补全功能通过QCompleter类实现在mywidget.cpp的构造函数里添加代码:12345678#include<QCompleter> QStringList wordList; //在行编辑器里输入“Q",自动出现"QT"和”QT Creator“两个选项 wordList<<"QT"<<"QT Creator"<<tr("你好"); QCompleter *completer=new QCompleter(wordList,this); //新建自动完成器 completer->setCaseSensitivity(Qt::CaseInsensitive); //设置大小写不敏感 ui->lineEdit4->setCompleter(completer); 运行结果: 12345Debugging starts"AB-12-01- ac#d""AB-12-01- ac#d""123""123" QAbstractSpinBox QAbstractSpinBox类是-一个抽象基类,提供了一个数值设定框和- 个行编辑器来显示设定值。它有3个子类QDate TimeEdit、QSpinBox和QDoubleSpinBox,分别用来完成日期时间、整数和浮点数的设定。 新建Qt Widgets 应用,项目名称myspinbox,基类为QWidget,类名MyWidget。 QDateTimeEdit QDateTimeEdit类提供了一个可以编辑日期和时间的部件。到设计模式,从部件栏中分别拖TimeEdit.DateEdit和Date/TimeEdit到界面上,然后设置timeEdit的;displayFormat为“h:mm:ssA” ,这就可以使用12 h制来进行显示。对于dateEdit, 选中它的calendarPopup属性,就可以使用弹出的日历部件来设置日期。然后在MyWid-get类的构造函数中添加代码:1234//设置时间为现在的系统时间 ui->dateEdit->setDateTime(QDateTime::currentDateTime()); //设置时间的显示格式 ui->dateTimeEdit->setDisplayFormat(tr("yyyy年MM月dd日dddHH时mm分ss秒")); 这里使用代码设置了dateTimeEdit中的日期和时间。简单说明一下:y表示年;M表示月;d表示日;而ddd表示星期;H表示小时,使用24 h制显示,而h也表示小时,如果最后有AM或者PM的,则是12 h制显示,否则使用24 h制;m表示分;s表示秒;还有一个z可以用来表示毫秒。更多的格式可以参考QDateTime类。现在运行程序查看效果。还要说明,可以使用该部件的text()函数获取设置的值,它返回QString类型的字符串;也可以使用dateTime()函数,它返回的是QDateTime类型数据。 QSpinBox 和QDoubleSpinBox QSpinBox用来设置整数, QDoubleSpinBox用来设置浮点数。从部件栏中找到Spin Box和Double Spin Box,并将它们拖入到界面上。可以在属性栏中看到spinBox的属性有:后缀suffix属性,可以设置为“%” ,这样就可以显示百分数了;前缀prefix属性,比如表示金钱时前面有“¥”字符;最小值minimum属性,设置其最小值;最大值maximum属性设置其最大值;单步值singleStep属性设置每次增加的数值,默认为l;value 为现在显示的数值。而doubleSpinBox又增加了一个小数位数decimals属性,用来设置小数点后面的位数。可以在代码中使用value()函数来获取设置的数值。 运行结果: QAbstractSlider QAbstractSlider类用于提供区间内的一个整数值,它有一个滑块,可以定位到一个整数区间的任意值。该类是一个抽象基类,它有3个子类QSerollBar、QSlider和QDial。其中,滚动条QScrollBar多数是用在QScrollArea类中来实现滚动区域;QSllider就是常见的音量控制或多媒体播放进度等滑块部件;QDial是-一个刻度表盘部件。 新建Qt Widgets应用,项目名称myslider,基类选择QWidget,类名为MyWidget。完成后到设计模式,从部件栏中分别将Dial、Horizontal Scroll Bar和Vertical Scroll Bar、 Horizontal Slider以及Vertical Slider 等部件拖人到界面上。 先看两个ScrollBar的属性: maximum属性用来设置最大值,minimum属性用来设置最小值; singleStep属性是每步的步长,默认是1,就是按下方向键后其数值增加或者减少1; pageStep是每页的步长,默认是10,就是按下PageUp或者PageDown按键后,其数值增加或者减少10; value与sliderPosition是当前值; tracking设置是否跟踪,.默认为是,就是在拖动滑块时,每移动一个刻度,都会发射valueChanged()信号,如果选择否,则只有拖动滑块释放时才发射该信号; orientation设置部件的方向,有水平和垂直两种选择; invertedAppearance属性设置滑块所在的位置,比如默认滑块开始在最左端,选中这个属性后,滑块默认就会在最右端。 invertedControls设置反向控制,比如默认是向上方向键是增大,向下方向键是减小,如果选中这个属性,那么控制就会正好反过来。 另外,为了使部件可以获得焦点,需要将focusPolicy设置为StrongFocus。 再来看两个Slider 它们有了自己的两个属性tickPosition和tickInterval,前者用来设置显示刻度的位置,默认是不显示刻度;后者是设置刻度的间隔。 而Dial有自己的属性wrapping,用来设置是否首尾相连,默认开始与结束是分开的; 属性notchTarget用来设置刻度之间的间隔; 属性notchesVisible用来设置是否显示刻度。 再往界面上拖人一个SpinBox,然后进人信号和槽编辑界面,将刻度表盘部件dial的sliderMoved(int)信号分别与其他各个部件的setValue(int)槽相连接。设置完成后运行程序,然后使用鼠标拖动刻度盘部件的滑块,可以看到其他所有的部件都跟着变化了。 运行结果:]]></content>
<categories>
<category>QT界面</category>
</categories>
<tags>
<tag>Qt Creator</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python_MySQL数据库]]></title>
<url>%2F2019%2F04%2F17%2FPython-MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%2F</url>
<content type="text"><![CDATA[MySQLMySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。 安装 安装教程查看链接Windows 上安装 MySQL MySQL常用命令以管理员身份打开cmd命令行工具,切换到你安装的mysql目录,这里是: 1D:\Mysql\mysql-8.0.15-winx64\bin 启动: 1net start mysql 登录 1mysql -h 主机名 -u 用户名 -p 如果是登录本机MySQL数据库,只需要输入以下命令:1mysql -u root -p 接下来会让你输入登录密码:输入即可,如图所示: 参数说明·: -h : 指定客户端所要登录的 MySQL 主机名(IP), 登录本机(localhost 或 127.0.0.1)该参数可以省略; -u : 登录的用户名; -p : 告诉服务器将会使用一个密码来登录, 如果所要登录的用户名密码为空, 可以忽略此选项。 退出和停止 123quit;//或者 exit...net stop mysql 修改密码 1mysqladmin -u用户名 -p旧密码 password 新密码 创建数据库 1create database <数据库名>; 显示数据库 1show databases; 注意:最后有个s 删除数据库 1drop database <数据库名> 如图所示: 更多常用命令访问:MySQL常用命令大全 mysql-connector使用 mysql-connector 来连接使用 MySQL, mysql-connector 是 MySQL 官方提供的驱动器。 安装我们可以使用 pip 命令来安装 mysql-connector:1python -m pip install mysql-connector 或者直接在pycharm的setting里面安装mysql-connect包 操作创建数据库连接123456789import mysql.connector mydb = mysql.connector.connect( host="localhost", # 数据库主机地址 user="username", # 数据库用户名 passwd="password" # 数据库密码) print(mydb) 创建数据库创建数据库使用 “CREATE DATABASE” 语句,我们创建一个名为 first_db 的数据库: 1234567891011import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456") mycursor = mydb.cursor() mycursor.execute("CREATE DATABASE first_db") 查看数据库输出所有数据库列表:1234567891011121314import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456") mycursor = mydb.cursor() mycursor.execute("SHOW DATABASES") for x in mycursor: print(x) 我们可以直接连接数据库,如果数据库不存在,会输出错误信息: 12345678import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456", database="runoob_db") 创建数据表1234567891011import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456", database="first_db" # 确保数据库first_db已存在)mycursor = mydb.cursor() mycursor.execute("CREATE TABLE sites (name VARCHAR(255), url VARCHAR(255))") 查看数据表是否已存在:1234567891011121314import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456", database="first_db")mycursor = mydb.cursor() mycursor.execute("SHOW TABLES") for x in mycursor: print(x) 主键设置创建表的时候我们一般都会设置一个主键(PRIMARY KEY),主键起始值为 1,逐步递增。 如果我们的表已经创建,我们需要使用 ALTER TABLE 来给表添加主键: 12345678910111213给 sites 表添加主键。import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456", database="first_db")mycursor = mydb.cursor() mycursor.execute("ALTER TABLE sites ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY") 如果你还未创建 sites 表,可以直接使用以下代码创建。 给表创建主键。123456789101112import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456", database="first_db")mycursor = mydb.cursor() mycursor.execute("CREATE TABLE sites (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), url VARCHAR(255))") 插入数据 向 sites 表插入一条记录。 1234567891011121314151617import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456", database="first_db")mycursor = mydb.cursor() sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"val = ("RUNOOB", "https://www.runoob.com")mycursor.execute(sql, val) mydb.commit() # 数据表内容有更新,必须使用到该语句 print(mycursor.rowcount, "记录插入成功。") 执行代码,输出结果为:11 记录插入成功 批量插入批量插入使用 executemany() 方法,该方法的第二个参数是一个元组列表,包含了我们要插入的数据: 向 sites 表插入多条记录。1234567891011121314151617181920212223import mysql.connector mydb = mysql.connector.connect( host="localhost", user="root", passwd="123456", database="first_db")mycursor = mydb.cursor() sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"val = [ ('Google', 'https://www.google.com'), ('Github', 'https://www.github.com'), ('Taobao', 'https://www.taobao.com'), ('stackoverflow', 'https://www.stackoverflow.com/')] mycursor.executemany(sql, val) mydb.commit() # 数据表内容有更新,必须使用到该语句 print(mycursor.rowcount, "记录插入成功。") 执行代码,输出结果为:124 记录插入成功。` 如果我们想在数据记录插入后,获取该记录的 ID ,可以使用以下代码:12345678 sql = "INSERT INTO sites (name, url) VALUES (%s, %s)"val = ("Zhihu", "https://www.zhihu.com")mycursor.execute(sql, val) mydb.commit() print("1 条记录已插入, ID:", mycursor.lastrowid) 执行代码,输出结果为:11 条记录已插入, ID: 6 查询数据 查询所有数据123456mycursor.execute("SELECT * FROM sites") myresult = mycursor.fetchall() # fetchall() 获取所有记录 for x in myresult: print(x) 执行代码,输出结果为:123456(1, 'RUNOOB', 'https://www.runoob.com')(2, 'Google', 'https://www.google.com')(3, 'Github', 'https://www.github.com')(4, 'Taobao', 'https://www.taobao.com')(5, 'stackoverflow', 'https://www.stackoverflow.com/')(6, 'Zhihu', 'https://www.zhihu.com') 也可以读取指定的字段数据:123456mycursor.execute("SELECT name, url FROM sites") myresult = mycursor.fetchall() for x in myresult: print(x) 执行代码,输出结果为:123456('RUNOOB', 'https://www.runoob.com')('Google', 'https://www.google.com')('Github', 'https://www.github.com')('Taobao', 'https://www.taobao.com')('stackoverflow', 'https://www.stackoverflow.com/')('Zhihu', 'https://www.zhihu.com') 如果我们只想读取一条数据,可以使用 fetchone() 方法:12345mycursor.execute("SELECT * FROM sites") myresult = mycursor.fetchone() print(myresult) 执行代码,输出结果为:1(1, 'RUNOOB', 'https://www.runoob.com') 如果我们要读取指定条件的数据,可以使用 where 语句:12345678sql = "SELECT * FROM sites WHERE name ='RUNOOB'" mycursor.execute(sql) myresult = mycursor.fetchall() for x in myresult: print(x) 执行代码,输出结果为:1(1, 'RUNOOB', 'https://www.runoob.com') 也可以使用通配符 %:12345678sql = "SELECT * FROM sites WHERE url LIKE '%oo%'" mycursor.execute(sql) myresult = mycursor.fetchall() for x in myresult: print(x) 执行代码,输出结果为:12(1, 'RUNOOB', 'https://www.runoob.com')(2, 'Google', 'https://www.google.com') 为了防止数据库查询发生 SQL 注入的攻击,我们可以使用 %s 占位符来转义查询的条件: 123456789sql = "SELECT * FROM sites WHERE name = %s"na = ("RUNOOB", ) mycursor.execute(sql, na) myresult = mycursor.fetchall() for x in myresult: print(x) 排序查询结果排序可以使用 ORDER BY 语句,默认的排序方式为升序,关键字为 ASC,如果要设置降序排序,可以设置关键字 DESC。 按 name 字段字母的升序排序:12345678sql = "SELECT * FROM sites ORDER BY name" mycursor.execute(sql) myresult = mycursor.fetchall() for x in myresult: print(x) 执行代码,输出结果为:123456(3, 'Github', 'https://www.github.com')(2, 'Google', 'https://www.google.com')(1, 'RUNOOB', 'https://www.runoob.com')(5, 'stackoverflow', 'https://www.stackoverflow.com/')(4, 'Taobao', 'https://www.taobao.com')(6, 'Zhihu', 'https://www.zhihu.com') 降序排序实例:12345678sql = "SELECT * FROM sites ORDER BY name DESC" mycursor.execute(sql) myresult = mycursor.fetchall() for x in myresult: print(x) 执行代码,输出结果为: 123456(6, 'Zhihu', 'https://www.zhihu.com')(4, 'Taobao', 'https://www.taobao.com')(5, 'stackoverflow', 'https://www.stackoverflow.com/')(1, 'RUNOOB', 'https://www.runoob.com')(2, 'Google', 'https://www.google.com')(3, 'Github', 'https://www.github.com') 限制查询的数据量指定起始位置,读取前 3 条记录. 例如,从第二条开始读取前 3 条记录:123456mycursor.execute("SELECT * FROM sites LIMIT 3 OFFSET 1") # 0 为 第一条,1 为第二条,以此类推 myresult = mycursor.fetchall() for x in myresult: print(x) 执行代码,输出结果为:123(2, 'Google', 'https://www.google.com')(3, 'Github', 'https://www.github.com')(4, 'Taobao', 'https://www.taobao.com') 删除记录1234567sql = "DELETE FROM sites WHERE name = 'stackoverflow'" mycursor.execute(sql) mydb.commit() print(mycursor.rowcount, " 条记录删除") 执行代码,输出结果为:11 条记录删除 注意:要慎重使用删除语句,删除语句要确保指定了 WHERE 条件语句,否则会导致整表数据被删除。 为了防止数据库查询发生 SQL 注入的攻击,我们可以使用 %s 占位符来转义删除语句的条件: 12sql = "DELETE FROM sites WHERE name = %s"na = ("stackoverflow", ) 更新表数据12345678sql = "UPDATE sites SET name = 'ZH' WHERE name = 'Zhihu'" mycursor.execute(sql) mydb.commit() print(mycursor.rowcount, " 条记录被修改")执行代码,输出结果为: 使用 %s 占位符来转义更新语句的条件:12sql = "UPDATE sites SET name = %s WHERE name = %s"val = ("Zhihu", "ZH") 删除表123sql = "DROP TABLE IF EXISTS sites" # 删除数据表 sites mycursor.execute(sql) PyMySQL 驱动PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,Python2中则使用mysqldb。 PyMySQL 遵循 Python 数据库 API v2.0 规范,并包含了 pure-Python MySQL 客户端库。 安装 命令: 1pip install PyMySQL pycharm 的setting里面安装pymysql包 操作在操作之前确保:1.您已经创建了数据库 TESTDB.2.在TESTDB数据库中您已经创建了表 EMPLOYEE3.EMPLOYEE表字段为 FIRST_NAME, LAST_NAME, AGE, SEX 和 INCOME。 数据库连接123456789101112131415161718import pymysql # 打开数据库连接db = pymysql.connect("localhost","testuser","test123","TESTDB" ) # 使用 cursor() 方法创建一个游标对象 cursorcursor = db.cursor() # 使用 execute() 方法执行 SQL 查询 cursor.execute("SELECT VERSION()") # 使用 fetchone() 方法获取单条数据.data = cursor.fetchone() print ("Database version : %s " % data) # 关闭数据库连接db.close() 创建数据库表1234567891011121314151617181920212223import pymysql # 打开数据库连接db = pymysql.connect("localhost","testuser","test123","TESTDB" ) # 使用 cursor() 方法创建一个游标对象 cursorcursor = db.cursor() # 使用 execute() 方法执行 SQL,如果表存在则删除cursor.execute("DROP TABLE IF EXISTS EMPLOYEE") # 使用预处理语句创建表sql = """CREATE TABLE EMPLOYEE ( FIRST_NAME CHAR(20) NOT NULL, LAST_NAME CHAR(20), AGE INT, SEX CHAR(1), INCOME FLOAT )""" cursor.execute(sql) # 关闭数据库连接db.close() 插入操作1234567891011121314151617# 部分代码省略# SQL 插入语句sql = """INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME) VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""try: # 执行sql语句 cursor.execute(sql) # 提交到数据库执行 db.commit()except: # 如果发生错误则回滚 db.rollback() # 关闭数据库连接db.close() 也可以写成如下形式:12345# SQL 插入语句sql = "INSERT INTO EMPLOYEE(FIRST_NAME, \ LAST_NAME, AGE, SEX, INCOME) \ VALUES ('%s', '%s', %s, '%s', %s)" % \ ('Mac', 'Mohan', 20, 'M', 2000) 查询操作Python查询Mysql使用 fetchone() 方法获取单条数据, 使用fetchall() 方法获取多条数据。 fetchone(): 该方法获取下一个查询结果集。结果集是一个对象 fetchall(): 接收全部的返回结果行. rowcount: 这是一个只读属性,并返回执行execute()方法后影响的行数。 查询EMPLOYEE表中salary(工资)字段大于1000的所有数据: 12345678910111213141516171819202122# SQL 查询语句sql = "SELECT * FROM EMPLOYEE \ WHERE INCOME > %s" % (1000)try: # 执行SQL语句 cursor.execute(sql) # 获取所有记录列表 results = cursor.fetchall() for row in results: fname = row[0] lname = row[1] age = row[2] sex = row[3] income = row[4] # 打印结果 print ("fname=%s,lname=%s,age=%s,sex=%s,income=%s" % \ (fname, lname, age, sex, income ))except: print ("Error: unable to fetch data") # 关闭数据库连接db.close() 更新操作将 TESTDB 表中 SEX 为 ‘M’ 的 AGE 字段递增 1: 1sql = "UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = '%c'" % ('M') 删除操作删除数据表 EMPLOYEE 中 AGE 大于 20 的所有数据:12# SQL 删除语句sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)]]></content>
<categories>
<category>MYSQL数据库</category>
</categories>
<tags>
<tag>MySQL</tag>
<tag>python</tag>
<tag>PyMySQL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[学习Django框架]]></title>
<url>%2F2019%2F04%2F11%2F%E5%AD%A6%E4%B9%A0Django%E6%A1%86%E6%9E%B6%2F</url>
<content type="text"><![CDATA[介绍Django是一个开放源代码的Web应用框架,由Python写成。 安装 pycharm社区版创建django项目:pycharm自带django数据包 项目pycharm创建第一个django请求步骤 在你要创建项目的目录下执行cmd命令1django-admin.py startproject 文件名 执行之后可以看到项目的目录结构1234567hellodjango|-- hellodjango| |-- __init__.py| |-- settings.py| |-- urls.py| `-- wsgi.py`-- manage.py 运行:我们进入 hellodjango 目录(pycharm下,点击 Terminal),输入以下命令,启动服务器: 1python3 manage.py runserver 127.0.0.1:8000 打开显示的网址(),可以看到第一个django请求运行成功. ####### 目录说明: HelloWorld: 项目的容器。 manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。 HelloWorld/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。 HelloWorld/settings.py: 该 Django 项目的设置/配置。 HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站”目录”。 HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。配置在settings.py文件中修改: 模板路径配置(html) 123456789TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR,'templates')], # os.path.join(BASE_DIR)表示基目录,你的project目录 # 'templates'表示模板路径 ... },] 静态文件配置(图片,css文件, js文件) 1234STATIC_URL = '/static/' # 使用时前缀STATICFILES_DIRS=( os.path.join(BASE_DIR,'static'), # 真实路径,干脆直接写成'static') 注意:逗号不能少!!! 额外配置(csrf注释) 123456789MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',] 路由关系(url对应关系)url—>函数 注释自带的url(path),按照自带格式创建你的页面12# path('admin/', admin.site.urls), url(r'^login/',login), 处理请求函数先看整体代码: 123456789101112131415161718192021222324from django.shortcuts import HttpResponse,render,redirectdef login(request): # return HttpResponse('hahahhahahah') # 自动找到模板路径下的html文件,读取类容并返回给用户 print(request.GET) if request.method=="GET": # 模板渲染 return render(request,'login.html') else: # 用户post提交的数据(请求体) u = request.POST.get('username') p = request.POST.get('password') if u=='root'and p=='123123': # 登陆成功,跳转到其他页面 return redirect('https://www.youdao.com/') else: # 登陆失败 return render(request,'login.html',{'msg':'用户名或密码错误'})urlpatterns = [ # path('admin/', admin.site.urls), url(r'^login/',login),] 步骤说明: 导入包 1from django.shortcuts import HttpResponse,render,redirect 定义函数 123def indox(request): 函数体 request的两种方法(request.method): request.GET , 从请求头中的url拿到值, GET请求只有request.GET有值 request.POST ,从请求头中拿到值, POST请求request.GET和request.POST都可能有值 request表示用户请求相关的所有信息(对象) 三种返回方式: return HttpResponse(”字符串”),只能在页面上显示支付串 return render(函数参数(request),’模板路径’(login.html),{}), 自带找到模板路径下的login.html,读取内容并返回给用户,模板路径是templates以后的路径。 return redirect(‘URL’),跳转到要跳转的网址(可以是其他网站,也可以是自己的网址) 说明:在前面我们已经配置过模板路径和静态文件路径,所以可以直接运用自己web网页设计知识对你的页面进行设计、美化和动态交互,记得在你html文件中要连接css、js或图片文件时,前面添加路径(/static/),例如:1<link rel="stylesheet" href="/static/commons.css"/> 模板标签过滤器urls.py123456789def index(request):return render(request,'模板路径‘),{ 'k1':'v1', 'k2':[1,2,32,34],#列表 'k3':{'k1':'v1','k2':'v2',...},# 字典 'user_list_dict':[{}] ,#列表[字典]}) index.html123<h1>{{k1}}</h1><h1>{{k2.2}}</h1><h1>{{k3.}}</h1> 模板过滤器可以在变量被显示前修改它,过滤器使用管道字符,如下所示: 1{{ name|lower }} name 变量被过滤器 lower 处理后,文档大写转换文本为小写。 过滤管道可以被 套接 ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入:1{{ my_list|first|upper }} 以上实例将第一个元素并将其转化为大写。 有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:1{{ bio|truncatewords:"30" }} 这个将显示变量 bio 的前30个词。 其他过滤器: addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。 date : 按指定的格式字符串参数格式化 date 或者 datetime 对象,实例: 1{{ pub_date|date:"F j, Y" }} length : 返回变量的长度。 说明:django标签通过 ‘.’ 索引 if/else 标签基本语法格式如下: 123{% if condition %} ... display{% endif %} 或者:1234567{% if condition1 %} ... display 1{% elif condition2 %} ... display 2{% else %} ... display 3{% endif %} 根据条件判断是否输出。if/else 支持嵌套。 说明: if 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not )。 循环123456<h3>循环</h3><ul>{% for item in users%} <li>{{item}}</li>{% endfor %}</ul> 说明: 可以与html搭配使用 ifequal/ifnotequal 标签 ifequal 标签比较两个值,当他们相等时,显示在 ifequal 和 endifequal 之中所有的值。 下面的例子比较两个模板变量 user 和 currentuser : 123{% ifequal user currentuser %} <h1>Welcome!</h1>{% endifequal %} 和 if 类似, ifequal 支持可选的 else标签: 12345{% ifequal section 'sitenews' %} <h1>Site News</h1>{% else %} <h1>No News Here</h1>{% endifequal %} 注释标签Django 注释使用 。 1{# 这是一个注释 #} include 标签 include 标签允许在模板中包含其它的模板的内容。 下面这个例子都包含了 nav.html 模板:1{% include "nav.html" %} 模板继承模板可以用继承的方式来实现复用。 接下来我们先创建之前项目的 templates 目录中添加 father.html 文件,代码如下: mysites/templates/father.html 文件代码:1234567891011121314<!DOCTYPE html><html><head><meta charset="utf-8"><title>模板继承</title></head><body> <h1>Hello World!</h1> <p>Django 测试。</p> {% block mainbody %} <p>original</p> {% endblock %}</body></html> 以上代码中,名为 mainbody 的 block 标签是可以被继承者们替换掉的部分。 所有的 block 标签告诉模板引擎,子模板可以重载这些部分。 son.html 中继承 father.html,并替换特定 block,son.html 修改后的代码如下: mysites/templates/son.html 文件代码: 12345{%extends "father.html" %} {% block mainbody %}<p>继承了 base.html 文件</p>{% endblock %} 第一行代码说明 son.html 继承了 father.html 文件。可以看到,这里相同名字的 block 标签用以替换 father.html 的相应 block。 django模型Django 对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。 Django 为这些数据库提供了统一的调用API。 安装mysqlclient直接在pycharm的setting里面导入mysqlclient包,如果提示安装错误可以查看下面的链接:mysqlclient安装出错 数据库配置我们在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:12345678910DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 或者使用 mysql.connector.django 'NAME': 'test_db',# 数据库名称 'USER': 'root',# 用户 'PASSWORD': 'an1234',# 密码 'HOST':'localhost',# ip 'PORT':'3306',# 端口 }} 注意:这里添加了中文注释,所以你需要在 HelloWorld/settings.py 文件头部添加 # -- coding: UTF-8 --。 上面包含数据库名称和用户的信息,它们与 MySQL 中对应数据库和用户的设置相同。Django 根据这一设置,与 MySQL 中相应的数据库和用户连接起来。 定义模型 创建 APPDjango规定,如果要使用模型,必须要创建一个app。我们使用以下命令创建一个 TestModel 的 app: 1django-admin startapp TestModel 目录结构如下:1234567Hellodjango|-- TestModel| |-- __init__.py| |-- admin.py| |-- models.py| |-- tests.py| `-- views.py 修改 TestModel/models.py 文件: 1234from django.db import modelsclass Test(models.Model): name = models.CharField(max_length=20) 接下来在settings.py中找到INSTALLED_APPS这一项,如下: 123456789INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'TestModel', # 添加此项) 在命令行中运行: 12345$ python manage.py migrate # 创建表结构$ python manage.py makemigrations TestModel # 让 Django 知道我们在我们的模型有一些变更$ python manage.py migrate TestModel # 创建表结构 看到几行 “Creating table…” 的字样,你的数据表就创建好了。1234Creating tables ...……Creating table TestModel_test #我们自定义的表…… 表名组成结构为:应用名_类名(如:TestModel_test)。 注意:尽管我们没有在models给表设置主键,但是Django会自动添加一个id作为主键。 接下来你就可以对数据库进行操作了 相关链接 python3 django配置数据库(mysql) Django中数据库配置 Django 配置MySQL数据库 [Django] Django(五) 配置mysql数据库 django-数据库【配置】]]></content>
<categories>
<category>框架</category>
</categories>
<tags>
<tag>python</tag>
<tag>django</tag>
<tag>mysql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[python巩固]]></title>
<url>%2F2019%2F03%2F30%2Fpython%E5%B7%A9%E5%9B%BA%2F</url>
<content type="text"><![CDATA[巩固python基础 ps: 该篇博客学习自菜鸟教程 中文编码Python中默认的编码格式是 ASCII 格式,在没修改编码格式时无法正确打印汉字,所以在读取中文时会报错。解决方法为只要在文件开头加入 # -*- coding: UTF-8 -*- 或者 #coding=utf-8 就行了 注意:**#coding=utf-8 的 =** 号两边不要空格。 标识符 以下划线开头的标识符是有特殊意义的。以单下划线开头 _foo 的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用 from xxx import * 而导入。 以双下划线开头的 __foo 代表类的私有成员,以双下划线开头和结尾的 foo 代表 Python 里特殊方法专用的标识,如 init() 代表类的构造函数。 行和缩进学习 Python 与其他语言最大的区别就是,Python 的代码块不使用大括号 {} 来控制类,函数以及其他逻辑判断。python 最具特色的就是用缩进来写模块。缩进的空白数量是可变的,但是所有代码块语句必须包含相同的缩进空白数量,这个必须严格执行。 多行语句Python语句中一般以新行作为语句的结束符。 但是我们可以使用斜杠( \)将一行的语句分为多行显示,如下所示:123total = item_one + \ item_two + \ item_three 语句中包含 [], {} 或 () 括号就不需要使用多行连接符。 Print 输出print 默认输出是换行的,如果要实现不换行需要在变量末尾加上逗号 , 12345678910111213# -*- coding: UTF-8 -*-x="a"y="b"# 换行输出print xprint yprint '---------'# 不换行输出print x,print y,# 不换行输出print x,y 以上实例执行结果为: a ba b a b 标准数据类型Python有五个标准的数据类型: Numbers(数字) String(字符串) List(列表) Tuple(元组) Dictionary(字典) 数字 长整型也可以使用小写 l,但是还是建议您使用大写 L,避免与数字 1 混淆。Python使用 L 来显示长整型。 Python 还支持复数,复数由实数部分和虚数部分构成,可以用 a + bj,或者 complex(a,b) 表示, 复数的实部 a 和虚部 b 都是浮点型。注意:long 类型只存在于 Python2.X 版本中,在 2.2 以后的版本中,int 类型数据溢出后会自动转为long类型。在 Python3.X 版本中 long 类型被移除,使用 int 替代。 字符串python的字串列表有2种取值顺序: 从左到右索引默认0开始的,最大范围是字符串长度少1 从右到左索引默认-1开始的,最大范围是字符串开头实例12345678910# -*- coding: UTF-8 -*- str = 'Hello World!' print str # 输出完整字符串print str[0] # 输出字符串中的第一个字符print str[2:5] # 输出字符串中第三个至第五个之间的字符串print str[2:] # 输出从第三个字符开始的字符串print str * 2 # 输出字符串两次print str + "TEST" # 输出连接的字符串 以上实例输出结果: Hello World!Hllollo World!Hello World!Hello World!Hello World!TEST Python 列表截取可以接收第三个参数,参数作用是截取的步长,以下实例在索引 1 到索引 4 的位置并设置为步长为 2(间隔一个位置)来截取字符串: 列表List(列表) 是 Python 中使用最频繁的数据类型。列表可以完成大多数集合类的数据结构实现。它支持字符,数字,字符串甚至可以包含列表(即嵌套)。列表用 [ ] 标识,是 python 最通用的复合数据类型。 列表方法 序号 方法 1 list.append(obj) 在列表末尾添加新的对象 2 list.count(obj) 统计某个元素在列表中出现的次数 3 list.extend(seq) 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表) 4 list.index(obj) 从列表中找出某个值第一个匹配项的索引位置 5 list.insert(index, obj) 将对象插入列表 6 list.pop([index=-1]) 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值 7 list.remove(obj) 移除列表中某个值的第一个匹配项 8 list.reverse() 反向列表中元素 9 list.sort(cmp=None, key=None, reverse=False) 对原列表进行排序 实例1234567# -*- coding: UTF-8 -*- # 列表vowels = ['e', 'a', 'u', 'o', 'i']# 降序vowels.sort(reverse=True)# 输出结果print '降序输出:', vowel 以上实例输出结果如下: 降序输出: [‘u’, ‘o’, ‘i’, ‘e’, ‘a’] 元组元组是另一个数据类型,类似于 List(列表)。元组用 , 标识。内部元素用逗号隔开。但是元组不能二次赋值,相当于只读列表。 创建空元组 tup1 = () 元组中只包含一个元素时,需要在元素后面添加逗号 tup1 = (50,) 实例1234567891011# -*- coding: UTF-8 -*-tuple = ( 'runoob', 786 , 2.23, 'john', 70.2 )tinytuple = (123, 'john') print tuple # 输出完整元组print tuple[0] # 输出元组的第一个元素print tuple[1:3] # 输出第二个至第三个的元素 print tuple[2:] # 输出从第三个开始至列表末尾的所有元素print tinytuple * 2 # 输出元组两次print tuple + tinytuple # 打印组合的元组 以上实例输出结果: (‘runoob’, 786, 2.23, ‘john’, 70.2)runoob(786, 2.23)(2.23, ‘john’, 70.2)(123, ‘john’, 123, ‘john’)(‘runoob’, 786, 2.23, ‘john’, 70.2, 123, ‘john’) 字典字典(dictionary)是除列表以外python之中最灵活的内置数据结构类型。列表是有序的对象集合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。字典用”{ }”标识。字典由索引(key)和它对应的值value组成。 删除字典元素####### 实例123456# -*- coding: UTF-8 -*-dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'} del dict['Name'] # 删除键是'Name'的条目dict.clear() # 清空词典所有条目del dict # 删除词典 内置方法: 序号 函数及描述 1 dict.clear() 删除字典内所有元素 2 dict.copy() 返回一个字典的浅复制 3 dict.fromkeys(seq[, val]) 创建一个新字典,以序列 seq 中元素做字典的键,val 为字典所有键对应的初始值 4 dict.get(key, default=None) 返回指定键的值,如果值不在字典中返回default值 5 dict.has_key(key) 如果键在字典dict里返回true,否则返回false 6 dict.items() 以列表返回可遍历的(键, 值) 元组数组 7 dict.keys() 以列表返回一个字典所有的键 8 dict.setdefault(key, default=None) 和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default 9 dict.update(dict2) 把字典dict2的键/值对更新添加到dict里 10 dict.values() 以列表返回字典中的所有值 11 pop(key[,default]) 删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。 12 popitem() 随机返回并删除字典中的一对键和值。 ####### 实例以下实例展示了 fromkeys()函数的使用方法: 123456789# -*- coding: UTF-8 -*- seq = ('Google', 'Runoob', 'Taobao') dict = dict.fromkeys(seq)print "新字典为 : %s" % str(dict) dict = dict.fromkeys(seq, 10)print "新字典为 : %s" % str(dict) 以上实例输出结果为: 新字典为 : {‘Google’: None, ‘Taobao’: None, ‘Runoob’: None}新字典为 : {‘Google’: 10, ‘Taobao’: 10, ‘Runoob’: 10} 实例123456789101112# -*- coding: UTF-8 -*- dict = {}dict['one'] = "This is one"dict[2] = "This is two"tinydict = {'name': 'john','code':6734, 'dept': 'sales'} print dict['one'] # 输出键为'one' 的值print dict[2] # 输出键为 2 的值print tinydict # 输出完整的字典print tinydict.keys() # 输出所有键print tinydict.values() # 输出所有值 输出结果为: This is oneThis is two{‘dept’: ‘sales’, ‘code’: 6734, ‘name’: ‘john’}[‘dept’, ‘code’, ‘name’][‘sales’, 6734, ‘john’] 运算符算数运算符 ** 幂 - 返回x的y次幂 a**b 为10的20次方, 输出结果 100000000000000000000 // 取整除 - 返回商的整数部分(向下取整) >>> 9//2 4 >>> -9//2 -5 位运算符 运算符 描述 实例 & 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0 (a & b) 输出结果 12 ,二进制解释: 0000 1100 | 按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1。 (a | b) 输出结果 61 ,二进制解释: 0011 1101 ^ 按位异或运算符:当两对应的二进位相异时,结果为1 (a ^ b) 输出结果 49 ,二进制解释: 0011 0001 ~ 按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1 。~x 类似于 -x-1 (~a ) 输出结果 -61 ,二进制解释: 1100 0011,在一个有符号二进制数的补码形式。 << 左移动运算符:运算数的各二进位全部左移若干位,由 << 右边的数字指定了移动的位数,高位丢弃,低位补0。 a << 2 输出结果 240 ,二进制解释: 1111 0000 >> 右移动运算符:把”>>”左边的运算数的各二进位全部右移若干位,>> 右边的数字指定了移动的位数 a >> 2 输出结果 15 ,二进制解释: 0000 1111 逻辑运算符Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20: 运算符 逻辑表达式 描述 实例 and x and y 布尔”与” - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 (a and b) 返回 20。 or x or y 布尔”或” - 如果 x 是非 0,它返回 x 的值,否则它返回 y 的计算值。 (a or b) 返回 10。 not not x 布尔”非” - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 not(a and b) 返回 False 成员运算符除了以上的一些运算符之外,Python还支持成员运算符,测试实例中包含了一系列的成员,包括字符串,列表或元组。 运算符 描述 实例 in 如果在指定的序列中找到值返回 True,否则返回 False。 x 在 y 序列中 , 如果 x 在 y 序列中返回 True。 not in 如果在指定的序列中没有找到值返回 True,否则返回 False。 x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。 身份运算符身份运算符用于比较两个对象的存储单元 运算符 描述 实例 is is 是判断两个标识符是不是引用自一个对象 x is y, 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 False is not is not 是判断两个标识符是不是引用自不同对象 x is not y , 类似 id(a) != id(b)。如果引用的不是同一个对象则返回结果 True,否则返回 False。 is 与 == 区别:is 用于判断两个变量引用对象是否为同一个, == 用于判断引用变量的值是否相等。 字符串运算符 运算符 描述 r/R 原始字符串 - 原始字符串:所有的字符串都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符。 原始字符串除在字符串的第一个引号前加上字母”r”(可以大小写)以外,与普通字符串有着几乎完全相同的语法。 >>>print r’\n’ \n >>> print R’\n’ \n 运算符优先级以下表格列出了从最高到最低优先级的所有运算符: 运算符 描述 ** 指数 (最高优先级) ~ + - 按位翻转, 一元加号和减号 (最后两个的方法名为 +@ 和 -@) * / % // 乘,除,取模和取整除 + - 加法减法 >> << 右移,左移运算符 & 位 ‘AND’ ^ \ | 位运算符 <= < > >= 比较运算符 <> == != 等于运算符 = %= /= //= -= += *= **= 赋值运算符 is is not 身份运算符 in not in 成员运算符 not and or 逻辑运算符 语句 python 并不支持 switch 语句,所以多个条件判断,只能用 elif 来实现,如果判断需要多个条件需同时判断时,可以使用 or (或),表示两个条件有一个成立时判断条件成功;使用 and (与)时,表示只有两个条件同时成立的情况下,判断条件才成功。 pass 语句Python pass 是空语句,是为了保持程序结构的完整性。pass 不做任何事情,一般用做占位语句。 实例:12345678910# -*- coding: UTF-8 -*- # 输出 Python 的每个字母for letter in 'Python': if letter == 'h': pass print '这是 pass 块' print '当前字母 :', letter print "Good bye!" 以上实例执行结果: 当前字母 : P当前字母 : y当前字母 : t这是 pass 块当前字母 : h当前字母 : o当前字母 : nGood bye! 数学函数math 模块、cmath 模块Python 中数学运算常用的函数基本都在 math 模块、cmath 模块中。 Python math 模块提供了许多对浮点数的数学运算函数。 Python cmath 模块包含了一些用于复数运算的函数。 cmath 模块的函数跟 math 模块函数基本一致,区别是 cmath 模块运算的是复数,math 模块运算的是数学运算。 查看 math 查看包中的内容:1234>>> import math>>> dir(math)['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']>>> 查看 cmath 查看包中的内容1234>>> import cmath>>> dir(cmath)['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cos', 'cosh', 'e', 'exp', 'inf', 'infj', 'isclose', 'isfinite', 'isinf', 'isnan', 'log', 'log10', 'nan', 'nanj', 'phase', 'pi', 'polar', 'rect', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau']>>> Python随机数函数随机数可以用于数学,游戏,安全等领域中,还经常被嵌入到算法中,用以提高算法效率,并提高程序的安全性。 Python包含以下常用随机数函数: 函数 描述 choice(seq) 从序列的元素中随机挑选一个元素,比如random.choice(range(10)),从0到9中随机挑选一个整数。 randrange ([start,] stop [,step]) 从指定范围内,按指定基数递增的集合中获取一个随机数,基数缺省值为1 random() 随机生成下一个实数,它在[0,1)范围内。 seed([x]) 改变随机数生成器的种子seed。如果你不了解其原理,你不必特别去设定seed,Python会帮你选择seed。 shuffle(lst) 将序列的所有元素随机排序 uniform(x, y) 随机生成下一个实数,它在[x,y]范围内。 Python三角函数Python包括以下三角函数: 函数 描述 acos(x) 返回x的反余弦弧度值。 asin(x) 返回x的反正弦弧度值。 atan(x) 返回x的反正切弧度值。 atan2(y, x) 返回给定的 X 及 Y 坐标值的反正切值。 cos(x) 返回x的弧度的余弦值。 hypot(x, y) 返回欧几里德范数 sqrt(xx + yy)。 sin(x) 返回的x弧度的正弦值。 tan(x) 返回x弧度的正切值。 degrees(x) 将弧度转换为角度,如degrees(math.pi/2) , 返回90.0 radians(x) 将角度转换为弧度 Python数学常量 常量 描述 pi 数学常量 pi(圆周率,一般以π来表示) e 数学常量 e,e即自然常数(自然常数)。 时间time时间间隔是以秒为单位的浮点小数。 每个时间戳都以自从1970年1月1日午夜(历元)经过了多长时间来表示。 Python 的 time 模块下有很多函数可以转换常见日期格式。如函数time.time()用于获取当前时间戳, Time 模块Time 模块包含了以下内置函数,既有时间处理的,也有转换时间格式的.详情访问:Python 日期和时间 实例以下实例展示了 time() 函数的使用方法:123456#!/usr/bin/pythonimport timeprint "time.time(): %f " % time.time()print time.localtime( time.time() )print time.asctime( time.localtime(time.time()) ) 运行结果: 1553931680.6086578time.struct_time(tm_year=2019, tm_mon=3, tm_mday=30, tm_hour=15, tm_min=41, tm_sec=57, tm_wday=5, tm_yday=89, tm_isdst=0)Sat Mar 30 15:43:23 2019 日历(Calendar)模块此模块的函数都是日历相关的,例如打印某月的字符月历。 星期一是默认的每周第一天,星期天是默认的最后一天。 实例获取某月日历Calendar模块有很广泛的方法用来处理年历和月历,例如打印某月的月历: 1234567# -*- coding: UTF-8 -*- import calendar cal = calendar.month(2019, 3)print "以下输出2019年3月份的日历:"print cal 运行结果: March 2019 Mo Tu We Th Fr Sa Su 1 2 3 4 5 6 7 8 9 1011 12 13 14 15 16 1718 19 20 21 22 23 2425 26 27 28 29 30 31 函数定义一个函数你可以定义一个由自己想要功能的函数,以下是简单的规则: 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。 函数内容以冒号起始,并且缩进。 return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。 可更改(mutable)与不可更改(immutable)对象在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。 不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。 可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。 python 函数的参数传递: 不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。 可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响 实例1234567# -*- coding: UTF-8 -*-def ChangeInt( a ): a = 10 b = 2ChangeInt(b)print b # 结果是 2 参数以下是调用函数时可使用的正式参数类型: 必备参数 关键字参数 默认参数 不定长参数 不定长参数你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,声明时不会命名。基本语法如下:1234def functionname([formal_args,] *var_args_tuple ): "函数_文档字符串" function_suite return [expression] 加了星号(*)的变量名会存放所有未命名的变量参数。不定长参数实例如下: ####### 实例1234567891011121314# -*- coding: UTF-8 -*- # 可写函数说明def printinfo( arg1, *vartuple ): "打印任何传入的参数" print "输出: " print arg1 for var in vartuple: print var return; # 调用printinfo 函数printinfo( 10 );printinfo( 70, 60, 50 ); 以上实例输出结果: 输出:10输出:706050 匿名函数python 使用 lambda 来创建匿名函数。 lambda只是一个表达式,函数体比def简单很多。 lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。 lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。语法lambda函数的语法只包含一个语句,如下: lambda [arg1 [,arg2,…..argn]]:expression 实例12345678# -*- coding: UTF-8 -*- # 可写函数说明sum = lambda arg1, arg2: arg1 + arg2; # 调用sum函数print "相加后的值为 : ", sum( 10, 20 )print "相加后的值为 : ", sum( 20, 20 ) 以上实例输出结果: 相加后的值为 : 30相加后的值为 : 40 Python中的包包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。 简单来说,包就是文件夹,但该文件夹下必须存在 init.py 文件, 该文件的内容可以为空。init.py 用于标识当前文件夹是一个包。例如:考虑一个在 package_runoob 目录下的 runoob1.py、runoob2.py、init.py 文件,test.py 为测试调用包的代码,目录结构如下:12345678910111213test.pypackage_runoob|-- __init__.py|-- runoob1.py|-- runoob2.py 源代码如下:package_runoob/runoob1.py```python# -*- coding: UTF-8 -*-def runoob1(): print "I'm in runoob1" package_runoob/runoob2.py 1234# -*- coding: UTF-8 -*-def runoob2(): print "I'm in runoob2" 现在,在 package_runoob 目录下创建 init.py: package_runoob/init.py1234567# -*- coding: UTF-8 -*-if __name__ == '__main__': print '作为主程序运行'else: print 'package_runoob 初始化' 然后我们在 package_runoob 同级目录下创建 test.py 来调用 package_runoob 包 test.py12345678# -*- coding: UTF-8 -*-# 导入 Phone 包from package_runoob.runoob1 import runoob1from package_runoob.runoob2 import runoob2runoob1()runoob2() 以上实例输出结果: package_runoob 初始化I’m in runoob1I’m in runoob2 文件读取键盘输入Python提供了两个内置函数从标准输入读入一行文本,默认的标准输入是键盘。如下: raw_input inputraw_input函数raw_input([prompt]) 函数从标准输入读取一个行,并返回一个字符串(去掉结尾的换行符): ####### 实例1234# -*- coding: UTF-8 -*- str = raw_input("请输入:")print "你输入的内容是: ", str 这会产生如下的对应着输入的结果: 请输入:Hello Python!你输入的内容是: Hello Python! input函数input([prompt]) 函数和 raw_input([prompt]) 函数基本类似,但是 input 可以接收一个Python表达式作为输入,并将运算结果返回。 ####### 实例1234# -*- coding: UTF-8 -*- str = input("请输入:")print "你输入的内容是: ", str 这会产生如下的对应着输入的结果: 请输入:[x*5 for x in range(2,10,2)]你输入的内容是: [10, 20, 30, 40] 打开和关闭文件Python 提供了必要的函数和方法进行默认情况下的文件基本操作。你可以用 file 对象做大部分的文件操作。 open 函数你必须先用Python内置的open()函数打开一个文件,创建一个file对象,相关的方法才可以调用它进行读写。 语法: 1file object = open(file_name [, access_mode][, buffering]) 各个参数的细节如下: file_name:file_name变量是一个包含了你要访问的文件名称的字符串值。 access_mode:access_mode决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)。 buffering:如果buffering的值被设为0,就不会有寄存。如果buffering的值取1,访问文件时会寄存行。如果将buffering的值设为大于1的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。 注意:使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。 open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。 1open(file, mode='r') 完整的语法格式为: 1open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None) 参数说明: file: 必需,文件路径(相对或者绝对路径)。 mode: 可选,文件打开模式 buffering: 设置缓冲 encoding: 一般使用utf8 errors: 报错级别 newline: 区分换行符 closefd: 传入的file参数类型 opener: 不同模式打开文件的完全列表: 模式 描述 t 文本模式 (默认)。 x 写模式,新建一个文件,如果该文件已存在则会报错。 b 二进制模式。 + 打开一个文件进行更新(可读可写)。 U 通用换行模式(不推荐)。 r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 r+ 打开一个文件用于读写。文件指针将会放在文件的开头。 rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 File对象的属性一个文件被打开后,你有一个file对象,你可以得到有关该文件的各种信息。 以下是和file对象相关的所有属性的列表: 属性 描述 file.closed 返回true如果文件已被关闭,否则返回false。 file.mode 返回被打开文件的访问模式。 file.name 返回文件的名称。 file.softspace 如果用print输出后,必须跟一个空格符,则返回false。否则返回true。 如下实例: 12345678# -*- coding: UTF-8 -*- # 打开一个文件fo = open("foo.txt", "w")print "文件名: ", fo.nameprint "是否已关闭 : ", fo.closedprint "访问模式 : ", fo.modeprint "末尾是否强制加空格 : ", fo.softspace 以上实例输出结果: 1234文件名: foo.txt是否已关闭 : False访问模式 : w末尾是否强制加空格 : 0 write()方法write()方法可将任何字符串写入一个打开的文件。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字。 write()方法不会在字符串的结尾添加换行符(‘\n’): read()方法read()方法从一个打开的文件中读取一个字符串。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字。语法: fileObject.read([count]) 在这里,被传递的参数是要从已打开文件中读取的字节计数。该方法从文件的开头开始读入,如果没有传入count,它会尝试尽可能多地读取更多的内容,很可能是直到文件的末尾。 文件定位 tell()方法告诉你文件内的当前位置, 换句话说,下一次的读写会发生在文件开头这么多字节之后。 seek(offset [,from])方法改变当前文件的位置。Offset变量表示要移动的字节数。From变量指定开始移动字节的参考位置。如果from被设为0,这意味着将文件的开头作为移动字节的参考位置。如果设为1,则使用当前的位置作为参考位置。如果它被设为2,那么该文件的末尾将作为参考位置。 重命名和删除文件Python的os模块提供了帮你执行文件处理操作的方法,比如重命名和删除文件。 要使用这个模块,你必须先导入它,然后才可以调用相关的各种功能。 ####### rename()方法: rename()方法需要两个参数,当前的文件名和新文件名。 语法: os.rename(current_file_name, new_file_name) ######## 实例下例将重命名一个已经存在的文件test1.txt。123456# -*- coding: UTF-8 -*-import os # 重命名文件test1.txt到test2.txt。os.rename( "test1.txt", "test2.txt" ) ####### remove()方法你可以用remove()方法删除文件,需要提供要删除的文件名作为参数。 语法: os.remove(file_name) ######## 实例下例将删除一个已经存在的文件test2.txt。12345# -*- coding: UTF-8 -*-import os# 删除一个已经存在的文件test2.txtos.remove("test2.txt") Python里的目录:所有文件都包含在各个不同的目录下,不过Python也能轻松处理。os模块有许多方法能帮你创建,删除和更改目录。目录方法 mkdir()方法可以使用os模块的mkdir()方法在当前目录下创建新的目录们。你需要提供一个包含了要创建的目录名称的参数。 语法: os.mkdir(“newdir”)例子: chdir()方法可以用chdir()方法来改变当前的目录。chdir()方法需要的一个参数是你想设成当前目录的目录名称。 语法: os.chdir(“newdir”) getcwd()方法:getcwd()方法显示当前的工作目录。 语法: os.getcwd() rmdir()方法rmdir()方法删除目录,目录名称以参数传递。 在删除这个目录之前,它的所有内容应该先被清除。 语法: os.rmdir(‘dirname’) 异常处理异常处理 try/except语句。try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。 如果你不想在异常发生时结束你的程序,只需在try里捕获它。 语法: 12345678try:<语句> #运行别的代码except <名字>:<语句> #如果在try部份引发了'name'异常except <名字>,<数据>:<语句> #如果引发了'name'异常,获得附加的数据else:<语句> #如果没有异常发生 try-finally 语句try-finally 语句无论是否发生异常都将执行最后的代码。语法 12345try:<语句>finally:<语句> #退出try时总会执行raise 实例以下为单个异常的实例: 12345678910# -*- coding: UTF-8 -*-# 定义函数def temp_convert(var): try: return int(var) except ValueError, Argument: print "参数没有包含数字\n", Argument# 调用函数temp_convert("xyz"); 以上程序执行结果如下: 123$ python test.py 参数没有包含数字invalid literal for int() with base 10: 'xyz' 触发异常我们可以使用raise语句自己触发异常 raise语法格式如下: 1raise [Exception [, args [, traceback]]] 语句中 Exception 是异常的类型(例如,NameError)参数标准异常中任一种,args 是自已提供的异常参数。 最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。 实例一个异常可以是一个字符串,类或对象。 1234def functionName( level ): if level < 1: raise Exception("Invalid level!", level) # 触发异常后,后面的代码就不会再执行 面向对象 getattr(obj, name[, default]) : 访问对象的属性。 hasattr(obj,name) : 检查是否存在一个属性。 setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。 delattr(obj, name) : 删除属性。 issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup) isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。 基础重载方法 序号 方法, 描述 & 简单的调用 1 init ( self [,args…] ) 构造函数 简单的调用方法: obj = className(args) 2 del( self ) 析构方法, 删除一个对象 简单的调用方法 : del obj 3 repr( self ) 转化为供解释器读取的形式 简单的调用方法 : repr(obj) 4 str( self ) 用于将值转化为适于人阅读的形式 简单的调用方法 : str(obj) 5 cmp ( self, x ) 对象比较 简单的调用方法 : cmp(obj, x)]]></content>
<categories>
<category>python</category>
</categories>
<tags>
<tag>python基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[第一篇博客:在Github上搭建Hexo博客]]></title>
<url>%2F2019%2F03%2F23%2F%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E5%AE%A2%2F</url>
<content type="text"><![CDATA[具体实现看下面的链接 这里是一些链接,请享用~ Github上搭建Hexo博客 next主题视频教程 next主题安装及其设置 GitHub Corners 三月里的樱花已经悄然开放,开的如火如荼,极其美丽。翠绿的枝叶,沾有清晨的露珠,在太阳慈爱的照耀下闪闪发亮。满树烂漫的樱花林,透出了一股甜美的气息。从远处看,如云似霞般炫目,不时引来一只只彩蝶绕花盘旋,花掩蝶蝶恋花,两种景物融为一体,时常迷得依依抱着胳膊趴在树枝上暗暗陶醉。]]></content>
<categories>
<category>安装链接</category>
</categories>
<tags>
<tag>搭建hexo博客</tag>
<tag>next主题安装</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>%2F2019%2F03%2F23%2Fhello-world%2F</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
</search>