AngularJS路由系列(2)--刷新、查看路由,路由事件和URL格式,获取路由参数,路由的Resolve
本系列探寻AngularJS的路由机制,在WebStorm下开发。主要包括:
● 刷新路由
● 查看当前路由以及所有路由
● 路由触发事件
● 获取路由参数
AngularJS路由系列包括:
1、AngularJS路由系列(1)--基本路由配置
2、AngularJS路由系列(2)--刷新、查看路由,路由事件和URL格式,获取路由参数,路由的Resolve
3、AngularJS路由系列(3)-- UI-Router初体验
4、AngularJS路由系列(4)-- UI-Router的$state服务、路由事件、获取路由参数
5、AngularJS路由系列(5)-- UI-Router的路由约束、Resolve属性、路由附加数据、路由进入退出事件
6、AngularJS路由系列(6)-- UI-Router的嵌套State
项目文件结构
node_modules/
public/
.....app/
..........bower_components/
...............toastr/
....................toastr.min.css
....................toastr.min.js
...............jquery/
....................dist/
.........................jquery.min.js
...............angular/
....................angular.min.js
...............angular-ui-router/
....................release/
.........................angular-ui-router.min.js
...............angular-route/
.........................angular-route.min.js
..........controllers/
...............HomeController.js
...............AllSchoolsController.js
...............AllClassroomsController.js
...............AllActivityiesController.js
...............ClassroomController.js
...............ClassroomSummaryController.js
...............ClassroomMessageController.js
..........css/
...............bootstrap.cerulean.min.css
..........filters/
...............activityMonthFilter.js
..........services/
...............dataServices.js
...............notifier.js
..........templates/
...............home.html
...............allSchools.html
...............allClassrooms.html
...............allActivities.html
...............classroom.html
...............classroomDetail.html
...............classroom_parent.html
..........app.js
.....index.html
.....favicon.ico
server/
.....data/
.....routes/
.....views/
.....helpers.js
package.json
server.js
刷新路由
有时候我们需要刷新路由而不是整个页面。该如何做呢?ngRoute这个module为我们准备了$route服务的reload()方法可以实现。
■ HomeController.js, 添加刷新路由功能,$route.reload
(function(){
angular.module('app')
.controller('HomeController',['dataService','notifier', '$route', '$log', HomeController]); function HomeController(dataService, notifier, $route, $log){
var vm = this;
vm.message = 'Welcome to School Buddy!'; //重新刷新路由
vm.refresh = function(){
$route.reload();
} dataService.getAllSchools()
.then(function(schools){
vm.allSchools = schools;
vm.schollCount = schools.length;
})
.catch(showError); dataService.getAllClassrooms()
.then(function(classroom){
vm.allClassrooms = classrooms;
vm.classroomCount = classrooms.length;
})
.catch(showError); dataService.getAllActivities()
then(function(activities)){
vm.allActivities = activities;
vm.activityCount = activities.length;
}
...
}
}());
■ home.html, 添加刷新路由的按钮
{{home.message}}
{{home.schoolCount}}
{{home.activityCount}}
<button ng-click="home.refresh()">刷新</button>
当点击"刷新"按钮,刷新的只是路由,不是整个页面。
查看当前路由以及所有路由
$route服务提供了current和routes属性。
■ HomeController.js, 查看当前路由以及所有路由,使用$route的current和routes属性
(function(){
angular.module('app')
.controller('HomeController',['dataService','notifier', '$route', '$log', HomeController]); function HomeController(dataService, notifier, $route, $log){
var vm = this;
vm.message = 'Welcome to School Buddy!'; //重新刷新路由
vm.refresh = function(){
$log.debug($route.current);
$log.debut($route.routes);
$route.reload();
} dataService.getAllSchools()
.then(function(schools){
vm.allSchools = schools;
vm.schollCount = schools.length;
})
.catch(showError); dataService.getAllClassrooms()
.then(function(classroom){
vm.allClassrooms = classrooms;
vm.classroomCount = classrooms.length;
})
.catch(showError); dataService.getAllActivities()
then(function(activities)){
vm.allActivities = activities;
vm.activityCount = activities.length;
}
...
}
}());
$route.current相关的如下:
$route.routes相关如下:
路由触发事件
$route服务提供以下几个事件:
● $routeChangeStart
● $routeChangesSuccess
● $routeChangeError
● $routeUpdate
使用$on来调用事件
这些事件是在路由发生变化、路由成功、路由异常、路由更新时被触发。谁来做这些事件的载体呢?我们可以使用$on方法把这些事件注册到$rootScope上去,这样,在全局范围内触发这些事件。
■ app.js,为$rootScope添加路由事件
(function(){
var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){
$logProvider.debugEnabled(true); $routeProvider
.when('/',{
controller: 'HomeController' ,
controllerAs: 'home',
templateUrl: '/app/templates/home.html'
})
.when('/schools',{
controller: 'AllSchoolsController',
controllerAs: 'schools',
templateUrl: '/app/templates/allSchools.html',
caseInsensitiveMatch: true
})
.when('/classrooms/:id',{
controller: 'AllClassroomsController',
controllerAs: 'classrooms',
templateUrl: '/app/templates/allClassrooms.html',
resolve:{
promise: function(){
throw 'error transitioning to classrooms';
}
}
})
.when('/activities',{
controller: 'AllActivitiesController',
controllerAs: 'activities',
templateUrl: '/app/templates/allActivities.html'
})
.otherwise('/');
}]); app.run(['$rootScope', '$log', function($rootScope, $log){
//通过$on为$rootScope添加路由事件
$rootScope.$on('$routeChangeSuccess',function(event, current, previous){
$log.debug('successfully changed routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
}); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
$log.debug('error changing routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
$log.debug(rejection);
});
}]);
}());
以上,使用resolve在controller初始化之前定义一个promise方法故意抛出一个异常。
在localhost:3000/#/下刷新浏览器,即触发了$rootScope的$routeChangeSuccess事件。
可见,event用来存放当前触发事件;current用来存放当前路由,previous用来存放上一个路由。
清空控制台记录,点击导航栏上的Activities
previous显示了上一个路由。
清空控制台记录,点击导航栏上的Classroom,即我们故意在该路由中设置了一个异常。
获取路由参数
有时候我们需要获取路由中的参数,ngRoute为我们提供了$routeParams服务。
.when('/classrooms/:id',{
})
在控制器中大致这样:
function ClassroomController($routeParams){
var classroomID = $routeParams.id;
//使用classroomID获取相关Classroom
}
■ app.js,针对Classroom添加一个带参数的路由
(function(){
var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){
$logProvider.debugEnabled(true); $routeProvider
.when('/',{
controller: 'HomeController' ,
controllerAs: 'home',
templateUrl: '/app/templates/home.html'
})
.when('/schools',{
controller: 'AllSchoolsController',
controllerAs: 'schools',
templateUrl: '/app/templates/allSchools.html',
caseInsensitiveMatch: true
})
.when('/classrooms/:id',{
controller: 'AllClassroomsController',
controllerAs: 'classrooms',
templateUrl: '/app/templates/allClassrooms.html'
})
.when('/activities',{
controller: 'AllActivitiesController',
controllerAs: 'activities',
templateUrl: '/app/templates/allActivities.html'
})
.when('/classrooms/:id',{
templateUrl: '/app/templates/classroom.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.otherwise('/');
}]); app.run(['$rootScope', '$log', function($rootScope, $log){
//通过$on为$rootScope添加路由事件
$rootScope.$on('$routeChangeSuccess',function(event, current, previous){
$log.debug('successfully changed routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
}); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
$log.debug('error changing routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
$log.debug(rejection);
});
}]);
}());
■ ClassroomController.js
(function(){
angular.module('app',[])
.controller('ClassroomController', ['dataService','notifier', '$routeParams', ClassroomController]); function ClassroomController(dataService, notifier, $routeParams){
var vm = this; dataService.getClassroom($routeParams.id)
.then(function(classroom){
vm.currentClassroom = classroom;
})
.catch(showError); function showError(message){
notifier.error(message);
}
}
}());
在浏览器中输入localhost:3000/#/classrooms/1
■ app.js,针对Classroom添加带更多参数的路由
(function(){
var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){
$logProvider.debugEnabled(true); $routeProvider
.when('/',{
controller: 'HomeController' ,
controllerAs: 'home',
templateUrl: '/app/templates/home.html'
})
.when('/schools',{
controller: 'AllSchoolsController',
controllerAs: 'schools',
templateUrl: '/app/templates/allSchools.html',
caseInsensitiveMatch: true
})
.when('/classrooms/:id',{
controller: 'AllClassroomsController',
controllerAs: 'classrooms',
templateUrl: '/app/templates/allClassrooms.html'
})
.when('/activities',{
controller: 'AllActivitiesController',
controllerAs: 'activities',
templateUrl: '/app/templates/allActivities.html'
})
.when('/classrooms/:id',{
templateUrl: '/app/templates/classroom.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.when('/classroom/:id/detail/:month?',{
templateUrl: '/app/templates/classroomDetail.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.otherwise('/');
}]); app.run(['$rootScope', '$log', function($rootScope, $log){
//通过$on为$rootScope添加路由事件
$rootScope.$on('$routeChangeSuccess',function(event, current, previous){
$log.debug('successfully changed routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
}); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
$log.debug('error changing routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
$log.debug(rejection);
});
}]);
}());
■ ClassroomController.js
(function(){
angular.module('app',[])
.controller('ClassroomController', ['dataService','notifier', '$routeParams', ClassroomController]); function ClassroomController(dataService, notifier, $routeParams){
var vm = this; vm.month = $routeParams.month; dataService.getClassroom($routeParams.id)
.then(function(classroom){
vm.currentClassroom = classroom; //判断路由中是否有month这个参数
if($routeParams.month){
//集合或数组是否为空
if(classroom.activities.length > 0){
vm.timePeriod = dataService.getMonthName($routeParams.month);
} else {
vm.timePeriod = 'No activities this month';
}
} else {
vm.timePeriod = 'All activities';
} })
.catch(showError); function showError(message){
notifier.error(message);
}
}
}());
■ AllActivitiesController.js
(function(){
angular.module('app')
.controller('AllActivitiesController', ['dataService', 'notifier', '$location', AllActivitiesController]); function AllActivitiesController(dataService, notifier, $location){
var vm = this; vm.seletedMonth = 1; //搜索过滤
vm.search = function(){
var classroom_detail_url = '/classrooms/' + vm.selectedClassroom.id + '/detail/' +vm.seletedMonth;
$location.url(classroom_detail_url);
}; dataService.getAllClassrooms()
.then(function(classrooms){
vm.allClassrooms = classroom;
vm.seletedClassroom = classrooms[0];
})
.catch(showError); dataService.getAllActivities()
.then(function(activities)){
vm.allActivities = activities;
}
.catch(showError); function showError(message){
notifier.error(message);
}
}
}());
可见,使用$location.url(route)方法可以轻松转到任何路由。
路由的Resolve属性
在配置路由的时候有一个Resolve属性,接受一个Object对象,对象的每一个属性接收一个函数,resolve中的事件发生在controller初始化之前,最终也将被注入到controller中。
.when('/activities', {
controller: 'AllActivitiesController',
controllerAd: 'activities',
templateUrl: '/app/tempaltes/allActivities.html',
resolve: {
activities: function(dataService){
return dataService.getAllActivities();
}
}
})
以上,dataService.getAllActivities方法返回一个promise,必须要被resolved之后才会转到相应的视图页。接着,activities可以被注入到AllActivitiesController中。
■ app.js, 在/activities路由下加上resolve属性
(function(){
var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', function($logProvider,$routeProvider){
$logProvider.debugEnabled(true); $routeProvider
.when('/',{
controller: 'HomeController' ,
controllerAs: 'home',
templateUrl: '/app/templates/home.html'
})
.when('/schools',{
controller: 'AllSchoolsController',
controllerAs: 'schools',
templateUrl: '/app/templates/allSchools.html',
caseInsensitiveMatch: true
})
.when('/classrooms/:id',{
controller: 'AllClassroomsController',
controllerAs: 'classrooms',
templateUrl: '/app/templates/allClassrooms.html'
})
.when('/activities',{
controller: 'AllActivitiesController',
controllerAs: 'activities',
templateUrl: '/app/templates/allActivities.html',
resolve: {
activities: function(dataService){
return dataService.getAllActivites();
}
}
})
.when('/classrooms/:id',{
templateUrl: '/app/templates/classroom.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.when('/classroom/:id/detail/:month?',{
templateUrl: '/app/templates/classroomDetail.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.otherwise('/');
}]); app.run(['$rootScope', '$log', function($rootScope, $log){
//通过$on为$rootScope添加路由事件
$rootScope.$on('$routeChangeSuccess',function(event, current, previous){
$log.debug('successfully changed routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
}); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
$log.debug('error changing routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
$log.debug(rejection);
});
}]);
}());
■ AllActivitiesController.js,把路由resolve属性中的activities注入到控制器中来
(function(){
angular.module('app')
.controller('AllActivitiesController', ['dataService', 'notifier', '$location', 'activities', AllActivitiesController]); function AllActivitiesController(dataService, notifier, $location, activities){
var vm = this; vm.seletedMonth = 1; //这里的activites中路由的resolve中来
//原先的getAllActivities方法就不需要存在了
vm.allActivities = actvities; //搜索过滤
vm.search = function(){
var classroom_detail_url = '/classrooms/' + vm.selectedClassroom.id + '/detail/' +vm.seletedMonth;
$location.url(classroom_detail_url);
}; dataService.getAllClassrooms()
.then(function(classrooms){
vm.allClassrooms = classroom;
vm.seletedClassroom = classrooms[0];
})
.catch(showError); function showError(message){
notifier.error(message);
}
}
}());
使用resolve的好处是:当视图页向$scope要数据的时候,由于在controller实例化之前已经准备好了数据,所以页面延迟时间大大缩短。
路由URL格式
● Hashbang格式: localhost:3000/#/classrooms/1/detail/12,默认的就是这种格式
● HTML5格式:localhost:3000/classrooms/1/detail/12,需要使用$locationProvider配置,如果使用这种格式,但浏览器不支持HTML5的历史API,Angular就会使用Hashbang格式。
■ app.js, 引用$locationProvider配置自定义url格式
(function(){
var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', '$locationProvider', function($logProvider,$routeProvider, $locationProvider){
$logProvider.debugEnabled(true); //使用自定义url格式
$locationProvider.hasPrefix('!'); $routeProvider
.when('/',{
controller: 'HomeController' ,
controllerAs: 'home',
templateUrl: '/app/templates/home.html'
})
.when('/schools',{
controller: 'AllSchoolsController',
controllerAs: 'schools',
templateUrl: '/app/templates/allSchools.html',
caseInsensitiveMatch: true
})
.when('/classrooms/:id',{
controller: 'AllClassroomsController',
controllerAs: 'classrooms',
templateUrl: '/app/templates/allClassrooms.html'
})
.when('/activities',{
controller: 'AllActivitiesController',
controllerAs: 'activities',
templateUrl: '/app/templates/allActivities.html',
resolve: {
activities: function(dataService){
return dataService.getAllActivites();
}
}
})
.when('/classrooms/:id',{
templateUrl: '/app/templates/classroom.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.when('/classroom/:id/detail/:month?',{
templateUrl: '/app/templates/classroomDetail.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.otherwise('/');
}]); app.run(['$rootScope', '$log', function($rootScope, $log){
//通过$on为$rootScope添加路由事件
$rootScope.$on('$routeChangeSuccess',function(event, current, previous){
$log.debug('successfully changed routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
}); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
$log.debug('error changing routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
$log.debug(rejection);
});
}]);
}());
■ index.html,为了配合以上的自定义url格式需要做些改变
<a href="#!/">School Buddy</a> <a href="#!/schools">Schools</a>
<a href="#!/classrooms">Classrooms</a>
<a href="#!/activities">Activities</a>
■ app.js, 引用$locationProvider配置HTML5的url格式
(function(){
var app = angular.module('app', ['ngRoute']); app.config(['$logProvider','$routeProvider', '$locationProvider', function($logProvider,$routeProvider, $locationProvider){
$logProvider.debugEnabled(true); //使用HTML5格式
$locationProvider.html5Mode({
enabled: true,
requireBase: true, //需要配置基地址
rewriteLinks: true //遇到旧版本的浏览器会使用默认的Hashbang模式
}); $routeProvider
.when('/',{
controller: 'HomeController' ,
controllerAs: 'home',
templateUrl: '/app/templates/home.html'
})
.when('/schools',{
controller: 'AllSchoolsController',
controllerAs: 'schools',
templateUrl: '/app/templates/allSchools.html',
caseInsensitiveMatch: true
})
.when('/classrooms/:id',{
controller: 'AllClassroomsController',
controllerAs: 'classrooms',
templateUrl: '/app/templates/allClassrooms.html'
})
.when('/activities',{
controller: 'AllActivitiesController',
controllerAs: 'activities',
templateUrl: '/app/templates/allActivities.html',
resolve: {
activities: function(dataService){
return dataService.getAllActivites();
}
}
})
.when('/classrooms/:id',{
templateUrl: '/app/templates/classroom.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.when('/classroom/:id/detail/:month?',{
templateUrl: '/app/templates/classroomDetail.html',
controller: 'ClassroomController',
controllerAs: 'classroom'
})
.otherwise('/');
}]); app.run(['$rootScope', '$log', function($rootScope, $log){
//通过$on为$rootScope添加路由事件
$rootScope.$on('$routeChangeSuccess',function(event, current, previous){
$log.debug('successfully changed routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
}); $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
$log.debug('error changing routes'); $log.debug(event);
$log.debug(current);
$log.debug(previous);
$log.debug(rejection);
});
}]);
}());
■ index.html,为了配合URL的HTML5格式需要做些改变
<head>
<base href="/">
</head> <a href="/">School Buddy</a> <a href="/schools">Schools</a>
<a href="/classrooms">Classrooms</a>
<a href="/activities">Activities</a>
但是url使用HTML5格式有一个不好的地方,当输入一个不存在的url就会报一个404的错。
未完待续~~
AngularJS路由系列(2)--刷新、查看路由,路由事件和URL格式,获取路由参数,路由的Resolve的更多相关文章
- AngularJS路由系列(6)-- UI-Router的嵌套State
本系列探寻AngularJS的路由机制,在WebStorm下开发.本篇主要涉及UI-Route的嵌套State. 假设一个主视图上有两个部分视图,部分视图1和部分视图2,主视图对应着一个state,两 ...
- AngularJS路由系列(5)-- UI-Router的路由约束、Resolve属性、路由附加数据、路由进入退出事件
本系列探寻AngularJS的路由机制,在WebStorm下开发.主要包括: ● UI-Router约束路由参数● UI-Router的Resolve属性● UI-Router给路由附加数据● UI- ...
- AngularJS路由系列(4)-- UI-Router的$state服务、路由事件、获取路由参数
本系列探寻AngularJS的路由机制,在WebStorm下开发.主要包括: ● UI-Router的$state服务● UI-Router的路由事件● UI-Router获取路由参数 Angular ...
- AngularJS路由系列(3)-- UI-Router初体验
本系列探寻AngularJS的路由机制,在WebStorm下开发. AngularJS路由系列包括: 1.AngularJS路由系列(1)--基本路由配置2.AngularJS路由系列(2)--刷新. ...
- AngularJS路由系列(1)--基本路由配置
本系列探寻AngularJS的路由机制,在WebStorm下开发.主要包括: ● 路由的Big Picture ● $routeProvider配置路由 ● 使用template属性 ● 使用temp ...
- 查看flask中所有的路由信息(同时查看/设置允许的请求方式get、post)
查看flask中所有的路由信息(同时查看/设置允许的请求方式get.post) # -*- coding: utf-8 -*- from flask import Flask app = Flask( ...
- 同一路由带参刷新,以及params和query两种方式传参的异同
同一路由应该不叫跳转了吧,就先叫刷新好了. 需求及问题 今天做web课设有这样一个需求: 在导航栏中一项叫做教师队伍一级菜单下,有三个二级菜单,分别为教授.副教授.讲师.这三个二级菜单分别对应一个页面 ...
- Flask系列03--Flask的路由 app.route中的参数, 动态参数路由
Flask–路由 添加路由的两种方式 第一种 @app.route("/my_de") def detail() 第二种(了解即可) app.add_url_rule(" ...
- day64 url用法以及django的路由系统
此篇博客是以备后查的,用到的时候记得过来查找即可! 路由系统:就是我们的django项目创建的时候自带的那个urls.py 它本身里面是映射的对应关系,一个大的列表里面,一个个元祖,元祖里面是url或 ...
随机推荐
- linux 串口驱动(二)初始化 【转】
转自:http://blog.chinaunix.net/uid-27717694-id-3493611.html 8250串口的初始化: (1)定义uart_driver.uart_ops.uart ...
- eclipse导入导出工作空间配置
首先,导出T1中的配置:打开T1,选择fileExport 在弹出框中选择General 下的preferencenext在export preferences 页面选择export all, 点Br ...
- elasticsearch安装ik分词器(非极速版)
1.下载下载地址为: https://github.com/medcl/elasticsearch-analysis-ik 2.解压把下载的 elasticsearch-analysis-ik.zip ...
- centos7执行 wget命令: command not found的两种解决方法
1.rpm 安装 下载wget的RPM包: http://mirrors.163.com/centos/6.8/os/x86_64/Packages/wget-1.12-8.el6.x86_64.rp ...
- C++ Primer读书笔记(2)
getline(cin,string s)可以读取一整行,包括空白符.使用ctrl+Z结束 字符串字面值与string是不同的类型.两个字符串字面值不能直接相加. 处理string对象中的字符时,C+ ...
- Java第三阶段学习(十、XML学习)
一.XML学习 1.模拟Servlet执行 在学习完前端及java与数据库后,将进行WEB编程阶段的学习.在WEB编程中,可以通过浏览器访问WEB服务器上的数据.这时WEB服务器就相当于另一台计算机. ...
- Struts2与Spring的Maven依赖冲突
今天在用Maven集成Struts2+Spring+Hibernate时发现只要不在web模块中声明spring-web的依赖,就会报java.lang.NoClassDefFoundError: [ ...
- JAVA-Exception&Error
JAVA--Exception&Error 在万物皆对象的JAVA中,先让我们看看Exception和Error的地位吧:
- rabbitmq学习(三) —— 工作队列
工作队列,又称任务队列,主要思想是避免立即执行资源密集型任务,并且必须等待完成.相反地,我们进行任务调度,我们将一个任务封装成一个消息,并将其发送到队列.工作进行在后台运行不断的从队列中取出任务然后执 ...
- openQPA[01]初次认识与使用
开源项目QPA 1.项目主页:[http://protocol.sinaapp.com/] 2.项目介绍: 3.运行项目: (1)安装python2.7,并安装PyQt4. 下载地址[https: ...