Repository: zccrs/QQStars Branch: master Commit: b2a18d88d27a Files: 89 Total size: 447.0 KB Directory structure: gitextract_ckkfy0u8/ ├── .gitignore ├── QQStars.pro ├── README.md ├── deployment.pri ├── faces.qrc ├── images.qrc ├── other.qrc ├── qml/ │ ├── Api/ │ │ ├── QQApi.qml │ │ └── api.js │ ├── Chat/ │ │ ├── ChatPage.qml │ │ ├── ChatWindowCommand.qml │ │ ├── DiscuChatPage.qml │ │ ├── FriendChatPage.qml │ │ ├── GroupChatPage.qml │ │ └── MessageListComponent.qml │ ├── Login/ │ │ ├── LoginPanel/ │ │ │ ├── AccountList.qml │ │ │ ├── LoginCheckBox.qml │ │ │ ├── LoginInputArea.qml │ │ │ └── LoginPage.qml │ │ ├── MyLoginButton.qml │ │ ├── SettingPage.qml │ │ └── main.qml │ ├── MainPanel/ │ │ ├── ListPage/ │ │ │ ├── AllListPage.qml │ │ │ ├── DiscuList.qml │ │ │ ├── FriendList.qml │ │ │ ├── GroupAndDiscuPage.qml │ │ │ ├── GroupList.qml │ │ │ └── RecentList.qml │ │ ├── MainPanelPage.qml │ │ └── main.qml │ ├── QQItemInfo/ │ │ ├── DiscuInfo.qml │ │ ├── FriendInfo.qml │ │ └── GroupInfo.qml │ └── Utility/ │ ├── CodeInput.qml │ ├── ComboBox/ │ │ ├── MyComboBox.qml │ │ └── MyComboBoxComponent.qml │ ├── KeyboardPage/ │ │ ├── SoftKeyboard.qml │ │ └── SoftKeyboardButton.qml │ ├── MyButton.qml │ ├── MyMessageBox.qml │ ├── MyRectangularGlow.qml │ ├── MyScrollView.qml │ ├── MyTextArea.qml │ ├── MyTextField.qml │ ├── MyTextView.qml │ ├── MyWindow.qml │ ├── SystemTray.qml │ └── TrayMessageWindow.qml ├── qml.qrc ├── qmlapplicationviewer.pri ├── qt_zh_CN.qm ├── src/ │ ├── main.cpp │ ├── mywidgets/ │ │ ├── myimage.cpp │ │ ├── myimage.h │ │ ├── mymessagebox.cpp │ │ ├── mymessagebox.h │ │ ├── mysvgview.cpp │ │ ├── mysvgview.h │ │ ├── mywindow.cpp │ │ ├── mywindow.h │ │ ├── systemtrayicon.cpp │ │ └── systemtrayicon.h │ ├── qqstars/ │ │ ├── qqiteminfo.cpp │ │ ├── qqiteminfo.h │ │ ├── qqstars.cpp │ │ └── qqstars.h │ ├── qxtglobalshortcut/ │ │ ├── myshortcut.cpp │ │ ├── myshortcut.h │ │ ├── qxtglobal.h │ │ ├── qxtglobalshortcut.cpp │ │ ├── qxtglobalshortcut.h │ │ ├── qxtglobalshortcut.pri │ │ ├── qxtglobalshortcut_mac.cpp │ │ ├── qxtglobalshortcut_p.h │ │ ├── qxtglobalshortcut_win.cpp │ │ └── qxtglobalshortcut_x11.cpp │ └── utility/ │ ├── downloadimage.cpp │ ├── downloadimage.h │ ├── myhttprequest.cpp │ ├── myhttprequest.h │ ├── mynetworkaccessmanagerfactory.cpp │ ├── mynetworkaccessmanagerfactory.h │ ├── texteditplaygif.cpp │ ├── texteditplaygif.h │ ├── utility.cpp │ └── utility.h ├── style/ │ ├── menuStyle.css │ └── messageBoxStyle.css └── style.qrc ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib # Qt-es /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.moc moc_*.cpp qrc_*.cpp ui_*.h Makefile* *-build-* # QtCreator *.autosave #QtCtreator Qml *.qmlproject.user *.qmlproject.user.* #custom /build ================================================ FILE: QQStars.pro ================================================ TEMPLATE = app TARGET = QQStars QT += widgets network webkit svg qml quick concurrent sql quick-private#widgets-private core-private gui-private QT += webkitwidgets linux:LIBS += -lXext INCLUDEPATH += \ src \ src/qxtglobalshortcut \ src/qqstars \ src/mywidgets \ src/utility SOURCES += src/main.cpp \ src/utility/mynetworkaccessmanagerfactory.cpp \ src/utility/utility.cpp \ src/mywidgets/mywindow.cpp \ src/qqstars/qqstars.cpp \ src/mywidgets/systemtrayicon.cpp \ src/mywidgets/mysvgview.cpp \ src/mywidgets/myimage.cpp \ src/mywidgets/mymessagebox.cpp \ src/utility/myhttprequest.cpp \ src/qqstars/qqiteminfo.cpp \ src/utility/downloadimage.cpp \ src/utility/texteditplaygif.cpp RESOURCES += \ images.qrc \ other.qrc \ faces.qrc \ qml.qrc \ style.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Default rules for deployment. include(deployment.pri) include (src/qxtglobalshortcut/qxtglobalshortcut.pri) #include (qmlapplicationviewer.pri) #qtcAddDeployment() HEADERS += \ src/utility/mynetworkaccessmanagerfactory.h \ src/utility/utility.h \ src/mywidgets/mywindow.h \ src/qqstars/qqstars.h \ src/mywidgets/systemtrayicon.h \ src/mywidgets/mysvgview.h \ src/mywidgets/myimage.h \ src/mywidgets/mymessagebox.h \ src/utility/myhttprequest.h \ src/qqstars/qqiteminfo.h \ src/utility/downloadimage.h \ src/utility/texteditplaygif.h OTHER_FILES += \ qml/Utility/CodeInput.qml \ qml/Utility/MyButton.qml \ qml/Utility/MyMessageBox.qml \ qml/Utility/MyTextArea.qml \ qml/Utility/MyTextField.qml \ qml/Utility/SystemTray.qml \ qml/Api/api.js \ qml/Api/QQApi.qml \ qml/Login/KeyboardPage/SoftKeyboard.qml \ qml/Login/KeyboardPage/SoftKeyboardButton.qml \ qml/Login/main.qml \ qml/Login/SettingPage.qml \ qml/MainPanel/ChatWindow/qqshow.png \ qml/MainPanel/ListPage/AllListPage.qml \ qml/MainPanel/ListPage/FriendList.qml \ qml/MainPanel/ListPage/GroupList.qml \ qml/MainPanel/ListPage/RecentList.qml \ qml/MainPanel/main.qml \ qml/MainPanel/MainPanelPage.qml \ qml/Utility/ComboBox/MyComboBox.qml \ qml/Utility/ComboBox/MyComboBoxComponent.qml \ qml/Login/MyLoginButton.qml \ qml/Chat/ChatWindowCommand.qml \ qml/Chat/MessageListComponent.qml \ qml/Chat/qqshow.png \ qml/Utility/KeyboardPage/SoftKeyboard.qml \ qml/Utility/KeyboardPage/SoftKeyboardButton.qml \ qml/Utility/MyRectangularGlow.qml \ qml/Utility/MyWindow.qml \ qml/Utility/MyScrollView.qml \ qml/Login/LoginPanel/AccountList.qml \ qml/Login/LoginPanel/LoginCheckBox.qml \ qml/Login/LoginPanel/LoginInputArea.qml \ qml/Login/LoginPanel/LoginPage.qml \ qml/QQItemInfo/DiscuInfo.qml \ qml/QQItemInfo/FriendInfo.qml \ qml/QQItemInfo/GroupInfo.qml \ qml/MainPanel/ListPage/DiscuList.qml \ qml/MainPanel/ListPage/GroupAndDiscuPage.qml \ style/menuStyle.css \ style/messageBoxStyle.css \ qml/Chat/FriendChatPage.qml \ qml/Chat/GroupChatPage.qml \ qml/Chat/DiscuChatPage.qml \ qml/Chat/ChatPage.qml \ qml/Utility/TrayMessageWindow.qml \ qml/Login/textedit.html \ qml/Utility/MyTextView.qml FORMS += ================================================ FILE: README.md ================================================ #### 接口都已失效,此程序不再更新。 QQStars ======= [![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source") 此开源代码使用GPL授权方式 基于webqq协议的qq客户端,qt开发,可跨平台(主要给linux用户用,windows用户肯定不稀罕了) 开发环境windows 7,qt 5.3.2 下载链接:http://pan.baidu.com/s/1ntwW5DZ 密码: eeir。 其中QQStars-20141215gitd665984-1.fc21.x86_64.rpmQQStars-20141215gitd665984-1.fc21.src.rpm 为朋友LunarShaddow编译和打包,在此表示衷心的感谢。 直接解压,windows下直接运行QQStars.exe。 linux下运行QQStars.sh,如果程序无法启动就给同目录的QQStars加可执行权限。如果还是无法启动就是用终端运行QQStars.sh看有什么错误提示 在LInux下如果无法输入中文的话请看此博客:http://www.cnblogs.com/AfterTheRainOfStars/p/3768484.html#3022718 ![登录界面](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/login.jpg) ![设置界面](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/设置界面.jpg) ![主面板](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/mainWindow.jpg) ![好友列表](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/好友列表.jpg) ![群列表](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/群列表.jpg) ![讨论组](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/讨论组列表.jpg) ![最近联系人列表](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/最近联系人列表.jpg) ![好友聊天](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/好友聊天页.jpg) ![群聊天](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/群聊天页.jpg) ![讨论组聊天](https://github.com/AfterTheRainOfStars/QQStars/blob/master/introduce/讨论组聊天页.jpg) ================================================ FILE: deployment.pri ================================================ android-no-sdk { target.path = /data/user/qt export(target.path) INSTALLS += target } else:android { x86 { target.path = /libs/x86 } else: armeabi-v7a { target.path = /libs/armeabi-v7a } else { target.path = /libs/armeabi } export(target.path) INSTALLS += target } else:unix { isEmpty(target.path) { qnx { target.path = /tmp/$${TARGET}/bin } else { target.path = /opt/$${TARGET}/bin } export(target.path) } INSTALLS += target } export(INSTALLS) ================================================ FILE: faces.qrc ================================================ faces/classic/0.gif faces/classic/0.png faces/classic/1.gif faces/classic/1.png faces/classic/2.gif faces/classic/2.png faces/classic/3.gif faces/classic/3.png faces/classic/4.gif faces/classic/4.png faces/classic/5.gif faces/classic/5.png faces/classic/6.gif faces/classic/6.png faces/classic/7.gif faces/classic/7.png faces/classic/8.gif faces/classic/8.png faces/classic/9.gif faces/classic/9.png faces/classic/10.gif faces/classic/10.png faces/classic/11.gif faces/classic/11.png faces/classic/12.gif faces/classic/12.png faces/classic/13.gif faces/classic/13.png faces/classic/14.gif faces/classic/14.png faces/classic/21.png faces/classic/23.gif faces/classic/23.png faces/classic/25.png faces/classic/26.gif faces/classic/26.png faces/classic/27.gif faces/classic/27.png faces/classic/29.gif faces/classic/29.png faces/classic/32.png faces/classic/33.png faces/classic/34.png faces/classic/36.png faces/classic/37.gif faces/classic/37.png faces/classic/38.gif faces/classic/38.png faces/classic/39.png faces/classic/42.png faces/classic/45.png faces/classic/46.gif faces/classic/46.png faces/classic/47.gif faces/classic/47.png faces/classic/50.png faces/classic/51.gif faces/classic/51.png faces/classic/53.gif faces/classic/53.png faces/classic/54.gif faces/classic/54.png faces/classic/55.gif faces/classic/55.png faces/classic/56.gif faces/classic/56.png faces/classic/57.gif faces/classic/57.png faces/classic/58.gif faces/classic/58.png faces/classic/59.png faces/classic/62.gif faces/classic/62.png faces/classic/63.gif faces/classic/63.png faces/classic/64.png faces/classic/71.gif faces/classic/71.png faces/classic/72.gif faces/classic/72.png faces/classic/73.gif faces/classic/73.png faces/classic/74.gif faces/classic/74.png faces/classic/75.gif faces/classic/75.png faces/classic/76.gif faces/classic/76.png faces/classic/77.gif faces/classic/77.png faces/classic/78.gif faces/classic/78.png faces/classic/79.gif faces/classic/79.png faces/classic/80.gif faces/classic/80.png faces/classic/81.gif faces/classic/81.png faces/classic/82.gif faces/classic/82.png faces/classic/83.gif faces/classic/83.png faces/classic/84.gif faces/classic/84.png faces/classic/85.png faces/classic/86.png faces/classic/87.gif faces/classic/87.png faces/classic/88.gif faces/classic/88.png faces/classic/91.png faces/classic/93.gif faces/classic/93.png faces/classic/95.gif faces/classic/95.png faces/classic/96.gif faces/classic/96.png faces/classic/97.gif faces/classic/97.png faces/classic/98.gif faces/classic/98.png faces/classic/99.gif faces/classic/99.png faces/classic/100.gif faces/classic/100.png faces/classic/101.gif faces/classic/101.png faces/classic/102.gif faces/classic/102.png faces/classic/103.gif faces/classic/103.png faces/classic/104.gif faces/classic/104.png faces/classic/105.gif faces/classic/105.png faces/classic/106.gif faces/classic/106.png faces/classic/107.gif faces/classic/107.png faces/classic/108.gif faces/classic/108.png faces/classic/109.gif faces/classic/109.png faces/classic/110.gif faces/classic/110.png faces/classic/111.gif faces/classic/111.png faces/classic/112.gif faces/classic/112.png faces/classic/113.gif faces/classic/113.png faces/classic/114.gif faces/classic/114.png faces/classic/115.gif faces/classic/115.png faces/classic/116.gif faces/classic/116.png faces/classic/117.gif faces/classic/117.png faces/classic/118.gif faces/classic/118.png faces/classic/119.gif faces/classic/119.png faces/classic/120.gif faces/classic/120.png faces/classic/121.gif faces/classic/121.png faces/classic/122.gif faces/classic/122.png faces/classic/123.gif faces/classic/123.png faces/classic/124.png faces/classic/125.gif faces/classic/125.png faces/classic/126.gif faces/classic/126.png faces/classic/127.gif faces/classic/127.png faces/classic/128.gif faces/classic/128.png faces/classic/129.gif faces/classic/129.png faces/classic/130.gif faces/classic/130.png faces/classic/131.gif faces/classic/131.png faces/classic/132.gif faces/classic/132.png faces/classic/133.gif faces/classic/133.png faces/classic/134.gif faces/classic/134.png faces/classic/dfp.png faces/classic/dfr.png faces/classic/dhp.png faces/classic/K歌.png faces/classic/棒棒糖.png faces/classic/爆筋.png faces/classic/鞭炮.png faces/classic/彩球.png faces/classic/钞票.png faces/classic/车厢.png faces/classic/打伞.png faces/classic/灯笼.png faces/classic/灯泡.png faces/classic/多云.png faces/classic/发财.png faces/classic/飞机.png faces/classic/风车.png faces/classic/高铁右车头.png faces/classic/高铁左车头.png faces/classic/购物.png faces/classic/喝彩.png faces/classic/喝奶.png faces/classic/开车.png faces/classic/闹钟.png faces/classic/祈祷.png faces/classic/青蛙.png faces/classic/沙发.png faces/classic/手枪.png faces/classic/帅.png faces/classic/双喜.png faces/classic/下面.png faces/classic/下雨.png faces/classic/香蕉.png faces/classic/熊猫.png faces/classic/药.png faces/classic/邮件.png faces/classic/纸巾.png faces/classic/钻戒.png ================================================ FILE: images.qrc ================================================ images/avatar.svg images/avatar_left.png images/avatar-border.svg images/imaway.png images/background_arabesquitic.svg images/background_input.png images/imbusy.png images/button-login.svg images/button-login-hover.svg images/button-login-press.svg images/button-minimize.svg images/button-quit.svg images/checkBox-hover.svg images/imoffline.png images/imonline.png images/inputBox.svg images/inputBox1.png images/inputBox2.png images/inputBox-close.svg images/inputBox-more.svg images/inputBox-password-clicked.svg images/inputBox-password-hover.svg images/inputBox-qq-clicked.svg images/inputBox-qq-hover.svg images/imhidden.png images/list_arrow_down.png images/list_arrow_up.png images/list_item.png images/list_item_bottom.png images/lock20.png images/login-panel.svg images/login-panel-shadow.png images/imsilent.png images/progress-bar.png images/imcallme.png images/QQ-for-ubuntu.svg images/soft-keyboard.svg images/status-away-1.svg images/status-busy-1.svg images/status-callme-1.svg images/status-hidden-1.svg images/status-online-1.svg images/status-silent-1.svg images/unfold_icon.png images/unlock20.png images/checkBox-select.svg images/checkBox-unselect.svg images/bit.bmp images/avatar.png images/contact_press.png images/group_press.png images/TempSession_press.png images/menu_background.png images/button-settings.svg images/inputBox1.svg images/inputBox2.svg images/bubble_放飞心情_left.png images/bubble_放飞心情_right.png images/bubble_经典_left.png images/bubble_经典_right.png images/bubble_祈福_left.png images/bubble_祈福_right.png images/bubble_微笑河马_left.png images/bubble_微笑河马_right.png images/friendList_select.svg images/friendList_unselect.svg images/groupList_select.svg images/groupList_unselect.svg images/recentList_select.svg images/recentList_unselect.svg images/未标题-1.png images/star.png images/greenStar.png images/blueStar.png images/login-panel2.svg images/loading.png images/dud.png images/duz.png ================================================ FILE: other.qrc ================================================ qt_zh_CN.qm ================================================ FILE: qml/Api/QQApi.qml ================================================ import QtQuick 2.2 import utility 1.0 import QtQuick.Window 2.1 import QQItemInfo 1.0 import qqstars 1.0 QQ{ id: root property string re_uin: ""//用来存放测试qq是否需要验证码后返回的uin值(密码加密中需要用到) property var loginReData//二次登陆后返回的数据(JSON格式) property var userData//储存用户资料(JSON格式) property var panelSize//存放主面板大小(网络数据) property string clientid//存放网络强求需要的clientid property var friendListData//储存好友列表 property string list_hash//获取好友列表时需要的hash property string ptwebqq//登录后返回的cookie property string psessionid: loginReData?loginReData.psessionid:""//登录后返回的数据 property string vfwebqq: loginReData?loginReData.vfwebqq:""//登录后返回的数据 windowScale: { var dosktopWidth = Screen.desktopAvailableWidth if(dosktopWidth<=1366) return 1 else if(dosktopWidth>1366&&dosktopWidth<=1600) return 1.2 else if(dosktopWidth>1600&&dosktopWidth<=1920) return 1.4 else if(dosktopWidth>1920&&dosktopWidth<=2300) return 1.6 else if(dosktopWidth>2300&&dosktopWidth<=2600) return 1.8 else if(dosktopWidth>2600) return 2 } onStateChanged: { editUserState()//改变在线状态 } function random(min,max){ return Math.floor(min+Math.random()*(max-min)); } function getClientid() { return String(random(0, 99)) + String((new Date()).getTime() % 1000000) } function showInputCodePage(callbackFun, uin) { var component = Qt.createComponent("../Utility/CodeInput.qml"); if (component.status == Component.Ready){ var url = "https://ssl.captcha.qq.com/getimage?aid=1003903&r=0.9101365606766194&uin="+myqq.userQQ+"&cap_cd="+uin var data = {"source": url, "backFun":callbackFun}; var sprite = component.createObject(null, data); } } function login(code) { if( myqq.loginStatus == QQ.Logining ){ if( code ) {//开始第一次登陆GET var p = encryptionPassword(re_uin, code) var url1 = "https://ssl.ptlogin2.qq.com/login?u="+myqq.userQQ+"&p="+p+"&verifycode="+code+"&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=5-42-29419&mibao_css=m_webqq&t=1&g=1&js_type=0&js_ver=10087&login_sig=0RH3iE1ODTjmJJtKJ5MtDyoG*Q*pwgh2ABgmvw0E0zjdJpjPBbS*H9aZ4WRwLSFk&pt_uistyle=5" utility.httpGet(login1Finished, url1) }else{//先检测qq号是否需要输入验证码 utility.socketAbort()//取消以前的网络请求 var url2 = "https://ssl.ptlogin2.qq.com/check?uin="+myqq.userQQ+"&appid=1003903&r=0.08757076971232891" utility.httpGet(testQQFinished, url2) } } } function testQQFinished(error, data) {//服务器返回qq是否需要验证码 if(error){//如果出错了 login() return } if( myqq.loginStatus == QQ.Logining ){ var temp = data.split("'") re_uin = temp[5]//储存用来加密密码或获取验证码的uin if( temp[1]=="0" ){ login(temp[3])//不需要验证码,直接登录 }else{ showCodeWindow(login, temp[3])//调用输入验证码,login为验证码获取成功后的回调函数 } } } function login1Finished(error, data){//登录之后服务器返回的数据 if(error){//如果出错了 login(myqq.codeText)//再次请求 return } if( myqq.loginStatus == QQ.Logining ){ var list = data.split ("'"); if( list[1]==0 ){ closeCodeWindow()//关闭输入验证码的窗口 var url = list[5]//先get一下返回数据中的url,来获取必要的Cookie utility.httpGet(login2, url)//此地址GET完成后调用二次登录 }else{ myqq.showWarningInfo("登录失败:"+list[9]) myqq.error(list[9]) } } } function login2() { if( myqq.loginStatus == QQ.Logining ){ var url = "http://d.web2.qq.com/channel/login2" ptwebqq = utility.getCookie("ptwebqq")//储存cookie list_hash = getHash()//储存hash clientid = getClientid()//设置clientid var data = 'r={"status":"'+myqq.stateToString+'","ptwebqq":"'+ptwebqq+'","passwd_sig":"","clientid":"'+clientid+'","psessionid":null}&clientid='+clientid+'&psessionid=null' data = encodeURI(data) utility.httpPost(login2Finished, url, data) } } function reLogin(){//用于掉线后重新登录 var url = "http://d.web2.qq.com/channel/login2" ptwebqq = utility.getCookie("ptwebqq")//储存cookie var data = 'r={"status":"'+myqq.stateToString+'","ptwebqq":"'+ptwebqq+'","passwd_sig":"","clientid":"'+clientid+'","psessionid":null}&clientid='+clientid+'&psessionid=null' data = encodeURI(data) utility.httpPost(reLoginFinished, url, data, true) } function reLoginFinished(error, data) { if(error){ reLogin() return } data = JSON.parse(data) if( data.retcode==0 ) { console.debug("重新登录完成") loginReData = data.result//将数据记录下来 var poll2data = 'r={"clientid":"'+clientid+'","psessionid":"'+psessionid+'","key":0,"ids":[]}&clientid='+clientid+'&psessionid='+psessionid myqq.startPoll2(encodeURI(poll2data))//启动心跳包的post }else{ console.debug("重新登录失败") showWarningInfo("QQ已掉线,请重新登录") root.loginStatus = QQ.WaitLogin//将登录状态设置为离线 } } function login2Finished(error, data) {//二次登录,这次才是真正的登录 if(error){//如果出错了 login2(null) return } if( myqq.loginStatus == QQ.Logining ){ var list = JSON.parse(data) if( list.retcode==0 ) { loginReData = list.result//将数据记录下来 getUserData(myqq.userQQ, getDataFinished)//获取自己的资料 myqq.openSqlDatabase();//登录完成后,打开数据库(用来储存聊天记录) myqq.loginStatus = QQ.LoginFinished//设置为登录成功 var poll2data = 'r={"clientid":"'+clientid+'","psessionid":"'+psessionid+'","key":0,"ids":[]}&clientid='+clientid+'&psessionid='+psessionid myqq.startPoll2(encodeURI(poll2data))//启动心跳包的post var url = "http://q.qlogo.cn/headimg_dl?spec=240&dst_uin="+myqq.userQQ downloadImage(QQItemInfo.Friend, url, myqq.userQQ, "240", getAvatarFinished)//获取头像 }else{ myqq.showWarningInfo("登陆出错,错误代码:"+list.retcode) } } } function getUserData(uin, backFun) {//获取用户资料,登录完成后的操作 var url = "http://s.web2.qq.com/api/get_friend_info2?tuin="+uin+"&verifysession=&code=&vfwebqq="+vfwebqq+"&t=1407324674215" utility.httpGet(backFun, url, true)//第三个参数为true,是使用高优先级的网络请求 } function getDataFinished(error, data) {//获取用户资料成功后 if(error){//如果出错了 getUserData(myqq.userQQ, getDataFinished)//再次获取自己的资料 return } var list = JSON.parse(data) if( list.retcode==0 ) { userData = list.result //console.debug("获取资料成功,我的昵称是:"+userData.nick) root.nick = String(userData.nick)//储存昵称 myqq.addLoginedQQInfo(userQQ, nick)//保存此账号的登录信息 //getPanelSize()//获取主面板的大小 }else{ getUserData(myqq.userQQ, getDataFinished)//再次获取自己的资料 //myqq.showWarningInfo("获取用户资料出错,错误代码:"+list.retcode) } } function getPanelSize() { if( myqq.loginStatus == QQ.Logining ){ //var url = "http://cgi.web2.qq.com/keycgi/qqweb/newuac/get.do" //var data = 'r={"appid":50,"itemlist":["width","height","defaultMode"]}&uin='+myqq.userQQ //data = encodeURI(data) //utility.httpPost(getPanelSizeFinished, url, data) getPanelSizeFinished(false, "") } } function getPanelSizeFinished ( error, data){ //var list = JSON.parse(data) //if( list.retcode==0 ) { //panelSize = list.result//保存获取的数据 //}else{ //utility.consoleLog("获取主面板大小出错,错误代码:"+list.retcode) panelSize = JSON.parse('{"height":500,"defaultMode":"restore","width":240}') //} /*var temp = myqq.value("rememberpassword", 0)==1 console.log("是否保存密码:"+temp) if( temp ){//如果要保存密码 var pass = utility.stringEncrypt(myqq.userPassword, "xingchenQQ")//加密后储存 //myqq.setValue("password", pass) console.log("保存的密码为:"+myqq.value("password", "")) }*/ //myqq.setValue( "nick", userData.nick)//保存昵称 } function getQQSignature(uin, backFun){//获取好友个性签名 backFun为签名获取成功后调用 var url = "http://s.web2.qq.com/api/get_single_long_nick2?tuin="+uin+"&vfwebqq="+vfwebqq utility.httpGet(backFun, url) } function getFriendList(backFun) {//获取好友列表 var url = "http://s.web2.qq.com/api/get_user_friends2" var data = 'r={"h":"hello","hash":"'+getHash()+'","vfwebqq":"'+vfwebqq+'"}' data = encodeURI(data) utility.httpPost(backFun, url, data, true) } function getGroupList(backFun) {//获取群列表 var url = "http://s.web2.qq.com/api/get_group_name_list_mask2" var data = 'r={"hash":"'+getHash()+'","vfwebqq":"'+vfwebqq+'"}' data = encodeURI(data) utility.httpPost(backFun, url, data, true) } function getRecentList(backFun) {//获取最近联系人 var url = "http://d.web2.qq.com/channel/get_recent_list2" var data = 'r={"vfwebqq":"'+vfwebqq+'","clientid":"'+clientid+'","psessionid":"'+psessionid+'"}&clientid='+clientid+'&psessionid='+psessionid data = encodeURI(data) utility.httpPost(backFun, url, data, true) } function getDiscusList(backFun) {//讨论组列表 var url = "http://s.web2.qq.com/api/get_discus_list?clientid="+clientid+"&psessionid="+psessionid+"&vfwebqq="+vfwebqq utility.httpGet(backFun, url, true) } function getFriendQQ( tuin, backFun ) {//获得好友真实的qq var url = "http://s.web2.qq.com/api/get_friend_uin2?tuin="+tuin+"&verifysession=&type=1&code=&vfwebqq="+vfwebqq utility.httpGet(backFun, url) } function getAvatarFinished( error, path, name ){//获得自己头像完成 if(error==DownloadImage.DownloadError){//如果是下载出错 downloadImage(QQItemInfo.Friend, url, myqq.userQQ, "240", getAvatarFinished)//重新获取头像 return } if(error==DownloadImage.NoError) myqq.avatar240 = path+"/"+name } function getFriendInfo( tuin,backFun ) {//获取好友资料 var url = "http://s.web2.qq.com/api/get_friend_info2?tuin="+tuin+"&verifysession=&code=&vfwebqq="+vfwebqq utility.httpGet(backFun, url) } function editUserState(){ if( loginStatus == QQ.LoginFinished ) { var url = "http://d.web2.qq.com/channel/change_status2?newstatus="+myqq.stateToString+"&clientid="+clientid+"&psessionid="+psessionid utility.httpGet(editUserStateFinished, url) } } function editUserStateFinished(error, data){ if(error){ editUserState()//再次请求 return } data = JSON.parse(data) if( data.retcode==0&&data.result=="ok" ){ console.log("状态改变成功") if(root.state==QQ.Offline){//如果状态变为离线 root.loginStatus = QQ.WaitLogin//改变登录状态 } } } function sendFriendMessage(backFun, uin, message){ while(message[message.length-1]=="\n"){ message = message.substr(0, message.length-1) } var url = "http://d.web2.qq.com/channel/send_buddy_msg2" var data = 'r={"to":'+uin+',"face":549,"content":"[\\"'+message+'\\",\\"\\",[\\"font\\",{\\"name\\":\\"宋体\\",\\"size\\":\\"10\\",\\"style\\":[0,0,0],\\"color\\":\\"000000\\"}]]","msg_id":45070001,"clientid":"'+clientid+'","psessionid":"'+psessionid+'"}&clientid='+clientid+'&psessionid='+psessionid data = encodeURI(data) utility.httpPost(backFun, url, data, true) } function sendGroupMessage(backFun, uin, message){ while(message[message.length-1]=="\n"){ message = message.substr(0, message.length-1) } var url = "http://d.web2.qq.com/channel/send_qun_msg2" var data = 'r={"group_uin":'+uin+',"content":"[\\"'+message+'\\",\\"\\",[\\"font\\",{\\"name\\":\\"宋体\\",\\"size\\":\\"10\\",\\"style\\":[0,0,0],\\"color\\":\\"000000\\"}]]","msg_id":29780002,"clientid":"'+clientid+'","psessionid":"'+psessionid+'"}&clientid='+clientid+'&psessionid='+psessionid data = encodeURI(data) utility.httpPost(backFun, url, data, true) } function sendDiscuMessage(backFun, uin, message){ while(message[message.length-1]=="\n"){ message = message.substr(0, message.length-1) } var url = "http://d.web2.qq.com/channel/send_discu_msg2" var data = 'r={"did":'+uin+',"content":"[\\"'+message+'\\",\\"\\",[\\"font\\",{\\"name\\":\\"宋体\\",\\"size\\":\\"10\\",\\"style\\":[0,0,0],\\"color\\":\\"000000\\"}]]","msg_id":29780002,"clientid":"'+clientid+'","psessionid":"'+psessionid+'"}&clientid='+clientid+'&psessionid='+psessionid data = encodeURI(data) utility.httpPost(backFun, url, data, true) } function getGroupMembersList(callbackFun, gcode){//获取群成员列表 var url = "http://s.web2.qq.com/api/get_group_info_ext2?gcode="+gcode+"&cb=undefined&vfwebqq="+vfwebqq utility.httpGet(callbackFun, url, true) } function getOnlineFriends(callbackFun){//获取在线好友列表 var url = "http://d.web2.qq.com/channel/get_online_buddies2?clientid="+clientid+"&psessionid="+psessionid utility.httpGet(callbackFun, url, true) } function getDiscusMemberList(callbackFun, did) {//获取讨论组成员列表 var url = "http://d.web2.qq.com/channel/get_discu_info?did="+did+"&clientid="+clientid+"&psessionid="+psessionid utility.httpGet(callbackFun, url, true) } } ================================================ FILE: qml/Api/api.js ================================================ var hexcase = 1; var b64pad = ""; var chrsz = 8; var mode = 32; function md5(a) { return hex_md5(a) } function hex_md5(a) { return binl2hex(core_md5(str2binl(a), a.length * chrsz)) } function str_md5(a) { return binl2str(core_md5(str2binl(a), a.length * chrsz)) } function hex_hmac_md5(a, b) { return binl2hex(core_hmac_md5(a, b)) } function b64_hmac_md5(a, b) { return binl2b64(core_hmac_md5(a, b)) } function str_hmac_md5(a, b) { return binl2str(core_hmac_md5(a, b)) } function core_md5(p, k) { p[k >> 5] |= 128 << ((k) % 32); p[(((k + 64) >>> 9) << 4) + 14] = k; var o = 1732584193; var n = -271733879; var m = -1732584194; var l = 271733878; for (var g = 0; g < p.length; g += 16) { var j = o; var h = n; var f = m; var e = l; o = md5_ff(o, n, m, l, p[g + 0], 7, -680876936); l = md5_ff(l, o, n, m, p[g + 1], 12, -389564586); m = md5_ff(m, l, o, n, p[g + 2], 17, 606105819); n = md5_ff(n, m, l, o, p[g + 3], 22, -1044525330); o = md5_ff(o, n, m, l, p[g + 4], 7, -176418897); l = md5_ff(l, o, n, m, p[g + 5], 12, 1200080426); m = md5_ff(m, l, o, n, p[g + 6], 17, -1473231341); n = md5_ff(n, m, l, o, p[g + 7], 22, -45705983); o = md5_ff(o, n, m, l, p[g + 8], 7, 1770035416); l = md5_ff(l, o, n, m, p[g + 9], 12, -1958414417); m = md5_ff(m, l, o, n, p[g + 10], 17, -42063); n = md5_ff(n, m, l, o, p[g + 11], 22, -1990404162); o = md5_ff(o, n, m, l, p[g + 12], 7, 1804603682); l = md5_ff(l, o, n, m, p[g + 13], 12, -40341101); m = md5_ff(m, l, o, n, p[g + 14], 17, -1502002290); n = md5_ff(n, m, l, o, p[g + 15], 22, 1236535329); o = md5_gg(o, n, m, l, p[g + 1], 5, -165796510); l = md5_gg(l, o, n, m, p[g + 6], 9, -1069501632); m = md5_gg(m, l, o, n, p[g + 11], 14, 643717713); n = md5_gg(n, m, l, o, p[g + 0], 20, -373897302); o = md5_gg(o, n, m, l, p[g + 5], 5, -701558691); l = md5_gg(l, o, n, m, p[g + 10], 9, 38016083); m = md5_gg(m, l, o, n, p[g + 15], 14, -660478335); n = md5_gg(n, m, l, o, p[g + 4], 20, -405537848); o = md5_gg(o, n, m, l, p[g + 9], 5, 568446438); l = md5_gg(l, o, n, m, p[g + 14], 9, -1019803690); m = md5_gg(m, l, o, n, p[g + 3], 14, -187363961); n = md5_gg(n, m, l, o, p[g + 8], 20, 1163531501); o = md5_gg(o, n, m, l, p[g + 13], 5, -1444681467); l = md5_gg(l, o, n, m, p[g + 2], 9, -51403784); m = md5_gg(m, l, o, n, p[g + 7], 14, 1735328473); n = md5_gg(n, m, l, o, p[g + 12], 20, -1926607734); o = md5_hh(o, n, m, l, p[g + 5], 4, -378558); l = md5_hh(l, o, n, m, p[g + 8], 11, -2022574463); m = md5_hh(m, l, o, n, p[g + 11], 16, 1839030562); n = md5_hh(n, m, l, o, p[g + 14], 23, -35309556); o = md5_hh(o, n, m, l, p[g + 1], 4, -1530992060); l = md5_hh(l, o, n, m, p[g + 4], 11, 1272893353); m = md5_hh(m, l, o, n, p[g + 7], 16, -155497632); n = md5_hh(n, m, l, o, p[g + 10], 23, -1094730640); o = md5_hh(o, n, m, l, p[g + 13], 4, 681279174); l = md5_hh(l, o, n, m, p[g + 0], 11, -358537222); m = md5_hh(m, l, o, n, p[g + 3], 16, -722521979); n = md5_hh(n, m, l, o, p[g + 6], 23, 76029189); o = md5_hh(o, n, m, l, p[g + 9], 4, -640364487); l = md5_hh(l, o, n, m, p[g + 12], 11, -421815835); m = md5_hh(m, l, o, n, p[g + 15], 16, 530742520); n = md5_hh(n, m, l, o, p[g + 2], 23, -995338651); o = md5_ii(o, n, m, l, p[g + 0], 6, -198630844); l = md5_ii(l, o, n, m, p[g + 7], 10, 1126891415); m = md5_ii(m, l, o, n, p[g + 14], 15, -1416354905); n = md5_ii(n, m, l, o, p[g + 5], 21, -57434055); o = md5_ii(o, n, m, l, p[g + 12], 6, 1700485571); l = md5_ii(l, o, n, m, p[g + 3], 10, -1894986606); m = md5_ii(m, l, o, n, p[g + 10], 15, -1051523); n = md5_ii(n, m, l, o, p[g + 1], 21, -2054922799); o = md5_ii(o, n, m, l, p[g + 8], 6, 1873313359); l = md5_ii(l, o, n, m, p[g + 15], 10, -30611744); m = md5_ii(m, l, o, n, p[g + 6], 15, -1560198380); n = md5_ii(n, m, l, o, p[g + 13], 21, 1309151649); o = md5_ii(o, n, m, l, p[g + 4], 6, -145523070); l = md5_ii(l, o, n, m, p[g + 11], 10, -1120210379); m = md5_ii(m, l, o, n, p[g + 2], 15, 718787259); n = md5_ii(n, m, l, o, p[g + 9], 21, -343485551); o = safe_add(o, j); n = safe_add(n, h); m = safe_add(m, f); l = safe_add(l, e) } if (mode == 16) { return Array(n, m) } else { return Array(o, n, m, l) } } function md5_cmn(h, e, d, c, g, f) { return safe_add(bit_rol(safe_add(safe_add(e, h), safe_add(c, f)), g), d) } function md5_ff(g, f, k, j, e, i, h) { return md5_cmn((f & k) | ((~f) & j), g, f, e, i, h) } function md5_gg(g, f, k, j, e, i, h) { return md5_cmn((f & j) | (k & (~j)), g, f, e, i, h) } function md5_hh(g, f, k, j, e, i, h) { return md5_cmn(f ^ k ^ j, g, f, e, i, h) } function md5_ii(g, f, k, j, e, i, h) { return md5_cmn(k ^ (f | (~j)), g, f, e, i, h) } function core_hmac_md5(c, f) { var e = str2binl(c); if (e.length > 16) { e = core_md5(e, c.length * chrsz) } var a = Array(16), d = Array(16); for (var b = 0; b < 16; b++) { a[b] = e[b] ^ 909522486; d[b] = e[b] ^ 1549556828 } var g = core_md5(a.concat(str2binl(f)), 512 + f.length * chrsz); return core_md5(d.concat(g), 512 + 128) } function safe_add(a, d) { var c = (a & 65535) + (d & 65535); var b = (a >> 16) + (d >> 16) + (c >> 16); return (b << 16) | (c & 65535) } function bit_rol(a, b) { return (a << b) | (a >>> (32 - b)) } function str2binl(d) { var c = Array(); var a = (1 << chrsz) - 1; for (var b = 0; b < d.length * chrsz; b += chrsz) { c[b >> 5] |= (d.charCodeAt(b / chrsz) & a) << (b % 32) } return c } function binl2str(c) { var d = ""; var a = (1 << chrsz) - 1; for (var b = 0; b < c.length * 32; b += chrsz) { d += String.fromCharCode((c[b >> 5] >>> (b % 32)) & a) } return d } function binl2hex(c) { var b = hexcase ? "0123456789ABCDEF": "0123456789abcdef"; var d = ""; for (var a = 0; a < c.length * 4; a++) { d += b.charAt((c[a >> 2] >> ((a % 4) * 8 + 4)) & 15) + b.charAt((c[a >> 2] >> ((a % 4) * 8)) & 15) } return d } function binl2b64(d) { var c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var f = ""; for (var b = 0; b < d.length * 4; b += 3) { var e = (((d[b >> 2] >> 8 * (b % 4)) & 255) << 16) | (((d[b + 1 >> 2] >> 8 * ((b + 1) % 4)) & 255) << 8) | ((d[b + 2 >> 2] >> 8 * ((b + 2) % 4)) & 255); for (var a = 0; a < 4; a++) { if (b * 8 + a * 6 > d.length * 32) { f += b64pad } else { f += c.charAt((e >> 6 * (3 - a)) & 63) } } } return f } function hexchar2bin(str) { var arr = []; for (var i = 0; i < str.length; i = i + 2) { arr.push("\\x" + str.substr(i, 2)) } arr = arr.join(""); eval("var temp = '" + arr + "'"); return temp } function uin2hex(str) { var maxLength = 16; str = parseInt(str); var hex = str.toString(16); var len = hex.length; for (var i = len; i < maxLength; i++) { hex = "0" + hex } var arr = []; for (var j = 0; j < maxLength; j += 2) { arr.push("\\x" + hex.substr(j, 2)) } var result = arr.join(""); eval('result="' + result + '"'); return result } function hexToString( hex ) { var temp = hex.split("\\x") hex = "" for( var i=1;i> 4 & 15], i += a[E[c] & 15]; return i; } ================================================ FILE: qml/Chat/ChatPage.qml ================================================ import QtQuick 2.2 import utility 1.0 import mywindow 1.0 import QQItemInfo 1.0 import "../" import "../Utility" Item{ id: root anchors.fill: parent property string myuin property int type//记录自己的类型(好友,群,讨论组) signal sendClicked//点击发送按钮好调用此函数 property alias menuBar: menu_bar property alias rightBar: right_bar property alias inputBox: input property alias listModel: mymodel property QQItemInfo myinfo//用来储存自己的各种信息(用uin标识) onMyinfoChanged: { if(myinfo){//从缓冲区中读取数据 var message_list = myinfo.getChatRecords()//获取内存中的所有聊天记录 for(var i=0;i") } } MyShortcut{ target: input shortcut: "Ctrl+Enter" onTrigger: { input.insert(input.cursorPosition, "
") } } MyShortcut{ target: input shortcut: "Return" onTrigger: { button_send.clicked() } } MyShortcut{ target: input shortcut: "Enter" onTrigger: { button_send.clicked() } } Rectangle{ anchors.fill: parent color: "#eee" radius: 10 Item{ id: menu_bar height: 30 anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top } Item{ id: right_bar anchors.top: menu_bar.bottom anchors.right: parent.right anchors.bottom: parent.bottom width: 180 } MyTextArea{ id: input //font.pointSize: 12 //wrapMode: TextEdit.Wrap anchors.bottom: button_send.top anchors.left: parent.left anchors.right: right_bar.left anchors.margins: 10 height: 100 } MyButton{ id: button_send anchors.right: right_bar.left anchors.bottom: parent.bottom anchors.margins: 10 text: "发送" property int image_index: 0 onClicked: { inputBox.selectAll()//先选中全部 var messageInfo = myinfo.getChatMessageInfoById(myinfo.getMessageIndex()) //创建一个新的聊天储存聊天内容各种信息的对象(例如发送时间等等) messageInfo.contentData = input.selectedText messageInfo.senderUin = myqq.userQQ//发送者为当前登录的qq var data = { "sender_info":myqq,//发送者是当前登录的用户qq "to_uin":myuin,//发送给当前聊天页的好友或群 "mode": "right", "message_info": messageInfo, "parent_info": myinfo } listModel.append(data) inputBox.text = "" scroll_list.contentAtEnd()//将内容放到最后 } } Rectangle{ anchors.top: menu_bar.bottom anchors.right: right_bar.left anchors.bottom: input.top anchors.left: parent.left anchors.margins: 10 radius: 5 color: "#f5f5f5" border.width: 2 border.color: "#ddd" MyScrollView{ id: scroll_list anchors.fill: parent anchors.margins: 5 function contentAtEnd(){ var flickableItem = scroll_list.flickableItem flickableItem.contentY = flickableItem.contentHeight-scroll_list.height+60 //console.debug(flickableItem.contentY+","+(flickableItem.contentHeight-scroll_list.height+60)) } function isContentEnd(){ var flickableItem = scroll_list.flickableItem //console.debug(flickableItem.contentY+","+(flickableItem.contentHeight-scroll_list.height-5)) return flickableItem.contentY > flickableItem.contentHeight-scroll_list.height-5 } Item{ height: list.contentHeight+10 width: scroll_list.width-25 x:5 implicitHeight: height implicitWidth: width ListView{ id: list anchors.fill: parent spacing: 10 property int old_contentHeight: 0 model: ListModel{ id: mymodel } delegate: MessageListComponent{} } } } } } } ================================================ FILE: qml/Chat/ChatWindowCommand.qml ================================================ import QtQuick 2.2 import utility 1.0 import mywindow 1.0 import QQItemInfo 1.0 import "../Utility" MyWindow{ id: root property ChatPage currentShowPage//记录当前显示中的聊天页面 property int chatPageWidth: myqq.value("chatPageWidth", 600)//获取聊天页面的width, 初始化为600,聊天也的width visible: true minimumHeight: 500 minimumWidth: item_chatPage.minWidth+left_bar.width//设置最小宽度 width: left_bar.width+item_chatPage.width noBorder: true//无边框的 removable: true//可移动的 fixedSize: false//固定大小的 dockableWindow: false//可停靠的 topHint: false//窗口保持在最前端 noNotifyIcon:false//隐藏任务栏图标 color: "transparent" windowGlowItem.color: "black"//"#f07000" windowIcon: currentShowPage?currentShowPage.myinfo.avatar40:"qrc:/images/avatar.png" setLeftBorder: function(arg){ if(left_bar.isOpen&&left_bar.setBarDefaultWidth(left_bar.defaultWidth+arg)){//如果窗口大小设置成功 root.mySetLeftBorder(arg)//设置窗口位置 }else if(!left_bar.isOpen){ if(item_chatPage.setPageWidth(chatPageWidth+arg)){ root.mySetLeftBorder(arg) } } } setRightBorder: function(arg){ if(item_chatPage.setPageWidth(chatPageWidth+arg)){ root.mySetRightBorder(arg) } } onVisibleChanged: { if(visible) root.showFront()//显示到最屏幕最前端 } function setCurrentShowPage(page){ //console.log(page+","+currentShowPage) if(currentShowPage!==page&&page){//判断是否page就是当前活动页面 page.myinfo.isActiveChatPage=true//将他信息中是否为活跃页面的属性设置为true page.visible = true if(currentShowPage){ currentShowPage.visible = false//先将旧的page设置为隐藏 currentShowPage.myinfo.isActiveChatPage=false//将他信息中是否为活跃页面的属性设置为false } currentShowPage = page// } } Item{ id: item_chatPage property int maxWidth: 99999 property int minWidth: 500 objectName: "ChatWindowCommandItem" anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: left_bar.right width: chatPageWidth function setPageWidth(arg){ if(arg<=maxWidth&&arg>=minWidth){ chatPageWidth = arg return true }else{ return false } } Rectangle{ anchors.left: parent.left color: "#eee" width: 10 height: width visible: left_bar.width>0 } Rectangle{ anchors.bottom: parent.bottom anchors.left: parent.left color: "#eee" width: 10 height: width visible: left_bar.width>0 } } Connections{ target: myqq onAddChatPageToWindow:{ setCurrentShowPage(item)//设置当前显示的页面为item left_bar.addItem(item)//增加左栏 } onActiveChatPageChanged:{//如果活跃的Page改变为item //console.log(item) setCurrentShowPage(item)//设置当前显示的页面为item } } Rectangle{//用来管理当前聊天窗口内聊天页面的左栏 id:left_bar property bool isOpen: false//记录此栏是否处于打开状态 property int maxWidth:200 property int minWidth:70 property int defaultWidth: myqq.value("chatWindowLeftBarWidth", 150)//获取上次储存的值 anchors.top: parent.top anchors.left: parent.left anchors.bottom: parent.bottom color:"#ddd" radius:10 clip: true onDefaultWidthChanged: { if(isOpen){//如果是打开状态 setBarWidth(defaultWidth)//如果默认宽度改变就设置此栏的当前width } } onWidthChanged: { root.width = width+chatPageWidth//设置窗口的大小 } function openBar(){ console.debug("调用了打开侧栏:"+isOpen) if(!animation_width.running&&(!isOpen)){ isOpen = true animation_width.to = defaultWidth animation_width.start()//启动动画 } } function hideBar(){ if(!animation_width.running&&isOpen){ isOpen = false animation_width.to = 0 animation_width.start()//启动动画 } } function setBarDefaultWidth(arg){//设置默认的width if(arg<=maxWidth&&arg>=minWidth){ defaultWidth = arg return true }else{ return false } } function addItem(item){ var data={ "obj_item": item } mymodel.append(data)//增加条目 } function setBarWidth(arg){ if(arg<=maxWidth&&arg>=minWidth){ width = arg } } NumberAnimation{//动画控件 id: animation_width target: left_bar running: false duration: 300 property: "width" } Rectangle{ anchors.right: parent.right color: left_bar.color width: parent.radius height: width visible: parent.width>0 } Rectangle{ anchors.bottom: parent.bottom anchors.right: parent.right color: left_bar.color width: parent.radius height: width visible: parent.width>0 } MyScrollView{ anchors.fill: parent anchors.topMargin: image_quit_icon.height+15 Item{ height: mymodel.count*45+10 width: left_bar.width implicitHeight: height implicitWidth: width ListView{ id: list interactive: false anchors.fill: parent anchors.margins: 5 model: ListModel{ id:mymodel onCountChanged: { if(count <= 1){//如果没有了Item就让左栏的width为0 left_bar.hideBar()//隐藏此栏 }else if(count==2){ left_bar.openBar()//打开此栏 } } } spacing :10 delegate: component } } } Component{ id: component Item{ id: item_root width: parent.width height: avatar.height property ChatPage my: obj_item Rectangle{ id: rect_hover width: parent.width+10 height: parent.height+10 anchors.centerIn: parent color: root.currentShowPage==my?"#f07000":"#eee" radius: 5 opacity: 0.8 visible: item_mouse.hovered||root.currentShowPage==my } MouseArea{ id: item_mouse anchors.fill: parent hoverEnabled: true property bool hovered onEntered: { hovered = true } onExited: { hovered = false } onClicked: { setCurrentShowPage(my)//将自己设置为活跃页面 } } MyImage{ id: avatar x:10 width:35 height: 35 sourceSize.width: width grayscale: {//头像是否显示为黑白 if(my) return my.myinfo.state == FriendInfo.Offline return false } source: { if(my!==null) return my.myinfo.avatar40 else return "" } maskSource: "qrc:/images/bit.bmp" } Text{ id:text_nick anchors.verticalCenter: avatar.verticalCenter anchors.left: avatar.right anchors.leftMargin: 10 //font.pointSize: 14 text: my?my.myinfo.aliasOrNick:"" } Rectangle{ width: image_close_page.width+16 height: image_close_page.height+10 anchors.centerIn: image_close_page color: "#eee" radius: 5 opacity: 0.75 visible: image_close_page.visible&&root.currentShowPage!=my } Rectangle{ width: text_message_count.implicitWidth+10 height: image_close_page.width anchors.right: rect_hover.right anchors.rightMargin: 5 anchors.verticalCenter: parent.verticalCenter color: "red" radius: height visible: my?my.myinfo.unreadMessagesCount>0:false Text{ id: text_message_count anchors.centerIn: parent text: my?my.myinfo.unreadMessagesCount:"0"//未读消息的个数 color: "white" onTextChanged: { if(text == "100"){ text = "99+" } } } } SvgView{ id:image_close_page width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-quit.svg" anchors.right: rect_hover.right anchors.rightMargin: 5 anchors.verticalCenter: parent.verticalCenter visible: item_mouse.hovered MouseArea{ anchors.fill: parent onClicked: { mymodel.remove(index)//移除自己 myqq.removeChatPage(my.myuin, my.type)//移除聊天页面 } } } } } MouseArea{//接收这栏右边的鼠标事件 cursorShape :enabled?Qt.SizeHorCursor:Qt.ArrowCursor anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter height: root.height width: 2 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX } onPositionChanged: { var num_temp = mouseX-pressedX left_bar.setBarDefaultWidth(left_bar.defaultWidth+num_temp)//设置左栏的width } } } SvgView{ id:image_quit_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-quit.svg" anchors.left: parent.left anchors.top: parent.top anchors.margins: 10 MouseArea{ anchors.fill: parent onClicked: { root.close()//关闭窗口 mymodel.clear()//清除所有model } } } SvgView{ id:image_minimize_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-minimize.svg" anchors.top: image_quit_icon.top anchors.left: image_quit_icon.right MouseArea{ anchors.fill: parent onClicked: { root.showMinimized() } } } } ================================================ FILE: qml/Chat/DiscuChatPage.qml ================================================ import QtQuick 2.2 import utility 1.0 import QQItemInfo 1.0 import mywindow 1.0 import "../Utility" ChatPage{ id: root myinfo: myuin!=""?myqq.createDiscuInfo(myuin):null onMyinfoChanged: { if(!myinfo) return if(myinfo.membersCount>0){//如果成员列表已经存在就直接添加到list中 for(var i=0;i0){//如果成员列表已经存在就直接添加到list中 for(var i=0;i[发送失败]') messageInfo.destroy()//销毁此对象 } }else{ console.log("发送失败") mytext.text+=":发送失败" } } MyImage{ id: avatar x:mode=="left"?0:root.width-width width:40 height: 40 sourceSize.width: width maskSource: "qrc:/images/bit.bmp" source: root.myinfo.avatar40 onLoadError: { avatar.source = "qrc:/images/avatar.png" } } Text{ id: nick x: mode=="left"?avatar.x+avatar.width+5:avatar.x-implicitWidth-5 anchors.top: avatar.top text: root.myinfo.aliasOrNick } BorderImage { id: backgound x: mode=="left"?avatar.x+avatar.width+5:avatar.x-width-5 anchors.top: nick.bottom anchors.topMargin: 5 source: "qrc:/images/bubble_放飞心情_"+mode+".png" height: mytext.height+20 width: mytext.width+30 border.left: 20; border.top: 20 border.right: 20; border.bottom: 20 TextEdit{ id: mytext anchors.centerIn: parent readOnly: true textFormat :TextEdit.RichText//支持富文本 selectByMouse :true selectByKeyboard :true wrapMode: TextEdit.Wrap text: root.messageInfo.contentData onWidthChanged: { var temp = root.width-avatar.width-35 if(width>temp){ width = temp } } onTextChanged: { //console.debug(text) if(text[text.length-1]=="\n"){ text = text.substr(0, text.length-1) } } TextEditPlayGif{ target: mytext } } } } } ================================================ FILE: qml/Login/LoginPanel/AccountList.qml ================================================ import QtQuick 2.2 import QtQuick.Window 2.1 import mywindow 1.0 import utility 1.0 Window{ id:root height: list.contentHeight flags: Qt.SplashScreen color: "transparent" visible: true property bool isCanClose: false signal listClose signal clicked( var qq ) property int highlightIndex: 0//初始化为第0个 Component{ id: component Image{ id: background_iamge source: index == mymodel.count-1?"qrc:/images/list_item_bottom.png":"qrc:/images/list_item.png" width: root.width height: { if(root.highlightIndex == index) return 5/21*width else return Math.max(5/21*width-10*Math.max(root.highlightIndex-index, index-root.highlightIndex), 4/21*width) } property bool is: false Behavior on height{ NumberAnimation{ duration: 200 } } Rectangle{ id: background_rect anchors.top: parent.top anchors.topMargin: 1 x:2 width: parent.width-4 anchors.bottom: parent.bottom anchors.bottomMargin: index == mymodel.count-1?2:1 color: root.highlightIndex == index?"#fd7000":"#F3F2F2" } MyImage{ id: image maskSource: "qrc:/images/bit.bmp" source: mysource x:5 width: parent.height-10 height: width sourceSize.width: width anchors.verticalCenter: parent.verticalCenter onLoadError: { source = "qrc:/images/avatar.png" } } Text{ id:text font.pointSize: parent.height/6 anchors.left: image.right anchors.leftMargin: 10 anchors.verticalCenter: parent.verticalCenter text: root.highlightIndex==index?""+nick+""+"

"+myaccount+"":myaccount color: "black" } MouseArea{ anchors.fill: parent hoverEnabled: true onEntered: { root.highlightIndex = index } onClicked: { root.clicked(myaccount) root.close() listClose() } } SvgView{ anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 20 width: defaultSize.width*myqq.windowScale visible: root.highlightIndex == index source: "qrc:/images/button-quit.svg" property string qq: myaccount MouseArea{ anchors.fill: parent onClicked: { mymodel.remove(index) myqq.removeLoginedQQInfo(myaccount)//清除此账号的信息 } } } } } Component.onCompleted: { var qq_list = myqq.getLoginedQQInfo()//读取已经登录过的qq的信息 for( var i=0;i\?,\.\/]{6,16}/ } text: myqq.userPassword onTextChanged: { //console.log(myqq.userPassword+","+text) myqq.userPassword=text } style: TextFieldStyle { textColor: "black" background: Item{ implicitWidth: border_password.width-soft_keyboard_icon.width implicitHeight: 23/64*border_password.height //color: "blue" SvgView { id: border_password x: -5/220*width width: defaultSize.width*myqq.windowScale anchors.bottom: parent.bottom anchors.bottomMargin: -6/64*height source: "qrc:/images/inputBox2.svg" } } } } SvgView { id: unfold_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/inputBox-more.svg" anchors.left: input_qq.right anchors.leftMargin: -1/16*root.width anchors.verticalCenter: input_qq.verticalCenter property bool isCloseing: false onIsCloseingChanged: { if(isCloseing&&!mymouse1.hovered) { unfold_icon.rotation = 0 isCloseing=false qqlistopen = false } } Connections{ id: connection target: null onClicked:{ input_qq.text = qq//填充qq号码 } onListClose:{ unfold_icon.isCloseing=true } } MouseArea{ id: mymouse1 anchors.fill: parent hoverEnabled: true property bool hovered: false onEntered: hovered = true onExited: hovered = false onClicked: { if( unfold_icon.isCloseing ){ unfold_icon.rotation = 0 unfold_icon.isCloseing=false qqlistopen = false }else if( unfold_icon.rotation == 0 ){ unfold_icon.rotation = 180 qqlistopen = true var component = Qt.createComponent("AccountList.qml"); if (component.status == Component.Ready){ var data={"width":21/22*root.width,"x": utility.mouseDesktopPos().x-mouse.x-unfold_icon.x,"y": utility.mouseDesktopPos().y-mouse.y-unfold_icon.y+input_qq.height} connection.target = component.createObject(input_qq, data); } } } } } SvgView { id: soft_keyboard_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/soft-keyboard.svg" anchors.left: input_password.right anchors.leftMargin: -1/16*root.width anchors.verticalCenter: input_password.verticalCenter property bool keyboardClose: true property bool isCloseing: false onIsCloseingChanged: { if(isCloseing&&!mymouse2.hovered) { keyboardClose=true isCloseing=false } } Connections{ id: connections target: null onInput:{ input_password.text+=arg } onBackspace:{ input_password.text = input_password.text.substr(0, input_password.text.length-1) } onKeyboardClose:{ soft_keyboard_icon.isCloseing=true } } MouseArea{ id: mymouse2 anchors.fill: parent property bool hovered: false hoverEnabled: true onEntered: hovered = true onExited: hovered = false onClicked: { if(soft_keyboard_icon.isCloseing){ soft_keyboard_icon.keyboardClose=true soft_keyboard_icon.isCloseing=false }else if( soft_keyboard_icon.keyboardClose){ soft_keyboard_icon.keyboardClose=false var component = Qt.createComponent("../../Utility/KeyboardPage/SoftKeyboard.qml"); if (component.status == Component.Ready){ var sprite = component.createObject(input_password); connections.target = sprite } } } } } } ================================================ FILE: qml/Login/LoginPanel/LoginPage.qml ================================================ import QtQuick 2.2 import utility 1.0 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import QtQuick.Particles 2.0 import mywindow 1.0 import qqstars 1.0 import "../../Utility" import "../" Item{ property alias contentItem: effect width: effect.actualWidth height: effect.actualHeight function reLogin(){ myqq.loginStatus = QQ.WaitLogin animation_avatar.stop()//停止动画 avatar_image.x = -30/77*avatar_image.width//将头像位置复原 } MyRectangularGlow{ id: effect glowRadius: 50 spread: 0.1 color: "black" glowOpacity: 0.75 width: root.width height: root.height biasY: 20 item:SvgView { id: root source: "qrc:/images/login-panel2.svg" width: defaultSize.width*myqq.windowScale Connections{ target: myqq onLoginStatusChanged:{ if( myqq.loginStatus == QQ.Logining ) animation_avatar.start() } } ParticleSystem { id: particles anchors.fill: parent //running: true ImageParticle { source: "qrc:/images/未标题-1.png" } Emitter { id: pulseEmitter anchors.right: parent.right anchors.top: parent.top anchors.margins: 120 emitRate: 10 lifeSpan: 2000 velocity: AngleDirection{ magnitude: 35; magnitudeVariation: 20 angle: -90 angleVariation: 45 } size: 5 sizeVariation: 20 } } MyImage{ id:avatar_image maskSource: "qrc:/images/bit.bmp" width: 80*myqq.windowScale height: width sourceSize.width: width source: myqq.avatar240 x:-30/80*width anchors.verticalCenter: inputarea.verticalCenter Connections{ target: myqq onUserQQChanged:{ avatar_image.source = myqq.avatar240 } } onLoadError:{ console.log("头像加载出错:"+myqq.avatar240) source = "qrc:/images/avatar.png" } SvgView{ id: rect_border source: "qrc:/images/avatar-border.svg" anchors.centerIn: parent width: avatar_image.width*1.23 } Timer{ id: timer_login interval: 10 onTriggered: myqq.login()//开始登录 } NumberAnimation{ id: animation_avatar target: avatar_image property: "x" duration: 300 from: -30/77*avatar_image.width to: root.width/2 - avatar_image.width/2 onStopped: timer_login.start()//必须在结束后启动定时器然后在定时器结束后在调用login(),不然会导致ui卡一下 } SvgView{ id: user_status_icon width: defaultSize.width*myqq.windowScale anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: -0.02*avatar_image.width //sourceSize.width: width //width: 5/16*avatar_image.width enabled: myqq.loginStatus == QQ.WaitLogin source: "qrc:/images/status-"+myqq.stateToString+"-1.svg" MyMenu{ id: menu_state styleSheet: "QMenu::item:selected { background: #F07000; color:#E6FFFF; height:25px; } QMenu{ background-image: url(':/images/menu_background.png'); } QMenu::icon{ padding-left:8px; } QMenu::item{ padding-left:32px; padding-right:20px; height:22px; } QMenu::separator { height: 1px; margin:5px 0px 5px 22px; background: #B2C0CD; } " MyMenuItem{ text: "我在线上" icon: "qrc:/images/imonline.png" onTriggered: { myqq.state = QQ.Online } } MyMenuItem{ text: "Q我吧" icon: "qrc:/images/imcallme.png" onTriggered: { myqq.state = QQ.Callme } } MyMenuItem{ text: "离开" icon: "qrc:/images/imaway.png" onTriggered: { myqq.state = QQ.Away } } MyMenuItem{ text: "忙碌" icon: "qrc:/images/imbusy.png" onTriggered: { myqq.state = QQ.Busy } } MyMenuItem{ text: "请勿打扰" icon: "qrc:/images/imsilent.png" onTriggered: { myqq.state = QQ.Silent } } MyMenuItem{ text: "隐身" icon: "qrc:/images/imhidden.png" onTriggered: { myqq.state = QQ.Hidden } } } MouseArea{ anchors.fill: parent onClicked: { menu_state.popup() } } } Text{ text: myqq.userQQ font.pointSize: avatar_image.width/8 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.bottom anchors.topMargin: 10 visible: myqq.loginStatus == QQ.Logining } } SvgView{ id:image_quit_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-quit.svg" anchors.left: parent.left anchors.top: parent.top anchors.margins: 10 MouseArea{ anchors.fill: parent onClicked: Qt.quit() } } SvgView { id: image_qq_for_ubuntu width: defaultSize.width*myqq.windowScale //sourceSize.width: width source: "qrc:/images/QQ-for-ubuntu.svg" anchors.top: parent.top anchors.right: parent.right anchors.margins: 20 } LoginInputArea{ id: inputarea width: 220*myqq.windowScale height: 64*myqq.windowScale visible: myqq.loginStatus == QQ.WaitLogin anchors.left: avatar_image.right anchors.leftMargin: 30 anchors.top: image_qq_for_ubuntu.bottom anchors.topMargin: root.height/10 Component.onCompleted: { //console.log(width) //console.log(height) } LoginCheckBox{ id: checkbox_rememberpassword //height:2/40*root.width checked: myqq.rememberPassword//myqq.value("rememberpassword", 0)==1 anchors.left: inputarea.left anchors.top: inputarea.bottom anchors.topMargin: root.height/12 text: "记住密码" onCheckedChanged: { if( !checked ){ checkbox_autologin.checked = false //myqq.removeValue("password")//将密码置空 } } } LoginCheckBox{ id:checkbox_autologin //height:2/40*root.width checked: myqq.autoLogin//myqq.value("autologin", 0)==1 anchors.right: inputarea.right anchors.rightMargin: 10 anchors.top: inputarea.bottom anchors.topMargin: root.height/12 text: "自动登录" onCheckedChanged: { if(checked) checkbox_rememberpassword.checked = true } } } Connections{ target: myqq onUserQQChanged:{ checkbox_rememberpassword.checked = myqq.rememberPassword//myqq.value("rememberpassword", 0)==1 checkbox_autologin.checked = myqq.autoLogin//myqq.value("autologin", 0)==1 } } Item{ id: progress_item clip: true anchors.bottom: parent.bottom anchors.bottomMargin: root.height/4.2-height visible: myqq.loginStatus == QQ.Logining onVisibleChanged: { if(visible) progress_animation1.start() else{ progress_animation1.stop() } } width: root.implicitWidth height: image_progress1.implicitHeight Image{ id: image_progress1 source: "qrc:/images/progress-bar.png" width: root.width NumberAnimation{ id:progress_animation1 running: false target: image_progress1 property: "x" from: 0 to: root.width duration: 5000 onStopped: { if( progress_item.visible ) start() } } } Image{ id: image_progress2 source: "qrc:/images/progress-bar.png" width: root.width anchors.right: image_progress1.left } } MyLoginButton{ id:button_login anchors.bottom: parent.bottom anchors.bottomMargin: 30/250*root.height-height/2-1*height/defaultSize.height width: defaultSize.width*myqq.windowScale anchors.horizontalCenter: parent.horizontalCenter text: myqq.loginStatus != QQ.WaitLogin?"取 消":"登 录" font.pointSize: width/15 onClicked: { if( myqq.loginStatus == QQ.WaitLogin ){ if( myqq.userQQ!=""&&myqq.userPassword!="" ){ myqq.loginStatus = QQ.Logining myqq.autoLogin = checkbox_autologin.checked myqq.rememberPassword = checkbox_rememberpassword.checked myqq.saveUserPassword()//保存密码 utility.setValue("mainqq", myqq.userQQ)//设置当前活动qq为myqq.userQQ } }else if( myqq.loginStatus == QQ.Logining ){ reLogin()//调用重新登录 myqq.loginStatus = QQ.WaitLogin } } } SvgView{ id: button_setting width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-settings.svg" anchors.right: parent.right anchors.verticalCenter: button_login.verticalCenter visible: myqq.loginStatus == QQ.WaitLogin MouseArea{ anchors.fill: parent onClicked: { main.openSettingPage()//进行设置 } } } Component.onCompleted: { //if( myqq.value("autologin", 0)==1 )//此账号如果设置了自动登录 if(myqq.autoLogin)//如果是自动登录 myqq.loginStatus = QQ.Logining forceActiveFocus() } Keys.onEnterPressed: { button_login.clicked() } Keys.onReturnPressed: { button_login.clicked() } } } } ================================================ FILE: qml/Login/MyLoginButton.qml ================================================ import QtQuick 2.2 import mywindow 1.0 SvgView{ id:root //sourceSize.width: width property alias text: button_text.text property alias font: button_text.font source: { if( mouse.pressed ) return "qrc:/images/button-login-press.svg" else if(mouse.hover) return "qrc:/images/button-login-hover.svg" else return "qrc:/images/button-login.svg" } signal clicked Rectangle{ radius: 10 anchors.fill: parent color: "#888" visible: !root.enabled } Text { id: button_text anchors.centerIn: parent color: "white" } MouseArea{ id:mouse enabled: root.enabled property bool hover: false anchors.fill: parent hoverEnabled: true onEntered: { hover = true } onExited: { hover = false } onClicked: { root.clicked() } } } ================================================ FILE: qml/Login/SettingPage.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import utility 1.0 import mywindow 1.0 import "../Utility" import "../Utility/ComboBox" Item{ width: effect.actualWidth height: effect.actualHeight MyRectangularGlow{ id: effect glowRadius: 50 spread: 0.1 color: "black" glowOpacity: 0.75 width: root.width height: root.height biasY: 20 item:SvgView { id:root source: "qrc:/images/login-panel.svg" width: defaultSize.width*myqq.windowScale SvgView{ id:image_quit_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-quit.svg" anchors.left: parent.left anchors.top: parent.top anchors.margins: 10 MouseArea{ anchors.fill: parent onClicked: { main.openLoginPage()//返回登录 } } } SvgView { id: image_qq_for_ubuntu width: defaultSize.width*myqq.windowScale source: "qrc:/images/QQ-for-ubuntu.svg" anchors.top: parent.top anchors.right: parent.right anchors.margins: 20 } Grid{ anchors.horizontalCenter: parent.horizontalCenter anchors.top: image_qq_for_ubuntu.bottom anchors.topMargin: root.height/10 columns: 2 rowSpacing: root.width/20 columnSpacing: root.height/20 horizontalItemAlignment: Qt.AlignHCenter verticalItemAlignment: Qt.AlignVCenter MyTextField{ id: proxy_location_input enabled: proxy_combo.currentIndex != 0 width: root.width/2.5 height: width/8 title: "地址:" text: utility.value("proxyLocation", "") } MyTextField{ id: proxy_port_input enabled: proxy_combo.currentIndex != 0 width: root.width/2.5 height: width/8 title: "端口:" text: utility.value("proxyPort", "") } MyTextField{ id: proxy_username_input enabled: proxy_combo.currentIndex != 0 width: root.width/2.5 height: width/8 title: "账户:" text: utility.value("proxyUsername", "") } MyTextField{ id: proxy_password_input enabled: proxy_combo.currentIndex != 0 width: root.width/2.5 height: width/8 title: "密码:" text: utility.value("proxyPassword", "") } Item{ width: root.width/2.5 height: width/8 Text { id: proxy_type_text text: "类型:" font.pointSize: parent.height/2 anchors.verticalCenter: parent.verticalCenter } MyComboBox{ id: proxy_combo height: parent.height anchors.left: proxy_type_text.right anchors.right: parent.right currentIndex: utility.value("proxyTypeIndex", 0) model: ListModel { ListElement { text: "不使用代理"; value: Utility.NoProxy} ListElement { text: "HTTP代理"; value: Utility.HttpProxy} ListElement { text: "SOCKS5代理"; value: Utility.Socks5Proxy} } } } MyButton { text: "测 试" width: root.width/4 height: width/4 visible: proxy_combo.currentIndex != 0 font.pointSize: proxy_type_text.font.pointSize onClicked:{ console.log("fdsafds") utility.setApplicationProxy(proxy_combo.currentValue, proxy_location_input.text, proxy_port_input.text, proxy_username_input.text, proxy_password_input.text) utility.httpGet(testNetwork, "http://d.web2.qq.com/channel/poll2") button_affirm.enabled = false enabled = false } function testNetwork(error ,data) { var temp1 = utility.value("proxyLocation", "") var temp2 = utility.value("proxyPort", "") var temp3 = utility.value("proxyUsername", "") var temp4 = utility.value("proxyPassword", "") var temp5 = utility.value("proxyType", Utility.NoProxy) utility.setApplicationProxy(temp5, temp1, temp2, temp3, temp4)//将代理设置复原 button_affirm.enabled = true enabled = true if( error ) myqq.showWarningInfo("测试失败") else myqq.showWarningInfo("测试通过") } } } MyLoginButton{ id: button_affirm anchors.bottom: parent.bottom anchors.bottomMargin: 30/250*root.height-height/2-1*height/defaultSize.height width: defaultSize.width*myqq.windowScale anchors.horizontalCenter: parent.horizontalCenter text:"确 认" font.pointSize: width/15 onClicked: { utility.setApplicationProxy(proxy_combo.currentValue, proxy_location_input.text, proxy_port_input.text, proxy_username_input.text, proxy_password_input.text) utility.setValue("proxyLocation", proxy_location_input.text) utility.setValue("proxyPort", proxy_port_input.text) utility.setValue("proxyUsername", proxy_username_input.text) utility.setValue("proxyPassword", proxy_password_input.text) utility.setValue("proxyType", proxy_combo.currentValue) utility.setValue("proxyTypeIndex", proxy_combo.currentIndex) main.openLoginPage()//返回登录 } } Keys.onEnterPressed: { button_affirm.clicked() } Keys.onReturnPressed: { button_affirm.clicked() } } } } ================================================ FILE: qml/Login/main.qml ================================================ import QtQuick 2.2 import QtQuick.Particles 2.0 import mywindow 1.0 import utility 1.0 //import Qt.labs.settings 1.0 import QQItemInfo 1.0 import MyTextEditPlugin 1.0 import "../Utility" import "../MainPanel" import "LoginPanel" import "../Chat" MyWindow{ id:main windowIcon: "qrc:/images/avatar.png" actualWidth: 350*myqq.windowScale+200 actualHeight: 250*myqq.windowScale+150 visible: true//可视的 contentItemAreaTop: login_page.contentItem.y///////////////////////////设置 contentItemAreaBottom: contentItemAreaTop+login_page.contentItem.height//响应 contentItemAreaLeft: login_page.contentItem.x/////////////////////////鼠标的 contentItemAreaRight: contentItemAreaLeft+login_page.contentItem.width//区域 noBorder: true//无边框的 removable: true//可移动的 fixedSize: true//固定大小的 dockableWindow: false//可停靠的 topHint: false//窗口保持在最前端 noNotifyIcon: true//隐藏任务栏图标 windowGlow: false//是否开启阴影 color: "transparent" function openSettingPage() {//进行设置 settings_page.enabled = true flipable.flipped = false } function openLoginPage() {//打开登录面板 login_page.enabled = true flipable.flipped = true } Connections{ target: myqq onError:{ if( message.indexOf("验证码")<0 ){ login_page.reLogin()//重新登录 myqq.closeCodeWindow()//关闭输入验证码的窗口 }else{ myqq.updataCode()//刷新验证码 } } } Connections{ target: systemTray onActivated:{ if( arg == MySystemTrayIcon.Trigger ) { main.showFront() } } onTriggered: { if(arg == "打开主面板"){ main.showFront() } } } ParticleSystem {//粒子系统 id: particles anchors.centerIn: parent width: main.actualWidth height: main.actualHeight Age{ system: particles anchors.left: parent.left height: parent.height width: 80 once: true lifeLeft: 3000 advancePosition: false } Age{ system: particles anchors.right: parent.right height: parent.height width: 80 once: true lifeLeft: 3000 advancePosition: false } Age{ system: particles anchors.bottom: parent.bottom height: 80 width: parent.width once: true lifeLeft: 3000 advancePosition: false } ImageParticle { source: "qrc:/images/star.png" groups: ['star'] alpha: 0.2 alphaVariation: 0.1 colorVariation: 2 autoRotation: true rotationVariation: 360 rotationVelocity: 40 } Emitter { id: pulseEmitter anchors.top: parent.top width: parent.width group: 'star' emitRate: 8 lifeSpan: parent.height*1000/40 velocityFromMovement: 20 velocity: AngleDirection{ angle: 90 angleVariation: 50 magnitude: 90 magnitudeVariation: 10 } acceleration: AngleDirection{ angle: -90 magnitude: 5 magnitudeVariation: 2 } size: 20 sizeVariation: 5 endSize: 0 } } Flipable { id: flipable anchors.fill: parent property bool flipped: true onFlippedChanged:{ timer.start() } Timer{ id: timer interval: 200 onTriggered: { if( flipable.flipped ){ settings_page.enabled = false }else{ login_page.enabled = false } } } front: LoginPage{ id: login_page anchors.right: parent.right } back: SettingPage{ id: settings_page; enabled: false anchors.right: parent.right } transform: Rotation { id: rotation origin.x: flipable.width/2 origin.y: flipable.height/2 axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis angle: 0 // the default angle } states: State { name: "back" PropertyChanges { target: rotation; angle: 180 } when: !flipable.flipped } transitions: Transition { NumberAnimation { target: rotation; property: "angle"; duration: timer.interval ; easing.type: Easing.InQuart } } } } ================================================ FILE: qml/MainPanel/ListPage/AllListPage.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import mywindow 1.0 TabView { id: frame property int old_current:0 Tab { active: true id: friend_list FriendList{ id: rect1 width: parent.width height: parent.height NumberAnimation{ id: animat1 target: rect1 duration: 200 running: false easing.type: Easing.InCubic property: "x" from: -width to:0 } Connections{ target: frame onCurrentIndexChanged: { if(frame.currentIndex==0){ animat1.start() frame.old_current = 0 } } } } } Tab { active: true GroupAndDiscuPage{ id:rect2 width: parent.width height: parent.height NumberAnimation{ id: animat2 target: rect2 duration: 200 running: false easing.type: Easing.InCubic property: "x" from: frame.old_current==0?width:-width to:0 } Connections{ target: frame onCurrentIndexChanged: { if(frame.currentIndex==1){ animat2.start() frame.old_current = 1 } } } } } Tab { active: true id: recent_list RecentList{ id:rect3 width: parent.width height: parent.height NumberAnimation{ id: animat3 target: rect3 duration: 200 running: false easing.type: Easing.InCubic property: "x" from: width to:0 } Connections{ target: frame onCurrentIndexChanged: { if(frame.currentIndex==2){ animat3.start() frame.old_current = 2 } } } } } style: TabViewStyle { //tabOverlap: 10 tabsMovable : true tabsAlignment:Qt.AlignHCenter tab: Item{ implicitHeight: icon.height+20 implicitWidth: control.width/3 //width: control.width/3 SvgView{ id: icon anchors.horizontalCenter: parent.horizontalCenter source: { if(index==0) return "qrc:/images/friendList_select.svg" else if(index==1) return "qrc:/images/groupList_select.svg" else return "qrc:/images/recentList_select.svg" } } } frame: Item {} } } ================================================ FILE: qml/MainPanel/ListPage/DiscuList.qml ================================================ import QtQuick 2.2 import mywindow 1.0 import utility 1.0 import "../" import "../../Utility" import QQItemInfo 1.0 Item{ id: root clip: true function getDiscusListFinished(error, data) {//获取讨论组列表完成 if(error){ myqq.getDiscusList(getDiscusListFinished) //讨论组列表 return } data = JSON.parse(data) if(data.retcode ==0 ) { var list_info = data.result.dnamelist for( var i=0; i< list_info.length;++i ) { var info = myqq.createDiscuInfo(list_info[i].did) info.nick = list_info[i].name//设置昵称 mymodel.append({"obj_info": info}) } } } Component.onCompleted: { myqq.getDiscusList(getDiscusListFinished) //讨论组列表 } MyScrollView{ anchors.fill: parent Item{ height: mymodel.count*40+10 width: root.width implicitHeight: height implicitWidth: width ListView{ id: list interactive: false anchors.fill: parent model: ListModel{ id:mymodel } spacing :10 delegate: component } } } Component{ id: component Item{ id: item_root width: parent.width height: avatar.height property DiscuInfo myinfo: obj_info MyImage{ id: avatar x:10 width:40 height: 40 sourceSize.width: width source: "qrc:/images/avatar.png" maskSource: "qrc:/images/bit.bmp" } Text{ id:text_nick anchors.top: avatar.top anchors.left: avatar.right anchors.leftMargin: 10 font.pointSize: 14 text: item_root.myinfo.aliasOrNick } MouseArea{ anchors.fill: parent onDoubleClicked: { myqq.addChatPage(item_root.myinfo.uin, QQItemInfo.Discu) } } } } } ================================================ FILE: qml/MainPanel/ListPage/FriendList.qml ================================================ import QtQuick 2.2 import mywindow 1.0 import utility 1.0 import "../" import "../../Utility" import QQItemInfo 1.0 Item{ id: friendlist_main width: parent.width height: parent.height clip:true function addModel( name, index ,obj_friendListData ){ mymodel.append({"obj_groupingName": name, "obj_groupingIndex": index, "obj_friendListData":obj_friendListData}) } function insertModel( sort, name, index ,obj_friendListData ){ mymodel.insert(sort, {"obj_groupingName": name, "obj_groupingIndex": index, "obj_friendListData":obj_friendListData}) } function getFriendListFinished(error, data) {//获取好友列表完成 if(error){ myqq.getFriendList(getFriendListFinished) //获取好友列表 return } data = JSON.parse(data) if( data.retcode == 0) { var marknames = data.result.marknames//备注信息 for( var i=0; i0&&categories[0].index>0)//如果分组数目大于0,但是第一个分组的index不为0 addModel("我的好友", 0, data.result)//则将默认的"我的好友"分组加进去 var arr = new Array for(i=0; ib.sort)) return a.sort>b.sort?1:-1//将数组按照里边的sort属性排序 }) for(i=0; i=onlineIndex) move(index, onlineIndex++, 1)//移动此项 } function stateToOffline(index){ //当序列为index的model项调用此函数后,会将他前移到尾部 if(index<=count-1){ --onlineIndex move(index, count-1, 1) } } } spacing: 10 delegate: component2 anchors.top: titleBar.bottom anchors.topMargin: 10 width: parent.width height: parent.height } Component{ id: component2 Item{ id: item_root width: parent.width height: avatar.height property FriendInfo myinfo: obj_info Component.onCompleted: { if(myinfo.state != FriendInfo.Offline){//如果不是离线状态 mymodel2.stateToOnline(index)//将自己移动到前面 } } Connections{ target: myinfo onStateChanged:{//如果状态改变 if(myinfo.state == FriendInfo.Offline){//如果是离线状态 mymodel2.stateToOffline(index)//将自己移动到最后 }else{ mymodel2.stateToOnline(index)//否则就往上移动 } } } MyImage{ id: avatar x:10 width:40 height: 40 sourceSize.width: width grayscale: myinfo.state==FriendInfo.Offline maskSource: "qrc:/images/bit.bmp" cache: false source: item_root.myinfo.avatar40 onLoadError: { myinfo.avatar40 = "qrc:/images/avatar.png" } } Text{ id:text_nick anchors.top: avatar.top anchors.left: avatar.right anchors.leftMargin: 10 font.pointSize: 14 text: myinfo.aliasOrNick//myqq.value(info.uin+"alias", info.nick) } Text{ id:text_signature//个性签名 anchors.left: text_nick.left anchors.bottom: avatar.bottom font.pointSize: 8 text: myinfo.QQSignature//myqq.value(info.uin+"signature", "获取中...") } MouseArea{ anchors.fill: parent onDoubleClicked: { myqq.addChatPage(myinfo.uin, QQItemInfo.Friend) } } } } } } } ================================================ FILE: qml/MainPanel/ListPage/GroupAndDiscuPage.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 Item{ TabView{ anchors.fill: parent Tab{ title: "QQ群" active: true GroupList{ width: parent.width height: parent.height } } Tab{ title: "讨论组" active: true DiscuList{ width: parent.width height: parent.height } } style: TabViewStyle { //tabOverlap: 10 frameOverlap: -10 tabsMovable : true tabsAlignment:Qt.AlignHCenter tab:Rectangle { color: styleData.selected ? "#f07000" :"#eee" border.color: "#888" border.width: 1 implicitWidth: Math.max(text.width + 4, 80) implicitHeight: 20 radius: 10 Rectangle{ height: parent.implicitHeight width: height x:index==1?0:parent.width-width color: parent.color border.width: parent.border.width border.color: parent.border.color Rectangle{ height: parent.height-2*width width: parent.border.width anchors.verticalCenter: parent.verticalCenter x:index==0?0:parent.width-width color: parent.color } } Text { id: text anchors.centerIn: parent text: styleData.title color: styleData.selected ? "white" : "black" } } frame: Item {} } } } ================================================ FILE: qml/MainPanel/ListPage/GroupList.qml ================================================ import QtQuick 2.2 import mywindow 1.0 import utility 1.0 import "../" import "../../Utility" import QQItemInfo 1.0 Item{ id: root clip: true function getGroupListFinished( error, data ) { if(error){ myqq.getGroupList(getGroupListFinished) //获取群列表 return } data = JSON.parse(data) if(data.retcode ==0 ) { var groupmarknames = data.result.gmarklist//群备注信息 var i=0; for( i=0; i0 Text{ id: text_message_count anchors.centerIn: parent text: myinfo.unreadMessagesCount//未读消息的个数 color: "white" onTextChanged: { if(text == "100"){ text = "99+" } } } } Rectangle{ height: 1 anchors.left: avatar.left anchors.right: rect_message_count.right anchors.bottom: parent.bottom color: "#ddd" } MouseArea{ anchors.fill: parent onDoubleClicked: { myqq.addChatPage(myinfo.uin, myinfo.mytype) } } } } } ================================================ FILE: qml/MainPanel/MainPanelPage.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import mywindow 1.0 import "ListPage" Item{ id: root clip:true Text{ id: text_nick x: 10 font.pointSize: 16 font.bold: true color: "black" text: myqq.nick } Text{ id: text_signature anchors.left: text_nick.left anchors.top: text_nick.bottom anchors.topMargin: 10 color: "black" font.pointSize: 8 Component.onCompleted: { myqq.getQQSignature(myqq.userQQ, getMeSignature)//去网络请求签名 } function getMeSignature(data){//获得自己的个性签名成功后 data = JSON.parse(data) if( data.retcode==0 ){ text = data.result[0].lnick } } } AllListPage{ anchors.top: text_signature.bottom anchors.topMargin: 1 width: root.width-4 anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: 2 } } ================================================ FILE: qml/MainPanel/main.qml ================================================ import QtQuick 2.2 import mywindow 1.0 import QtQuick.Window 2.1 import utility 1.0 import "../Utility" import "../Chat" import qqstars 1.0 //import Qt.labs.settings 1.0 MyWindow{ id:main visible: true//可视的 noBorder: true//无边框的 removable: true//可移动的 fixedSize: false//固定大小的 dockableWindow: true//可停靠的 topHint: true//窗口保持在最前端 noNotifyIcon: true//隐藏任务栏图标 color: "transparent" centered: false minimumWidth: 220 minimumHeight: 400 maximumWidth: 500 maximumHeight: 700 width: 240 height: 500 Component.onCompleted: { width = Math.max(200, minimumWidth) height = Math.max(500, minimumHeight) main.x = Screen.desktopAvailableWidth - main.actualWidth main.y = 20 } function windowToActive(){//将窗口转为活动状态 main.showFront() if(main.visible) { main.showWindow()//从停靠状态转为可停靠状态 } } Connections{ target: systemTray onActivated:{ if( arg == MySystemTrayIcon.Trigger ) { windowToActive()//将窗口转为活动状态 } } onTriggered: { if(arg == "打开主面板"){ windowToActive()//将窗口转为活动状态 } } } Rectangle{ anchors.fill: parent radius: 10 gradient: Gradient { GradientStop { position: 0 ; color: "#EEEDEC" } GradientStop { position: 120/main.height ; color: "#E7E5E4" } GradientStop { position: 120/main.height+0.01 ; color: "#f9f9f8" } GradientStop { position: 1 ; color: "#f9f9f8" } } SvgView{ id:image_quit_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-quit.svg" anchors.left: parent.left anchors.top: parent.top anchors.margins: 10 MouseArea{ anchors.fill: parent onClicked: { myqq.state = QQ.Offline//将状态改为离线 Qt.quit() } } } SvgView{ id:image_minimize_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-minimize.svg" anchors.top: image_quit_icon.top anchors.left: image_quit_icon.right MouseArea{ anchors.fill: parent onClicked: { main.showMinimized() } } } MainPanelPage{ id: panel_page anchors.top: image_minimize_icon.bottom anchors.topMargin: 10 anchors.bottom: parent.bottom width: parent.width } } } ================================================ FILE: qml/QQItemInfo/DiscuInfo.qml ================================================ import QtQuick 2.2 import QQItemInfo 1.0 DiscuInfo{ userQQ: myqq.userQQ } ================================================ FILE: qml/QQItemInfo/FriendInfo.qml ================================================ import QQItemInfo 1.0 import QtQuick 2.2 FriendInfo{ id: root property bool getImageing: false//记录是否正在获取图片 userQQ: myqq.userQQ onUinChanged: {//如果uin变了 myqq.getFriendQQ( uin, getQQFinished )//获取好友的真实qq号 } function getUserDataFinished(error, data){//获取资料完成 if(error){//如果出错了 myqq.getUserData(uin, getUserDataFinished)//去获取好友资料 return } data = JSON.parse(data) if( data.retcode==0 ){ nick = data.result.nick//设置昵称 } } function getQQFinished(error, data){//获取好友真实qq后调用的函数 //console.log(root.uin+"获得真实QQ结束:"+data) if(error){ myqq.getFriendQQ( uin, getQQFinished )//获取好友的真实qq号 return } data = JSON.parse(data) if( data.retcode==0 ){ account = data.result.account if( avatar40=="qrc:/images/avatar.png" ) getAvatar(40)//去获取头像 } } function getAvatarFinished( error, path ,name){ getImageing = false//获取图像结束 if(error){//如果出错了 getAvatar(40)//重新获取 return } var imageName = path+"/"+name avatar40 = imageName//保存自己头像的地址 //console.log(nick+"获取头像完成:"+imageName) } function getQQSignatureFinished(error, data){//获取个性签名完成 if(error){ myqq.getQQSignature( uin, getQQSignatureFinished )//获取个性签名 return } data = JSON.parse(data) if( data.retcode==0 ){ QQSignature = data.result[0].lnick//储存个性签名 } } onHttpGetQQSignature:{//如果收到c++端发来的去获取个性签名的信号 myqq.getQQSignature( uin, getQQSignatureFinished )//获取个性签名 } function getAvatar(size){ if(account!=""&&!getImageing){ getImageing = true//正在获取头像 myqq.downloadImage(QQItemInfo.Friend , "http://q.qlogo.cn/headimg_dl?spec="+String(size)+"&dst_uin="+account , account, String(size), getAvatarFinished)//下载头像 } } onAvatar40Changed: { if( avatar40=="qrc:/images/avatar.png" ) getAvatar(40) } } ================================================ FILE: qml/QQItemInfo/GroupInfo.qml ================================================ import QtQuick 2.2 import QQItemInfo 1.0 GroupInfo{ id: root property bool getImageing: false//记录是否正在获取图片 userQQ: myqq.userQQ onCodeChanged: { //console.log(nick+"将要获取真实qq") if(code!=""){ myqq.getFriendQQ(code, getQQFinished)//获得真实qq } } onAccountChanged: { if(account==""&&code!="") myqq.getFriendQQ(nick, getQQFinished)//获得真实qq } function getQQFinished(error, data){//获取真实群号后调用的函数 if(error){ myqq.getFriendQQ(code, getQQFinished) return } data = JSON.parse(data) if( data.retcode==0 ){ account = data.result.account //console.log(code+"的真实qq:"+account) if(avatar40=="qrc:/images/avatar.png") getAvatar(40) } } function getAvatarFinished( error, path ,name){ getImageing=false//将正在获取头像置为false if(error){//如果出错了 getAvatar(40)//重新获取 return } var imageName = path+"/"+name avatar40 = imageName } function getAvatar(size){ if(account!=""&&!getImageing){ getImageing = true//置为true,不然可能会多次请求头像 myqq.downloadImage(QQItemInfo.Group , "http://p.qlogo.cn/gh/"+account+"/"+account+"/"+String(size) , account, String(size), getAvatarFinished)//下载头像 } } onAvatar40Changed: { if(avatar40=="qrc:/images/avatar.png") getAvatar(40) } } ================================================ FILE: qml/Utility/CodeInput.qml ================================================ import mywindow 1.0 import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import utility 1.0 import qqstars 1.0 MyWindow{ id: root_window visible: true//可视的 color: "transparent" noBorder: true//无边框的 removable: true//可移动的 fixedSize: true//固定大小的 dockableWindow: false//可停靠的 topHint: false//窗口保持在最前端 noNotifyIcon: true//隐藏任务栏图标 modality : Qt.ApplicationModal width: root_page.width height: root_page.height property string code: code_input.text property var backFun//验证码获取成功后调用此函数 property alias source: code_image.source function updateCode(){ var temp = source source = "" source = temp code_input.text = "" } SvgView { id:root_page width: 300*myqq.windowScale source: "qrc:/images/login-panel.svg" SvgView{ id:image_quit_icon width: defaultSize.width*myqq.windowScale source: "qrc:/images/button-quit.svg" anchors.left: parent.left anchors.top: parent.top anchors.margins: 10 MouseArea{ anchors.fill: parent onClicked: root_window.close() } } Text{ id: text_code text: "请输入验证码" anchors.left: parent.left anchors.top: parent.top anchors.leftMargin: root_page.width/15 anchors.topMargin: root_page.height/6 color: "#f47421" font.pointSize: root_page.height/25 } TextField{ id: code_input anchors.left: code_image.left anchors.top: code_image.bottom anchors.topMargin: 10 width: code_image.width height: root_page.height/12 font.pointSize: text_code.font.pointSize style: TextFieldStyle { textColor: "black" background: BorderImage { source: "qrc:/images/background_input.png" border.left: 5; border.top: 5 border.right: 5; border.bottom: 5 } } Component.onCompleted: code_input.forceActiveFocus() } Image{ id: code_image width: 130 height: 53 cache: false anchors.horizontalCenter: parent.horizontalCenter anchors.top :text_code.top anchors.topMargin: root_page.height/12 MouseArea{ anchors.fill: parent onClicked: { updateCode() } } } MyButton{ id: button_affirm anchors.bottom: parent.bottom anchors.bottomMargin: root_page.height/20 width: 19/42*parent.width anchors.horizontalCenter: parent.horizontalCenter text:"确 认" font.pointSize: width/15 onClicked: { if( myqq.loginStatus == QQ.Logining&&code_input.text!="" ) { backFun(code_input.text) } } } Keys.onEnterPressed: { button_affirm.clicked() } Keys.onReturnPressed: { button_affirm.clicked() } } } ================================================ FILE: qml/Utility/ComboBox/MyComboBox.qml ================================================ import QtQuick 2.2 import mywindow 1.0 Item{ id: root signal accepted property int currentIndex property var currentValue property ListModel model property MyComboBoxComponent mycomboboxcomponent onCurrentIndexChanged: { mytext.text = model.get(currentIndex).text currentValue = model.get(currentIndex).value } property alias currentText: mytext.text property bool hovered: false property bool pressed: false property bool isComboBoxCloseing: false onIsComboBoxCloseingChanged: { if(isComboBoxCloseing&&!root.hovered){ pressed = false isComboBoxCloseing = false } } BorderImage { id: background anchors.fill: parent source: "qrc:/images/background_input.png" border.left: 5; border.top: 5 border.right: 5; border.bottom: 5 Text { id: mytext anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 5 text: model.get(0).text font.pointSize: root.height/2 } SvgView { source: "qrc:/images/inputBox-more.svg" rotation: root.pressed?180:0 anchors.right: parent.right anchors.rightMargin: 5 anchors.verticalCenter: parent.verticalCenter } MouseArea{ id: mymouse anchors.fill: parent hoverEnabled: true onEntered: root.hovered=true onExited: root.hovered=false onClicked: { root.pressed=!root.pressed if(isComboBoxCloseing){ isComboBoxCloseing = false }else if( root.pressed ){ var component = Qt.createComponent("MyComboBoxComponent.qml"); if (component.status == Component.Ready){ var temp = utility.mouseDesktopPos(); var data = {"root":root, "mymodel":model, "x":temp.x-mouse.x, "y":temp.y+root.height-mouse.y+5, "width": root.width, "height": root.height*model.count, "visible":true} mycomboboxcomponent = component.createObject(root, data); } } } } } } ================================================ FILE: qml/Utility/ComboBox/MyComboBoxComponent.qml ================================================ import QtQuick 2.2 import QtQuick.Window 2.0 Window{ id: rootwindow flags: Qt.SplashScreen color: "transparent" property bool isCanClose: false property ListModel mymodel property var root onFocusObjectChanged: { if( isCanClose ){ root.isComboBoxCloseing=true close() } else isCanClose = true } Rectangle{ anchors.fill: parent color: "white" radius: 5 border.width: 1 border.color: "#f07000" } ListView{ id: list interactive: false anchors.fill: parent model: mymodel delegate: Component{ Item{ width: root.width height: root.height Rectangle{ width: parent.width-4 height: parent.height-4 anchors.centerIn: parent color: "#f07900" radius: 5 visible: mymouse.hovered } Text { id: text anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 5 text: model.text color: mymouse.hovered?"white":"black" font.pointSize: parent.height/2 } MouseArea{ id: mymouse anchors.fill: parent hoverEnabled: true property bool hovered: false onEntered: { hovered = true } onExited: { hovered = false } onClicked: { root.pressed=false root.currentIndex = index root.accepted() rootwindow.close() } } } } } } ================================================ FILE: qml/Utility/KeyboardPage/SoftKeyboard.qml ================================================ import QtQuick 2.2 import QtQuick.Window 2.1 import mywindow 1.0 Window{ id:root visible: true property bool is: false x: utility.mouseDesktopPos().x - 200 y: utility.mouseDesktopPos().y + 13 width: 450*myqq.windowScale height: 2/7*width flags: Qt.SplashScreen signal input(var arg) signal backspace(var arg) signal keyboardClose Rectangle { anchors.fill: parent radius: 3 border.width:1 border.color: "#F07000" gradient: Gradient { GradientStop { position: 0 ; color: "#E7E5E4" } GradientStop { position: 1 ; color: "#D9D7D6" } } } SvgView{ width: defaultSize.width*myqq.windowScale anchors.top: parent.top anchors.right: parent.right anchors.margins: 2 source: "qrc:/images/inputBox-close.svg" MouseArea{ anchors.fill: parent onClicked: { root.close() keyboardClose() } } } Component.onCompleted: { object.createKeyboard() } QtObject{ id: object property string ascii1_1: "0123456789`-=[]\\;',./" property string ascii1_2: ")!@#$%^&*(~_+{}|:\"<>?" property string ascii2_1: "abcdefghijklmnopqrstuvwxyz" property string ascii2_2: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" function randomOrder (targetArray) { var arrayLength = targetArray.length//先创建一个正常顺序的数组 var tempArray1 = new Array(); for (var i = 0; i < arrayLength; i ++){ tempArray1 [i] = i } var tempArray2 = new Array();//再根据上一个数组创建一个随机乱序的数组 for (var i = 0; i < arrayLength; i ++){//从正常顺序数组中随机抽出元素 tempArray2 [i] = tempArray1.splice (Math.floor (Math.random () * tempArray1.length) , 1) } var tempArray3 = new Array();//最后创建一个临时数组存储 根据上一个乱序的数组从targetArray中取得数据 for (var i = 0; i < arrayLength; i ++){ tempArray3 [i] = targetArray [tempArray2 [i]] } return tempArray3//返回最后得出的数组 } function createObject(parent, data) { var component = Qt.createComponent("SoftKeyboardButton.qml"); if (component.status == Component.Ready){ return component.createObject(parent, data); } } function createKeyboard(){ var data = {"text1": "Shift", "isShiftButton": true,"width": root.width/8, "height":root.width/17} var shift = createObject(row2, data) var temp = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]; temp = randomOrder(temp)//随机排序 for( var i=0;i<11;++i ){ data = {"width": root.width/17, "height":root.width/17,"text1": ascii1_1[temp[i]],"text2": ascii1_2[temp[i]],"switchButton": shift,"backFun": input} createObject(row1, data); } data = {"text1": "Backspace","width": root.width/6, "height":root.width/17,"backFun": backspace} var backsoace = createObject(row1,data); for( i=11;i<21;++i ){ data = {"width": root.width/17, "height":root.width/17,"text1": ascii1_1[temp[i]],"text2": ascii1_2[temp[i]],"switchButton": shift,"backFun": input} createObject(row2, data); } data = {"text1": "CapsLock","width": root.width/6, "height":root.width/17,"isShiftButton":true} var capslock = createObject(row2,data); temp = [0,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]; temp = randomOrder(temp)//随机排序 for( i=0;i<13;++i ){ data = {"width": root.width/17, "height":root.width/17,"text1": ascii2_1[temp[i]],"text2": ascii2_2[temp[i]],"switchButton": capslock,"backFun": input} createObject(row3, data); } for( i=13;i<26;++i ){ data = {"width": root.width/17, "height":root.width/17,"text1": ascii2_1[temp[i]],"text2": ascii2_2[temp[i]],"switchButton": capslock,"backFun": input} createObject(row4, data); } } } Grid{ anchors.centerIn: parent spacing: 3 rows:4 horizontalItemAlignment : Qt.AlignHCenter Row{id:row1;spacing: 3} Row{id:row2;spacing: 3} Row{id:row3;spacing: 3} Row{id:row4;spacing: 3} } onFocusObjectChanged: { if( is ){ root.close() keyboardClose() } else is = true } } ================================================ FILE: qml/Utility/KeyboardPage/SoftKeyboardButton.qml ================================================ import QtQuick 2.2 Rectangle{ id: root border.width: 1//control.activeFocus ? 2 : 1 border.color: "#888" radius: 4 property string text1: "" property string text2: "" property Item switchButton: null property bool shiftText: false property bool isShiftButton: false property bool isLetter: false property var backFun//回调函数,按下按键就调用这个函数 signal trigger(var arg) onTrigger: { if( backFun ) backFun(arg) } Connections{ target: (isShiftButton||isLetter)?null:switchButton onTrigger: { shiftText = !shiftText } } Text{ text:text1 anchors.left: parent.left anchors.bottom: parent.bottom anchors.margins: 5 visible: text2!="" color: shiftText?"#999":"black" font.bold: shiftText?false:true font.pointSize: root.width/3 } Text{ text:text2 anchors.top: parent.top anchors.right: parent.right anchors.margins: 5 visible: text2!="" color: !shiftText?"#999":"black" font.bold: !shiftText?false:true font.pointSize: root.width/3 } Text{ text: text1 anchors.centerIn: parent visible: text2=="" font.pointSize: root.width/7 } MouseArea{ id:mouse anchors.fill: parent hoverEnabled: true property bool mouseArea: false onEntered: { mouseArea = true } onExited: { mouseArea = false } onClicked: { if(isShiftButton){ shiftText =!shiftText trigger(text1) }else if( text2=="" ) trigger(text1) else{ if( shiftText ) trigger(text2) else trigger(text1) } } } gradient: Gradient { GradientStop { position: 0 color: { if(isShiftButton&&shiftText){ return "#f09000" }else return (mouse.pressed||mouse.mouseArea)?"#f09000":"#fff" } } GradientStop { position: 1 color: { if(isShiftButton&&shiftText){ return "#f07000" }else return (mouse.pressed||mouse.mouseArea)?"#f07000":"#eee" } } } } ================================================ FILE: qml/Utility/MyButton.qml ================================================ import QtQuick 2.2 Rectangle { id: root property alias pressed: mymouse.pressed property bool hovered: false property alias font: text.font property alias text: text.text signal clicked implicitWidth: 80 implicitHeight: 20 radius: 6 border.width: 1 border.color: "#aaa" gradient: Gradient { GradientStop { position: 0 ; color: { if(!root.enabled) return "#888" if( root.pressed ) return "#ff9000" if( root.hovered ) return "#eee" return "#fff" } } GradientStop { position: 1 ; color: { if(!root.enabled) return "#777" if( root.pressed ) return "#f07000" if( root.hovered ) return "#ddd" return "#eee" } } } Text{ id: text text: parent.text anchors.centerIn: parent color: parent.pressed?"white":"black" } MouseArea{ id: mymouse anchors.fill: parent hoverEnabled: true onClicked: { root.clicked() } onEntered: { root.hovered = true } onExited: { root.hovered = false } } } ================================================ FILE: qml/Utility/MyMessageBox.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 MyWindow { id:root visible: true//可视的 noBorder: true//无边框的 removable: true//可移动的 fixedSize: true//固定大小的 dockableWindow: false//可停靠的 topHint: true//窗口保持在最前端 noNotifyIcon: true//隐藏任务栏图标 modality : Qt.ApplicationModal color: "transparent" width: 360 height: 200 property string text Component.onCompleted: { button_ok.forceActiveFocus() } Rectangle{ anchors.fill: parent gradient: Gradient { GradientStop { position: 0 ; color: "#EEEDEC" } GradientStop { position: 1 ; color: "#f9f9f8" } } radius: 10 Text{ id: text text: root.text anchors.centerIn: parent wrapMode :Text.WordWrap//自动换行 color: "#f47421" width: parent.width-20 anchors.bottom: button_ok.top anchors.top: parent.top anchors.margins: 10 } MyButton{ id: button_ok anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: 10 width: 60 height: 25 text: "确认" Keys.onEnterPressed: { button_ok.clicked() } Keys.onReturnPressed: { button_ok.clicked() } onClicked: { root.close() } } } } ================================================ FILE: qml/Utility/MyRectangularGlow.qml ================================================ import QtQuick 2.2 Item{ id: root x: actualX+glowLeftWidth y: actualY+glowTopHeight property Item item: null property int glowTopHeight: (shaderItem.height-rootItem.height)/2-rootItem.y property int glowBottomHeight: shaderItem.height-height-glowTopHeight property int glowLeftWidth: (shaderItem.height-rootItem.height)/2-rootItem.x property int glowRightWidth: shaderItem.width-width-glowLeftWidth property int actualWidth: glowLeftWidth+width+glowRightWidth property int actualHeight: glowTopHeight+height+glowBottomHeight onItemChanged: { item.parent=root } property real biasX: 0//阴影偏移量 property real biasY: 0//阴影偏移量 property alias glowRadius: rootItem.glowRadius property alias spread: rootItem.spread property alias color: rootItem.color property alias cornerRadius: rootItem.cornerRadius property alias cached: rootItem.cached property alias glowOpacity: rootItem.opacity property int actualX: 0//真实的X,算着阴影 onActualXChanged: { x = actualX+glowLeftWidth } property int actualY: 0//真实的Y,算着阴影 onActualYChanged: { y = actualY+glowTopHeight } onXChanged: { actualX = x-glowLeftWidth } onYChanged: { actualY = y-glowTopHeight } Item { id: rootItem property real glowRadius: 0.0 property real spread: 0.0 property color color: "white" property real cornerRadius: glowRadius property bool cached: false x: (biasX>0?biasX:0) y: (biasY>0?biasY:0) width: root.width-biasX height: root.height-biasY ShaderEffectSource { id: cacheItem anchors.fill: shaderItem visible: rootItem.cached smooth: true sourceItem: shaderItem live: true hideSource: visible } ShaderEffect { id: shaderItem x: (parent.width - width) / 2.0 y: (parent.height - height) / 2.0 width: parent.width + rootItem.glowRadius * 2 + cornerRadius * 2 height: parent.height + rootItem.glowRadius * 2 + cornerRadius * 2 function clampedCornerRadius() { var maxCornerRadius = Math.min(rootItem.width, rootItem.height) / 2 + glowRadius; return Math.max(0, Math.min(rootItem.cornerRadius, maxCornerRadius)) } property color color: rootItem.color property real inverseSpread: 1.0 - rootItem.spread property real relativeSizeX: ((inverseSpread * inverseSpread) * rootItem.glowRadius + cornerRadius * 2.0) / width property real relativeSizeY: relativeSizeX * (width / height) property real spread: rootItem.spread / 2.0 property real cornerRadius: clampedCornerRadius() fragmentShader: " uniform highp float qt_Opacity; uniform mediump float relativeSizeX; uniform mediump float relativeSizeY; uniform mediump float spread; uniform lowp vec4 color; varying highp vec2 qt_TexCoord0; highp float linearstep(highp float e0, highp float e1, highp float x) { return clamp((x - e0) / (e1 - e0), 0.0, 1.0); } void main() { lowp float alpha = smoothstep(0.0, relativeSizeX, 0.5 - abs(0.5 - qt_TexCoord0.x)) * smoothstep(0.0, relativeSizeY, 0.5 - abs(0.5 - qt_TexCoord0.y)); highp float spreadMultiplier = linearstep(spread, 1.0 - spread, alpha); gl_FragColor = color * qt_Opacity * spreadMultiplier * spreadMultiplier; } " } } } ================================================ FILE: qml/Utility/MyScrollView.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 ScrollView{ id:root anchors.fill: parent style: ScrollViewStyle{ id: scroll_style property int hovered_count: 0 property bool hovered: hovered_count>0 handle: Rectangle{ radius: 10 implicitWidth: 10 property bool hovered: styleData.hovered onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } color: scroll_style.hovered?"#808080":"#BDBFBE" } scrollBarBackground:Rectangle{ implicitWidth: 10 color:"#f0f0f0" radius: 10 property bool hovered: styleData.hovered onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } opacity: scroll_style.hovered?1:0 Behavior on opacity{ NumberAnimation{ duration: 200 } } } decrementControl : Item{ implicitHeight: 10 implicitWidth: 10 Image{ source: "qrc:/images/list_arrow_up.png" property bool hovered: styleData.hovered anchors.centerIn: parent onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } opacity: scroll_style.hovered?1:0 Behavior on opacity{ NumberAnimation{ duration: 200 } } } } incrementControl : Item{ implicitHeight: 10 implicitWidth: 10 Image{ anchors.centerIn: parent source: "qrc:/images/list_arrow_down.png" property bool hovered: styleData.hovered onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } opacity: scroll_style.hovered?1:0 Behavior on opacity{ NumberAnimation{ duration: 200 } } } } } } ================================================ FILE: qml/Utility/MyTextArea.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import QtQuick.Controls.Private 1.0 ScrollView { id: area property alias activeFocusOnPress: edit.activeFocusOnPress property alias baseUrl: edit.baseUrl readonly property alias canPaste: edit.canPaste readonly property alias canRedo: edit.canRedo readonly property alias canUndo: edit.canUndo property alias textColor: edit.color property alias cursorPosition: edit.cursorPosition property alias font: edit.font property alias horizontalAlignment: edit.horizontalAlignment readonly property alias effectiveHorizontalAlignment: edit.effectiveHorizontalAlignment property alias verticalAlignment: edit.verticalAlignment property alias inputMethodHints: edit.inputMethodHints readonly property alias length: edit.length readonly property alias lineCount: edit.lineCount property alias readOnly: edit.readOnly Accessible.readOnly: readOnly readonly property alias selectedText: edit.selectedText readonly property alias selectionEnd: edit.selectionEnd readonly property alias selectionStart: edit.selectionStart property bool tabChangesFocus: false property alias text: edit.text property alias textFormat: edit.textFormat property alias wrapMode: edit.wrapMode property alias selectByMouse: edit.selectByMouse property alias selectByKeyboard: edit.selectByKeyboard signal linkActivated(string link) signal linkHovered(string link) readonly property alias hoveredLink: edit.hoveredLink property alias textDocument: edit.textDocument default property alias data: area.data property alias textMargin: edit.textMargin frameVisible: true activeFocusOnTab: true Accessible.role: Accessible.EditableText function append (string) { edit.append(string) __verticalScrollBar.value = __verticalScrollBar.maximumValue } function copy() { edit.copy(); } function cut() { edit.cut(); } function deselect() { edit.deselect(); } function getFormattedText(start, end) { return edit.getFormattedText(start, end); } function getText(start, end) { return edit.getText(start, end); } function insert(position, text) { edit.insert(position, text); } function isRightToLeft(start, end) { return edit.isRightToLeft(start, end); } function moveCursorSelection(position, mode) { edit.moveCursorSelection(position, mode); } function paste() { edit.paste(); } function positionAt(x, y) { return edit.positionAt(x, y); } function positionToRectangle(position) { return edit.positionToRectangle(position); } function redo() { edit.redo(); } function remove(start, end) { return edit.remove(start, end); } function select(start, end) { edit.select(start, end); } function selectAll() { edit.selectAll(); } function selectWord() { edit.selectWord(); } function undo() { edit.undo(); } style: ScrollViewStyle { id: scroll_style //textColor: "black" frame: Rectangle{ color: "#f5f5f5" radius: 5 border.width: 2 border.color: "#ddd" } property int hovered_count: 0 property bool hovered: hovered_count>0 handle: Rectangle{ radius: 10 implicitWidth: 10 property bool hovered: styleData.hovered onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } color: scroll_style.hovered?"#808080":"#BDBFBE" } scrollBarBackground:Rectangle{ implicitWidth: 10 color:"#f0f0f0" radius: 10 property bool hovered: styleData.hovered onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } opacity: scroll_style.hovered?1:0 Behavior on opacity{ NumberAnimation{ duration: 200 } } } decrementControl : Item{ implicitHeight: 10 implicitWidth: 10 Image{ source: "qrc:/images/list_arrow_up.png" property bool hovered: styleData.hovered anchors.centerIn: parent onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } opacity: scroll_style.hovered?1:0 Behavior on opacity{ NumberAnimation{ duration: 200 } } } } incrementControl : Item{ implicitHeight: 10 implicitWidth: 10 Image{ anchors.centerIn: parent source: "qrc:/images/list_arrow_down.png" property bool hovered: styleData.hovered onHoveredChanged: { if( hovered ) scroll_style.hovered_count++ else scroll_style.hovered_count-- } opacity: scroll_style.hovered?1:0 Behavior on opacity{ NumberAnimation{ duration: 200 } } } } } Flickable { id: flickable interactive: false anchors.fill: parent TextEdit { id: edit focus: true property int layoutRecursionDepth: 0 textFormat :TextEdit.RichText function doLayout() { // scrollbars affect the document/viewport size and vice versa, so we // must allow the layout loop to recurse twice until the sizes stabilize if (layoutRecursionDepth <= 2) { layoutRecursionDepth++ if (wrapMode == TextEdit.NoWrap) { __horizontalScrollBar.visible = edit.contentWidth > viewport.width edit.width = Math.max(viewport.width, edit.contentWidth) } else { __horizontalScrollBar.visible = false edit.width = viewport.width } edit.height = Math.max(viewport.height, edit.contentHeight) flickable.contentWidth = edit.contentWidth flickable.contentHeight = edit.contentHeight layoutRecursionDepth-- } } Connections { target: area.viewport onWidthChanged: edit.doLayout() onHeightChanged: edit.doLayout() } onContentWidthChanged: edit.doLayout() onContentHeightChanged: edit.doLayout() onWrapModeChanged: edit.doLayout() renderType: Text.NativeRendering font: TextSingleton.font color: "black" selectionColor: "#888" selectedTextColor: "white" wrapMode: TextEdit.WordWrap textMargin: 4 selectByMouse: Qt.platform.os !== "android" // Workaround for QTBUG-36515 readOnly: false Keys.forwardTo: area KeyNavigation.priority: KeyNavigation.BeforeItem KeyNavigation.tab: area.tabChangesFocus ? area.KeyNavigation.tab : null KeyNavigation.backtab: area.tabChangesFocus ? area.KeyNavigation.backtab : null // keep textcursor within scroll view onCursorPositionChanged: { if (cursorRectangle.y >= flickableItem.contentY + viewport.height - cursorRectangle.height - textMargin) { // moving down flickableItem.contentY = cursorRectangle.y - viewport.height + cursorRectangle.height + textMargin } else if (cursorRectangle.y < flickableItem.contentY) { // moving up flickableItem.contentY = cursorRectangle.y - textMargin } if (cursorRectangle.x >= flickableItem.contentX + viewport.width - textMargin) { // moving right flickableItem.contentX = cursorRectangle.x - viewport.width + textMargin } else if (cursorRectangle.x < flickableItem.contentX) { // moving left flickableItem.contentX = cursorRectangle.x - textMargin } } onLinkActivated: area.linkActivated(link) onLinkHovered: area.linkHovered(link) MouseArea { parent: area.viewport anchors.fill: parent cursorShape: edit.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor acceptedButtons: Qt.NoButton } } } Keys.onPressed: { if (event.key == Qt.Key_PageUp) { __verticalScrollBar.value -= area.height } else if (event.key == Qt.Key_PageDown) __verticalScrollBar.value += area.height } } ================================================ FILE: qml/Utility/MyTextField.qml ================================================ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 Item{ id: root property alias title: text.text property alias text: input.text property alias font: input.font property alias field: input Text { id: text anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter font.pointSize: root.height/2 } TextField{ id: input anchors.left: text.right anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom font.pointSize:text.font.pointSize style: TextFieldStyle { textColor: "black" background: BorderImage { source: "qrc:/images/background_input.png" border.left: 5; border.top: 5 border.right: 5; border.bottom: 5 } } } Rectangle{ radius: 1 anchors.top: input.top anchors.topMargin: 2 anchors.bottom: input.bottom anchors.bottomMargin: 0 anchors.left: input.left anchors.leftMargin: 1 anchors.right: parent.right anchors.rightMargin: 1 visible: !root.enabled color: "#f0efef" opacity: 0.75 } } ================================================ FILE: qml/Utility/MyTextView.qml ================================================ import QtQuick 2.2 import QtWebKit 3.0 Item{ id:root property string html: "" property alias url: webview.url implicitWidth: webview.contentWidth implicitHeight: webview.contentHeight onHtmlChanged: { webview.loadHtml(html) } WebView{ id: webview anchors.fill: parent } } ================================================ FILE: qml/Utility/MyWindow.qml ================================================ import QtQuick 2.2 import mywindow 1.0 import QtQuick.Window 2.1 MyQuickWindow{ id: root property bool removable: true//窗口可移动? property bool fixedSize: false//窗口是否固定大小? property bool fixedTopBorder: false//固定上边框,上边不可拉动 property bool fixedBottomBorder: false//同上 property bool fixedLeftBorder: false//同上 property bool fixedRightBorder: false//同上 property var setTopBorder: mySetTopBorder//用鼠标拉动上边框后调用的函数 property var setBottomBorder: mySetBottomBorder//同上 property var setLeftBorder: mySetLeftBorder//同上 property var setRightBorder: mySetRightBorder//同上 property bool dockableWindow: false//窗口可停靠? property bool windowGlow: true//开启窗口阴影? property alias windowGlowItem: glow//阴影Item property int windowShakeInterval: animation_shake.duration*16///窗口抖动的时间 property bool centered: true//初次显示时是否居中 property int contentItemAreaTop: -5//设定内容区域的上边界坐标 property int contentItemAreaBottom: contentItemAreaTop+height+10//同上 property int contentItemAreaLeft: -5//同上 property int contentItemAreaRight: contentItemAreaLeft+width+10//同上 signal manulPullLeftBorder//如果用户在窗口左边拉动改变了窗口大小 signal manulPullRightBorder//同上 signal manulPullTopBorder//同上 signal manulPullBottomBorder//同上 actualWidth: windowGlow?glow.actualWidth:width+2*mouse_left.width actualHeight: windowGlow?glow.actualHeight:height+2*mouse_top.height onContentItemAreaTopChanged: updateMousePenetrateArea() onContentItemAreaBottomChanged: updateMousePenetrateArea() onContentItemAreaLeftChanged: updateMousePenetrateArea() onContentItemAreaRightChanged: updateMousePenetrateArea() Component.onCompleted: { if(centered){ var old_visible = visible visible = false actualX = Screen.desktopAvailableWidth/2 - actualWidth/2 actualY = Screen.desktopAvailableHeight/2 - actualHeight/2 visible = old_visible } } contentItem{ x: windowGlow?glow.glowLeftWidth:mouse_left.width y: windowGlow?glow.glowTopHeight:mouse_top.height } Connections{ target: windowGlow?contentItem:null onWidthChanged:{ contentItem.width=width } onHeightChanged:{ contentItem.height=height } } QtObject{ id: obj property int windowShakeCount: 0 property real back_glowOpacity Component.onCompleted: { back_glowOpacity = glow.glowOpacity } } function updateMousePenetrateArea(){ var rect = Qt.rect(contentItemAreaLeft, contentItemAreaTop, 0, 0) rect.width = contentItemAreaRight - rect.x + 1 rect.height = contentItemAreaBottom - rect.y + 1 setMousePenetrateArea(rect) } function windowShake() {//抖动窗口 if(obj.windowShakeCount>=root.windowShakeInterval/animation_shake.duration/4){ obj.windowShakeCount=0 return } ++obj.windowShakeCount showFront()//先把窗口显示在最前端 animation_shake.property = "x" animation_shake.to = root.x-20 animation_shake.backFun=function(){ animation_shake.property = "y" animation_shake.to = root.y-20 animation_shake.backFun=function(){ animation_shake.property="x" animation_shake.to=root.x+20 animation_shake.backFun=function(){ animation_shake.property="y" animation_shake.to=root.y+20 animation_shake.backFun=function(){ windowShake()//进行递归调用 } animation_shake.start() } animation_shake.start() } animation_shake.start() } animation_shake.start() } function showFront() {//显示到最前面 if(root.visible) { if( root.visibility== MyQuickWindow.Minimized){ root.show() } root.requestActivate()//让窗体显示到最前端 } } function berthWindow(){ if( root.windowStatus!=MyQuickWindow.BerthPrepare||animation.running ) {//如果不是准停状态 return } if( root.x==root.borderLeft ){ root.windowStatus = MyQuickWindow.BerthLeft animation.property = "x" animation.to = root.borderLeft-width+1 animation.start() }else if( root.x==root.borderRight-root.width ){ root.windowStatus = MyQuickWindow.BerthRight animation.property = "x" animation.to = root.borderRight-1 animation.start() }else if( root.y==root.borderTop ){ root.windowStatus = MyQuickWindow.BerthTop animation.property = "y" animation.to = root.borderTop-height+1 animation.start() } } function showWindow(){///从停靠状态转为可停靠状态 if( animation.running ) {//如果动画正在运行 return } switch( windowStatus ){ case MyQuickWindow.BerthTop: { animation.property = "y" animation.to = root.borderTop animation.start() root.windowStatus = MyQuickWindow.BerthPrepare//进入准停靠状态 break; } case MyQuickWindow.BerthLeft: { animation.property = "x" animation.to = root.borderLeft animation.start() root.windowStatus = MyQuickWindow.BerthPrepare//进入准停靠状态 break; } case MyQuickWindow.BerthRight: { animation.property = "x" animation.to = root.borderRight-root.width animation.start() root.windowStatus = MyQuickWindow.BerthPrepare//进入准停靠状态 break; } default:break } } function mySetLeftBorder(arg){//当从左边改变窗口的width时 if(!fixedLeftBorder){ var temp = root.width root.width+=arg; temp = root.width-temp//计算出其中的差值 if(temp!=0){ root.x-=temp//改变窗口坐标 manulPullLeftBorder()//发送拉动了左边界的信号 } } } function mySetRightBorder(arg){//当从右边改变窗口的width时 if(!fixedRightBorder){ var temp = root.width root.width+=arg; temp = root.width-temp//计算出其中的差值 if(temp!=0){ manulPullRightBorder()//发送拉动了右边界的信号 } } } function mySetTopBorder(arg){//当从上边改变窗口的width时 if(!fixedTopBorder){ var temp = root.height root.height+=arg; temp = root.height-temp//计算出其中的差值 if(temp!=0){ root.y-=temp//改变窗口坐标 manulPullTopBorder()//发送拉动了上边界的信号 } } } function mySetBottomBorder(arg){//当从下边改变窗口的width时 if(!fixedBottomBorder){ var temp = root.height root.height+=arg; temp = root.height-temp//计算出其中的差值 if(temp!=0){ manulPullBottomBorder()//发送拉动了下边界的信号 } } } NumberAnimation{ id: animation target: root duration: 100 } NumberAnimation{ id: animation_shake target: root duration: 50 easing.type: Easing.OutInElastic property var backFun: null onStopped: { if(backFun) backFun() } } NumberAnimation{ id: animation_shake_x target: root duration: 100 property: "x" easing.type: Easing.OutInElastic property var backFun: null onStopped: { if(backFun) backFun() } } NumberAnimation{ id: animation_shake_y target: root duration: 100 property: "y" easing.type: Easing.OutInElastic property var backFun: null onStopped: { if(backFun) backFun() } } MyRectangularGlow{ id: glow visible: windowGlow&&root.windowActive x:0 y:0 glowRadius: 50 glowOpacity: 0.75 biasY: 20 spread: 0.1 color: "black" width: root.width height:root.height } MyRectangularGlow{ id: glow_inactive//当窗口处于非活跃状态时显示的阴影 anchors.fill: glow glowRadius: glow.glowRadius/2 spread: glow.spread color: glow.color glowOpacity: glow.glowOpacity cornerRadius: glow.cornerRadius biasX: glow.biasX biasY: glow.biasY cached: glow.cached visible: windowGlow&&(!root.windowActive) } MouseArea{ id: mouse_main property real pressedX: 0 property real pressedY: 0 property point pressedCursorPos enabled: removable anchors.fill: parent hoverEnabled: dockableWindow onPressed: { pressedX = root.x pressedY = root.y pressedCursorPos = cursorPos } onEntered: { if(dockableWindow){ showWindow()//将窗口从停靠地方显示出来 } } onExited: { if(dockableWindow){//如果开启了窗口停靠 mouse_main_connections.target = root//接收全局鼠标改变的信号 } } onReleased: { if(dockableWindow&&(!animation.running)&&(root.windowStatus==MyQuickWindow.StopCenter||root.windowStatus==MyQuickWindow.BerthPrepare)){ if( root.x<=root.borderLeft ){ root.x=root.borderLeft root.windowStatus = MyQuickWindow.BerthPrepare//进入准停靠状态 }else if( root.x>root.borderRight-root.width ){ root.x = root.borderRight-root.width root.windowStatus = MyQuickWindow.BerthPrepare//进入准停靠状态 }else if( root.y=contentItemAreaLeft &&y<=contentItemAreaBottom&&y>=contentItemAreaTop)){ berthWindow()//调用函数让窗口进行停靠 mouse_main_connections.target = null//关闭接收全局鼠标改变的信号 } } } } MouseArea{//接收窗口上部的鼠标事件,用于朝上拉动窗口来改变窗口的大小 id: mouse_top enabled: noBorder&&!fixedSize&&!fixedTopBorder&&root.windowStatus==MyQuickWindow.StopCenter cursorShape :enabled?Qt.SizeVerCursor:Qt.ArrowCursor//鼠标样式 anchors.bottom: parent.top anchors.horizontalCenter: parent.horizontalCenter width: root.width-6 height: 3 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX pressedY = mouseY } onPositionChanged: { var num_temp = pressedY-mouseY setTopBorder(num_temp) } } MouseArea{//接收窗口下部的鼠标事件,用于朝下拉动窗口来改变窗口的大小 id: mouse_bottom enabled: noBorder&&!fixedSize&&!fixedBottomBorder&&root.windowStatus==MyQuickWindow.StopCenter cursorShape :enabled?Qt.SizeVerCursor:Qt.ArrowCursor anchors.top: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: root.width-6 height: 3 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedY = mouseY } onPositionChanged: { var num_temp = mouseY-pressedY setBottomBorder(num_temp) } } MouseArea{//接收窗口左部的鼠标事件,用于朝左拉动窗口来改变窗口的大小 id: mouse_left enabled: noBorder&&!fixedSize&&!fixedLeftBorder&&root.windowStatus==MyQuickWindow.StopCenter cursorShape :enabled?Qt.SizeHorCursor:Qt.ArrowCursor anchors.right: parent.left anchors.verticalCenter: parent.verticalCenter height: root.height-6 width: 3 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX } onPositionChanged: { var num_temp = pressedX-mouseX setLeftBorder(num_temp) } } MouseArea{//接收窗口右部的鼠标事件,用于朝右拉动窗口来改变窗口的大小 id: mouse_right enabled: noBorder&&!fixedSize&&!fixedRightBorder&&root.windowStatus==MyQuickWindow.StopCenter cursorShape :enabled?Qt.SizeHorCursor:Qt.ArrowCursor anchors.left: parent.right anchors.verticalCenter: parent.verticalCenter height: root.height-6 width: 3 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX } onPositionChanged: { var num_temp = mouseX-pressedX setRightBorder(num_temp) } } MouseArea{//接收窗口左上部的鼠标事件,用于朝左拉动窗口来改变窗口的大小 enabled: mouse_left.enabled&&mouse_top.enabled cursorShape :enabled?Qt.SizeFDiagCursor:Qt.ArrowCursor anchors.right: parent.left anchors.bottom: parent.top height: 5 width: 5 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX pressedY = mouseY } onPositionChanged: { var num_temp1 = pressedX-mouseX setLeftBorder(num_temp1) var num_temp2 = pressedY-mouseY setTopBorder(num_temp2) } } MouseArea{//接收窗口右上部的鼠标事件,用于朝左拉动窗口来改变窗口的大小 enabled: mouse_right.enabled&&mouse_top.enabled cursorShape :enabled?Qt.SizeBDiagCursor:Qt.ArrowCursor anchors.left: parent.right anchors.bottom: parent.top height: 5 width: 5 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX pressedY = mouseY } onPositionChanged: { var num_temp1 = mouseX-pressedX setRightBorder(num_temp1) var num_temp2 = pressedY-mouseY setTopBorder(num_temp2) } } MouseArea{//接收窗口左下部的鼠标事件,用于朝左拉动窗口来改变窗口的大小 enabled:mouse_left.enabled&&mouse_bottom.enabled cursorShape :enabled?Qt.SizeBDiagCursor:Qt.ArrowCursor anchors.right: parent.left anchors.top: parent.bottom height: 5 width: 5 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX pressedY = mouseY } onPositionChanged: { var num_temp1 = pressedX-mouseX setLeftBorder(num_temp1) var num_temp2 = mouseY-pressedY setBottomBorder(num_temp2) } } MouseArea{//接收窗口右下部的鼠标事件,用于朝左拉动窗口来改变窗口的大小 enabled: mouse_right.enabled&&mouse_bottom.enabled cursorShape :enabled?Qt.SizeFDiagCursor:Qt.ArrowCursor anchors.left: parent.right anchors.top: parent.bottom height: 5 width: 5 z:1 property real pressedX: 0 property real pressedY: 0 onPressed: { pressedX = mouseX pressedY = mouseY } onPositionChanged: { var num_temp1 = mouseX-pressedX setRightBorder(num_temp1) var num_temp2 = mouseY-pressedY setBottomBorder(num_temp2) } } } ================================================ FILE: qml/Utility/SystemTray.qml ================================================ import QtQuick 2.2 import mywindow 1.0 import utility 1.0 import QQItemInfo 1.0 import qqstars 1.0 MySystemTrayIcon{ id: root property QQItemInfo currentInfo//当前最新消息的发送人的信息 property bool hovered: false//鼠标是否悬浮在托盘上空 property TrayMessageWindow trayMessageWindow signal triggered(var arg) visible: true windowIcon: "qrc:/images/avatar.png" menu: myqq.loginStatus == QQ.LoginFinished?menu2:menu1 toolTip: { if( myqq.loginStatus == QQ.LoginFinished ){ return "QQ:"+myqq.nick+"("+myqq.userQQ+")" }else return "星辰QQ" } function iconShakeStart(){//开启图标闪动 windowIcon = currentInfo.avatar40//设置图标 timer_shake.start() console.log("开启了头像闪动") } function iconShakeStop(){//停止鼠标闪动 timer_shake.stop() windowIcon = "qrc:/images/avatar.png" } onCurrentInfoChanged: { if(timer_shake.running) windowIcon = currentInfo.avatar40//设置图标 } Connections{ target: timer_shake.running?currentInfo:null onAvatar40Changed:{ windowIcon = currentInfo.avatar40//设置图标 } } Component.onCompleted: { var component = Qt.createComponent("TrayMessageWindow.qml"); if (component.status == Component.Ready){ trayMessageWindow = component.createObject(null, {}); } } onMessageClicked:{ iconShakeStop()//停止闪动头像 myqq.addChatPage(currentInfo.uin, currentInfo.mytype)//增加聊天页面 } onActivated:{ if( timer_shake.running&&arg == MySystemTrayIcon.Trigger ) {//如果头像正在闪动 iconShakeStop()//停止闪动头像 myqq.addChatPage(currentInfo.uin, currentInfo.mytype)//增加聊天页面 } } Connections{ target: trayMessageWindow onStopShakeIcon: {//如果清除全部消息提示 iconShakeStop()//停止闪动头像 } onSetCurrentInfo:{ currentInfo = info } } Connections{ target: timer_shake.running?utility:null onMouseDesktopPosChanged:{ if(arg.x>=root.x&&arg.y>=root.y&&arg.x<=root.x+root.width&&arg.y<=root.y+root.height){ if(!hovered){ hovered = true //console.debug("进入了托盘区域") root.toolTip = "" trayMessageWindow.showWindow(root.x, root.y, root.width, root.height)//显示消息通知框 } }else if(hovered){ hovered = false trayMessageWindow.closeWindow()//隐藏消息通知栏 root.toolTip = "QQ:"+myqq.nick+"("+myqq.userQQ+")" } } } Connections{ target: myqq onNewMessage:{ if(!myqq.isChatPageExist(fromUin, type)){//判断聊天页面是否存在,如果存在的话 不用提示新消息 console.debug("收到了新的未读消息:"+currentInfo) if(type==QQItemInfo.Friend){//如果是qq消息 currentInfo = myqq.createFriendInfo(fromUin) }else if(type==QQItemInfo.Group){//如果是群消息 currentInfo = myqq.createGroupInfo(fromUin) }else if(type==QQItemInfo.Discu){//如果是讨论组消息 currentInfo = myqq.createDiscuInfo(fromUin) } trayMessageWindow.appendModel(currentInfo) iconShakeStart()//开始闪动 } } } Timer{ id: timer_shake interval: 300 repeat: true//开启重复闪动 property string old_icon onTriggered: { if(root.windowIcon!=""){ old_icon=root.windowIcon root.windowIcon="" }else{ root.windowIcon=old_icon } } } MyMenu{ id:menu1 styleSource: "qrc:/style/menuStyle.css" MyMenuItem{ text: "打开主面板" onTriggered: { root.triggered(text) } } MyMenuItem{ text: "退出" onTriggered: Qt.quit() } } MyMenu{ id: menu2 styleSource: "qrc:/style/menuStyle.css" MyMenuItem{ text: "我在线上" icon: "qrc:/images/imonline.png" onTriggered: { myqq.state = QQ.Online } } MyMenuItem{ text: "Q我吧" icon: "qrc:/images/imcallme.png" onTriggered: { myqq.state = QQ.Callme } } MyMenuItem{ text: "离开" icon: "qrc:/images/imaway.png" onTriggered: { myqq.state = QQ.Away } } MyMenuItem{ text: "忙碌" icon: "qrc:/images/imbusy.png" onTriggered: { myqq.state = QQ.Busy } } MyMenuItem{ text: "请勿打扰" icon: "qrc:/images/imsilent.png" onTriggered: { myqq.state = QQ.Silent } } MyMenuItem{ text: "隐身" icon: "qrc:/images/imhidden.png" onTriggered: { myqq.state = QQ.Hidden } } MyMenuItem{ text: "离线" icon: "qrc:/images/imoffline.png" onTriggered: { myqq.state = QQ.Offline } } MenuSeparator{} MyMenuItem{ text: "关闭所有声音" } MyMenuItem{ text: "关闭头像闪动" } MenuSeparator{} MyMenuItem{ text: lock_qq?"解锁QQ":"锁定QQ" shortcut: "Ctrl+Alt+L" property bool lock_qq: false icon: lock_qq?"qrc:/images/unlock20.png":"qrc:/images/lock20.png" onTriggered: { lock_qq = !lock_qq } } MenuSeparator{} MyMenuItem{ text: "打开主面板" onTriggered: { root.triggered(text) } } MenuSeparator{} MyMenuItem{ text: "&退出" onTriggered: { Qt.quit() } } } } ================================================ FILE: qml/Utility/TrayMessageWindow.qml ================================================ import QtQuick 2.2 import QtQuick.Window 2.0 import mywindow 1.0 //import "../QQItemInfo" Window{ id: root flags: Qt.SplashScreen|Qt.WindowStaysOnTopHint height: list.height+user_nick.implicitHeight+item_button.height+30 width: 200 color: "transparent" signal stopShakeIcon//停止对托盘图标的闪动 signal setCurrentInfo(var info) property var infosQueue: new Array function appendModel(senderInfo){ for(var i in infosQueue){ if(infosQueue[i] == senderInfo){//如果对象已经存在 return } } mymodel.append({"sender_info": senderInfo}) infosQueue.push(senderInfo)//加到队列里边 } function removeModel(index){ if(index>=0){ mymodel.remove(index) infosQueue.splice(index, 1) //console.debug(mymodel.count+","+infosQueue.length) if(mymodel.count==0) stopShakeIcon()//停止闪动 } } function clearModel(){ stopShakeIcon()//发送信号 mymodel.clear() infosQueue.length=0 } function getLatestInfo(){ if(infosQueue.length>0) return infosQueue[infosQueue.length-1] else return null } function showWindow(trayX, trayY, trayWidth, trayHeight){ timer_close.stop()//先停止动画 root.opacity = 1 root.show() var tempx = trayX-width/2+trayWidth/2 if(tempx<0) x=trayX+trayWidth else if(tempx+width>Screen.desktopAvailableWidth) x=trayX-width else x=tempx var tempy = trayY-height/2+trayHeight/2 if(tempy<0) y=trayY+trayHeight else if(tempy+height>Screen.desktopAvailableHeight) y=trayY-height else y=tempy } function closeWindow(){ if(!mouse_area.hovered)//如果当前鼠标没有在自己区域 timer_close.start()//启动动画定时器 } NumberAnimation{ id: timer_close target: root property: "opacity" running: false duration: 800 from: 1 to: 0 onStopped: {//当动画结束后 if(root.opacity==0){ root.close() } } } MouseArea{ id: mouse_area anchors.fill: parent property bool hovered: false hoverEnabled: true onEntered: { hovered = true timer_close.stop()//停止关闭窗口的动画 root.opacity = 1 //console.debug("进入了区域") } onExited: { var globalX = utility.mouseDesktopPos().x var globalY = utility.mouseDesktopPos().y //console.log("x:"+root.x+","+width+","+globalX) //console.log("y:"+root.y+","+height+","+globalY) if(globalXroot.x+root.width||globalYroot.y+root.height){ hovered = false root.closeWindow() //console.debug("离开了区域") } } } Rectangle{ width: parent.width height: list.height+user_nick.implicitHeight+item_button.height+30 border.width: 1 border.color: "#f07000" color: "white" radius: 10 Text{ id: user_nick text: myqq.nick x:10 y:10 } ListView{ id:list delegate: list_delegate anchors.left: parent.left anchors.right: parent.right anchors.top: user_nick.bottom height: mymodel.count*40 clip: true anchors.margins: 10 signal openAllChatPage//当点击"查看全部"时被发射 model: ListModel{ id:mymodel } } Item{ id: item_button anchors.top: list.bottom anchors.topMargin: 10 anchors.left: list.left anchors.right: list.right height: 30 Rectangle{ width: parent.width height: 1 color: "#f07000" } Text{ text: "忽略全部" color: "#f07000" font.underline: true anchors.verticalCenter: parent.verticalCenter MouseArea{ anchors.fill: parent onClicked: { clearModel()//先清除所有数据 root.close()//然后关闭窗口 } } } Text{ text: "查看全部" color: "#f07000" font.underline: true anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right MouseArea{ anchors.fill: parent onClicked: { list.openAllChatPage()//发送信号 clearModel()//先清除所有数据 root.close()//然后关闭窗口 } } } } } Component{ id: list_delegate Item{ id: myitem width: parent.width height: 40 property var myinfo: sender_info function removeMe(){ root.close()//关闭窗口 removeModel(index)//清除自己 var temp_info = getLatestInfo() if(temp_info!=null){ setCurrentInfo(temp_info) } } Connections{ target: list onOpenAllChatPage: { myqq.addChatPage(myinfo.uin, myinfo.mytype) } } Rectangle{ anchors.fill: parent color: "#aaa" opacity: 0.8 visible: item_mouse.hovered } MyImage{ id: avatar x:10 width:parent.height-10 height: width sourceSize.width: width anchors.verticalCenter: parent.verticalCenter maskSource: "qrc:/images/bit.bmp" cache: false source: myinfo.avatar40 } Text{ id: nick anchors.left: avatar.right anchors.leftMargin: 10 anchors.top: avatar.top text: myinfo.aliasOrNick } Rectangle{ width: text_message_count.implicitWidth+10 height: text_message_count.implicitHeight+10 anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 5 color: "red" opacity: 0.75 radius: height Text{ id: text_message_count anchors.centerIn: parent text: myinfo.unreadMessagesCount//未读消息的个数 color: "white" onTextChanged: { if(text == "100"){ text = "99+" }else if(text == "0"){//如果未读消息个数变为0 myitem.removeMe()//移除自己 } } } } MouseArea{ id: item_mouse anchors.fill: parent property bool hovered: false hoverEnabled: true onEntered: { hovered = true } onExited: { hovered = false } onClicked: { myqq.addChatPage(myinfo.uin, myinfo.mytype)//点击了之后开始聊天 myitem.removeMe()//移除自己 } } } } } ================================================ FILE: qml.qrc ================================================ qml/QQItemInfo/DiscuInfo.qml qml/QQItemInfo/FriendInfo.qml qml/QQItemInfo/GroupInfo.qml qml/Api/api.js qml/Api/QQApi.qml qml/Chat/ChatPage.qml qml/Chat/ChatWindowCommand.qml qml/Chat/DiscuChatPage.qml qml/Chat/FriendChatPage.qml qml/Chat/GroupChatPage.qml qml/Chat/MessageListComponent.qml qml/Chat/qqshow.png qml/Login/LoginPanel/AccountList.qml qml/Login/LoginPanel/LoginCheckBox.qml qml/Login/LoginPanel/LoginInputArea.qml qml/Login/LoginPanel/LoginPage.qml qml/Login/main.qml qml/Login/MyLoginButton.qml qml/Login/SettingPage.qml qml/MainPanel/ListPage/AllListPage.qml qml/MainPanel/ListPage/DiscuList.qml qml/MainPanel/ListPage/FriendList.qml qml/MainPanel/ListPage/GroupAndDiscuPage.qml qml/MainPanel/ListPage/GroupList.qml qml/MainPanel/ListPage/RecentList.qml qml/MainPanel/main.qml qml/MainPanel/MainPanelPage.qml qml/Utility/ComboBox/MyComboBox.qml qml/Utility/ComboBox/MyComboBoxComponent.qml qml/Utility/KeyboardPage/SoftKeyboard.qml qml/Utility/KeyboardPage/SoftKeyboardButton.qml qml/Utility/CodeInput.qml qml/Utility/MyButton.qml qml/Utility/MyMessageBox.qml qml/Utility/MyRectangularGlow.qml qml/Utility/MyScrollView.qml qml/Utility/MyTextArea.qml qml/Utility/MyTextField.qml qml/Utility/MyTextView.qml qml/Utility/MyWindow.qml qml/Utility/SystemTray.qml qml/Utility/TrayMessageWindow.qml ================================================ FILE: qmlapplicationviewer.pri ================================================ # checksum 0x5b42 version 0x70013 # This file was generated by the Qt Quick Application wizard of Qt Creator. # The code below adds the QmlApplicationViewer to the project and handles the # activation of QML debugging. # It is recommended not to modify this file, since newer versions of Qt Creator # may offer an updated version of it. #QT += declarative S#OURCES += $$PWD/qmlapplicationviewer.cpp #HEADERS += $$PWD/qmlapplicationviewer.h INCLUDEPATH += $$PWD # Include JS debugger library if QMLJSDEBUGGER_PATH is set !isEmpty(QMLJSDEBUGGER_PATH) { include($$QMLJSDEBUGGER_PATH/qmljsdebugger-lib.pri) } else { DEFINES -= QMLJSDEBUGGER } contains(CONFIG,qdeclarative-boostable):contains(MEEGO_EDITION,harmattan) { DEFINES += HARMATTAN_BOOSTER } # This file was generated by an application wizard of Qt Creator. # The code below handles deployment to Symbian and Maemo, aswell as copying # of the application data to shadow build directories on desktop. # It is recommended not to modify this file, since newer versions of Qt Creator # may offer an updated version of it. defineTest(qtcAddDeployment) { for(deploymentfolder, DEPLOYMENTFOLDERS) { item = item$${deploymentfolder} itemsources = $${item}.sources $$itemsources = $$eval($${deploymentfolder}.source) itempath = $${item}.path $$itempath= $$eval($${deploymentfolder}.target) export($$itemsources) export($$itempath) DEPLOYMENT += $$item } MAINPROFILEPWD = $$PWD symbian { isEmpty(ICON):exists($${TARGET}.svg):ICON = $${TARGET}.svg isEmpty(TARGET.EPOCHEAPSIZE):TARGET.EPOCHEAPSIZE = 0x20000 0x2000000 } else:win32 { copyCommand = for(deploymentfolder, DEPLOYMENTFOLDERS) { source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) source = $$replace(source, /, \\) sourcePathSegments = $$split(source, \\) target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments) target = $$replace(target, /, \\) target ~= s,\\\\\\.?\\\\,\\, !isEqual(source,$$target) { !isEmpty(copyCommand):copyCommand += && isEqual(QMAKE_DIR_SEP, \\) { copyCommand += $(COPY_DIR) \"$$source\" \"$$target\" } else { source = $$replace(source, \\\\, /) target = $$OUT_PWD/$$eval($${deploymentfolder}.target) target = $$replace(target, \\\\, /) copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\" } } } !isEmpty(copyCommand) { copyCommand = @echo Copying application data... && $$copyCommand copydeploymentfolders.commands = $$copyCommand first.depends = $(first) copydeploymentfolders export(first.depends) export(copydeploymentfolders.commands) QMAKE_EXTRA_TARGETS += first copydeploymentfolders } } else:unix { maemo5 { desktopfile.files = $${TARGET}.desktop desktopfile.path = /usr/share/applications/hildon icon.files = $${TARGET}64.png icon.path = /usr/share/icons/hicolor/64x64/apps } else:!isEmpty(MEEGO_VERSION_MAJOR) { desktopfile.files = $${TARGET}_harmattan.desktop desktopfile.path = /usr/share/applications icon.files = $${TARGET}80.png icon.path = /usr/share/icons/hicolor/80x80/apps } else { # Assumed to be a Desktop Unix copyCommand = for(deploymentfolder, DEPLOYMENTFOLDERS) { source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) source = $$replace(source, \\\\, /) macx { target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target) } else { target = $$OUT_PWD/$$eval($${deploymentfolder}.target) } target = $$replace(target, \\\\, /) sourcePathSegments = $$split(source, /) targetFullPath = $$target/$$last(sourcePathSegments) targetFullPath ~= s,/\\.?/,/, !isEqual(source,$$targetFullPath) { !isEmpty(copyCommand):copyCommand += && copyCommand += $(MKDIR) \"$$target\" copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\" } } !isEmpty(copyCommand) { copyCommand = @echo Copying application data... && $$copyCommand copydeploymentfolders.commands = $$copyCommand first.depends = $(first) copydeploymentfolders export(first.depends) export(copydeploymentfolders.commands) QMAKE_EXTRA_TARGETS += first copydeploymentfolders } } installPrefix = /opt/$${TARGET} for(deploymentfolder, DEPLOYMENTFOLDERS) { item = item$${deploymentfolder} itemfiles = $${item}.files $$itemfiles = $$eval($${deploymentfolder}.source) itempath = $${item}.path $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target) export($$itemfiles) export($$itempath) INSTALLS += $$item } !isEmpty(desktopfile.path) { export(icon.files) export(icon.path) export(desktopfile.files) export(desktopfile.path) INSTALLS += icon desktopfile } target.path = $${installPrefix}/bin export(target.path) INSTALLS += target } export (ICON) export (INSTALLS) export (DEPLOYMENT) export (TARGET.EPOCHEAPSIZE) export (TARGET.CAPABILITY) export (LIBS) export (QMAKE_EXTRA_TARGETS) } ================================================ FILE: src/main.cpp ================================================ #include #include #include #include #include #include #include #include #include #include "mynetworkaccessmanagerfactory.h" #include "systemtrayicon.h" #include "utility.h" #include "mywindow.h" #include "qqstars.h" #include "myimage.h" #include "mysvgview.h" #include "myshortcut.h" #include "myhttprequest.h" #include "mymessagebox.h" #include "downloadimage.h" #include "texteditplaygif.h" #include int main(int argc, char *argv[]) { QApplication app(argc, argv); app.setApplicationName ("QQStars"); app.setApplicationVersion ("1.0.0"); app.setOrganizationName ("雨后星辰"); app.setApplicationDisplayName ("星辰QQ"); QTranslator *translator = new QTranslator; translator->load (":/qt_zh_CN.qm"); QApplication::installTranslator (translator); QQmlApplicationEngine *engine = new QQmlApplicationEngine; engine->setNetworkAccessManagerFactory (new MyNetworkAccessManagerFactory());//给qml设置网络请求所用的类 qmlRegisterType("MyTextEditPlugin", 1, 0, "TextEditPlayGif"); qmlRegisterType("mywindow", 1,0, "MyQuickWindow"); qmlRegisterType("mywindow", 1,0, "MySystemTrayIcon"); qmlRegisterType("mywindow", 1,0, "MyMenu"); qmlRegisterType("mywindow", 1,0, "MenuSeparator"); qmlRegisterType("mywindow", 1,0, "MyMenuItem"); qmlRegisterType("utility", 1,0, "MyShortcut"); qmlRegisterType("utility", 1, 0, "DownloadImage"); qmlRegisterType("qqstars", 1,0, "QQ"); qmlRegisterType("QQItemInfo", 1,0, "FriendInfo"); qmlRegisterType("QQItemInfo", 1,0, "GroupInfo"); qmlRegisterType("QQItemInfo", 1,0, "DiscuInfo"); qmlRegisterType("QQItemInfo", 1,0, "QQItemInfo"); qmlRegisterType("QQItemInfo", 1, 0, "ChatMessageInfo"); qmlRegisterType("QQItemInfo", 1, 0, "ChatMessageInfoList"); qmlRegisterType("mywindow", 1,0, "MyImage"); qmlRegisterType("mywindow", 1, 0, "SvgView"); qmlRegisterType("mywindow", 1, 0, "MessageBox"); Utility *utility=Utility::createUtilityClass (); QNetworkRequest* request = utility->getHttpRequest ()->getNetworkRequest (); request->setRawHeader ("Referer", "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=2");//和腾讯服务器打交道需要设置这个 request->setHeader (QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request = utility->getDownloadImage ()->getHttpRequest ()->getNetworkRequest (); request->setRawHeader ("Referer", "http://web2.qq.com/webqq.html");//需要设置这个,不然腾讯服务器不响应你的请求 request->setRawHeader ("Accept", "image/webp,*/*;q=0.8"); utility->initUtility (new QSettings, engine); QQmlComponent component0(engine, QUrl("qrc:/qml/Api/QQApi.qml")); QQCommand *qqapi = qobject_cast(component0.create ()); engine->rootContext ()->setContextProperty ("myqq", qqapi); QQmlComponent component(engine, QUrl("qrc:/qml/Utility/SystemTray.qml")); SystemTrayIcon *systemTray = qobject_cast(component.create ()); #ifdef Q_OS_WIN systemTray->setParent (Utility::createUtilityClass ());//不设置父对象会导致程序退出后托盘还存在的问题 #endif engine->rootContext ()->setContextProperty ("systemTray", systemTray);//将程序托盘注册过去 qqapi->loadLoginWindow ();//加载登录窗口 return app.exec(); } ================================================ FILE: src/mywidgets/myimage.cpp ================================================ #include "myimage.h" #include #include #include #include #include QCache MyImage::pixmapCache; #if(QT_VERSION>=0x050000) MyImage::MyImage(QQuickItem *parent) : QQuickPaintedItem(parent) #else MyImage::MyImage(QDeclarativeItem *parent) : QDeclarativeItem(parent) #endif { #if(QT_VERSION<0x050000) setFlag(QGraphicsItem::ItemHasNoContents, false); #endif m_status = Null; m_cache = true; m_grayscale = false; m_source = ""; m_sourceSize = QSize(0,0); m_defaultSize = QSize(0,0); pixmap = NULL; reply = NULL; connect(&manager, SIGNAL(finished(QNetworkReply*)), SLOT(onDownImageFinished(QNetworkReply*))); } MyImage::~MyImage() { if(pixmap!=NULL) delete pixmap; } QUrl MyImage::source() const { return m_source; } QUrl MyImage::maskSource() const { return m_maskSource; } bool MyImage::cache() const { return m_cache; } bool MyImage::grayscale() const { return m_grayscale; } void MyImage::chromaticToGrayscale(QImage &image) { if(image.isNull()||image.isGrayscale ()) return; for(int i=0;iabort(); //先结束上次的网络请求 } QNetworkRequest request; request.setUrl(url); reply = manager.get(request); } void MyImage::setImage(QImage &image) { setDefaultSize(image.size()); if(m_cache){ QPixmap *temp_pixmap = new QPixmap(QPixmap::fromImage(image)); pixmapCache.insert(m_source.toString(), temp_pixmap); } QSize temp_size = m_sourceSize; if(temp_size==QSize(0,0)){ temp_size = m_defaultSize; }else if(temp_size.width()==0){ temp_size.setWidth((double)temp_size.height()/m_defaultSize.height()*m_defaultSize.width()); }else if(temp_size.height()==0){ temp_size.setHeight((double)temp_size.width()/m_defaultSize.width()*m_defaultSize.height()); } if(m_defaultSize!=temp_size) image = image.scaled(temp_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); if(m_grayscale){//如果为黑白 chromaticToGrayscale(image);//转换为黑白图 } if(pixmap!=NULL) delete pixmap; pixmap = new QPixmap(QPixmap::fromImage(image)); QString str = m_maskSource.toLocalFile(); if(str==""){ str = m_maskSource.toString(); } if(str!=""){ if( str.mid (0, 3) == "qrc") str = str.mid (3, str.count ()-3); QBitmap bitmap(str); if(!bitmap.isNull()){ int max_width = bitmap.width(); int max_height = bitmap.height(); max_width = pixmap->width()>max_width?pixmap->width():max_width; max_height = pixmap->height()>max_height?pixmap->height():max_height; QPixmap temp_pixmap = pixmap->scaled(max_width, max_height); temp_pixmap.setMask (bitmap.scaled(temp_pixmap.size())); *pixmap = temp_pixmap.scaled(image.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } } setImplicitWidth(image.width()); setImplicitHeight(image.height()); update(); setStatus(Ready); emit loadReady(); } void MyImage::onDownImageFinished(QNetworkReply *reply) { if(reply->error() == QNetworkReply::NoError){ QImage image; if( !image.loadFromData(reply->readAll())){ emit loadError (); setStatus(Error); return; } setImage(image); }else{ setStatus(Error); emit loadError(); } } #if(QT_VERSION>=0x050000) void MyImage::paint(QPainter *painter) #else void MyImage::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) #endif { if(pixmap==NULL||pixmap->isNull()) return; if(smooth()) painter->setRenderHint(QPainter::SmoothPixmapTransform); painter->drawPixmap (boundingRect().toRect(), *pixmap); } MyImage::State MyImage::status() const { return m_status; } QSize MyImage::sourceSize() const { return m_sourceSize; } QSize MyImage::defaultSize() const { return m_defaultSize; } const QPixmap *MyImage::getPixmap() const { return pixmap; } void MyImage::setSource(QUrl arg) { if (!m_cache||m_source != arg) { setStatus(Loading); bool can_emit = m_source==arg; m_source = arg; reLoad(); //加载图片 if(can_emit) emit sourceChanged(arg); } } void MyImage::setMaskSource(QUrl arg) { if (m_maskSource != arg) { m_maskSource = arg; if(pixmap!=NULL) reLoad(); emit maskSourceChanged(arg); } } void MyImage::setCache(bool arg) { if (m_cache != arg) { m_cache = arg; if(!m_cache){ QPixmap* temp_pixmap = pixmapCache.object(m_source.toString()); pixmapCache.remove(m_source.toString()); if(temp_pixmap!=NULL) delete temp_pixmap; } emit cacheChanged(arg); } } void MyImage::setGrayscale(bool arg) { if(m_grayscale!=arg){ m_grayscale = arg; if(pixmap!=NULL){ if(m_grayscale){ QImage image = pixmap->toImage(); chromaticToGrayscale(image); *pixmap = QPixmap::fromImage(image); update(); }else{ reLoad(); } } emit grayscaleChanged(arg); } } void MyImage::setStatus(MyImage::State arg) { if (m_status != arg) { m_status = arg; emit statusChanged(arg); } } void MyImage::reLoad() { QString str = m_source.toLocalFile(); if(str == ""){ str = m_source.toString(); if(str==""){ if(pixmap!=NULL){ delete pixmap; pixmap = NULL; } return; } } QPixmap *temp_pixmap = pixmapCache.object(m_source.toString()); if(temp_pixmap!=NULL){ bool temp_cache = m_cache; m_cache = false;//先设置false,防止setImage中再次更新缓存 QImage image = temp_pixmap->toImage(); setImage(image); m_cache = temp_cache; return; } if(str.indexOf("http")==0){//如果是网络图片 downloadImage(m_source); return; } if( str.mid (0, 3) == "qrc") str = str.mid (3, str.count ()-3); QImage image; if( !image.load (str)){ emit loadError (); setStatus(Error); return; } setImage(image); } void MyImage::setSourceSize(QSize arg) { if (m_sourceSize != arg) { m_sourceSize = arg; if(pixmap!=NULL) reLoad(); emit sourceSizeChanged(arg); } } void MyImage::setDefaultSize(QSize arg) { if (m_defaultSize != arg) { m_defaultSize = arg; emit defaultSizeChanged(arg); } } bool MyImage::save(const QString& fileName) const { if(pixmap!=NULL){ return pixmap->save(fileName); } return false; } ================================================ FILE: src/mywidgets/myimage.h ================================================ #ifndef MYIMAGE_H #define MYIMAGE_H #include #include #include #include #include #if(QT_VERSION>=0x050000) #include #else #include #endif #if(QT_VERSION>=0x050000) class MyImage : public QQuickPaintedItem #else class MyImage : public QDeclarativeItem #endif { Q_OBJECT Q_PROPERTY(QUrl maskSource READ maskSource WRITE setMaskSource NOTIFY maskSourceChanged) Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged) Q_PROPERTY(bool grayscale READ grayscale WRITE setGrayscale NOTIFY grayscaleChanged) Q_PROPERTY(State status READ status NOTIFY statusChanged FINAL) Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize NOTIFY sourceSizeChanged) Q_PROPERTY(QSize defaultSize READ defaultSize NOTIFY defaultSizeChanged FINAL) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_ENUMS(State) public: enum State{ Null, Ready, Loading, Error }; #if(QT_VERSION>=0x050000) explicit MyImage(QQuickItem *parent = 0); #else explicit MyImage(QDeclarativeItem *parent = 0); #endif ~MyImage(); QUrl source() const; QUrl maskSource() const; bool cache() const; bool grayscale() const; void chromaticToGrayscale(QImage &image); static QString imageFormatToString(const QByteArray& array); #if(QT_VERSION>=0x050000) void paint(QPainter * painter); #else void paint(QPainter *painter, const QStyleOptionGraphicsItem *new_style, QWidget *new_widget=0); #endif State status() const; QSize sourceSize() const; QSize defaultSize() const; const QPixmap* getPixmap() const; signals: void sourceChanged(QUrl arg); void maskSourceChanged(QUrl arg); void loadError();//加载图片出错 void loadReady(); void cacheChanged(bool arg); void grayscaleChanged(bool arg); void statusChanged(State arg); void sourceSizeChanged(QSize arg); void defaultSizeChanged(QSize arg); public slots: void setSource(QUrl arg); void setMaskSource(QUrl arg); void setCache(bool arg); void setGrayscale(bool arg); void setStatus(State arg); void reLoad(); void setSourceSize(QSize arg); void setDefaultSize(QSize arg); bool save(const QString& fileName) const; private slots: void onDownImageFinished(QNetworkReply* reply); private: QUrl m_source; QPixmap *pixmap; static QCache pixmapCache; QUrl m_maskSource; bool m_cache; bool m_grayscale; QNetworkAccessManager manager; QNetworkReply *reply; State m_status; QSize m_sourceSize; QSize m_defaultSize; void downloadImage(const QUrl& url); void setImage(QImage& image); }; #endif // MYIMAGE_H ================================================ FILE: src/mywidgets/mymessagebox.cpp ================================================ #include "mymessagebox.h" #include #include #include #include #include MyMessageBox::MyMessageBox(QWidget *parent) : QMessageBox(parent) { //setAttribute(Qt::WA_TranslucentBackground); setFixedSize (300, 200); setWindowFlags (windowFlags ()|Qt::FramelessWindowHint); //QImage image(":/images/menu_background.png"); //background_pixmap = QPixmap::fromImage (image); //QLabel *label = new QLabel(this); //label->setPixmap (background_pixmap); //label->setGeometry (0,0,width(),height ()); //label->setBackgroundRole (); } QUrl MyMessageBox::styleSource() const { return m_styleSource; } void MyMessageBox::mousePressEvent(QMouseEvent *event) { if(event->button () == Qt::LeftButton){ press_point = event->pos (); allow_move = true; event->accept (); }else{ QMessageBox::mousePressEvent (event); } } void MyMessageBox::mouseMoveEvent(QMouseEvent *event) { if(allow_move){ move (pos()+event->pos ()-press_point); event->accept (); }else{ QMessageBox::mouseMoveEvent (event); } } void MyMessageBox::mouseReleaseEvent(QMouseEvent *event) { if(event->button () == Qt::LeftButton){ allow_move = false; event->accept (); }else{ QMessageBox::mouseReleaseEvent (event); } } void MyMessageBox::setStyleSource(QUrl arg) { if (m_styleSource != arg) { m_styleSource = arg; QFile file(arg.toLocalFile ()); if(file.open (QIODevice::ReadOnly)){ setStyleSheet (file.readAll ()); }else{ qDebug()<<"打开"+arg.toLocalFile ()+"失败;"< #include #include class MyMessageBox : public QMessageBox { Q_OBJECT Q_PROPERTY(QUrl styleSource READ styleSource WRITE setStyleSource NOTIFY styleSourceChanged) public: explicit MyMessageBox(QWidget *parent = 0); QUrl styleSource() const; private: void mousePressEvent(QMouseEvent * event); void mouseMoveEvent(QMouseEvent * event); void mouseReleaseEvent(QMouseEvent * event); QUrl m_styleSource; QPoint press_point; bool allow_move; QPixmap *background_pixmap; signals: void styleSourceChanged(QUrl arg); public slots: void setStyleSource(QUrl arg); }; #endif // MYMESSAGEBOX_H ================================================ FILE: src/mywidgets/mysvgview.cpp ================================================ #include "mysvgview.h" #include #include #include #if(QT_VERSION>=0x050000) MySvgView::MySvgView(QQuickItem *parent) : QQuickPaintedItem(parent) #else MySvgView::MySvgView(QDeclarativeItem *parent) : QDeclarativeItem(parent) #endif { #if(QT_VERSION<0x050000) setFlag(QGraphicsItem::ItemHasNoContents, false); #endif m_defaultSize = QSize(0,0); svg = new QSvgRenderer(this); } QUrl MySvgView::source() const { return m_source; } QSize MySvgView::defaultSize() const { return m_defaultSize; } void MySvgView::setDefaultSize(QSize arg) { if(arg!=m_defaultSize){ m_defaultSize = arg; emit defaultSizeChanged (arg); } } #if(QT_VERSION>=0x050000) void MySvgView::paint(QPainter *painter) #else void MySvgView::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) #endif { svg->render (painter, boundingRect()); } void MySvgView::setSource(QUrl arg) { if (m_source != arg) { m_source = arg; QString str = m_source.toLocalFile(); if(str == ""){ str = m_source.toString(); if(str==""){ svg->deleteLater(); svg = new QSvgRenderer(this); return; } } if( str.mid (0, 3) == "qrc") str = str.mid (3, str.count ()-3); svg->load (str); setDefaultSize (svg->defaultSize ()); int width = svg->defaultSize ().width (); int height = svg->defaultSize ().height (); setImplicitWidth(width); setImplicitHeight(height); update (); emit sourceChanged(arg); } } ================================================ FILE: src/mywidgets/mysvgview.h ================================================ #ifndef MYSVGVIEW_H #define MYSVGVIEW_H #include #if(QT_VERSION>=0x050000) #include #else #include #endif #if(QT_VERSION>=0x050000) class MySvgView : public QQuickPaintedItem #else class MySvgView : public QDeclarativeItem #endif { Q_OBJECT Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(QSize defaultSize READ defaultSize WRITE setDefaultSize NOTIFY defaultSizeChanged FINAL) public: #if(QT_VERSION>=0x050000) explicit MySvgView(QQuickItem *parent = 0); #else explicit MySvgView(QDeclarativeItem *parent = 0); #endif QUrl source() const; QSize defaultSize() const; public slots: void setSource(QUrl arg); void setDefaultSize( QSize arg ); signals: void sourceChanged(QUrl arg); void rotationModeChanged(Qt::Axis arg); void rotationOriginChanged(QPoint arg); void defaultSizeChanged( QSize arg ); private: QSvgRenderer *svg; QUrl m_source; QSize m_defaultSize; #if(QT_VERSION>=0x050000) void paint(QPainter * painter); #else void paint(QPainter *painter, const QStyleOptionGraphicsItem *new_style, QWidget *new_widget=0); #endif }; #endif // MYSVGVIEW_H ================================================ FILE: src/mywidgets/mywindow.cpp ================================================ #include "mywindow.h" #include "utility.h" #include #include #include #include #include #include #ifdef Q_OS_WIN #include #elif defined(Q_OS_LINUX) #include #include #endif #include bool test(QObject *, Qt::ShortcutContext) { return true; } MyWindow::MyWindow(QQuickWindow *parent) : QQuickWindow(parent) { setObjectName ("MyWindow"); m_noBorder = false; m_windowStatus = StopCenter; m_topHint = false; old_topHint=false; m_noNotifyIcon = false; m_windowActive = false; m_mousePenetrate = false; m_minimumWidth = 0; m_minimumHeight = 0; m_maximumWidth = 9999999; m_maximumHeight = 9999999; connect (this, &QQuickWindow::widthChanged, this, &MyWindow::actualWidthChanged); connect (this, &QQuickWindow::heightChanged, this, &MyWindow::actualHeightChanged); connect (this, &QQuickWindow::xChanged, this, &MyWindow::actualXChanged); connect (this, &QQuickWindow::yChanged, this, &MyWindow::actualYChanged); connect (this, &QQuickWindow::xChanged, this, &MyWindow::onActualXChanged); connect (this, &QQuickWindow::yChanged, this, &MyWindow::onActualYChanged); connect (contentItem (), &QQuickItem::xChanged, this, &MyWindow::xChanged); connect (contentItem (), &QQuickItem::yChanged, this, &MyWindow::yChanged); } bool MyWindow::noNotifyIcon() const { return m_noNotifyIcon; } int MyWindow::width() const { return m_width; } int MyWindow::height() const { return m_height; } int MyWindow::actualWidth() const { return QQuickWindow::width (); } int MyWindow::actualHeight() const { return QQuickWindow::height (); } bool MyWindow::windowActive() const { return m_windowActive; } int MyWindow::x() const { return QQuickWindow::x ()+contentItem ()->x (); } int MyWindow::y() const { return QQuickWindow::y ()+contentItem ()->y (); } int MyWindow::actualX() const { return QQuickWindow::x (); } int MyWindow::actualY() const { return QQuickWindow::y (); } int MyWindow::minimumWidth() const { return m_minimumWidth; } int MyWindow::minimumHeight() const { return m_minimumHeight; } int MyWindow::maximumWidth() const { return m_maximumWidth; } int MyWindow::maximumHeight() const { return m_maximumHeight; } bool MyWindow::topHint() const { return m_topHint; } bool MyWindow::mousePenetrate() const { return m_mousePenetrate; } QPoint MyWindow::cursorPos() const { return QCursor::pos(); } QUrl MyWindow::windowIcon() { return m_windowIcon; } void MyWindow::setWindowIcon(QUrl icon) { if( icon!=m_windowIcon ){ QString str = icon.toString (); if( str.mid (0, 3) == "qrc") str = str.mid (3, str.count ()-3); setIcon (QIcon(str)); m_windowIcon = icon; emit windowIconChanged (); } } void MyWindow::setWindowActive(bool arg) { if(arg!=m_windowActive){ m_windowActive = arg; emit windowActiveChanged (arg); } } void MyWindow::setMousePenetrateArea(QRect rect) { #ifdef Q_OS_LINUX XRectangle* myrect = new XRectangle; myrect->x = rect.x(); myrect->y = rect.y(); myrect->width = rect.width (); myrect->height = rect.height (); qDebug() << myrect->x << myrect->y << myrect->width << myrect->height; XShapeCombineRectangles(QX11Info::display(), winId(), ShapeInput, 0, 0, myrect, 1, ShapeSet, YXBanded); #endif } void MyWindow::focusInEvent(QFocusEvent *ev) { QQuickWindow::focusInEvent(ev); setWindowActive (true); } void MyWindow::focusOutEvent(QFocusEvent *ev) { QQuickWindow::focusOutEvent (ev); setWindowActive (false); } void MyWindow::onActualXChanged() { emit xChanged(); } void MyWindow::onActualYChanged() { emit yChanged(); } bool MyWindow::noBorder() { return m_noBorder; } void MyWindow::setNoBorder(bool isNoBroder) { if( isNoBroder!=m_noBorder ) { m_noBorder = isNoBroder; if(!isVisible ()) { if( isNoBroder ) setFlags (flags ()|Qt::FramelessWindowHint); else setFlags (flags ()&~Qt::FramelessWindowHint); }else{ if( isNoBroder ) setFlags (flags ()|Qt::FramelessWindowHint); else setFlags (flags ()&~Qt::FramelessWindowHint); } emit noBorderIconChanged(); } } MyWindow::WindowStatus MyWindow::windowStatus() { return m_windowStatus; } void MyWindow::setWindowStatus(MyWindow::WindowStatus new_status) { if( new_status!=m_windowStatus ) { if( new_status == BerthPrepare&&m_windowStatus!=StopCenter ) { setTopHint (old_topHint); }else if(new_status!=StopCenter&&new_status!=BerthPrepare){ old_topHint = topHint (); setTopHint (true);//设置窗口为最前端 } m_windowStatus = new_status; emit windowStatusChanged (); } } int MyWindow::borderLeft() { #ifdef Q_OS_WIN return 0; #elif defined(Q_OS_MACX) return 0; #elif defined(Q_OS_LINUX) return 65; #else return 0; #endif } int MyWindow::borderRight() { #ifdef Q_OS_WIN return QApplication::screens ()[0]->size ().width (); #elif defined(Q_OS_MACX) return QApplication::screens ()[0]->size ().width (); #elif defined(Q_OS_LINUX) return QApplication::screens ()[0]->size ().width (); #else return QApplication::screens ()[0]->size ().width (); #endif } int MyWindow::borderTop() { #ifdef Q_OS_WIN return 0; #elif defined(Q_OS_OSX) return 0; #elif defined(Q_OS_LINUX) return 25; #else return 0; #endif } void MyWindow::setTopHint(bool arg) { if (m_topHint != arg) { m_topHint = arg; if( arg ){ setFlags (flags ()|Qt::WindowStaysOnTopHint); #ifdef Q_OS_LINUX if(isVisible()){ //setVisible(false); //setVisible(true); close(); show(); } #endif }else{ setFlags (flags ()&~Qt::WindowStaysOnTopHint); #ifdef Q_OS_LINUX if(isVisible()){ close(); show(); //setVisible(false); //setVisible(true); } #endif #ifdef Q_OS_WIN SetWindowPos ((HWND)this->winId (),HWND_NOTOPMOST,0,0,width(),height(),SWP_NOSIZE|SWP_NOMOVE); #endif } emit topHintChanged(arg); } } void MyWindow::setNoNotifyIcon(bool arg) { if ( m_noNotifyIcon != arg ) { m_noNotifyIcon = arg; if( arg ) setFlags (flags ()&~Qt::Tool); else{ setFlags (flags ()|Qt::Tool); } emit noNotifyIconChanged(arg); } } void MyWindow::setWidth(int arg) { if (m_width != arg&&arg<=maximumWidth ()&&arg>=minimumWidth ()) { m_width = arg; contentItem ()->setWidth (arg); emit widthChanged(arg); } } void MyWindow::setHeight(int arg) { if (m_height != arg&&arg<=maximumHeight ()&&arg>=minimumHeight ()) { m_height = arg; contentItem ()->setHeight (arg); emit heightChanged(arg); } } void MyWindow::setActualWidth(int arg) { QQuickWindow::setWidth (arg); } void MyWindow::setActualHeight(int arg) { QQuickWindow::setHeight (arg); } void MyWindow::setX(int arg) { QQuickWindow::setX (arg-contentItem ()->x ()); } void MyWindow::setY(int arg) { QQuickWindow::setY (arg-contentItem ()->y ()); } void MyWindow::setActualX(int arg) { QQuickWindow::setX (arg); } void MyWindow::setActualY(int arg) { QQuickWindow::setY (arg); } void MyWindow::setMinimumWidth(int arg) { if (m_minimumWidth != arg) { m_minimumWidth = arg; int temp = actualWidth ()-width();//算出真实宽和内容宽(不算阴影的宽)的差值 QQuickWindow::setMinimumWidth (temp+arg);//设置真实宽的限制 if(width()arg){ setWidth (arg); } emit maximumWidthChanged(arg); } } void MyWindow::setMaximumHeight(int arg) { if (m_maximumHeight != arg) { m_maximumHeight = arg; int temp = actualHeight ()-height(); QQuickWindow::setMinimumHeight (temp+arg);//设置真实高的限制 if(height()>arg){ setHeight (arg); } emit maximumHeightChanged(arg); } } void MyWindow::setMousePenetrate(bool arg) { if (m_mousePenetrate != arg) { m_mousePenetrate = arg; #ifdef Q_OS_LINUX if(arg){ XShapeCombineRectangles(QX11Info::display(), winId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, YXBanded); }else{ XRectangle* myrect = new XRectangle; myrect->x = 0; myrect->y = 0; myrect->width = actualWidth (); myrect->height = actualHeight (); XShapeCombineRectangles(QX11Info::display(), winId(), ShapeInput, 0, 0, myrect, 1, ShapeSet, YXBanded); } #elif defined(Q_OS_OSX) qDebug()<<"mac os暂不支持鼠标穿透"; #elif defined(Q_OS_WIN) HWND my_hwnd = (HWND)this->winId (); if(arg){ SetWindowLong(my_hwnd, GWL_EXSTYLE, GetWindowLong(my_hwnd, GWL_EXSTYLE) | WS_EX_TRANSPARENT); }else{ SetWindowLong(my_hwnd, GWL_EXSTYLE, GetWindowLong(my_hwnd, GWL_EXSTYLE)&(~WS_EX_TRANSPARENT)); } #endif emit mousePenetrateChanged(arg); } } void MyWindow::setCursorPos(QPoint cursorPos) { QCursor::setPos(cursorPos); } void MyWindow::close() { emit closeing (); QQuickWindow::close (); } void MyWindow::deleteWindow() { deleteLater ();//销毁自己 } ================================================ FILE: src/mywidgets/mywindow.h ================================================ #ifndef MYWINDOW_H #define MYWINDOW_H #include #include #include #include #include #include class MyWindow : public QQuickWindow { Q_OBJECT Q_PROPERTY(QUrl windowIcon READ windowIcon WRITE setWindowIcon NOTIFY windowIconChanged)//状态栏图标 Q_PROPERTY(bool noBorder READ noBorder WRITE setNoBorder NOTIFY noBorderIconChanged)//无边框 Q_PROPERTY(WindowStatus windowStatus READ windowStatus WRITE setWindowStatus NOTIFY windowStatusChanged)//窗口状态 Q_PROPERTY(int borderLeft READ borderLeft CONSTANT)//离屏幕左边的距离 Q_PROPERTY(int borderRight READ borderRight CONSTANT)//离屏幕右边的距离 Q_PROPERTY(int borderTop READ borderTop CONSTANT)//离屏幕上边的距离 Q_PROPERTY(bool topHint READ topHint WRITE setTopHint NOTIFY topHintChanged)//窗体保持在最前端 Q_PROPERTY(bool noNotifyIcon READ noNotifyIcon WRITE setNoNotifyIcon NOTIFY noNotifyIconChanged)//无状态栏图标 Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged)//窗口的width(不包含边框的阴影) Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged)//窗口的height(不包含边框的阴影) Q_PROPERTY(int actualWidth READ actualWidth WRITE setActualWidth NOTIFY actualWidthChanged)//真实的width,包含阴影 Q_PROPERTY(int actualHeight READ actualHeight WRITE setActualHeight NOTIFY actualHeightChanged)//真实的height,包含阴影 Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged)//窗口内容相对于桌面的绝对坐标(不包含阴影部分) Q_PROPERTY(int y READ y WRITE setY NOTIFY yChanged)//窗口内容相对于桌面的绝对坐标(不包含阴影部分) Q_PROPERTY(int actualX READ actualX WRITE setActualX NOTIFY actualXChanged)//窗口相对于桌面的绝对坐标(包含阴影部分) Q_PROPERTY(int actualY READ actualY WRITE setActualY NOTIFY actualYChanged)//窗口相对于桌面的绝对坐标(包含阴影部分) Q_PROPERTY(bool windowActive READ windowActive NOTIFY windowActiveChanged FINAL)//窗口是否获得焦点,是否为活跃窗口 Q_PROPERTY(int minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) Q_PROPERTY(int minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged) Q_PROPERTY(int maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged) Q_PROPERTY(int maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged) Q_PROPERTY(bool mousePenetrate READ mousePenetrate WRITE setMousePenetrate NOTIFY mousePenetrateChanged) //是否穿透鼠标 Q_PROPERTY(QPoint cursorPos READ cursorPos WRITE setCursorPos FINAL) Q_ENUMS(WindowStatus) public: explicit MyWindow(QQuickWindow *parent = 0); enum WindowStatus{ StopCenter,//初始状态,停在屏幕中,不靠任何边界 BerthPrepare,//进入准停靠状态(此时鼠标离开窗体就会停靠) BerthLeft,//停靠在左边 BerthRight,//停靠在右边 BerthTop//停靠在上边 }; QUrl windowIcon(); bool noNotifyIcon() const; int width() const; int height() const; int actualWidth() const; int actualHeight() const; bool windowActive() const; int x() const; int y() const; int actualX() const; int actualY() const; int minimumWidth() const; int minimumHeight() const; int maximumWidth() const; int maximumHeight() const; WindowStatus windowStatus(); bool noBorder(); int borderLeft(); int borderRight(); int borderTop(); bool topHint() const; bool mousePenetrate() const; QPoint cursorPos() const; private: QUrl m_windowIcon; bool m_noBorder; WindowStatus m_windowStatus; bool m_topHint, old_topHint; bool m_noNotifyIcon; qreal m_width; qreal m_height; bool m_windowActive; int m_minimumWidth; int m_minimumHeight; int m_maximumWidth; int m_maximumHeight; bool m_mousePenetrate; void setWindowActive(bool arg); protected: void focusInEvent(QFocusEvent * ev); void focusOutEvent(QFocusEvent * ev); private slots: void onActualXChanged(); void onActualYChanged(); signals: void windowIconChanged(); void noBorderIconChanged(); void windowStatusChanged(); void topHintChanged(bool arg); void noNotifyIconChanged(bool arg); void widthChanged(int arg); void heightChanged(int arg); void actualWidthChanged(int arg); void actualHeightChanged(int arg); void windowActiveChanged(bool arg); void xChanged(); void yChanged(); void actualXChanged(int arg); void actualYChanged(int arg); void minimumWidthChanged(int arg); void minimumHeightChanged(int arg); void maximumWidthChanged(int arg); void maximumHeightChanged(int arg); void mousePenetrateChanged(bool arg); void closeing();//当调用close时发射; public slots: void setNoBorder( bool isNoBroder ); void setWindowIcon( QUrl icon ); void setWindowStatus( WindowStatus new_status ); void setTopHint(bool arg); void setNoNotifyIcon(bool arg); void setWidth(int arg); void setHeight(int arg); void setActualWidth(int arg); void setActualHeight(int arg); void setX(int arg); void setY(int arg); void setActualX(int arg); void setActualY(int arg); void setMinimumWidth(int arg); void setMinimumHeight(int arg); void setMaximumWidth(int arg); void setMaximumHeight(int arg); void setMousePenetrate(bool arg); void setCursorPos(QPoint cursorPos); void setMousePenetrateArea(QRect rect); void close(); void deleteWindow(); }; #endif // MYWINDOW_H ================================================ FILE: src/mywidgets/systemtrayicon.cpp ================================================ #include "systemtrayicon.h" #include #include #include #include "utility.h" #include "qxtglobalshortcut.h" SystemTrayIcon::SystemTrayIcon(QQuickItem *parent) : QQuickItem(parent) { setVisible (false); systempTray = new QSystemTrayIcon(this); connect (systempTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(onActivated(QSystemTrayIcon::ActivationReason))); connect (systempTray, SIGNAL(messageClicked()), this, SIGNAL(messageClicked())); connect (this, SIGNAL(visibleChanged()), SLOT(onVisibleChanged())); } void SystemTrayIcon::onActivated(QSystemTrayIcon::ActivationReason reason) { emit activated (ActivationReason(reason)); } void SystemTrayIcon::onVisibleChanged() { systempTray->setVisible (isVisible ()); } MyMenu *SystemTrayIcon::menu() const { return m_menu; } QUrl SystemTrayIcon::windowIcon() const { return m_windowIcon; } QString SystemTrayIcon::toolTip() const { return m_toolTip; } int SystemTrayIcon::x() const { return systempTray->geometry ().x (); } int SystemTrayIcon::y() const { return systempTray->geometry ().y (); } int SystemTrayIcon::width() const { return systempTray->geometry ().width (); } int SystemTrayIcon::height() const { return systempTray->geometry ().height (); } void SystemTrayIcon::setWindowIcon(QUrl icon) { if( icon!=m_windowIcon ){ QString str = icon.toLocalFile(); if(str == ""){ str = icon.toString(); } if( str.mid (0, 3) == "qrc") str = str.mid (3, str.count ()-3); systempTray->setIcon (QIcon(str)); m_windowIcon = icon; emit windowIconChanged (); } } void SystemTrayIcon::showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon icon, int millisecondsTimeoutHint) { systempTray->showMessage (title, message, icon, millisecondsTimeoutHint); } void SystemTrayIcon::setMenu(MyMenu *arg) { if (m_menu != arg) { m_menu = arg; systempTray->setContextMenu (m_menu->menu); emit menuChanged(arg); } } void SystemTrayIcon::setToolTip(QString arg) { if (m_toolTip != arg) { m_toolTip = arg; systempTray->setToolTip (arg); emit toolTipChanged(arg); } } MyMenuItem::MyMenuItem(QObject *parent) : QAction(parent) { setObjectName ("MyMenuItem"); m_shortcut = new QxtGlobalShortcut(); connect(m_shortcut, SIGNAL(activated()), SLOT(trigger())); } QUrl MyMenuItem::icon() const { return m_myIcon; } QString MyMenuItem::shortcut() const { return m_shortcut->shortcut ().toString (); } void MyMenuItem::setIcon(QUrl icon) { if( icon!=m_myIcon ){ QString str = icon.toLocalFile(); if(str == ""){ str = icon.toString(); } if( str.mid (0, 3) == "qrc") str = str.mid (3, str.count ()-3); QAction::setIcon (QIcon(str)); m_myIcon = icon; emit iconChanged (); } } void MyMenuItem::setShortcut(const QString &shortcut) { if( m_shortcut->setShortcut (QKeySequence(shortcut)) ){ QAction::setShortcut (QKeySequence(shortcut)); emit shortcutChanged (shortcut); }else{ qDebug()<<"设置"+shortcut+"热键出错"; emit shortcutChanged ("set shortcut error"); } } void MyMenu::componentComplete() { QObjectList temp_list = children (); for (int i=0; iobjectName ()=="MyMenuItem"){ MyMenuItem *item = qobject_cast(obj); menu->addAction(item); }else if(obj->objectName ()=="MenuSeparator"){ menu->addSeparator (); }else if(obj->objectName ()=="MyMenu"){ MyMenu *item = qobject_cast(obj); menu->addMenu (item->menu); } } } MyMenu::MyMenu(QQuickItem *parent) : QQuickItem(parent) { m_styleSource = QUrl(""); setObjectName ("MyMenu"); menu = new MenuPrivate(); setVisible (false); } QUrl MyMenu::styleSource() const { return m_styleSource; } int MyMenu::width() const { return menu->width (); } int MyMenu::height() const { return menu->height (); } QString MyMenu::styleSheet() const { return menu->styleSheet (); } void MyMenu::clear() { menu->clear (); } void MyMenu::addSeparator() { menu->addSeparator (); } void MyMenu::addMenuItem(MyMenuItem *item) { menu->addAction(item); } void MyMenu::addMenu(MyMenu *mymenu) { menu->addMenu (mymenu->menu); } void MyMenu::setStyleSource(QUrl arg) { if (m_styleSource != arg) { m_styleSource = arg; QString str = arg.toLocalFile(); if(str == ""){ str = arg.toString(); if(str==""){ return; } } if( str.mid (0, 3) == "qrc") str = str.mid (3, str.count ()-3); QFile file(str); if(file.open (QIODevice::ReadOnly)){ menu->setStyleSheet (file.readAll ()); }else{ qDebug()<<"打开"+str+"失败;"<mouseDesktopPos(); menu->move (pos); menu->show (); } void MyMenu::setWidth(int arg) { int m_width = menu->width (); if (m_width != arg) { menu->setFixedWidth (arg); emit widthChanged(arg); } } void MyMenu::setHeight(int arg) { int m_height = menu->height (); if (m_height != arg) { menu->setFixedHeight (arg); emit heightChanged(arg); } } void MyMenu::setStyleSheet(QString arg) { QString m_styleSheet = menu->styleSheet (); if (m_styleSheet != arg) { menu->setStyleSheet (arg); emit styleSheetChanged(arg); } } MenuSeparator::MenuSeparator(QObject *parent): QObject(parent) { setObjectName ("MenuSeparator"); } MenuPrivate::MenuPrivate(QWidget *parent): QMenu(parent) { } MenuPrivate::MenuPrivate(const QString &title, QWidget *parent): QMenu(title, parent) { } ================================================ FILE: src/mywidgets/systemtrayicon.h ================================================ #ifndef SYSTEMTRAYICON_H #define SYSTEMTRAYICON_H #include #include #include #include #include #include class QxtGlobalShortcut; class MenuPrivate: public QMenu { Q_OBJECT public: explicit MenuPrivate(QWidget *parent = 0); explicit MenuPrivate(const QString &title, QWidget *parent = 0); }; class MyMenuItem : public QAction { Q_OBJECT Q_PROPERTY(QUrl icon READ icon WRITE setIcon NOTIFY iconChanged) Q_PROPERTY(QString shortcut READ shortcut WRITE setShortcut NOTIFY shortcutChanged) QUrl m_myIcon; QxtGlobalShortcut* m_shortcut; public: explicit MyMenuItem(QObject *parent = 0); QUrl icon() const; QString shortcut() const; public slots: void setIcon(QUrl icon); void setShortcut(const QString &shortcut); signals: void iconChanged(); void shortcutChanged(QString arg); }; class MenuSeparator : public QObject { public: explicit MenuSeparator(QObject *parent=0); }; class SystemTrayIcon; class MyMenu : public QQuickItem { Q_OBJECT Q_PROPERTY(QUrl styleSource READ styleSource WRITE setStyleSource NOTIFY styleSourceChanged) Q_PROPERTY(QString styleSheet READ styleSheet WRITE setStyleSheet NOTIFY styleSheetChanged) Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged) Q_PROPERTY(int height READ height WRITE setHeight NOTIFY heightChanged) MenuPrivate *menu; QUrl m_styleSource; protected: void componentComplete(); public: explicit MyMenu(QQuickItem *parent = 0); QUrl styleSource() const; int width() const; int height() const; QString styleSheet() const; public slots: void clear(); void addSeparator (); void addMenuItem(MyMenuItem *item); void addMenu( MyMenu *mymenu); friend class SystemTrayIcon; void setStyleSource(QUrl arg); void popup(); void setWidth(int arg); void setHeight(int arg); void setStyleSheet(QString arg); signals: void styleSourceChanged(QUrl arg); void widthChanged(int arg); void heightChanged(int arg); void styleSheetChanged(QString arg); }; class SystemTrayIcon : public QQuickItem { Q_OBJECT Q_PROPERTY(QUrl windowIcon READ windowIcon WRITE setWindowIcon NOTIFY windowIconChanged) Q_PROPERTY(MyMenu* menu READ menu WRITE setMenu NOTIFY menuChanged) Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged) Q_PROPERTY(int x READ x CONSTANT) Q_PROPERTY(int y READ y CONSTANT) Q_PROPERTY(int width READ width CONSTANT) Q_PROPERTY(int height READ height CONSTANT) Q_ENUMS(ActivationReason) QUrl m_windowIcon; MyMenu* m_menu; QSystemTrayIcon *systempTray; QString m_toolTip; protected: public: explicit SystemTrayIcon(QQuickItem *parent = 0); QUrl windowIcon() const; enum ActivationReason { Unknown, Context, DoubleClick, Trigger, MiddleClick }; QString toolTip() const; int x() const; int y() const; int width() const; int height() const; private slots: void onActivated( QSystemTrayIcon::ActivationReason reason ); void onVisibleChanged(); MyMenu* menu() const; signals: void windowIconChanged(); void menuChanged(MyMenu* arg); void activated(ActivationReason arg); void toolTipChanged(QString arg); void messageClicked(); public slots: void setWindowIcon(QUrl icon); void showMessage(const QString & title, const QString & message, QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::Information, int millisecondsTimeoutHint = 10000); void setMenu(MyMenu* arg); void setToolTip(QString arg); }; #endif // SYSTEMTRAYICON_H ================================================ FILE: src/qqstars/qqiteminfo.cpp ================================================ #include "qqiteminfo.h" #include "utility.h" #include "qqstars.h" QString ChatMessageInfo::senderUin() const { return m_senderUin; } QString ChatMessageInfo::contentData() const { return m_contentData; } QDate ChatMessageInfo::date() const { return m_date; } QTime ChatMessageInfo::time() const { return m_time; } int ChatMessageInfo::messageId() const { return m_messageId; } int ChatMessageInfo::messageId2() const { return m_messageId2; } const QQItemInfo *ChatMessageInfo::getParent() { return qobject_cast(parent()); } ChatMessageInfo::ChatMessageInfo(QQItemInfo *parent) { ChatMessageInfo(-1, parent); } ChatMessageInfo::ChatMessageInfo(int messageID, QQItemInfo *parent): QObject(parent) { m_messageId=messageID; } void ChatMessageInfo::setSenderUin(QString arg) { if (m_senderUin == arg) return; m_senderUin = arg; emit senderUinChanged(arg); } void ChatMessageInfo::setContentData(QString arg) { if (m_contentData == arg) return; m_contentData = arg; emit contentDataChanged(arg); } void ChatMessageInfo::setDate(QDate arg) { if (m_date == arg) return; m_date = arg; emit dateChanged(arg); } void ChatMessageInfo::setTime(QTime arg) { if (m_time == arg) return; m_time = arg; emit timeChanged(arg); } void ChatMessageInfo::setMessageId2(int arg) { if (m_messageId2 != arg) { m_messageId2 = arg; emit messageId2Changed(arg); } } QSqlDatabase DatabaseOperation::sqlite_db; DatabaseOperation *DatabaseOperation::createDatabaseOperation() { static DatabaseOperation me; return &me; } DatabaseOperation::DatabaseOperation(): QObject(0) { if (!sqlite_db.isValid()){//如果数据库无效,则进行初始化 sqlite_db = QSqlDatabase::addDatabase ("QSQLITE"); } } DatabaseOperation::~DatabaseOperation() { closeSqlDatabase();//关闭数据库 } bool DatabaseOperation::tableAvailable(const QString &tableName) { if(tableName!=""&&sqlite_db.isOpen ()){//如果数据库已经打开 QString temp = "create table if not exists "+tableName+ "(myindex INTEGER,senderUin VARCHAR[16],message TEXT,mydate DATE,mytime TIME)"; //创建一个表,如果这表不存在,表的列为uin message mydate mytime QSqlQuery query = sqlite_db.exec (temp); if(query.lastError ().type ()==QSqlError::NoError){//如果上面的语句执行没有出错 return true; }else{ qDebug()<<"执行"<senderUin ()); insert_query.bindValue (":message", utility->stringEncrypt (data->contentData (), "XingchenQQ123")); //将contentData加密后储存 insert_query.bindValue (":mydate", data->date()); insert_query.bindValue (":mytime", data->time()); if(insert_query.exec ()){//如果上面的语句执行没有出错 //qDebug()<<"插入数据成功"; }else{ qDebug()<<"执行"<size ();++i) { ChatMessageInfo* data = datas->at (i); if(data!=NULL){ insertData (tableName, data); } } if(sqlite_db.commit ()){//提交事务操作,如果上面的语句执行没有出错 qDebug()<<"插入"+QString::number (datas->size ())+"条数据成功"; }else{ qDebug()<<"执行多条插入出错:"<setSenderUin (sql_query.value (1).toString ());//从第一个开始,因为0为index data->setContentData (utility->stringUncrypt (sql_query.value (2).toString (), "XingchenQQ123")); //取回聊天内容时要解密 data->setDate (QDate::fromString (sql_query.value (3).toString ())); data->setTime (QTime::fromString (sql_query.value (4).toString ())); datas->append (data);//将查询到的结果添加到列表中 } //emit getDatasFinished (datas);//发送信号,告知数据获取完成 }else{ qDebug()<<"执行"<clear ();//清除内存中的聊天记录 } void QQItemInfo::initSettings() { QString userqq = userQQ (); QString account = this->account (); if(account==""||userqq=="") return; QString name = QDir::homePath ()+"/.webqq/"+userqq+"/"+typeString+"_"+account+"/.config.ini"; //qDebug()<<"设置了QSettings为"<fileName (); if(mysettings->fileName ()==name) return; mysettings->deleteLater (); } mysettings = new QSettings(name, QSettings::IniFormat); emit settingsChanged (); } bool QQItemInfo::isCanUseSetting() const { return (mytype()!=Discu&&userQQ()!=""&&account()!=""&&mysettings); } void QQItemInfo::removeOldChatRecord() { queue_chatRecords->dequeue ()->deleteLater ();//销毁最老的那条消息 } QString QQItemInfo::uin() const { return m_uin; } QString QQItemInfo::nick() const { return m_nick; } QString QQItemInfo::alias() const { return m_alias; } QString QQItemInfo::avatar40() const { if(isCanUseSetting()){ QString temp_str = mysettings->value ("avatar-40", "qrc:/images/avatar.png").toString (); if(temp_str.left(3)!="qrc") temp_str = "file:///"+temp_str; return temp_str; } return "qrc:/images/avatar.png"; } QString QQItemInfo::avatar240() const { if(isCanUseSetting()){ QString temp_str = mysettings->value ("avatar-240", "qrc:/images/avatar.png").toString (); if(temp_str.left(3)!="qrc") temp_str = "file:///"+temp_str; return temp_str; } return "qrc:/images/avatar.png"; } QString QQItemInfo::aliasOrNick() { QString m_alias = alias(); if(m_alias!="") return m_alias; return nick (); } QString QQItemInfo::userQQ() const { return m_userQQ; } QString QQItemInfo::typeToString() { return typeString; } const QString QQItemInfo::typeToString(QQItemInfo::QQItemType type) { switch (type) { case Friend: return "Friend"; break; case Group: return "Group"; case Discu: return "Discu"; default: return ""; } } const QString QQItemInfo::localCachePath(QQItemInfo::QQItemType type, const QString &userqq, const QString &account) { QString typeString = typeToString (type); return QDir::homePath ()+"/.webqq/"+userqq+"/"+typeString+"_"+account; } QQItemInfo::QQItemType QQItemInfo::mytype() const { return m_mytype; } int QQItemInfo::unreadMessagesCount() const { return m_unreadMessagesCount; } bool QQItemInfo::isActiveChatPage() const { return m_isActiveChatPage; } QString QQItemInfo::account() const { return m_account; } void QQItemInfo::setUin(QString arg) { if (m_uin != arg) { m_uin = arg; if(mytype ()==Discu)//如果是讨论组 setAccount (arg);//讨论组无真实qq,所以用uin充当 emit uinChanged (); setIsActiveChatPage (false);//默认为false } } void QQItemInfo::setNick(QString arg) { if (m_nick != arg) { m_nick = arg; emit nickChanged (); } } void QQItemInfo::setAlias(QString arg) { if(m_alias!=arg){ m_alias = arg; emit aliasChanged(); } } void QQItemInfo::setAccount(QString arg) { if (m_account != arg) { m_account = arg; initSettings(); emit accountChanged(); } } void QQItemInfo::setAvatar40(QString arg) { if(isCanUseSetting ()){ mysettings->setValue ("avatar-40", arg); emit avatar40Changed(); } } void QQItemInfo::setAvatar240(QString arg) { if(isCanUseSetting ()){ mysettings->setValue ("avatar-240", arg); emit avatar240Changed(); } } void QQItemInfo::updataAliasOrNick() { QString arg = aliasOrNick (); if (m_aliasOrNick != arg) { m_aliasOrNick = arg; emit aliasOrNickChanged(); } } void QQItemInfo::setUserQQ(QString arg) { if(m_userQQ!=arg) { m_userQQ = arg; initSettings(); emit userQQChanged (); } } void QQItemInfo::clearSettings() { if(isCanUseSetting()){ mysettings->clear ();//清除所有储存的信息 qDebug()<fileName ()<<"清除成功"; } } const QString QQItemInfo::localCachePath() const { return QDir::homePath ()+"/.webqq/"+userQQ()+"/"+typeString+"_"+account(); } ChatMessageInfoList *QQItemInfo::getChatRecords() { clearUnreadMessages();//将未读消息清空 return queue_chatRecords;//返回所有消息 } void QQItemInfo::addChatRecord(ChatMessageInfo *data) { if(data!=NULL){ queue_chatRecords->append (data);//将此条记录加到队列当中 //qDebug()<<"现在缓冲区中消息的条数为:"<size (); if(queue_chatRecords->size ()>max_chatMessage_count){ removeOldChatRecord ();//移除最老的那条消息 } QQCommand* command = QQCommand::getFirstQQCommand (); if(data->senderUin ()!=command->uin ()&&!isActiveChatPage ()){ //如果发送人不是自己(意思是这条消息不是你发给别人的,而是收到的),并且聊天页面不是活跃的, setUnreadMessagesCount (unreadMessagesCount ()+1);//增加未读消息的个数 } command->addRecentContacts (this);//将自己添加到最近联系人列表 } } void QQItemInfo::clearUnreadMessages() { setUnreadMessagesCount (0); } void QQItemInfo::setIsActiveChatPage(bool arg) { if (m_isActiveChatPage != arg) { m_isActiveChatPage = arg; emit isActiveChatPageChanged(arg); } } ChatMessageInfo *QQItemInfo::getChatMessageInfoById(int messageID) { ChatMessageInfo* info = queue_chatRecords->find (messageID);//先查找这条消息是否存在 if(info==NULL){//为空证明没有找到 info = new ChatMessageInfo(messageID, this); } return info; } int QQItemInfo::getMessageIndex() { return messageID++; } void QQItemInfo::setUnreadMessagesCount(int arg) { if (m_unreadMessagesCount != arg) { m_unreadMessagesCount = arg; emit unreadMessagesCountChanged(arg); } } FriendInfo::FriendInfo(QObject *parent): QQItemInfo(Friend, parent) { m_signature = ""; m_state = Offline; m_stateToString = "offline"; max_chatMessage_count=150;//最大缓存消息数量 saveRecord_coount=50;//一次将50条消息记录插入到本地(不能大于max_chatMessage_count) connect (this, &QQItemInfo::settingsChanged, this, &FriendInfo::onSettingsChanged); //链接信号,处理settings对象改变的信号 getChatRecordsing=false;//记录现在是否在请求获取聊天记录 itemInfoPrivate = DatabaseOperation::createDatabaseOperation (); } FriendInfo::~FriendInfo() { saveChatMessageToLocal ();//销毁此对象之前不要忘记将聊天记录存起来 } QString FriendInfo::QQSignature() { return m_signature; } FriendInfo::States FriendInfo::state() const { return m_state; } QString FriendInfo::stateToString() const { return m_stateToString; } void FriendInfo::removeOldChatRecord() { ChatMessageInfoList list; for(int i=0;idequeue ()); } QString tableName = "table_"+typeToString ()+account(); itemInfoPrivate->insertDatas (tableName, &list); list.clear ();//保存后记得清空 } void FriendInfo::onSettingsChanged() { if(QQSignature ()==""){//如果个性签名为空 QString temp = mysettings->value ("signature", "").toString (); if(temp!=""){ setQQSignature (temp); }else{//如果获得的个性签名为空,就发送信号在qml端去网络获取个性签名 emit httpGetQQSignature();//发送信号 } } } void FriendInfo::setQQSignature(QString arg) { if (m_signature != arg) { m_signature = arg; if(isCanUseSetting ())//如果可以储存配置信息 mysettings->setValue ("signature", arg); emit qQSignatureChanged(); } } void FriendInfo::openSqlDatabase(const QString &userqq) { if(!itemInfoPrivate->openSqlDatabase (userqq)){ qDebug()<<"FriendInfo:数据库打开失败!"; } } void FriendInfo::closeSqlDatabase() { itemInfoPrivate->closeSqlDatabase (); } void FriendInfo::saveChatMessageToLocal() { if(account ()!=""){//qq账户(qq号码)一定不能为空,因为它是消息发送者的唯一标识 QString tableName = "table_"+typeToString ()+account(); itemInfoPrivate->insertDatas (tableName, queue_chatRecords);//将所有聊天记录保存下来 //将内存中的消息添加到数据库 } } void FriendInfo::setState(FriendInfo::States arg) { if (m_state != arg) { m_state = arg; switch(arg) { case Online: m_stateToString = "online"; break; case Callme: m_stateToString = "callme"; break; case Away: m_stateToString = "away"; break; case Busy: m_stateToString = "busy"; break; case Silent: m_stateToString = "silent"; break; case Hidden: m_stateToString = "hidden"; break; case Offline: m_stateToString = "offline"; break; default:break; } emit stateChanged(arg); emit stateToStringChanged (m_stateToString); } } void FriendInfo::setStateToString(const QString &str) { if(str!=m_stateToString){ if(str=="online"){ setState (Online); }else if(str=="callme"){ setState (Callme); }else if(str=="away"){ setState (Away); }else if(str=="busy"){ setState (Busy); }else if(str=="silent"){ setState (Silent); }else if(str=="hidden"){ setState (Hidden); }else if(str=="offline"){ setState (Offline); } } } void FriendInfo::saveChatMessageToLocal(ChatMessageInfo* data) { if(account ()!=""){//qq账户(qq号码)一定不能为空,因为它是消息发送者的唯一标识 QString tableName = "table_"+typeToString ()+account(); itemInfoPrivate->insertData (tableName, data); } } void FriendInfo::getLocalChatRecords(ChatMessageInfo *currentData, int count) { if(account ()!=""&&!getChatRecordsing){//qq账户(qq号码)一定不能为空,因为它是消息发送者的唯一标识 QString tableName = "table_"+typeToString ()+account(); getChatRecordsing = true;//将此值置为true ChatMessageInfoList list; itemInfoPrivate->getDatas (tableName, count, currentData, &list); //开始获取聊天记录 for(int i=0;iinsert (i, list.at (i)); } } } GroupInfo::GroupInfo(QObject *parent): QQItemInfo(Group, parent) { m_code = ""; } QString GroupInfo::code() const { return m_code; } int GroupInfo::membersCount() const { return queue_members.count (); } QString GroupInfo::announcement() const { return m_announcement; } void GroupInfo::setCode(QString arg) { if (m_code == arg) return; m_code = arg; emit codeChanged(arg); } void GroupInfo::addMember(FriendInfo *info) { if(!queue_members.contains (info)){//如果不存在 queue_members<uin ()==uin){ queue_members.removeAt (i); emit memberCountChanged (queue_members.count ()); emit memberReduce (i); break; } } } void GroupInfo::removeMemberByInfo(const FriendInfo *info) { for (int i=0;iuin ()==uin){ queue_members.removeOne (info); emit memberCountChanged (queue_members.count ()); emit memberReduce (i); break; } } } void DiscuInfo::removeMemberByInfo(const FriendInfo *info) { for (int i=0;ideleteLater (); } void ChatMessageInfoList::clear() { foreach (ChatMessageInfo* info, list) { if(info!=NULL){ info->deleteLater ();//销毁这条消息 } } list.clear ();//清空list; } ChatMessageInfo *ChatMessageInfoList::dequeue() { return list.dequeue (); } ChatMessageInfo* ChatMessageInfoList::find(int messageID) { foreach (ChatMessageInfo* info, list) { if(info!=NULL&&info->messageId ()==messageID) return info; } return NULL; } ================================================ FILE: src/qqstars/qqiteminfo.h ================================================ #ifndef QQITEMINFO_H #define QQITEMINFO_H #include #include #include #include #include #include #include #include #include #include #include class QQItemInfo; class ChatMessageInfo:public QObject//用来储存聊天消息的各种信息 { Q_OBJECT Q_PROPERTY(QString senderUin READ senderUin WRITE setSenderUin NOTIFY senderUinChanged)//储存发送人的uin Q_PROPERTY(QString contentData READ contentData WRITE setContentData NOTIFY contentDataChanged)//储存消息内容 Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged)//储存收到此条信息的日期 Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged)//储存收到此条信息的时间 Q_PROPERTY(int messageId READ messageId) Q_PROPERTY(int messageId2 READ messageId2 WRITE setMessageId2 NOTIFY messageId2Changed) public: ChatMessageInfo(QQItemInfo* parent=0); ChatMessageInfo(int messageID, QQItemInfo* parent); QString senderUin() const; QString contentData() const; QDate date() const; QTime time() const; int messageId() const; int messageId2() const; const QQItemInfo* getParent();//获取此条消息所属的ItemInfo对象 private: QString m_senderUin; QString m_contentData; QDate m_date; QTime m_time; int m_messageId; int m_messageId2; signals: void senderUinChanged(QString arg); void contentDataChanged(QString arg); void dateChanged(QDate arg); void timeChanged(QTime arg); void messageId2Changed(int arg); public slots: void setSenderUin(QString arg); void setContentData(QString arg); void setDate(QDate arg); void setTime(QTime arg); void setMessageId2(int arg); }; class ChatMessageInfoList:public QObject { Q_OBJECT public: ChatMessageInfoList(QObject* parent=0); private: QQueue list; public slots: ChatMessageInfo* at(int i); int length(); int size(); void append( ChatMessageInfo *obj); void insert(int pos, ChatMessageInfo *obj); void destroy(); void clear();//清除储存的聊天记录 ChatMessageInfo* dequeue();//出队 ChatMessageInfo *find(int messageID); }; class DatabaseOperation:public QObject//提供数据库的操作(用于储存聊天记录) { Q_OBJECT public: static DatabaseOperation* createDatabaseOperation(); private: static QSqlDatabase sqlite_db; DatabaseOperation(); ~DatabaseOperation(); bool tableAvailable(const QString& tableName);//判断表名为tableName的表是可操作 public slots: bool openSqlDatabase(const QString& userqq);//初始化数据库 void closeSqlDatabase(); void insertData(const QString& tableName, ChatMessageInfo *data);//向数据库中插入数据 void insertDatas(const QString& tableName, ChatMessageInfoList *datas);//向数据库中插入多条数据 void getDatas(const QString& tableName, int count, ChatMessageInfo* currentData, ChatMessageInfoList *datas); //获取数据库中的count条数据,将获得的数据存入datas当中 }; class QQItemInfo:public QObject { Q_OBJECT Q_PROPERTY(QString userQQ READ userQQ WRITE setUserQQ NOTIFY userQQChanged) Q_PROPERTY(QString uin READ uin WRITE setUin NOTIFY uinChanged) Q_PROPERTY(QString nick READ nick WRITE setNick NOTIFY nickChanged) Q_PROPERTY(QString alias READ alias WRITE setAlias NOTIFY aliasChanged) Q_PROPERTY(QString aliasOrNick READ aliasOrNick NOTIFY aliasOrNickChanged FINAL) Q_PROPERTY(QString avatar40 READ avatar40 WRITE setAvatar40 NOTIFY avatar40Changed) Q_PROPERTY(QString avatar240 READ avatar240 WRITE setAvatar240 NOTIFY avatar240Changed) Q_PROPERTY(QString account READ account WRITE setAccount NOTIFY accountChanged) Q_PROPERTY(int unreadMessagesCount READ unreadMessagesCount NOTIFY unreadMessagesCountChanged FINAL)//未读消息的条数 Q_PROPERTY(QQItemType mytype READ mytype NOTIFY mytypeChanged FINAL) Q_PROPERTY(bool isActiveChatPage READ isActiveChatPage WRITE setIsActiveChatPage NOTIFY isActiveChatPageChanged) Q_ENUMS(QQItemType) friend class FriendInfo; friend class GroupInfo; friend class DiscuInfo; friend class RecentInfo; public: explicit QQItemInfo(QObject *parent=0); ~QQItemInfo(); enum QQItemType{ Friend,//好友 Group,//群 Discu//讨论组 }; private: explicit QQItemInfo(QQItemType type, QObject *parent=0); void initSettings(); void setUnreadMessagesCount(int arg);//设置未读消息的个数 QString m_uin;//uin,为此qq的唯一标识 QString m_account;//qq账号 QString m_aliasOrNick; QString m_userQQ; QString m_nick;//储存昵称 QString m_alias; QString typeString; int m_unreadMessagesCount; QQItemType m_mytype; bool m_isActiveChatPage; int messageID;//为自己个好友发送的消息的id(为此消息的唯一标识) protected: ChatMessageInfoList* queue_chatRecords;//储存聊天记录的队列 QPointer mysettings; int max_chatMessage_count;//在queue_chatRecords中最多保存多少条聊天记录 bool isCanUseSetting() const;//是否可以调用settings virtual void removeOldChatRecord();//将最老的那条消息清除 public: static const QString typeToString(QQItemType type); static const QString localCachePath(QQItemType type, const QString& userqq, const QString& account);//本地缓存路径 QString uin() const; QString nick() const; QString alias() const; QString avatarSource() const; QString account() const; QString avatar40() const; QString avatar240() const; QString aliasOrNick(); QString userQQ() const; QString typeToString(); QQItemType mytype() const; int unreadMessagesCount() const; bool isActiveChatPage() const; private slots: void updataAliasOrNick(); void clearUnreadMessages();//清空未读消息 public slots: void setUin(QString arg); void setNick(QString arg); void setAlias(QString arg); void setAccount(QString arg); void setAvatar40(QString arg); void setAvatar240(QString arg); void setUserQQ(QString arg); void clearSettings();//清除配置信息 const QString localCachePath() const;//本地缓存路径 ChatMessageInfoList *getChatRecords();//将内存中的聊天记录读回 void addChatRecord(ChatMessageInfo *data);//增加聊天记录,记录在内存当中 void setIsActiveChatPage(bool arg); ChatMessageInfo* getChatMessageInfoById(int messageID);//通过messageID返回一个储存聊天记录的信息的对象 int getMessageIndex();//返回一个本次在线中的一个唯一的数字,用于收到或者发送的消息的id signals: void nickChanged(); void aliasChanged(); void accountChanged(); void avatar40Changed(); void avatar240Changed(); void aliasOrNickChanged(); void userQQChanged(); void uinChanged(); void settingsChanged(); void mytypeChanged(QQItemType arg); void unreadMessagesCountChanged(int arg); void isActiveChatPageChanged(bool arg); }; class FriendInfo:public QQItemInfo { Q_OBJECT Q_PROPERTY(QString QQSignature READ QQSignature WRITE setQQSignature NOTIFY qQSignatureChanged)//个性签名 Q_PROPERTY(States state READ state WRITE setState NOTIFY stateChanged) Q_PROPERTY(QString stateToString READ stateToString WRITE setStateToString NOTIFY stateToStringChanged) Q_ENUMS(States) public: explicit FriendInfo(QObject *parent=0); ~FriendInfo(); QString QQSignature(); enum States{//登录后的用户的qq状态 Offline,//离线中 Online,//在线 Callme,//Q我吧 Away,//离开 Busy,//忙碌 Silent,//请勿打扰 Hidden//隐身 }; States state() const; QString stateToString() const; private: QString m_signature;//用来储存个性签名 DatabaseOperation *itemInfoPrivate;//里边定义了数据库的操作,用来储存聊天记录 bool getChatRecordsing;//记录现在是否正在请求获取本地聊天记录 States m_state; QString m_stateToString; int saveRecord_coount;//记录一次插入多少条聊天记录到数据库 void removeOldChatRecord();//当消息数量超过最大缓存量时被调用 protected slots: virtual void onSettingsChanged();//处理settings对象改变的信号 public slots: void setQQSignature(QString arg); void openSqlDatabase(const QString &userqq);//初始化数据库 void closeSqlDatabase(); void getLocalChatRecords(ChatMessageInfo *currentData, int count);//读取本地聊天记录(从数据库) void saveChatMessageToLocal(ChatMessageInfo *data);//将此消息记录保存到到本地(保存到数据库中) void saveChatMessageToLocal();//将当前内存中的消息记录保存到到本地(保存到数据库中) void setState(States arg); void setStateToString(const QString &str); signals: void qQSignatureChanged(); void httpGetQQSignature();//发送信号告诉qml端去获取个性签名 void stateChanged(States arg); void stateToStringChanged(QString arg); }; class GroupInfo:public QQItemInfo { Q_OBJECT Q_PROPERTY(QString code READ code WRITE setCode NOTIFY codeChanged)//相当于好友的uin的功能 Q_PROPERTY(int membersCount READ membersCount NOTIFY memberCountChanged FINAL)//群成员个数 Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged) QString m_code; QQueue queue_members;//储存群成员列表 QMap map_card; QString m_announcement; public: explicit GroupInfo(QObject *parent=0); QString code() const; int membersCount() const; QString announcement() const; public slots: void setCode(QString arg); void addMember(FriendInfo* info);//增加群成员 void removeMemberByUin(const QString& uin);//删除群成员(根据uin) void removeMemberByInfo(const FriendInfo *info);//删除群成员(根据uin) void setMemberCard(const QString& uin, const QString& card);//给群成员设置群名片 QString getMemberCardByUin(const QString& uin, const QString& defaultCard); FriendInfo* getMemberInfoByIndex(int index);//获取群成员信息 void setAnnouncement(QString arg); signals: void codeChanged(QString arg); void memberCountChanged(int arg); void memberIncrease(FriendInfo* info);//群成员增加了 void memberReduce(int index);//群成员减少了,index为被移除的群成员序号 void announcementChanged(QString arg); }; class DiscuInfo:public QQItemInfo { Q_OBJECT Q_PROPERTY(int membersCount READ membersCount NOTIFY memberCountChanged FINAL)//讨论组成员个数 QQueue queue_members;//储存讨论组成员列表 public: explicit DiscuInfo(QObject *parent=0); int membersCount() const; public slots: void addMember(FriendInfo* info);//增加群成员 void removeMemberByUin(const QString& uin);//删除群成员(根据uin) void removeMemberByInfo(const FriendInfo *info);//删除群成员(根据uin) FriendInfo* getMemberInfoByIndex(int index);//获取讨论组成员信息 signals: void memberCountChanged(int arg); void memberIncrease(FriendInfo* info);//群成员增加了 void memberReduce(int index);//群成员减少了,index为被移除的群成员序号 }; class RecentInfo:public QObject { Q_OBJECT Q_PROPERTY(QObject* infoData READ infoData NOTIFY infoDataChanged FINAL) Q_PROPERTY(FriendInfo* infoToFriend READ infoToFriend NOTIFY infoToFriendChanged FINAL) Q_PROPERTY(GroupInfo* infoToGroup READ infoToGroup NOTIFY infoToGroupChanged FINAL) Q_PROPERTY(DiscuInfo* infoToDiscu READ infoToDiscu NOTIFY infoToDiscuChanged FINAL) public: //explicit RecentInfo(QQuickItem *parent=0); explicit RecentInfo(FriendInfo *info, QObject *parent=0); explicit RecentInfo(GroupInfo *info, QObject *parent=0); explicit RecentInfo(DiscuInfo *info, QObject *parent=0); QObject* infoData() const; FriendInfo* infoToFriend() const; GroupInfo* infoToGroup() const; DiscuInfo* infoToDiscu() const; signals: void infoDataChanged(); void infoToFriendChanged(FriendInfo* arg); void infoToGroupChanged(GroupInfo* arg); void infoToDiscuChanged(DiscuInfo* arg); private: void setInfoData(QObject* info); void setInfoToFriend(FriendInfo* arg); void setInfoToGroup(GroupInfo* arg); void setInfoToDiscu(DiscuInfo* arg); QPointer m_infoData; QPointer m_infoToFriend; QPointer m_infoToGroup; QPointer m_infoToDiscu; }; #endif // QQITEMINFO_H ================================================ FILE: src/qqstars/qqstars.cpp ================================================ #include "qqstars.h" #include "utility.h" #include #include #include "mywindow.h" #include "myhttprequest.h" #include "mymessagebox.h" #include "mynetworkaccessmanagerfactory.h" QQCommand *QQCommand::firstQQCommand = NULL; QQCommand *QQCommand::getFirstQQCommand() { return firstQQCommand; } QQCommand::QQCommand( QObject *parent) : FriendInfo(parent) { if(firstQQCommand==NULL) firstQQCommand = this; connect (this, &FriendInfo::stateChanged, this, &QQCommand::onStateChanged); Utility *utility=Utility::createUtilityClass (); int temp1 = utility->value ("proxyType", QNetworkProxy::NoProxy).toInt (); QString temp2 = utility->value ("proxyLocation", "").toString (); QString temp3 = utility->value ("proxyPort", "").toString (); QString temp4 = utility->value ("proxyUsername", "").toString (); QString temp5 = utility->value ("proxyPassword", "").toString (); utility->setApplicationProxy (temp1, temp2, temp3, temp4, temp5); setUserQQ (utility->value ("mainqq","").toString ()); m_loginStatus = WaitLogin;//当前为离线(还未登录) m_windowScale = 1;//缺省窗口比例为1 chatImageID = 0;//初始化为0 request = new QNetworkRequest; request->setUrl (QUrl("http://d.web2.qq.com/channel/poll2")); request->setRawHeader ("Origin", "http://d.web2.qq.com"); request->setRawHeader ("Accept", "*/*"); request->setRawHeader ("Referer", "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=2"); request->setRawHeader ("Content-Type", "application/x-www-form-urlencoded"); request->setRawHeader ("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER"); manager = new NetworkAccessManager(this); connect (manager, SIGNAL(finished(QNetworkReply*)), SLOT(poll2Finished(QNetworkReply*))); jsEngine = new QJSEngine();//此对象用来加载js文件(为qq提供api) loadApi ();//加载api的js文件 reply = NULL; poll2Timerout_count=0;//记录网络请求的连续超时次数 poll2Error_count=0;//记录网络请求连续出错的次数 connect (utility, &Utility::networkOnlineStateChanged, this, &QQCommand::onNetworkOnlineStateChanged); poll2_timer = new QTimer(this); poll2_timer->setSingleShot (true);//设置为单发射器 connect (poll2_timer, &QTimer::timeout, this, &QQCommand::onPoll2Timeout); abortPoll_timer = new QTimer(this); abortPoll_timer->setSingleShot (true);//设置为单发射器 http_image = new MyHttpRequest(this);//此网络请求对象专门用来获取聊天中收到图片的真实下载地址 http_image->getNetworkRequest ()->setRawHeader ( "Referer", "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=2");//必须设置,不然请求腾讯的数据会返回出错 } QQCommand::LoginStatus QQCommand::loginStatus() const { return m_loginStatus; } QString QQCommand::userPassword() const { return m_userPassword; } double QQCommand::windowScale() const { return m_windowScale; } QString QQCommand::userQQ() const { return m_userQQ; } bool QQCommand::rememberPassword() const { if(isCanUseSetting()) return mysettings->value ("rememberPassword", false).toBool (); return false; } bool QQCommand::autoLogin() const { if(isCanUseSetting()) return mysettings->value ("autoLogin", false).toBool (); return false; } QString QQCommand::codeText() const { if(code_window){ return code_window->property ("code").toString (); } return ""; } void QQCommand::beginPoll2() { disconnect (abortPoll_timer, &QTimer::timeout, reply, &QNetworkReply::abort); //qDebug()<<"reply1:"<post (*request, poll2_data); //qDebug()<<"reply2:"<start (100000);//网络请求超时定时器 } void QQCommand::poll2Finished(QNetworkReply *replys) { poll2_timer->stop ();//停止计算请求是否超时的计时器 if(replys->error ()==QNetworkReply::NoError) { QByteArray array = replys->readAll (); emit poll2ReData (array); QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson (array, &json_error); if(json_error.error == QJsonParseError::NoError) { if( document.isObject () ){ QJsonObject obj = document.object (); if( obj["retcode"].isDouble () ){ int retcode = obj["retcode"].toInt (); if( retcode==0 ){ QJsonArray arr = obj["result"].toArray (); foreach (QJsonValue temp, arr) { obj = temp.toObject (); QString poll_type=obj["poll_type"].toString (); obj = obj["value"].toObject (); if( poll_type=="message" ){ disposeFriendMessage (obj);//解析好友的普通消息 }else if( poll_type=="input_notify" ){ disposeFriendMessage (obj, InputNotify);//解析好友正在输入的消息 }else if( poll_type=="buddies_status_change" ){ disposeFriendStatusChanged(obj);//好友状态改变信息 }else if( poll_type=="group_message" ){ disposeGroupMessage (obj);//解析群消息 }else if( poll_type=="discu_message" ){ disposeDiscuMessage (obj);//解析讨论组消息 }else if( poll_type=="file_message" ){ //qDebug()<<"发送文件消息"; disposeFriendMessage (obj, FileMessage); }else if( poll_type=="av_request" ){ //qDebug()<<"视频聊天消息"; disposeFriendMessage (obj, AvRequest); }else if( poll_type=="av_refuse" ){ //qDebug()<<"取消开视频"; disposeFriendMessage (obj, AvRefuse); }else if( poll_type=="shake_message" ){ //qDebug()<<"窗口抖动消息"; disposeFriendMessage (obj, ShakeWindow);//解析窗口抖动消息 }else if( poll_type=="system_message" ){ disposeSystemMessage (obj);//解析系统消息 }else if( poll_type=="sys_g_msg" ){ disposeSystemMessage (obj);//解析系统消息 }else if(poll_type == "sess_message"){ //disposeStrangerMessage (obj);//解析陌生人的消息 disposeFriendMessage (obj);//解析好友的普通消息 }else{ qDebug()<<"QQCommand:其他消息"<error ()!=QNetworkReply::OperationCanceledError){//如果不是手动取消的 ++poll2Error_count; if(poll2Error_count>1){ qDebug()<<"QQCommand:网络请求连续出错"<value ("password", "").toString (); setUserPassword (Utility::createUtilityClass ()->stringUncrypt (pass, "xingchenQQ")); } } void QQCommand::onChatMainWindowClose()//如果主聊天窗口关闭,那就销毁所有已经建立的聊天页面 { foreach (QQuickItem *item, map_chatPage) { if(item!=NULL){ QString key = map_chatPage.key (item); QString uin,typeStr; for(int i=0;ideleteLater ();//销毁此页面 } } map_chatPage.clear ();//清空所有对象 } void QQCommand::onSettingsChanged() { emit rememberPasswordChanged (); emit autoLoginChanged (); initUserPassword (); setState ((States)mysettings->value ("myState", (int)Online).toInt ());//设置自己的状态 } void QQCommand::onStateChanged() { mysettings->setValue ("myState", (int)state()); } void QQCommand::onPoll2Timeout() { ++poll2Timerout_count;//记录网络请求的连续超时次数 qDebug()<<"QQCommand:网络请求超时:"<abort ();//取消网络请求 } beginPoll2 ();//再次开始网络请求 } void QQCommand::onNetworkOnlineStateChanged(bool isOnline) { qDebug()<<"QQCommand:网络在线状态改变为:"<stop ();//停止计时器 if(!reply||!reply->isRunning ()){//如果心跳包没有在进行 qDebug()<<"QQCommand:由于断网影响,将重新登录qq"; QMetaObject::invokeMethod (this, "reLogin");//调用槽reLogin重新登录(在qml中定义) } }else{ abortPoll_timer->start (60000);//启动取消心跳包的定时器 } } void QQCommand::downImageFinished(DownloadImage::ErrorType error, const QString &path, const QString &name) { QStringList list = name.split ("_");//分割字符串 QString rootUin=list[0];//图片发送者本身或者图片发送者所在群的uin int messageID = list[1].toInt ();//分离出messageID int imageID = list[2].toInt ();//分离出imageID QString senderType; QRegExp reg("[A-Z][a-z]+_[0-9]+");//符合条件的字符串,例如Friend_826169080 if(reg.indexIn (path)>=0){//如果找到了 senderType = reg.cap (0); QStringList list = senderType.split ("_"); senderType=list[0];//图片所有者的类型(如果是好友发送的就是Friend,如果是群成员发送的就是Group) }else{ qDebug()<<"QQCommand:从下载路径中提取uin和消息发送者类型出错"; return; } QQItemInfo* info = createQQItemInfo (rootUin, senderType); //获取图片所有者的信息 if(error==DownloadImage::DownloadError){//如果是下载出错 qDebug()<<"QQCommand:图片"<//重新去下载图片 downloadImage (this, SLOT(downImageFinished(bool,QString,QString)), QUrl(image_url), path, save_name);//重新下载图片 return; } ChatMessageInfo* message_info = info->getChatMessageInfoById (messageID); //通过图片所在消息的id获取储存消息信息的对象 QString content = message_info->contentData ();//获得消息储存的内容 QString old_img = "";//旧的img标签中的内容 QString new_img; if(error==DownloadImage::SaveError||error==DownloadImage::NotSupportFormat){ new_img = "";//新img标签内容 }else if(error==DownloadImage::NoError){//如果没有错误 new_img = "";//新img标签内容 } content.replace (old_img, new_img);//将old_img标签替换为新的内容 message_info->setContentData (content);//替换消息内容,qml端会自动刷新消息 } void QQCommand::getImageUrlFinished(QNetworkReply *replys) { ImageInfo image_info = queue_imageInfo.dequeue ();//从列队中取出队首 if(replys->error() == QNetworkReply::NoError)//如果网络请求没有出错 { QString image_url = replys->rawHeader ("Location");//从返回的http头部中取出图片的真实下载地址 if(image_url==""){//如果真实下载地址为空 qDebug()<<"QQCommand:获取图片"<readAll (); return; } const QQItemInfo* root_info = image_info.messageInfo->getParent (); //获取图片所有者信息的对象(如果图片为好友发送,那就是那个好友的info,如果是群,那就是群的ifno) QString save_path = root_info->localCachePath ()+"/cacheImage";//获取图片的缓存路径 QString save_name = root_info->uin ()+"_"+ QString::number (image_info.messageInfo->messageId ())+"_"+ QString::number (image_info.imageID)+"_"+ QDateTime::currentDateTime ().toString (Qt::ISODate).replace (QRegExp("\\D"),"");//设置要保存的图片名(不加后缀) setImageUrlById (image_info.imageID, image_url);//将图片id和真实下载链接的对应关系储存起来 Utility::createUtilityClass ()->//启动网络请求获取图片 downloadImage (this, SLOT(downImageFinished(bool,QString,QString)), QUrl(image_url), save_path, save_name); }else{//如果获取图片的真实下载地址出错 queue_imageInfo<get (this, SLOT(getImageUrlFinished(QNetworkReply*)), image_info.filePath); //重新去获取图片的真实下载地址 } } void QQCommand::loadApi() { QString fileName = ":/qml/Api/api.js"; QFile scriptFile(fileName); if (!scriptFile.open(QIODevice::ReadOnly)) qDebug()<<"QQCommand:打开"+fileName+"失败"; QString contents = scriptFile.readAll (); scriptFile.close(); jsEngine->evaluate(contents, fileName); } QString QQCommand::disposeMessage(QJsonObject &obj, ChatMessageInfo* message_info) { QString result=""; FontStyle font_style; QJsonArray content = obj["content"].toArray (); QJsonValue temp2 = content[0]; if(temp2.isArray ()){ QJsonArray font = temp2.toArray (); foreach (QJsonValue temp3, font) { if(temp3.isObject ()){ obj = temp3.toObject (); font_style.size = 3;//obj["size"].toInt (); font_style.color = "black";//obj["color"].toString (); //QJsonArray style = obj["style"].toArray (); font_style.bold = false;//(bool)style[0].toInt ();//加黑 font_style.italic = false;//(bool)style[1].toInt ();//斜体 font_style.underline = false;//(bool)style[2].toInt ();//下划线 font_style.family = "新宋体";//obj["name"].toString (); } } } for( int i=1;i(const_cast(message_info->getParent ())); if(root_info==NULL){ qDebug()<<"QQCommand:将消息对象从QQItemInfo转换为GroupInfo失败"; result.append (textToHtml (font_style, "[图片加载失败...]")); }else{ QString file_path = "http://web2.qq.com/cgi-bin/get_group_pic?type=0&gid="+ root_info->code ()+"&uin="+message_info->senderUin ()+ "&rip="+server+"&rport="+port+"&fid="+file_id+ "&pic="+name+"&vfwebqq="+property ("vfwebqq").toString (); result.append (disposeImageMessage(message_info, file_path)); } } } }else if(array_name=="offpic"){//为图片消息 foreach (QJsonValue temp3, array) { if(temp3.isObject ()){ obj = temp3.toObject (); QString file_path = obj["file_path"].toString (); file_path.replace ("/", "%2F"); file_path = "http://d.web2.qq.com/channel/get_offpic2?file_path="+file_path+"&f_uin="+message_info->senderUin () +"&clientid="+property ("clientid").toString () +"&psessionid="+property ("psessionid").toString (); result.append (disposeImageMessage(message_info, file_path)); //处理图片消息,并将处理的结果添加到result } } }else if(array_name=="face"){//为表情消息 QString faceName = QString::number (array[1].toInt ());//转化为int QString pngFace_number = "21 25 32 33 34 36 39 42 45 50 59 64 85 86 91 124"; if(pngFace_number.indexOf (faceName)>=0) faceName.append (".png"); else faceName.append (".gif"); QString data = ""; //qDebug()<=0){//如果是表情代码 str = ""; }else{ str = textToHtml (font_style, "["+str+"]"); } result.append (str); } } } } return result+""; } void QQCommand::disposeFriendStatusChanged(QJsonObject &obj) { QString uin = doubleToString (obj, "uin"); QString status = obj["status"].toString (); createFriendInfo (uin)->setStateToString (status);//设置好友状态 } void QQCommand::disposeFriendMessage(QJsonObject &obj, QQCommand::MessageType type) { //qDebug()<<"是聊天消息"; QString from_uin = doubleToString (obj, "from_uin"); //int msg_id = obj.value ("msg_id").toInt (); //int msg_id2 = obj.value ("msg_id2").toInt (); //QString msg_type = doubleToString (obj, "msg_type"); //QString reply_ip = doubleToString (obj, "reply_ip"); //QString to_uin = doubleToString (obj, "to_uin"); switch (type) { case GeneralMessage:{ QQItemInfo *info = createQQItemInfo (from_uin, QQItemInfo::Friend);//先获取消息发送者的信息 int msg_id = info->getMessageIndex();//为新消息获取一个id ChatMessageInfo *message_info = info->getChatMessageInfoById (msg_id); //通过消息id获得一个储存消息各种信息的对象 message_info->setSenderUin (from_uin);//将消息的信息存进去 message_info->setDate (QDate::currentDate ());//将消息发送日期存进去 message_info->setTime (QTime::currentTime ());//将消息发送时间存进去 QString data = disposeMessage (obj, message_info);//处理这条消息的内容(一定要放在SenderUin之后) message_info->setContentData (data);//将处理后的内容设置给消息对象 info->addChatRecord (message_info);//将消息加到发送者info对象中 //qDebug()<<"收到了好友消息:"<getMessageIndex();//同好友消息 ChatMessageInfo *message_info = info->getChatMessageInfoById (msg_id); message_info->setSenderUin (send_uin);//注意!!!此处的发送者uin不是群的uin,而是将消息发到群的成员的uin message_info->setDate (QDate::currentDate ()); message_info->setTime (QTime::currentTime ()); QString data = disposeMessage (obj, message_info);//先处理消息内容 message_info->setContentData (data); info->addChatRecord (message_info);//给from_uin的info对象增加聊天记录 emit newMessage (from_uin, (int)QQItemInfo::Group, message_info); break; } default: break; } } void QQCommand::disposeDiscuMessage(QJsonObject &obj, QQCommand::MessageType type) { //qDebug()<<"是讨论组消息"; //QString from_uin = doubleToString (obj, "from_uin");; QString did = doubleToString (obj, "did"); //int msg_id = obj.value ("msg_id").toInt (); //int msg_id2 = obj.value ("msg_id2").toInt (); //QString msg_type = doubleToString (obj, "msg_type"); //QString reply_ip = doubleToString (obj, "reply_ip"); //QString to_uin = doubleToString (obj, "to_uin"); QString send_uin = doubleToString (obj, "send_uin"); switch (type) { case GeneralMessage:{ QQItemInfo *info = createQQItemInfo (did, QQItemInfo::Discu); int msg_id = info->getMessageIndex(); ChatMessageInfo *message_info = info->getChatMessageInfoById (msg_id); message_info->setSenderUin (send_uin);//注意!!!此处的发送者uin不是群的uin,而是将消息发到讨论组的成员的uin message_info->setDate (QDate::currentDate ()); message_info->setTime (QTime::currentTime ()); QString data = disposeMessage (obj, message_info);//先处理基本消息 message_info->setContentData (data); info->addChatRecord (message_info);//给from_uin的info对象增加聊天记录 emit newMessage (did, (int)QQItemInfo::Discu, message_info); break; } default: break; } } void QQCommand::disposeStrangerMessage(QJsonObject &, QQCommand::MessageType ) { /*QString from_uin = doubleToString (obj, "from_uin"); QString msg_id = doubleToString (obj, "msg_id"); QString msg_id2 = doubleToString (obj, "msg_id2"); QString msg_type = doubleToString (obj, "msg_type"); QString reply_ip = doubleToString (obj, "reply_ip"); QString to_uin = doubleToString (obj, "to_uin");*/ } void QQCommand::disposeSystemMessage(QJsonObject &obj) { QString type = obj["type"].toString (); if(type == "verify_required"){//好友验证信息 QString account = doubleToString (obj, "account"); QString from_uin = doubleToString (obj, "from_uin"); //emit messageArrive (SystemMessage, from_uin, "{\"type\":"+QString::number (FriendVerify)+",\"account\"\":"+account+"\"}"); }else if(type == "group_admin_op"){//管理员变动信息 QString from_uin = doubleToString (obj, "from_uin"); QString uin = doubleToString (obj, "uin"); QString uin_flag = doubleToString (obj, "uin_flag"); //emit messageArrive (SystemMessage, from_uin, "{\"type\":"+QString::number (GroupAdmin)+",\"uin\":\""+uin+"\",\"flag\":\""+uin_flag+"\"}"); }else if(type == "group_leave"){//群成员变动信息 QString from_uin = doubleToString (obj, "from_uin"); QString old_member = doubleToString (obj, "old_member"); //emit messageArrive (SystemMessage, from_uin, "{\"type\":"+QString::number (GroupLeave)+",\"old_member\":\""+old_member+"\"}"); }else{//其他系统消息 qDebug()<<"其他系统消息:"<",">"); data.replace("<","<"); data.replace("\"","""); data.replace("\'","'"); data.replace(" "," "); data.replace("\n","
"); data.replace("\r","
"); //上面这几行代码的顺序不能乱,否则会造成多次替换 QString result="0) result.append (" size=\""+QString::number (style.size)+"\""); if(style.color!=""){ if(style.color[0].isNumber ()) result.append (" color=\"#"+style.color+"\""); else result.append (" color=\""+style.color+"\""); } if(style.family!="") result.append (" face=\""+style.family+"\""); result.append (">"); if(style.bold) result.append (""); if(style.underline) result.append (""); if(style.italic) result.append (""); result.append (data);//把文本包含进去 if(style.italic) result.append (""); if(style.underline) result.append (""); if(style.bold) result.append (""); result.append (""); return result; } QQItemInfo *QQCommand::createQQItemInfo(const QString& uin, const QString& typeString) { if(uin==""||typeString==""){ qDebug()<<"QQCommand-createQQItemInfo:参数不合法,uin:"<(map_itemInfo[name]); return info; } QQmlEngine *engine = Utility::createUtilityClass ()->qmlEngine (); QQmlComponent component(engine, QUrl("qrc:/qml/QQItemInfo/"+typeString+"Info.qml")); QQItemInfo* info = qobject_cast(component.create ()); if(info!=NULL){ map_itemInfo[name] = info; info->setParent (this); info->setUserQQ (userQQ()); info->setUin (uin); } return info; } void QQCommand::setLoginStatus(QQCommand::LoginStatus arg) { if (m_loginStatus != arg) { if(arg == WaitLogin&&m_loginStatus==LoginFinished){//如果登录状态变为离线 poll2_timer->stop ();//停止计算请求是否超时的计时器 reply->abort ();//停止心跳包 closeChatWindow();//关闭好友聊天的窗口 clearQQItemInfos();//清空所有的好友信息 chatImageID = 0;//chatImageID回到缺省值 map_imageUrl.clear ();//情况image的id和url值对 if(!window_mainPanel.isNull ())//销毁主面板窗口 window_mainPanel->deleteLater (); loadLoginWindow();//打开登录窗口 }else if(arg == LoginFinished){//如果登录完成 if(!window_login.isNull ())//关闭聊天窗口 window_login->deleteLater (); loadMainPanelWindow ();//加载主面板窗口 } m_loginStatus = arg; emit loginStatusChanged(); } } void QQCommand::startPoll2(const QByteArray &data) { poll2_data = data; //poll2_timer.start (); beginPoll2(); } void QQCommand::setUserQQ(QString arg) { if (m_userQQ != arg) { m_userQQ = arg; FriendInfo::setUserQQ (arg); FriendInfo::setAccount (arg); FriendInfo::setUin (arg); emit userQQChanged(); } } void QQCommand::setUserPassword(QString arg) { if (m_userPassword != arg) { //qDebug()<<"设置了密码"<qmlEngine (); if(warning_info_window){ warning_info_window->show (); }else{ QQmlComponent component(engine, QUrl("qrc:/qml/Utility/MyMessageBox.qml")); QObject *obj = component.create (); warning_info_window = qobject_cast(obj); if(obj) obj->setProperty ("text", QVariant(message)); else qDebug()<<"创建MyMessageBox.qml失败"; } } void QQCommand::downloadImage(int senderType, QUrl url, QString account, QString imageSize, QJSValue callbackFun) { QString path = QQItemInfo::localCachePath ((QQItemInfo::QQItemType)senderType, userQQ(), account); //先获取此qq为account,类型为senderType的缓存目录,将此目录传给下载图片的函数。此图片下载完成就会存入此路径 Utility::createUtilityClass ()->downloadImage (callbackFun, url, path, "avatar-"+imageSize); } void QQCommand::showCodeWindow(const QJSValue callbackFun, const QString code_uin) { QQmlEngine *engine = Utility::createUtilityClass ()->qmlEngine (); if(!code_window){ QQmlComponent component(engine, QUrl("qrc:/qml/Utility/CodeInput.qml")); QObject *obj = component.create (); if(obj){ code_window = qobject_cast(obj); }else{ qDebug()<<"创建CodeInput.qml失败"; return; } } //qDebug()<<"显示验证码"<newQObject (code_window); if(value.isObject ()) value.setProperty ("backFun", callbackFun); QString url = "https://ssl.captcha.qq.com/getimage?aid=1003903&r=0.9101365606766194&uin="+userQQ()+"&cap_cd="+code_uin; code_window->setProperty ("source", url); code_window->show (); } } void QQCommand::closeCodeWindow() { if(code_window){ code_window->close (); code_window->deleteLater (); } } void QQCommand::updataCode() { if(code_window){ QMetaObject::invokeMethod (code_window, "updateCode");//调用刷新验证码 } } FriendInfo* QQCommand::createFriendInfo(const QString uin) { FriendInfo* info = qobject_cast(createQQItemInfo(uin, QQItemInfo::Friend)); return info; } GroupInfo* QQCommand::createGroupInfo(const QString uin) { GroupInfo* info = qobject_cast(createQQItemInfo(uin, QQItemInfo::Group)); return info; } DiscuInfo* QQCommand::createDiscuInfo(const QString uin) { DiscuInfo* info = qobject_cast(createQQItemInfo(uin, QQItemInfo::Discu)); return info; } void QQCommand::addChatPage(QString uin, int senderType) { if(uin==""||senderType<0) return; QString typeStr = QQItemInfo::typeToString ((QQItemInfo::QQItemType)senderType);//获取此类型的字符串表达形式 qDebug()<<"QQCommand:将要增加聊天页面的类型是"<show ();//显示出聊天窗口 return;//如果已经处在此Page就返回 } QQmlEngine *engine = Utility::createUtilityClass ()->qmlEngine (); if(mainChatWindowCommand.isNull ()){ QQmlComponent component(engine, QUrl("qrc:/qml/Chat/ChatWindowCommand.qml")); QObject *temp_obj = component.create (); //qDebug()<<"创建窗口是否出错:"<(temp_obj); if(mainChatWindowCommand){ connect (mainChatWindowCommand.data (), &MyWindow::closeing, this, &QQCommand::onChatMainWindowClose); //链接信号和槽,为聊天主窗口关闭时销毁对象所用 foreach (QQuickItem *item, mainChatWindowCommand->contentItem ()->childItems ()) { if(item->objectName () == "ChatWindowCommandItem"){ mainChatWindowCommand_item = item;//将聊天页面的父对象储存起来 break; } } }else{ qDebug()<<"创建ChatWindowCommand.qml出错"; return;//如果出错就返回 } } QString qmlName = "qrc:/qml/Chat/"+typeStr+"ChatPage.qml"; QQmlComponent component(engine, QUrl(qmlName)); QQuickItem *item = qobject_cast(component.create ());//新建聊天页面 if(item&&mainChatWindowCommand_item){ item->setParentItem (mainChatWindowCommand_item);//设置聊天页面的父对象 item->setProperty ("myuin", uin);//设置他的uin item->setProperty ("type", senderType);//设置他的类型 map_chatPage[typeStr+uin] = item;//储存聊天页面 QQItemInfo* item_info = createQQItemInfo (uin, typeStr); if(item_info==NULL){//如果对象为空就返回 qDebug()<<"QQCommand-addChatPage:创建QQItemInfo对象失败"; return; } emit addChatPageToWindow (item);//发送信号告知qml增加了聊天页 }else{ qDebug()<<"创建"+qmlName+"出错"; } mainChatWindowCommand->show ();//显示出聊天窗口 } void QQCommand::removeChatPage(QString uin, int senderType) { QQItemInfo::QQItemType type = (QQItemInfo::QQItemType)senderType; QString typeStr = QQItemInfo::typeToString (type);//获取此类型的字符串表达形式 qDebug()<<"QQCommand:要关闭的聊天页的类型是:"<deleteLater ();//销毁此对象 }else{ qDebug()<value (key, defaultValue); } void QQCommand::setValue(const QString &key, const QVariant &value) { mysettings->setValue (key, value); } void QQCommand::shakeChatMainWindow(QQuickItem *item) { emit activeChatPageChanged (item); if(QMetaObject::invokeMethod (mainChatWindowCommand, "windowShake")){ qDebug()<<"窗口抖动成功"; }else{ qDebug()<<"窗口抖动失败"; } } void QQCommand::openSqlDatabase() { FriendInfo::openSqlDatabase (userQQ());//打开数据库 } void QQCommand::closeChatWindow() { if(!mainChatWindowCommand.isNull ()) mainChatWindowCommand->close (); } QString QQCommand::getMovieImageFrameCachePath() { return QDir::homePath ()+"/.webqq"; } /*void QQCommand::saveAlias(int type, QString uin, QString alias) { QString name = QQItemInfo::typeToString ((QQItemInfo::QQItemType)type)+uin; map_alias[name] = alias; }*/ void QQCommand::updataApi(const QString& content) { qDebug()<<"更新api.js"<deleteLater (); } map_itemInfo.clear (); } QString QQCommand::disposeImageMessage(ChatMessageInfo* message_info, QString image_url) { int image_id = getImageIndex();//为这个图片获得一个唯一的id ImageInfo image_info;//为图片创建一个储存自己信息的结构体对象 image_info.filePath=image_url;//储存能获取图片真实下载地址的字符串 image_info.imageID=image_id;//储存图片的id image_info.messageInfo=message_info;//储存图片所在消息的对象的指针 queue_imageInfo<get (this, SLOT(getImageUrlFinished(QNetworkReply*)), image_url); //进行网络请求,获取图片的真实下载地址 return ""; //返回此字符串,用于img标签的占位,等图片下载完成会替换此img标签 } int QQCommand::getImageIndex() { return chatImageID++; } QString QQCommand::getImageUrlById(int image_id) { return map_imageUrl[image_id]; } void QQCommand::setImageUrlById(int image_id, const QString &url) { //qDebug()<<"将id为:"<qmlEngine (); QQmlComponent component(engine, QUrl("qrc:/qml/Login/main.qml")); QObject *temp_obj = component.create (); window_login = qobject_cast(temp_obj); if(window_login.isNull ()){ qDebug()<<"QQCommand:加载登录窗口失败,"<show (); } } void QQCommand::loadMainPanelWindow() { if(window_mainPanel.isNull ()){ QQmlEngine *engine = Utility::createUtilityClass ()->qmlEngine (); QQmlComponent component(engine, QUrl("qrc:/qml/MainPanel/main.qml")); QObject *temp_obj = component.create (); window_login = qobject_cast(temp_obj); if(window_login.isNull ()){ qDebug()<<"QQCommand:加载主面板窗口失败,"<show (); } } bool QQCommand::isChatPageExist(const QString& uin, int senderType) { QString typeStr = QQItemInfo::typeToString ((QQItemInfo::QQItemType)senderType);//获取此类型的字符串表达形式 return map_chatPage.contains(typeStr+uin); } void QQCommand::addFriendUin(const QString &uin) { friendsUin.append (uin+" "); //qDebug()<<"增加好友uin:"+uin<getCookie ("ptwebqq")); return jsEngine->globalObject ().property ("getHash").call (list).toString (); } QString QQCommand::encryptionPassword(const QString &uin, const QString &code) { QJSValueList list; list<globalObject ().property ("encryptionPassword").call (list).toString (); } QVariant QQCommand::getLoginedQQInfo() { Utility *utility = Utility::createUtilityClass (); QByteArray reply="["; QString qqs = utility->value ("qq_account", "").toString (); QStringList qq_list = qqs.split (","); foreach (QString qq, qq_list) { if(qq!=""){ QStringList temp = qq.split ("."); if(temp.size ()==2){//如果有两个,一个为qq号,一个为昵称 FriendInfo info; QString account = temp[0]; info.setUserQQ (account); info.setAccount (account); reply.append ("{\"account\":\""+account +"\",\"nick\":\""+QByteArray::fromHex (temp[1].toUtf8 ()) +"\",\"avatarSource\":\""+info.avatar240 ()+"\"},"); } } } reply.replace (reply.size ()-1,1,"]"); return QVariant(QJsonDocument::fromJson (reply).array ()); } void QQCommand::removeLoginedQQInfo(const QString account, bool rmLocalCache) { Utility *utility = Utility::createUtilityClass (); QString qqs = utility->value ("qq_account", "").toString (); QStringList qq_list = qqs.split (","); foreach (QString qq, qq_list) { if(qq!=""){ QStringList temp = qq.split ("."); if(temp.size ()==2){//如果有两个,一个为qq号,一个为昵称 if(temp[0]==account){//如果查找到此qq qqs.replace (qq+",", "");//替换掉 utility->setValue ("qq_account", qqs);//替换掉原来的值 FriendInfo info; info.setUserQQ (account); info.setAccount (account); info.clearSettings ();//清除配置内容 if(rmLocalCache){//如果要删除本地缓存 utility->removePath (info.localCachePath ()); } return; } } } } } void QQCommand::addLoginedQQInfo(const QString account, const QString nick) { Utility *utility = Utility::createUtilityClass (); QString qqs = utility->value ("qq_account", "").toString (); QString addStr = account+"."+nick.toUtf8 ().toHex ()+","; if(qqs.indexOf (addStr)<0){//如果这条信息不存在 qqs.insert (0, addStr); utility->setValue ("qq_account", qqs);//添加进去 } } void QQCommand::setWindowScale(double arg) { if (m_windowScale != arg) { m_windowScale = arg; emit windowScaleChanged(); } } int QQCommand::openMessageBox(QJSValue value) { MyMessageBox message; message.setStyleSource (QUrl::fromLocalFile ("style/messageBoxStyle.css")); QJSValue temp = value.property ("icon"); if( !temp.isUndefined () ){ message.setIcon ((MyMessageBox::Icon)temp.toInt ()); } temp = value.property ("detailedText"); if( !temp.isUndefined () ) { message.setDetailedText (temp.toString ()); } temp = value.property ("standardButtons"); if( !temp.isUndefined () ) { message.setStandardButtons ((MyMessageBox::StandardButtons)temp.toInt ()); } temp = value.property ("text"); if( !temp.isUndefined () ) { message.setText (temp.toString ()); } temp = value.property ("iconPixmap"); if( !temp.isUndefined () ) { message.setIconPixmap (QPixmap(temp.toString ())); } temp = value.property ("textFormat"); if( !temp.isUndefined () ) { message.setTextFormat ((Qt::TextFormat)temp.toInt ()); } temp = value.property ("informativeText"); if( !temp.isUndefined () ) { message.setInformativeText (temp.toString ()); } temp = value.property ("textInteractionFlags"); if( !temp.isUndefined () ) { message.setTextInteractionFlags ((Qt::TextInteractionFlags)temp.toInt ()); } return message.exec (); } void QQCommand::setRememberPassword(bool arg) { if (mysettings&&rememberPassword ()!= arg) { mysettings->setValue ("rememberPassword", arg); if(!arg) mysettings->remove ("password"); emit rememberPasswordChanged(); } } void QQCommand::setAutoLogin(bool arg) { if (mysettings&&autoLogin() != arg) { mysettings->setValue ("autoLogin", arg); emit autoLoginChanged(); } } void QQCommand::saveUserPassword() { if(rememberPassword()&&mysettings){//先判断是否记住了密码 QString pass = Utility::createUtilityClass ()->stringEncrypt (userPassword (), "xingchenQQ"); mysettings->setValue ("password", pass); } } ================================================ FILE: src/qqstars/qqstars.h ================================================ #ifndef QQCommand_H #define QQCommand_H #include #include #include #include #include #include #include "qqiteminfo.h" #include "downloadimage.h" class MyWindow; class MyHttpRequest; class NetworkAccessManager; class QNetworkRequest; class QNetworkReply; class QQCommand : public FriendInfo { Q_OBJECT Q_PROPERTY(QString userQQ READ userQQ WRITE setUserQQ NOTIFY userQQChanged) Q_PROPERTY(QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged) Q_PROPERTY(LoginStatus loginStatus READ loginStatus WRITE setLoginStatus NOTIFY loginStatusChanged) Q_PROPERTY(double windowScale READ windowScale WRITE setWindowScale NOTIFY windowScaleChanged) Q_PROPERTY(bool rememberPassword READ rememberPassword WRITE setRememberPassword NOTIFY rememberPasswordChanged)//是否记住密码 Q_PROPERTY(bool autoLogin READ autoLogin WRITE setAutoLogin NOTIFY autoLoginChanged)//是否自动登录 Q_PROPERTY(QString codeText READ codeText CONSTANT) Q_ENUMS(States) Q_ENUMS(LoginStatus) Q_ENUMS(SenderType) Q_ENUMS(MessageType) private: static QQCommand *firstQQCommand; public: static QQCommand *getFirstQQCommand();//返回第一个被创建的QQCommand对象 explicit QQCommand( QObject *parent = 0); enum LoginStatus{//登录状态 WaitLogin,//离线 Logining,//登录中 LoginFinished//登录完成 }; enum SenderType{//发送消息的人的类型 Friend,//好友 Group,//群 Discu,//讨论组 Stranger,//陌生人 SystemMessage//系统消息,包含群管理员的更改,成员的更改,还有好友验证消息 }; enum MessageType{//消息的类型 InputNotify,//正在输入 Text,//文本 Image,//图片 Face,//表情 GeneralMessage,//普通消息,包含Text Image Face等 //SendFile,//发送文件 //CancleSendFile,//取消发送文件 FileMessage,//文件消息,包含发送文件和取消发送文件 AvRequest,//请求开视频 AvRefuse,//取消开视频 ShakeWindow,//窗口抖动 FriendStatusChanged,//好友状态改变 FriendVerify,//好友验证消息poll_type=system_message,type=verify_required GroupAdmin,//群管理员消息poll_type=sys_g_msg,type=group_admin_op,uin_flag=1为设为管理员,0为取消管理员 GroupLeave//群T人的消息 }; LoginStatus loginStatus() const; QString userQQ() const; QString userPassword() const; double windowScale() const; bool rememberPassword() const; bool autoLogin() const; QString codeText() const; private slots: void beginPoll2();//启动心跳包 void poll2Finished(QNetworkReply *replys);//qq心跳包获取完成时调用 void initUserPassword();//初始化用户密码(从QSettings中) void onChatMainWindowClose();//接收主聊天窗口关闭的信号 void onSettingsChanged();//处理settings对象改变的信号 void onStateChanged();//当状态改变后调用,将状态存到本地 void onPoll2Timeout();//如果心跳包超时 void onNetworkOnlineStateChanged(bool isOnline);//如果网络在线状态改变 void downImageFinished(DownloadImage::ErrorType error, const QString& path, const QString& name); //下载图片完成时调用,下载的图片是好友发送过来的 void getImageUrlFinished(QNetworkReply *replys);//获取图片真实的下载地址完成 private: struct FontStyle{ int size;//字体大小 QString color;//字体颜色 bool bold;//加黑 bool italic;//斜体 bool underline;//下划线 QString family;//字体 }; struct ImageInfo{ int imageID;//图片的编号 ChatMessageInfo* messageInfo;//所属消息的信息 QString filePath;//能获取图片真实下载地址的url地址 }; QPointer window_login, window_mainPanel;//记录登录窗口的指针 LoginStatus m_loginStatus;//储存当前用户的登录状态 QByteArray poll2_data;//post心跳包的数据 NetworkAccessManager *manager;//储存管理心跳包网络请求的对象 QNetworkRequest *request;//储存发送心跳包的网络请求对象 QNetworkReply *reply;//储存进行网络请求的应答对象 QString m_userQQ;//储存当前用户qq号码 QString m_userPassword;//储存当前用户密码 QPointer code_window;//储存指向输入验证码窗口的指针 QJSEngine *jsEngine;//储存加载了api(*.js文件)的js引擎 double m_windowScale;//储存可视控件的比例 QString m_codeText;//储存输入验证码 QPointer warning_info_window;//储存指向警告窗口的指针 QMap map_itemInfo;//储存每个好友或群讨论组的Info QPointer mainChatWindowCommand;//储存所有聊天窗口的主管理窗口 QPointer mainChatWindowCommand_item;//储存每一个聊天页面的父对象(聊天窗口anchors.fill此父对象) QMap map_chatPage;//储存备已经打开的聊天页面 QString friendsUin;//用来储存所有好友的uin,陌生人不存在这里,为判断一个uin是否为陌生人做支持 QTimer* poll2_timer;//心跳包的计时器,如果超时就中断当前心跳包,然后重新重新心跳 QTimer* abortPoll_timer;//中断心跳包的定时器(为中断心跳包提供一个延时) int poll2Timerout_count;//记录网络请求的连续超时次数 int poll2Error_count;//记录网络请求连续出错的次数 MyHttpRequest* http_image;//下载图片专用的网络请求对象 int chatImageID;//聊天过程中收到的图片的id编号 QMap map_imageUrl;//图片的id和真实下载地址之间的映射 QQueue queue_imageInfo; void loadApi(); QString disposeMessage(QJsonObject &obj , ChatMessageInfo *message_info);//解析基本消息 //void disposeInputNotify( QJsonObject &obj );//处理好友正在输入消息 void disposeFriendStatusChanged( QJsonObject &obj );//处理好友状态改变 void disposeFriendMessage( QJsonObject &obj, MessageType type=GeneralMessage );//处理好友消息 void disposeGroupMessage( QJsonObject &obj, MessageType type=GeneralMessage );//处理群消息 void disposeDiscuMessage( QJsonObject &obj, MessageType type=GeneralMessage );//处理讨论组消息 void disposeStrangerMessage( QJsonObject &obj, MessageType type=GeneralMessage );//处理陌生人消息 void disposeSystemMessage(QJsonObject &obj);//处理系统消息 //void disposeFileMessage( QJsonObject &obj );//处理文件传输方面的消息 //void disposeAvMessage( QJsonObject &obj, bool open/*true为开视频,false为取消开视频*/ );//处理视频聊天方面的消息 //void disposeShakeMessage( QJsonObject &obj ); QString doubleToString( QJsonObject &obj, const QString& name );//将obj中类型为double的数据转化为QString类型 QString textToHtml(FontStyle &style, QString data);//将文本内容转化为富文本 QQItemInfo* createQQItemInfo(const QString& uin ,const QString& typeString); QQItemInfo* createQQItemInfo(const QString& uin, QQItemInfo::QQItemType type); void clearQQItemInfos();//清空保存的所有的好友信息 QString disposeImageMessage(ChatMessageInfo* message_info, QString file_path); //获取qq中好友发过来的图片的真实url(需要先get一下某个url,然后返回的数据中有真实的url地址) int getImageIndex();//返回一个本次在线中的一个唯一的数字,用于给接收到的图片编码 QString getImageUrlById(int image_id); void setImageUrlById(int image_id, const QString &url); signals: void loginStatusChanged(); void poll2ReData( QString data ); void userQQChanged(); void error( QString message );//有错误产生就发送信号 void userPasswordChanged(); void windowScaleChanged();//窗口比例改变 void rememberPasswordChanged(); void autoLoginChanged(); void friendInputNotify(QString fromUin);//好友正在输入的信号 void newMessage(QString fromUin, int type, ChatMessageInfo* info);//新的聊天消息信号,qml中的聊天页面会接收此消息 void shakeWindow(QString fromUin);//窗口抖动信号 void addChatPageToWindow(QQuickItem* item);//增加聊天页面的信号,此信号被聊天页面所在的window接收 void activeChatPageChanged(QQuickItem* item);//将item这个page变为活跃的page void addRecentContacts(QQItemInfo* info);//发送信号告诉qml的最近联系人列表添加item public slots: void loadLoginWindow();//加载登录窗口 void loadMainPanelWindow();//加载qq主面板窗口 void setRememberPassword(bool arg); void setAutoLogin(bool arg); void saveUserPassword(); void setLoginStatus(LoginStatus arg); void startPoll2( const QByteArray& data ); void setUserQQ(QString arg); void setUserPassword(QString arg); void setWindowScale(double arg); QString getHash();//获取请求好友列表需要的hsah QString encryptionPassword(const QString &uin, const QString &code);//加密密码,用来登录 QVariant getLoginedQQInfo();//获取所有在此电脑上登录过的qq的信息 void removeLoginedQQInfo(const QString account, bool rmLocalCache=false);//移除qq号码为account的账号信息 void addLoginedQQInfo(const QString account, const QString nick);//增加一个登录过的qq的记录 int openMessageBox( QJSValue value );//打开一个对话窗口 void showWarningInfo(QString message);//显示一个警告窗口 void downloadImage( int senderType/*QQItemType类型*/, QUrl url, QString account, QString imageSize, QJSValue callbackFun );//下载图片 void showCodeWindow(const QJSValue callbackFun, const QString code_uin);//显示出输入验证码的窗口 void closeCodeWindow();//关闭输入验证码的窗口 void updataCode();//刷新验证码的显示 void updataApi(const QString& content);//重新载入api.js,用于更新api后的操作 FriendInfo* createFriendInfo(const QString uin);//创建一个储存好友信息的对象 GroupInfo* createGroupInfo(const QString uin);//创建一个储存群信息的对象 DiscuInfo* createDiscuInfo(const QString uin);//创建一个储存讨论组信息的对象 void addChatPage(QString uin, int senderType/*QQItemType类型*/);//新增聊天窗口 void removeChatPage(QString uin, int senderType/*QQItemType类型*/);//移除已有的聊天Page bool isChatPageExist(const QString& uin, int senderType/*QQItemType类型*/);//判断聊天页面是否存在 void addFriendUin(const QString& uin);//将此uin添加到好友uin列表 bool isStranger(const QString& uin);//判断此uin是否为陌生人 QVariant value(const QString & key, const QVariant & defaultValue = QVariant()) const;//返回储存在QSettings里边的value; void setValue(const QString & key, const QVariant & value); void shakeChatMainWindow (QQuickItem *item);//抖动聊天窗口 void openSqlDatabase();//打开数据库(储存聊天记录等消息) void closeChatWindow();//关闭聊天窗口 QString getMovieImageFrameCachePath();//返回给qml显示gif图时每一帧的缓存路径 }; #endif // QQCommand_H ================================================ FILE: src/qxtglobalshortcut/myshortcut.cpp ================================================ #include "myshortcut.h" #include #include #include #include #include MyShortcut::MyShortcut(MyShortcut::Type type, QObject *parent): QObject(parent) { m_shortcut = ""; m_enabled = true; m_filterOut = true; m_shortcutType = type; } MyShortcut::MyShortcut(QString shortcut, MyShortcut::Type type, QObject *parent): QObject(parent) { m_shortcut = shortcut; m_enabled = true; m_filterOut = true; m_shortcutType = type; } QString MyShortcut::shortcut() const { return m_shortcut; } bool MyShortcut::isEnabled() const { return m_enabled; } bool MyShortcut::filterOut() const { return m_filterOut; } QObject *MyShortcut::target() const { return m_target; } MyShortcut::Type MyShortcut::shortcutType() const { return m_shortcutType; } void MyShortcut::setShortcut(QString arg) { if (m_shortcut != arg) { m_shortcut = arg; if(shortcutType () == SystemGlobalShortcut){//如果是系统全局热键 updataSystemGlobalShortcut (arg);//更新系统全局热键 } key_list.clear (); QStringList list = arg.split ("+"); foreach (QString key, list) { if(key!=""){ if(QString::compare (key, "Ctrl", Qt::CaseInsensitive)==0){ key_list.append (Qt::Key_Control); }else if(QString::compare (key, "Shift", Qt::CaseInsensitive)==0){ key_list.append (Qt::Key_Shift); }else if(QString::compare (key, "Alt", Qt::CaseInsensitive)==0){ key_list.append (Qt::Key_Alt); }else if(QString::compare (key, "Meta", Qt::CaseInsensitive)==0){ key_list.append (Qt::Key_Meta); }else{ QKeySequence sequence=QKeySequence::fromString (key); if(sequence!=Qt::Key_unknown){ key_list.append (sequence[0]); }else{ emit error ("存在未知按键"); break; } } }else{ emit error ("不可有空按键"); break; } //qDebug()<installEventFilter (this);//安装过滤器 else obj->removeEventFilter (this);//移除过滤器 } if(shortcutType ()==SystemGlobalShortcut){//如果是全局的 if(global_shortcut) global_shortcut->setEnabled (arg); } emit enabledChanged(arg); } } bool MyShortcut::onKeyPressed(QQueue &list) { if(list==key_list){//判断是否触发了热键 emit trigger (); return true; } return false; } void MyShortcut::setFilterOut(bool arg) { if (m_filterOut != arg) { m_filterOut = arg; emit filterOutChanged(arg); } } void MyShortcut::setTarget(QObject *arg) { if (m_target != arg) { m_target = arg; if(shortcutType ()==LocalShortcut)//如果是局部热键才能设置obj setObj (arg); emit targetChanged(arg); } } void MyShortcut::setShortcutType(MyShortcut::Type arg) { if (m_shortcutType != arg) { m_shortcutType = arg; if(arg == AppGlobalShortcut){//如果是程序内部的全局热键 //qDebug()<<"设置程序内部全局热键:"<removeEventFilter (this);//移除过滤器 if(global_shortcut.isNull ()){ global_shortcut = new QxtGlobalShortcut(this); connect(global_shortcut, SIGNAL(activated()), SIGNAL(trigger()));//连接信号和槽 } updataSystemGlobalShortcut(shortcut ()); } emit shortcutTypeChanged(arg); } } void MyShortcut::setObj(QObject *arg) { if(obj!=arg){ if(isEnabled()&&arg){ arg->installEventFilter (this);//为他安装事件过滤器 //qDebug()<<"安装了过滤器"; } if(obj) obj->removeEventFilter (this);//移除原来的过滤器 obj = arg;//设置为新的过滤对象 } } void MyShortcut::updataSystemGlobalShortcut(const QString &arg) { if(arg=="")//不可为空 return; QKeySequence temp = QKeySequence(arg); if(global_shortcut&&global_shortcut->shortcut ()!=temp){ if(!global_shortcut->setShortcut (temp)){ qDebug()<<"MyShortcut"<<"设置系统全局热键:"+arg+"出错"; emit error ("设置系统全局热键:"+arg+"出错"); } } } bool MyShortcut::eventFilter(QObject *watched, QEvent *event) { if(watched == obj||shortcutType () == AppGlobalShortcut){//判断一下是不是obj的事件,或者是全局热键事件 if(event->type () == QEvent::KeyPress){ QKeyEvent *keyEvent = static_cast(event); int key = keyEvent->key (); if(queue_key.count ()==0||key!=queue_key.last ())//如果按键不是重复的 queue_key.append (key); else return false; //qDebug()<key (); return (onKeyPressed (queue_key)&&m_filterOut); //如果符合自己的热键设定,就再判断是否过滤掉此次按键事件,是的话才返回true,否则就算符合了热键的设定也将此事件放行给target响应 }else if(event->type () == QEvent::KeyRelease){ QKeyEvent *keyEvent = static_cast(event); queue_key.removeOne (keyEvent->key()); return false; } return false; }else{//如果是其他obj的时间就返回给parent中的eventFilter处理 return QObject::eventFilter (watched, event); } } ================================================ FILE: src/qxtglobalshortcut/myshortcut.h ================================================ #ifndef MYSHORTCUT_H #define MYSHORTCUT_H /*! * 使用须知: * 此类为 雨后星辰后来添加,封装了app内部全局热键(通过给QApplication安装事件过滤器) * 封装了指定对象热键(为此对象安装事件过滤器实现) * 最后封装了qxtglobalshortcut实现了系统全局热键,在这里感谢原作者的贡献 * 另外,由于qxtglobalshortcut不支持qt5,所以我额外给他增加了一下代码 * 总结,此类支持对象内热键(必须为QObject类型或继承QObject),程序全局热键,系统全局热键,支持qt5和qt4(未尝试,可以需要修改部分代码) * 请不要删除这段话,谢谢! */ #include #include #include #include "qxtglobalshortcut.h"//系统全局热键 class MyShortcut : public QObject { Q_OBJECT Q_PROPERTY(QString shortcut READ shortcut WRITE setShortcut NOTIFY shortcutChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool filterOut READ filterOut WRITE setFilterOut NOTIFY filterOutChanged) //是将事件传递给过滤的对象,对于系统全局热键不可用 Q_PROPERTY(QObject* target READ target WRITE setTarget NOTIFY targetChanged) Q_PROPERTY(Type shortcutType READ shortcutType WRITE setShortcutType NOTIFY shortcutTypeChanged) Q_ENUMS(Type) public: enum Type{ LocalShortcut,//局部热键 AppGlobalShortcut,//整个程序的热键 SystemGlobalShortcut//系统全局热键 }; explicit MyShortcut(Type type = LocalShortcut, QObject *parent = 0); explicit MyShortcut(QString shortcut, Type type = LocalShortcut, QObject *parent = 0); QString shortcut() const; bool isEnabled() const; bool filterOut() const; QObject* target() const; Type shortcutType() const; public slots: void setShortcut(QString arg); void setEnabled(bool arg); void setFilterOut(bool arg); void setTarget(QObject* arg); void setShortcutType(Type arg); signals: void shortcutChanged(QString arg); void trigger(); void enabledChanged(bool arg); void error(QString arg); void filterOutChanged(bool arg); void targetChanged(QObject* arg); void shortcutTypeChanged(Type arg); private: QString m_shortcut; QQueue key_list; QQueue queue_key; bool m_enabled; bool m_filterOut; QPointer m_target; QPointer obj; QPointer global_shortcut; Type m_shortcutType; void setObj(QObject *arg); void updataSystemGlobalShortcut(const QString &arg);//更新系统全局热键 bool eventFilter ( QObject * watched, QEvent * event ); bool onKeyPressed(QQueue& list); }; #endif // MYSHORTCUT_H ================================================ FILE: src/qxtglobalshortcut/qxtglobal.h ================================================ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtCore module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #ifndef QXTGLOBAL_H #define QXTGLOBAL_H #include #define QXT_VERSION 0x000600 #define QXT_VERSION_STR "0.6.0" //--------------------------global macros------------------------------ #ifndef QXT_NO_MACROS #endif // QXT_NO_MACROS //--------------------------export macros------------------------------ #define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE #if !defined(QXT_STATIC) # if defined(BUILD_QXT_CORE) # define QXT_CORE_EXPORT Q_DECL_EXPORT # else # define QXT_CORE_EXPORT Q_DECL_IMPORT # endif #else # define QXT_CORE_EXPORT #endif // BUILD_QXT_CORE #if !defined(QXT_STATIC) # if defined(BUILD_QXT_GUI) # define QXT_GUI_EXPORT Q_DECL_EXPORT # else # define QXT_GUI_EXPORT Q_DECL_IMPORT # endif #else # define QXT_GUI_EXPORT #endif // BUILD_QXT_GUI #if !defined(QXT_STATIC) # if defined(BUILD_QXT_NETWORK) # define QXT_NETWORK_EXPORT Q_DECL_EXPORT # else # define QXT_NETWORK_EXPORT Q_DECL_IMPORT # endif #else # define QXT_NETWORK_EXPORT #endif // BUILD_QXT_NETWORK #if !defined(QXT_STATIC) # if defined(BUILD_QXT_SQL) # define QXT_SQL_EXPORT Q_DECL_EXPORT # else # define QXT_SQL_EXPORT Q_DECL_IMPORT # endif #else # define QXT_SQL_EXPORT #endif // BUILD_QXT_SQL #if !defined(QXT_STATIC) # if defined(BUILD_QXT_WEB) # define QXT_WEB_EXPORT Q_DECL_EXPORT # else # define QXT_WEB_EXPORT Q_DECL_IMPORT # endif #else # define QXT_WEB_EXPORT #endif // BUILD_QXT_WEB #if !defined(QXT_STATIC) # if defined(BUILD_QXT_BERKELEY) # define QXT_BERKELEY_EXPORT Q_DECL_EXPORT # else # define QXT_BERKELEY_EXPORT Q_DECL_IMPORT # endif #else # define QXT_BERKELEY_EXPORT #endif // BUILD_QXT_BERKELEY #if !defined(QXT_STATIC) # if defined(BUILD_QXT_ZEROCONF) # define QXT_ZEROCONF_EXPORT Q_DECL_EXPORT # else # define QXT_ZEROCONF_EXPORT Q_DECL_IMPORT # endif #else # define QXT_ZEROCONF_EXPORT #endif // QXT_ZEROCONF_EXPORT #if defined BUILD_QXT_CORE || defined BUILD_QXT_GUI || defined BUILD_QXT_SQL || defined BUILD_QXT_NETWORK || defined BUILD_QXT_WEB || defined BUILD_QXT_BERKELEY || defined BUILD_QXT_ZEROCONF # define BUILD_QXT #endif QXT_CORE_EXPORT const char* qxtVersion(); #ifndef QT_BEGIN_NAMESPACE #define QT_BEGIN_NAMESPACE #endif #ifndef QT_END_NAMESPACE #define QT_END_NAMESPACE #endif #ifndef QT_FORWARD_DECLARE_CLASS #define QT_FORWARD_DECLARE_CLASS(Class) class Class; #endif /**************************************************************************** ** This file is derived from code bearing the following notice: ** The sole author of this file, Adam Higerd, has explicitly disclaimed all ** copyright interest and protection for the content within. This file has ** been placed in the public domain according to United States copyright ** statute and case law. In jurisdictions where this public domain dedication ** is not legally recognized, anyone who receives a copy of this file is ** permitted to use, modify, duplicate, and redistribute this file, in whole ** or in part, with no restrictions or conditions. In these jurisdictions, ** this file shall be copyright (C) 2006-2008 by Adam Higerd. ****************************************************************************/ #define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface qxt_d; #define QXT_DECLARE_PUBLIC(PUB) friend class PUB; #define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this); #define QXT_D(PUB) PUB##Private& d = qxt_d() #define QXT_P(PUB) PUB& p = qxt_p() template class QxtPrivate { public: virtual ~QxtPrivate() {} inline void QXT_setPublic(PUB* pub) { qxt_p_ptr = pub; } protected: inline PUB& qxt_p() { return *qxt_p_ptr; } inline const PUB& qxt_p() const { return *qxt_p_ptr; } private: PUB* qxt_p_ptr; }; template class QxtPrivateInterface { friend class QxtPrivate; public: QxtPrivateInterface() { pvt = new PVT; } ~QxtPrivateInterface() { delete pvt; } inline void setPublic(PUB* pub) { pvt->QXT_setPublic(pub); } inline PVT& operator()() { return *static_cast(pvt); } inline const PVT& operator()() const { return *static_cast(pvt); } private: QxtPrivateInterface(const QxtPrivateInterface&) { } QxtPrivateInterface& operator=(const QxtPrivateInterface&) { } QxtPrivate* pvt; }; #endif // QXT_GLOBAL ================================================ FILE: src/qxtglobalshortcut/qxtglobalshortcut.cpp ================================================ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #include "qxtglobalshortcut.h" #include "qxtglobalshortcut_p.h" #include #include bool QxtGlobalShortcutPrivate::error = false; int QxtGlobalShortcutPrivate::ref = 0; #if(QT_VERSION<0x050000)//雨后星辰 QAbstractEventDispatcher::EventFilter QxtGlobalShortcutPrivate::prevEventFilter = 0; #endif QHash, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts; QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate() : enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier) { if (!ref++){ #if(QT_VERSION<0x050000)//雨后星辰 prevEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(eventFilter); #else QAbstractEventDispatcher::instance ()->installNativeEventFilter (this); #endif } } QxtGlobalShortcutPrivate::~QxtGlobalShortcutPrivate() { if (!--ref){ #if(QT_VERSION<0x050000)//雨后星辰 QAbstractEventDispatcher::instance()->setEventFilter(prevEventFilter); #else QAbstractEventDispatcher::instance ()->removeNativeEventFilter (this); #endif } } bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut) { Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier; key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((shortcut[0] ^ allMods) & shortcut[0]); mods = shortcut.isEmpty() ? Qt::KeyboardModifiers(0) : Qt::KeyboardModifiers(shortcut[0] & allMods); const quint32 nativeKey = nativeKeycode(key); const quint32 nativeMods = nativeModifiers(mods); const bool res = registerShortcut(nativeKey, nativeMods); shortcuts.insert(qMakePair(nativeKey, nativeMods), &qxt_p()); if (!res) qWarning() << "QxtGlobalShortcut failed to register:" << QKeySequence(key + mods).toString(); return res; } bool QxtGlobalShortcutPrivate::unsetShortcut() { const quint32 nativeKey = nativeKeycode(key); const quint32 nativeMods = nativeModifiers(mods); const bool res = unregisterShortcut(nativeKey, nativeMods); shortcuts.remove(qMakePair(nativeKey, nativeMods)); if (!res) qWarning() << "QxtGlobalShortcut failed to unregister:" << QKeySequence(key + mods).toString(); key = Qt::Key(0); mods = Qt::KeyboardModifiers(0); return res; } void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods) { QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods)); if (shortcut && shortcut->isEnabled()) emit shortcut->activated(); } /*! \class QxtGlobalShortcut \inmodule QxtGui \brief The QxtGlobalShortcut class provides a global shortcut aka "hotkey". A global shortcut triggers even if the application is not active. This makes it easy to implement applications that react to certain shortcuts still if some other application is active or if the application is for example minimized to the system tray. Example usage: \code QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window); connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility())); shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12")); \endcode \bold {Note:} Since Qxt 0.6 QxtGlobalShortcut no more requires QxtApplication. */ /*! \fn QxtGlobalShortcut::activated() This signal is emitted when the user types the shortcut's key sequence. \sa shortcut */ /*! Constructs a new QxtGlobalShortcut with \a parent. */ QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent) : QObject(parent) { QXT_INIT_PRIVATE(QxtGlobalShortcut); } /*! Constructs a new QxtGlobalShortcut with \a shortcut and \a parent. */ QxtGlobalShortcut::QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent) : QObject(parent) { QXT_INIT_PRIVATE(QxtGlobalShortcut); setShortcut(shortcut); } /*! Destructs the QxtGlobalShortcut. */ QxtGlobalShortcut::~QxtGlobalShortcut() { if (qxt_d().key != 0) qxt_d().unsetShortcut(); } /*! \property QxtGlobalShortcut::shortcut \brief the shortcut key sequence \bold {Note:} Notice that corresponding key press and release events are not delivered for registered global shortcuts even if they are disabled. Also, comma separated key sequences are not supported. Only the first part is used: \code qxtShortcut->setShortcut(QKeySequence("Ctrl+Alt+A,Ctrl+Alt+B")); Q_ASSERT(qxtShortcut->shortcut() == QKeySequence("Ctrl+Alt+A")); \endcode */ QKeySequence QxtGlobalShortcut::shortcut() const { return QKeySequence(qxt_d().key | qxt_d().mods); } bool QxtGlobalShortcut::setShortcut(const QKeySequence& shortcut) { if (qxt_d().key != 0) qxt_d().unsetShortcut(); return qxt_d().setShortcut(shortcut); } /*! \property QxtGlobalShortcut::enabled \brief whether the shortcut is enabled A disabled shortcut does not get activated. The default value is \c true. \sa setDisabled() */ bool QxtGlobalShortcut::isEnabled() const { return qxt_d().enabled; } void QxtGlobalShortcut::setEnabled(bool enabled) { qxt_d().enabled = enabled; } /*! Sets the shortcut \a disabled. \sa enabled */ void QxtGlobalShortcut::setDisabled(bool disabled) { qxt_d().enabled = !disabled; } ================================================ FILE: src/qxtglobalshortcut/qxtglobalshortcut.h ================================================ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #ifndef QXTGLOBALSHORTCUT_H #define QXTGLOBALSHORTCUT_H #include "qxtglobal.h" #include #include class QxtGlobalShortcutPrivate; class QxtGlobalShortcut : public QObject { Q_OBJECT QXT_DECLARE_PRIVATE(QxtGlobalShortcut) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) public: explicit QxtGlobalShortcut(QObject* parent = 0); explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0); virtual ~QxtGlobalShortcut(); QKeySequence shortcut() const; bool setShortcut(const QKeySequence& shortcut); bool isEnabled() const; public Q_SLOTS: void setEnabled(bool enabled = true); void setDisabled(bool disabled = true); Q_SIGNALS: void activated(); }; #endif // QXTGLOBALSHORTCUT_H ================================================ FILE: src/qxtglobalshortcut/qxtglobalshortcut.pri ================================================ INCLUDEPATH += $$PWD DEPENDPATH += $$PWD HEADERS += $$PWD/qxtglobal.h \ $$PWD/qxtglobalshortcut.h \ $$PWD/qxtglobalshortcut_p.h \ $$PWD/myshortcut.h SOURCES += $$PWD/qxtglobalshortcut.cpp \ $$PWD/myshortcut.cpp win32{ SOURCES += $$PWD/qxtglobalshortcut_win.cpp LIBS += -luser32 } unix{ QT += x11extras LIBS += -lX11 SOURCES += $$PWD/qxtglobalshortcut_x11.cpp } mac{ SOURCES += $$PWD/qxtglobalshortcut_mac.cpp } ================================================ FILE: src/qxtglobalshortcut/qxtglobalshortcut_mac.cpp ================================================ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #include #include "qxtglobalshortcut_p.h" #include #include #include #include typedef QPair Identifier; static QMap keyRefs; static QHash keyIDs; static quint32 hotKeySerial = 0; static bool qxt_mac_handler_installed = false; OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data) { // pass event to the app event filter Q_UNUSED(data); qApp->macEventFilter(nextHandler, event); return noErr; } #if(QT_VERSION<0x050000) bool QxtGlobalShortcutPrivate::eventFilter(void* message) //bool QxtGlobalShortcutPrivate::macEventFilter(EventHandlerCallRef caller, EventRef event) { EventRef event = (EventRef) message; if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) { EventHotKeyID keyID; GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID); Identifier id = keyIDs.key(keyID.id); activateShortcut(id.second, id.first); } return false; } #else bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray &, void *message, long *) { EventRef event = (EventRef) message; if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) { EventHotKeyID keyID; GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID); Identifier id = keyIDs.key(keyID.id); activateShortcut(id.second, id.first); } return false; } #endif quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { quint32 native = 0; if (modifiers & Qt::ShiftModifier) native |= shiftKeyBit; if (modifiers & Qt::ControlModifier) native |= cmdKey; if (modifiers & Qt::AltModifier) native |= optionKey; if (modifiers & Qt::MetaModifier) native |= controlKey; if (modifiers & Qt::KeypadModifier) native |= kEventKeyModifierNumLockMask; return native; } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) { UTF16Char ch; // Constants found in NSEvent.h from AppKit.framework if (key == Qt::Key_Up) ch = 0xF700; else if (key == Qt::Key_Down) ch = 0xF701; else if (key == Qt::Key_Left) ch = 0xF702; else if (key == Qt::Key_Right) ch = 0xF703; else if (key >= Qt::Key_F1 && key <= Qt::Key_F35) ch = key - Qt::Key_F1 + 0xF704; else if (key == Qt::Key_Insert) ch = 0xF727; else if (key == Qt::Key_Delete) ch = 0xF728; else if (key == Qt::Key_Home) ch = 0xF729; else if (key == Qt::Key_End) ch = 0xF72B; else if (key == Qt::Key_PageUp) ch = 0xF72C; else if (key == Qt::Key_PageDown) ch = 0xF72D; else if (key == Qt::Key_Print) ch = 0xF72E; else if (key == Qt::Key_ScrollLock) ch = 0xF72F; else if (key == Qt::Key_Pause) ch = 0xF730; else if (key == Qt::Key_SysReq) ch = 0xF731; else if (key == Qt::Key_Stop) ch = 0xF734; else if (key == Qt::Key_Menu) ch = 0xF735; else if (key == Qt::Key_Select) ch = 0xF741; else if (key == Qt::Key_Execute) ch = 0xF742; else if (key == Qt::Key_Help) ch = 0xF746; else if (key == Qt::Key_Mode_switch) ch = 0xF747; else if (key == Qt::Key_Escape) ch = 27; else if (key == Qt::Key_Return) ch = 13; else if (key == Qt::Key_Enter) ch = 3; else if (key == Qt::Key_Tab) ch = 9; else ch = key; KeyboardLayoutRef layout; KeyboardLayoutKind layoutKind; KLGetCurrentKeyboardLayout(&layout); KLGetKeyboardLayoutProperty(layout, kKLKind, const_cast(reinterpret_cast(&layoutKind))); if (layoutKind == kKLKCHRKind) { // no Unicode available if (ch > 255) return 0; char* data; KLGetKeyboardLayoutProperty(layout, kKLKCHRData, const_cast(reinterpret_cast(&data))); int ct = *reinterpret_cast(data + 258); for (int i = 0; i < ct; i++) { char* keyTable = data + 260 + 128 * i; for (int j = 0; j < 128; j++) { if (keyTable[j] == ch) return j; } } return 0; } char* data; KLGetKeyboardLayoutProperty(layout, kKLuchrData, const_cast(reinterpret_cast(&data))); UCKeyboardLayout* header = reinterpret_cast(data); UCKeyboardTypeHeader* table = header->keyboardTypeList; for (quint32 i=0; i < header->keyboardTypeCount; i++) { UCKeyStateRecordsIndex* stateRec = 0; if (table[i].keyStateRecordsIndexOffset != 0) { stateRec = reinterpret_cast(data + table[i].keyStateRecordsIndexOffset); if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0; } UCKeyToCharTableIndex* charTable = reinterpret_cast(data + table[i].keyToCharTableIndexOffset); if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue; for (quint32 j=0; j < charTable->keyToCharTableCount; j++) { UCKeyOutput* keyToChar = reinterpret_cast(data + charTable->keyToCharTableOffsets[j]); for (quint32 k=0; k < charTable->keyToCharTableSize; k++) { if (keyToChar[k] & kUCKeyOutputTestForIndexMask) { long idx = keyToChar[k] & kUCKeyOutputGetIndexMask; if (stateRec && idx < stateRec->keyStateRecordCount) { UCKeyStateRecord* rec = reinterpret_cast(data + stateRec->keyStateRecordOffsets[idx]); if (rec->stateZeroCharData == ch) return k; } } else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE) { if (keyToChar[k] == ch) return k; } } // for k } // for j } // for i return 0; } bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { if (!qxt_mac_handler_installed) { EventTypeSpec t; t.eventClass = kEventClassKeyboard; t.eventKind = kEventHotKeyPressed; InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL); } EventHotKeyID keyID; keyID.signature = 'cute'; keyID.id = ++hotKeySerial; EventHotKeyRef ref = 0; bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref); if (rv) { keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id); keyRefs.insert(keyID.id, ref); } qDebug() << ref; return rv; } bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { Identifier id(nativeMods, nativeKey); if (!keyIDs.contains(id)) return false; EventHotKeyRef ref = keyRefs.take(keyIDs[id]); keyIDs.remove(id); return !UnregisterEventHotKey(ref); } ================================================ FILE: src/qxtglobalshortcut/qxtglobalshortcut_p.h ================================================ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #ifndef QXTGLOBALSHORTCUT_P_H #define QXTGLOBALSHORTCUT_P_H #include "qxtglobalshortcut.h" #include #include #include #if(QT_VERSION>=0x050000)//雨后星辰 #include #endif class QxtGlobalShortcutPrivate : public QxtPrivate #if(QT_VERSION>=0x050000) , public QAbstractNativeEventFilter #endif { public: QXT_DECLARE_PUBLIC(QxtGlobalShortcut) QxtGlobalShortcutPrivate(); ~QxtGlobalShortcutPrivate(); bool enabled; Qt::Key key; Qt::KeyboardModifiers mods; bool setShortcut(const QKeySequence& shortcut); bool unsetShortcut(); static bool error; static int ref; #if(QT_VERSION<0x050000)//雨后星辰 static QAbstractEventDispatcher::EventFilter prevEventFilter; static bool eventFilter(void* message); #else bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); #endif private: static quint32 nativeKeycode(Qt::Key keycode); static quint32 nativeModifiers(Qt::KeyboardModifiers modifiers); static bool registerShortcut(quint32 nativeKey, quint32 nativeMods); static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods); static void activateShortcut(quint32 nativeKey, quint32 nativeMods); static QHash, QxtGlobalShortcut*> shortcuts; }; #endif // QXTGLOBALSHORTCUT_P_H ================================================ FILE: src/qxtglobalshortcut/qxtglobalshortcut_win.cpp ================================================ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #include "qxtglobalshortcut_p.h" #include #include #if(QT_VERSION<0x050000) bool QxtGlobalShortcutPrivate::eventFilter(void* message) { MSG* msg = static_cast(message); if (msg->message == WM_HOTKEY) { const quint32 keycode = HIWORD(msg->lParam); const quint32 modifiers = LOWORD(msg->lParam); activateShortcut(keycode, modifiers); } return false; } #else bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray &, void *message, long *) { MSG* msg = static_cast(message); if (msg->message == WM_HOTKEY) { const quint32 keycode = HIWORD(msg->lParam); const quint32 modifiers = LOWORD(msg->lParam); activateShortcut(keycode, modifiers); } return false; } #endif quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { // MOD_ALT, MOD_CONTROL, (MOD_KEYUP), MOD_SHIFT, MOD_WIN quint32 native = 0; if (modifiers & Qt::ShiftModifier) native |= MOD_SHIFT; if (modifiers & Qt::ControlModifier) native |= MOD_CONTROL; if (modifiers & Qt::AltModifier) native |= MOD_ALT; if (modifiers & Qt::MetaModifier) native |= MOD_WIN; // TODO: resolve these? //if (modifiers & Qt::KeypadModifier) //if (modifiers & Qt::GroupSwitchModifier) return native; } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) { switch (key) { case Qt::Key_Escape: return VK_ESCAPE; case Qt::Key_Tab: case Qt::Key_Backtab: return VK_TAB; case Qt::Key_Backspace: return VK_BACK; case Qt::Key_Return: case Qt::Key_Enter: return VK_RETURN; case Qt::Key_Insert: return VK_INSERT; case Qt::Key_Delete: return VK_DELETE; case Qt::Key_Pause: return VK_PAUSE; case Qt::Key_Print: return VK_PRINT; case Qt::Key_Clear: return VK_CLEAR; case Qt::Key_Home: return VK_HOME; case Qt::Key_End: return VK_END; case Qt::Key_Left: return VK_LEFT; case Qt::Key_Up: return VK_UP; case Qt::Key_Right: return VK_RIGHT; case Qt::Key_Down: return VK_DOWN; case Qt::Key_PageUp: return VK_PRIOR; case Qt::Key_PageDown: return VK_NEXT; case Qt::Key_F1: return VK_F1; case Qt::Key_F2: return VK_F2; case Qt::Key_F3: return VK_F3; case Qt::Key_F4: return VK_F4; case Qt::Key_F5: return VK_F5; case Qt::Key_F6: return VK_F6; case Qt::Key_F7: return VK_F7; case Qt::Key_F8: return VK_F8; case Qt::Key_F9: return VK_F9; case Qt::Key_F10: return VK_F10; case Qt::Key_F11: return VK_F11; case Qt::Key_F12: return VK_F12; case Qt::Key_F13: return VK_F13; case Qt::Key_F14: return VK_F14; case Qt::Key_F15: return VK_F15; case Qt::Key_F16: return VK_F16; case Qt::Key_F17: return VK_F17; case Qt::Key_F18: return VK_F18; case Qt::Key_F19: return VK_F19; case Qt::Key_F20: return VK_F20; case Qt::Key_F21: return VK_F21; case Qt::Key_F22: return VK_F22; case Qt::Key_F23: return VK_F23; case Qt::Key_F24: return VK_F24; case Qt::Key_Space: return VK_SPACE; case Qt::Key_Asterisk: return VK_MULTIPLY; case Qt::Key_Plus: return VK_ADD; case Qt::Key_Comma: return VK_SEPARATOR; case Qt::Key_Minus: return VK_SUBTRACT; case Qt::Key_Slash: return VK_DIVIDE; // numbers case Qt::Key_0: case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: case Qt::Key_9: return key; // letters case Qt::Key_A: case Qt::Key_B: case Qt::Key_C: case Qt::Key_D: case Qt::Key_E: case Qt::Key_F: case Qt::Key_G: case Qt::Key_H: case Qt::Key_I: case Qt::Key_J: case Qt::Key_K: case Qt::Key_L: case Qt::Key_M: case Qt::Key_N: case Qt::Key_O: case Qt::Key_P: case Qt::Key_Q: case Qt::Key_R: case Qt::Key_S: case Qt::Key_T: case Qt::Key_U: case Qt::Key_V: case Qt::Key_W: case Qt::Key_X: case Qt::Key_Y: case Qt::Key_Z: return key; default: return 0; } } bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { return RegisterHotKey(0, nativeMods ^ nativeKey, nativeMods, nativeKey); } bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { return UnregisterHotKey(0, nativeMods ^ nativeKey); } ================================================ FILE: src/qxtglobalshortcut/qxtglobalshortcut_x11.cpp ================================================ /**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #include "qxtglobalshortcut_p.h" #include #include static int (*original_x_errhandler)(Display* display, XErrorEvent* event); static int qxt_x_errhandler(Display* display, XErrorEvent *event) { Q_UNUSED(display); switch (event->error_code) { case BadAccess: case BadValue: case BadWindow: if (event->request_code == 33 /* X_GrabKey */ || event->request_code == 34 /* X_UngrabKey */) { QxtGlobalShortcutPrivate::error = true; //TODO: //char errstr[256]; //XGetErrorText(dpy, err->error_code, errstr, 256); } default: return 0; } } #if(QT_VERSION<0x050000) bool QxtGlobalShortcutPrivate::eventFilter(void* message) { XEvent* event = static_cast(message); if (event->type == KeyPress) { XKeyEvent* key = (XKeyEvent*) event; activateShortcut(key->keycode, // Mod1Mask == Alt, Mod4Mask == Meta key->state & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask)); } return false; } #else bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray &, void *message, long *) { XEvent* event = static_cast(message); if (event->type == KeyPress) { XKeyEvent* key = (XKeyEvent*) event; activateShortcut(key->keycode, // Mod1Mask == Alt, Mod4Mask == Meta key->state & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask)); } return false; } #endif quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { // ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask quint32 native = 0; if (modifiers & Qt::ShiftModifier) native |= ShiftMask; if (modifiers & Qt::ControlModifier) native |= ControlMask; if (modifiers & Qt::AltModifier) native |= Mod1Mask; // TODO: resolve these? //if (modifiers & Qt::MetaModifier) //if (modifiers & Qt::KeypadModifier) //if (modifiers & Qt::GroupSwitchModifier) return native; } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) { Display* display = QX11Info::display(); return XKeysymToKeycode(display, XStringToKeysym(QKeySequence(key).toString().toLatin1().data())); } bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { Display* display = QX11Info::display(); Window window = QX11Info::appRootWindow(); Bool owner = True; int pointer = GrabModeAsync; int keyboard = GrabModeAsync; error = false; original_x_errhandler = XSetErrorHandler(qxt_x_errhandler); XGrabKey(display, nativeKey, nativeMods, window, owner, pointer, keyboard); XGrabKey(display, nativeKey, nativeMods | Mod2Mask, window, owner, pointer, keyboard); // allow numlock XSync(display, False); XSetErrorHandler(original_x_errhandler); return !error; } bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { Display* display = QX11Info::display(); Window window = QX11Info::appRootWindow(); error = false; original_x_errhandler = XSetErrorHandler(qxt_x_errhandler); XUngrabKey(display, nativeKey, nativeMods, window); XUngrabKey(display, nativeKey, nativeMods | Mod2Mask, window); // allow numlock XSync(display, False); XSetErrorHandler(original_x_errhandler); return !error; } ================================================ FILE: src/utility/downloadimage.cpp ================================================ #include "downloadimage.h" #include #include #include "utility.h" #include "myhttprequest.h" DownloadImage::DownloadImage(QObject *parent): QObject(parent) { httpRequest = new MyHttpRequest(this); } DownloadImage::DownloadImage(MyHttpRequest *http, QObject *parent): QObject(parent) { httpRequest = http; } MyHttpRequest *DownloadImage::getHttpRequest() { return httpRequest; } QString DownloadImage::imageFormatToString(const QByteArray &array) { QByteArray str = array.toHex (); if(str.mid (2,6)=="504e47") return "png"; if(str.mid (12,8)=="4a464946") return "jpg"; if(str.left (6)=="474946") return "gif"; if(str.left (4)=="424d") return "bmp"; return ""; } void DownloadImage::downloadFinished(QNetworkReply *replys) { Data data = queue_data.dequeue (); ReplyType type=data.replyType; ErrorType error=NoError; QString saveName = data.saveName; QString savePath = data.savePath; QString format = "."; if(replys->error() == QNetworkReply::NoError) { QString imageName = savePath; QDir dir(imageName); if( !dir.exists () ) dir.mkpath (imageName); QByteArray temp=replys->readAll(); imageName+="/"+saveName; format += imageFormatToString (temp); if(format!="."){ imageName.append (format); qDebug()<<"DownloadImage:要保存的图片名为:"<=0x050000) QJSValueList list; list<=0x050000) void DownloadImage::getImage(QJSValue callbackFun, QUrl url, QString savePath, QString saveName) #else void DownloadImage::getImage(QScriptValue callbackFun, QUrl url, QString savePath, QString saveName) #endif { bool isFun=false; #if(QT_VERSION>=0x050000) isFun=callbackFun.isCallable (); #else isFun = callbackFun.isFunction (); #endif if((!isFun)||url.toString ()=="") return; Data data; data.replyType = CallbackFun; data.callbackFun = callbackFun; data.savePath = savePath; data.saveName = saveName; queue_data<get (this, SLOT(downloadFinished(QNetworkReply*)), url);//去获取 } void DownloadImage::getImage(QObject *caller, QByteArray slotName, QUrl url, QString savePath, QString saveName) { if(caller==NULL||slotName==""||url.toString ()=="") return; QRegExp reg("[_A-Za-z][_A-Za-z0-9]*");//提取函数名 if(reg.indexIn (slotName)>=0){ slotName = reg.cap (0).toLatin1 (); Data data; data.replyType = ConnectSlot; data.caller = caller; data.slotName = slotName; data.savePath = savePath; data.saveName = saveName; queue_data<get (this, SLOT(downloadFinished(QNetworkReply*)), url);//去获取 }else{ qDebug()<<"DownloadImage:"< #include #include #include #include #include #if(QT_VERSION>=0x050000) #include #include #else #include #include #endif class MyHttpRequest; class QNetworkReply; class DownloadImage : public QObject { Q_OBJECT Q_ENUMS(ErrorType) public: explicit DownloadImage(QObject *parent = 0); explicit DownloadImage(MyHttpRequest* http, QObject *parent = 0); MyHttpRequest *getHttpRequest(); static QString imageFormatToString(const QByteArray& array); enum ErrorType{ NoError,//无错误 DownloadError,//下载出错 SaveError,//保存出错 NotSupportFormat//不支持的图片格式 }; private: enum ReplyType{//回调的几种方式 CallbackFun, ConnectSlot }; struct Data{ ReplyType replyType; #if(QT_VERSION>=0x050000) QJSValue callbackFun; #else QScriptValue callbackFun; #endif QObject* caller; QByteArray slotName; QString savePath; QString saveName; }; QQueue queue_data; MyHttpRequest* httpRequest; private slots: void downloadFinished(QNetworkReply *replys);//当图片下载完成的时候调用 public slots: #if(QT_VERSION>=0x050000) void getImage(QJSValue callbackFun, QUrl url, QString savePath, QString saveName); #else void getImage(QScriptValue callbackFun, QUrl url, QString savePath, QString saveName); #endif void getImage(QObject *caller, QByteArray slotName, QUrl url, QString savePath, QString saveName); /* * savePath中最后一个目录后面不要加"/",saveName不要带后缀 */ }; #endif // DOWNLOADIMAGE_H ================================================ FILE: src/utility/myhttprequest.cpp ================================================ #include "myhttprequest.h" #include "utility.h" #include "mynetworkaccessmanagerfactory.h" MyHttpRequest::MyHttpRequest(QObject *parent) : QObject(parent) { m_status = Idle; manager = new NetworkAccessManager(this); connect (manager, SIGNAL(finished(QNetworkReply*)), SLOT(finished(QNetworkReply*))); } MyHttpRequest::RequestStatus MyHttpRequest::status() { return m_status; } void MyHttpRequest::setStatus(MyHttpRequest::RequestStatus new_status) { if( new_status!=m_status ) { m_status = new_status; emit statusChanged(); } } const NetworkAccessManager *MyHttpRequest::getNetworkAccessManager() { return manager; } QNetworkRequest* MyHttpRequest::getNetworkRequest() { return &request; } void MyHttpRequest::finished(QNetworkReply *reply) { Data temp = queue_replyData.dequeue (); ReplyType type=temp.replyType; bool isError = !(reply->error () == QNetworkReply::NoError); if(type==CallbackFun){ #if(QT_VERSION>=0x050000) QJSValueList list; list.append (QJSValue(isError)); list.append (QJSValue(isError?reply->errorString ():reply->readAll ())); temp.callbackFun.call (list); #else QScriptValueList list; list.append (QScriptValue(isError)); list.append (QScriptValue(isError?reply->errorString ():reply->readAll ())); temp.callbackFun.call (QScriptValue(), list); #endif }else if(type==ConnectSlot){ QObject* obj = temp.caller; QByteArray slotName = temp.slotName; if(obj!=NULL){ bool ok;//记录调用槽是否成功 int parameterCount = obj->metaObject()->method(obj->metaObject()->indexOfMethod(slotName)).parameterTypes().length(); QRegExp reg("^[^(]+"); reg.indexIn (slotName); slotName = reg.cap (0).toLatin1 (); if(parameterCount==0){//如果形参个数为0个 ok = QMetaObject::invokeMethod(obj, slotName); }else if(parameterCount==1){ ok = QMetaObject::invokeMethod(obj, slotName, Q_ARG(QNetworkReply*, reply)); }else if(parameterCount==2){ ok = QMetaObject::invokeMethod(obj, slotName, Q_ARG(QVariant, isError), Q_ARG(QVariant, (isError?reply->errorString ():reply->readAll ()))); }else{ ok = false; } if(!ok){ qDebug()<<"MyHttpRequest:调用槽"+slotName+"失败"; } } } send();//继续请求 } #if(QT_VERSION>=0x050000) void MyHttpRequest::send(QJSValue callbackFun, QUrl url, QByteArray data, bool highRequest/*=false*/) #else void MyHttpRequest::send(QScriptValue callbackFun, QUrl url, QByteArray data, bool highRequest/*=false*/) #endif { bool isFun=false; #if(QT_VERSION>=0x050000) isFun=callbackFun.isCallable (); #else isFun = callbackFun.isFunction (); #endif if((!isFun)||url.toString ()=="") return; if(highRequest){ new MyHttpRequestPrivate(request, callbackFun, url, data);//进行高优先级的网络请求 }else { requestData request_data; request_data.url = url; request_data.data = data; queue_requestData<=0x050000) void MyHttpRequest::get(QJSValue callbackFun, QUrl url, bool highRequest/*=false*/) { send (callbackFun, url, "", highRequest); } void MyHttpRequest::post(QJSValue callbackFun, QUrl url, QByteArray data, bool highRequest/*=false*/) { send (callbackFun, url, data, highRequest); } #else void MyHttpRequest::get(QScriptValue callbackFun, QUrl url, bool highRequest/*=false*/) { send (callbackFun, url, "", highRequest); } void MyHttpRequest::post(QScriptValue callbackFun, QUrl url, QByteArray data, bool highRequest/*=false*/) { send (callbackFun, url, data, highRequest); } #endif void MyHttpRequest::send(QObject *caller, QByteArray slotName, QUrl url, QByteArray data, bool highRequest/*=false*/) { if(caller==NULL||slotName==""||url.toString ()=="") return; if(highRequest){ new MyHttpRequestPrivate(request, caller, slotName, url, data);//进行高优先级的网络请求 }else{ QRegExp reg("[_A-Za-z][_A-Za-z0-9]*(.+)$");//提取函数名 if(reg.indexIn (slotName)>=0){ requestData request_data; request_data.url = url; request_data.data = data; queue_requestData<abort (); } } void MyHttpRequest::send() { //qDebug()<0){ setStatus (Busy); requestData temp = queue_requestData.dequeue (); request.setUrl (temp.url); QByteArray data = temp.data; if( data=="" ) m_reply = manager->get (request); else m_reply = manager->post (request, data); }else setStatus (Idle);//设置状态为空闲 } QString MyHttpRequest::errorString() { return m_reply->errorString (); } #if(QT_VERSION>=0x050000) MyHttpRequestPrivate::MyHttpRequestPrivate(QNetworkRequest request, QJSValue callbackFun, QUrl url, QByteArray data): #else MyHttpRequestPrivate::MyHttpRequestPrivate(QNetworkRequest request, QScriptValue callbackFun, QUrl url, QByteArray data): #endif MyHttpRequest(0) { this->request = request; send(callbackFun, url, data, false); } MyHttpRequestPrivate::MyHttpRequestPrivate(QNetworkRequest request, QObject *caller, QByteArray slotName, QUrl url, QByteArray data): MyHttpRequest(0) { this->request = request; send(caller, slotName, url, data, false); } void MyHttpRequestPrivate::finished(QNetworkReply *reply) { MyHttpRequest::finished (reply); deleteLater ();//销毁自己 } ================================================ FILE: src/utility/myhttprequest.h ================================================ #ifndef MYHTTPREQUEST_H #define MYHTTPREQUEST_H #include #include #include #if(QT_VERSION>=0x050000) #include #include #else #include #include #endif class NetworkAccessManager; class MyHttpRequest : public QObject { Q_OBJECT Q_PROPERTY( RequestStatus status READ status WRITE setStatus NOTIFY statusChanged) Q_ENUMS(RequestStatus) public: explicit MyHttpRequest(QObject *parent = 0); enum RequestStatus{ Idle,//初始状态 Busy//请求中 }; const NetworkAccessManager *getNetworkAccessManager(); QNetworkRequest *getNetworkRequest(); protected: NetworkAccessManager *manager; QNetworkRequest request; QPointer m_reply; RequestStatus m_status; RequestStatus status(); void setStatus( RequestStatus new_status ); enum ReplyType{//回调的几种方式 CallbackFun, ConnectSlot }; struct Data{ ReplyType replyType; #if(QT_VERSION>=0x050000) QJSValue callbackFun; #else QScriptValue callbackFun; #endif QObject* caller; QByteArray slotName; }; struct requestData{ QUrl url; QByteArray data; }; QQueue queue_replyData; QQueue queue_requestData; void send(QObject *caller, QByteArray slotName, QUrl url, QByteArray data="", bool highRequest=false );//highRequest记录是否为高级的网络请求 #if(QT_VERSION>=0x050000) void send( QJSValue callbackFun, QUrl url, QByteArray data="", bool highRequest=false ); #else void send( QScriptValue callbackFun, QUrl url, QByteArray data="", bool highRequest=false ); #endif protected slots: virtual void finished( QNetworkReply *reply ); void send(); signals: void statusChanged(); public slots: void get(QObject *caller, QByteArray slotName, QUrl url, bool highRequest=false ); void post(QObject *caller, QByteArray slotName, QUrl url, QByteArray data="", bool highRequest=false ); #if(QT_VERSION>=0x050000) void get(QJSValue callbackFun, QUrl url, bool highRequest=false ); void post(QJSValue callbackFun, QUrl url, QByteArray data="", bool highRequest=false ); #else void get(QScriptValue callbackFun, QUrl url, bool highRequest=false ); void post(QScriptValue callbackFun, QUrl url, QByteArray data="", bool highRequest=false ); #endif void abort();//取消当前网络请求 QString errorString(); }; class MyHttpRequestPrivate : public MyHttpRequest { Q_OBJECT private: #if(QT_VERSION>=0x050000) explicit MyHttpRequestPrivate(QNetworkRequest request, QJSValue callbackFun, QUrl url, QByteArray data); #else explicit MyHttpRequestPrivate(QNetworkRequest request, QScriptValue callbackFun, QUrl url, QByteArray data); #endif explicit MyHttpRequestPrivate(QNetworkRequest request, QObject *caller, QByteArray slotName, QUrl url, QByteArray data); friend class MyHttpRequest; private slots: void finished( QNetworkReply *reply ); }; #endif // MYHTTPREQUEST_H ================================================ FILE: src/utility/mynetworkaccessmanagerfactory.cpp ================================================ #include "mynetworkaccessmanagerfactory.h" #include #include MyNetworkAccessManagerFactory::MyNetworkAccessManagerFactory(QObject *parent) : QObject(parent) { m_networkManager = new NetworkAccessManager(this); connect(m_networkManager,SIGNAL(sslErrors(QNetworkReply*,QList)),this,SLOT(onIgnoreSSLErrors(QNetworkReply*,QList))); } QNetworkAccessManager* MyNetworkAccessManagerFactory::create(QObject *parent) { QMutexLocker lock(&mutex); Q_UNUSED(lock); QNetworkAccessManager* manager = new NetworkAccessManager(parent); return manager; } void MyNetworkAccessManagerFactory::onIgnoreSSLErrors(QNetworkReply *reply, QList error) { qDebug()<ignoreSslErrors(error); } NetworkAccessManager::NetworkAccessManager(QObject *parent) : QNetworkAccessManager(parent) { QNetworkCookieJar* cookieJar = NetworkCookieJar::GetInstance(); setCookieJar(cookieJar); cookieJar->setParent(0); } QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) { QNetworkRequest req(request); QSslConfiguration config; config.setPeerVerifyMode(QSslSocket::VerifyNone); config.setProtocol(QSsl::TlsV1_0); req.setSslConfiguration(config); // set user-agent if (op == PostOperation){ req.setRawHeader("User-Agent", "IDP"); } else { req.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER"); } QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData); return reply; } NetworkCookieJar::NetworkCookieJar(QObject *parent) : QNetworkCookieJar(parent) { //keepAliveCookie = QNetworkCookie("ka", "open"); load (); } NetworkCookieJar::~NetworkCookieJar() { } void NetworkCookieJar::load() { QMutexLocker lock(&mutex); Q_UNUSED(lock); } NetworkCookieJar* NetworkCookieJar::GetInstance() { static NetworkCookieJar cookieJar; return &cookieJar; } void NetworkCookieJar::clearCookies() { QList emptyList; setAllCookies(emptyList); } QList NetworkCookieJar::cookiesForUrl(const QUrl &url) const { QMutexLocker lock(&mutex); Q_UNUSED(lock); QList cookies = QNetworkCookieJar::cookiesForUrl(url); return cookies; } bool NetworkCookieJar::setCookiesFromUrl(const QList &cookieList, const QUrl &url) { QMutexLocker lock(&mutex); Q_UNUSED(lock); return QNetworkCookieJar::setCookiesFromUrl(cookieList, url); } QList NetworkCookieJar::cookies() const { return allCookies (); } ================================================ FILE: src/utility/mynetworkaccessmanagerfactory.h ================================================ #ifndef MYNETWORKACCESSMANAGERFACTORY_H #define MYNETWORKACCESSMANAGERFACTORY_H #include #include #include class MyNetworkAccessManagerFactory : public QObject,public QQmlNetworkAccessManagerFactory { Q_OBJECT public: explicit MyNetworkAccessManagerFactory( QObject *parent = 0 ); virtual QNetworkAccessManager* create(QObject *parent); public slots: void onIgnoreSSLErrors(QNetworkReply* reply,QList error); private: QMutex mutex; QNetworkAccessManager* m_networkManager; }; class NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT public: explicit NetworkAccessManager(QObject *parent = 0); protected: QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData); }; class NetworkCookieJar : public QNetworkCookieJar { public: static NetworkCookieJar* GetInstance(); void clearCookies(); virtual QList cookiesForUrl(const QUrl &url) const; virtual bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); QList cookies() const; private: explicit NetworkCookieJar(QObject *parent = 0); ~NetworkCookieJar(); void load(); mutable QMutex mutex; //QNetworkCookie keepAliveCookie; }; #endif // MYNETWORKACCESSMANAGERFACTORY_H ================================================ FILE: src/utility/texteditplaygif.cpp ================================================ #include "texteditplaygif.h" #include #include #include #include #include #include #include #include #include TextEditPlayGif::TextEditPlayGif(QObject *parent) : QObject(parent) { m_enabled=true; } QQuickTextEdit *TextEditPlayGif::target() const { return m_target.data (); } QUrl TextEditPlayGif::cachePath() const { return m_cachePath; } bool TextEditPlayGif::enabled() const { return m_enabled; } void TextEditPlayGif::setTarget(QQuickTextEdit *arg) { if (m_target == arg) return; disconnect (m_target, SIGNAL(textChanged()), this, SLOT(onTextChanged())); m_target = arg; connect (m_target, SIGNAL(textChanged()), SLOT(onTextChanged())); emit targetChanged(arg); } void TextEditPlayGif::setCachePath(QUrl arg) { if (m_cachePath == arg) return; m_cachePath = arg; emit cachePathChanged(arg); } void TextEditPlayGif::removeErrorUrl(const QString &url) { list_errorUrl.removeOne (url); } void TextEditPlayGif::setEnabled(bool arg) { if (m_enabled == arg) return; m_enabled = arg; if(arg){ startAllMovie ();//启动所有动画 connect (m_target, SIGNAL(textChanged()), this, SLOT(onTextChanged()));//启动分析 }else{ disconnect (m_target, SIGNAL(textChanged()), this, SLOT(onTextChanged()));//停止分析 stopAllMovie ();//停止所有动画 } emit enabledChanged(arg); } void TextEditPlayGif::clearMovie() { foreach (MovieData data, list_movie) { data.movie->deleteLater (); } list_movie.clear (); } void TextEditPlayGif::addMovie(QMovie *movie, const QString &url, const QString &gif_name, QSize size) { MovieData data; data.movie=movie; data.url=url; data.gifName=gif_name; data.size=size; list_movie<gifName==name&&data->size==size) return data; } return NULL; } TextEditPlayGif::MovieData *TextEditPlayGif::getDataByMovie(const QMovie *movie) { for(int i=0;imovie==movie) return data; } return NULL; } void TextEditPlayGif::setUrlByMovie(QMovie *movie, const QString &url) { for(int i=0;imovie==movie) data->url=url; } } bool TextEditPlayGif::isErrorUrl(const QString url) { foreach (QString str, list_errorUrl) { if(str==url){ return true; } } return false; } void TextEditPlayGif::addErrorUrl(const QString url) { if(!isErrorUrl (url)) list_errorUrl<movie->state () != QMovie::Running) data->movie->start (); } } void TextEditPlayGif::stopAllMovie() { for(int i=0;imovie->state () == QMovie::Running) data->movie->stop (); } } void TextEditPlayGif::setTextEditContent(const QString &data) { int pos = m_target->cursorPosition (); int select_start = m_target->selectionStart (); int select_end = m_target->selectionEnd (); m_target->setText (data); m_target->setCursorPosition (pos); m_target->select (select_start, select_end); } void TextEditPlayGif::onTextChanged() { QString content = m_target->text (); if((content==old_content)) return; old_content = content; //qDebug()<"); reg.setMinimal (true); reg.indexIn (content); foreach (QString img, reg.capturedTexts ()) { if(img=="") break; //qDebug()<=0){//如果设置了width QString width_str = reg_width.cap (0); width_str.replace ("width=\"",""); width_str = width_str.left (width_str.size ()-1); width = width_str.toInt (); } QRegExp reg_height("height=\"\\d+\""); reg_height.setMinimal (true); int height=-1; if(reg_height.indexIn (img)>=0){//如果设置了width QString height_str = reg_height.cap (0); height_str.replace ("height=\"",""); height_str = height_str.left (height_str.size ()-1); height = height_str.toInt (); } if(reg.indexIn (img)>=0){//如果src存在 QString src = reg.cap (0); if(src=="") break; img = src; src = src.mid (5, src.size ()-6); QString gifName=""; foreach (QChar ch, src) { if(ch.isLetter ()||ch.isNumber ()) gifName.append (ch); } QUrl url(src); if(url.isLocalFile ()) src = url.toLocalFile (); else if(src.left (3)=="qrc") src=src.mid (3); if(isErrorUrl (src)) break; QImage image; if(image.load (src)){ if(width==-1) width = image.width (); if(height==-1) height = image.height (); }else{ addErrorUrl (src); break; } MovieData* data = getDataByGifNameAndSize (gifName, QSize(width, height)); if(data!=NULL){ QString html = m_target->text ().replace (img, data->url); setTextEditContent (html); }else{ QMovie *movie=new QMovie(src, "", this); movie->setCacheMode(QMovie::CacheAll); addMovie (movie, img, gifName, QSize(width, height)); connect (movie, SIGNAL(frameChanged(int)), SLOT(onMovie(int))); connect (movie, SIGNAL(finished()), SLOT(onMovieFinished())); movie->start (); } } } } void saveImage(const QString& name, QImage image) { image.save (name); } void TextEditPlayGif::onMovie(int frame) { QMovie* movie = qobject_cast(sender()); MovieData* movie_data = getDataByMovie (movie); if(movie&&movie_data&&movie_data->url!=""){ QString name = QDir::homePath ()+"/webqq/cacheImage/"+movie_data->gifName+"/"; QDir dir; if(dir.mkpath (name)){//创建路径 QImage image = movie->currentImage (); name.append (QString::number (frame)+ QString::number (movie_data->size.width ())+ QString::number (movie_data->size.height ())+ ".png"); if(QFile::exists (name)){//如果存在 name = "src=\"file:///"+name+"\""; //qDebug()<text ().replace (movie_data->url, name); //qDebug()<text ()<size, Qt::KeepAspectRatio, Qt::SmoothTransformation); QtConcurrent::run(saveImage, name, image); } }else{ qDebug()<<"TextEditPlayGif:路径"<(sender()); if(movie!=NULL&&movie->currentFrameNumber ()!=-1) movie->start (); } ================================================ FILE: src/utility/texteditplaygif.h ================================================ #ifndef TEXTEDITPLAYGIF_H #define TEXTEDITPLAYGIF_H #include #include #include #include #include #include class QQuickTextEdit; class TextEditPlayGif : public QObject { Q_OBJECT Q_PROPERTY(QQuickTextEdit* target READ target WRITE setTarget NOTIFY targetChanged) Q_PROPERTY(QUrl cachePath READ cachePath WRITE setCachePath NOTIFY cachePathChanged)//此属性暂时未使用 Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) public: TextEditPlayGif(QObject *parent=0); QQuickTextEdit* target() const; QUrl cachePath() const; bool enabled() const; private: struct MovieData{ QMovie* movie; QString url; QString gifName; QSize size; }; QPointer m_target; QList list_movie; QStringList list_errorUrl;//记录那些解析出错的gif的路径 QUrl m_cachePath; QString old_content; bool m_enabled; void clearMovie(); void addMovie(QMovie *movie, const QString &url, const QString& gif_name, QSize size); QString getUrlByMovie(QMovie* movie); QString getGifNameByMovie(QMovie* movie); MovieData* getDataByGifNameAndSize(const QString& name, QSize size); MovieData* getDataByMovie(const QMovie* movie); void setUrlByMovie(QMovie* movie, const QString &url); bool isErrorUrl(const QString url); void addErrorUrl(const QString url); void startAllMovie(); void stopAllMovie(); void setTextEditContent(const QString& data); public slots: void onTextChanged(); private slots: void onMovie(int frame); void onMovieFinished(); signals: void targetChanged(QQuickTextEdit* arg); void cachePathChanged(QUrl arg); void error(const QString& errorString); void enabledChanged(bool arg); public slots: void setTarget(QQuickTextEdit* arg); void setCachePath(QUrl arg); void removeErrorUrl(const QString& url); void setEnabled(bool arg); }; #endif // TEXTEDITPLAYGIF_H ================================================ FILE: src/utility/utility.cpp ================================================ #include "utility.h" #include #include #include #include "mynetworkaccessmanagerfactory.h" #include "downloadimage.h" #include "myhttprequest.h" Utility *Utility::createUtilityClass() { static Utility my_utility; return &my_utility; } Utility::Utility(QObject *parent) : QObject(parent) { qmlRegisterType("utility", 1, 0, "Utility"); http_request = new MyHttpRequest(this); download_image = new DownloadImage(this); connect (&networkConfigurationManager, &QNetworkConfigurationManager::onlineStateChanged, this, &Utility::networkOnlineStateChanged); } Utility::~Utility() { } char Utility::numToStr(int num) { QByteArray str="QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; return str[num%str.size ()]; } QByteArray Utility::strZoarium(const QByteArray &str) { QByteArray result; for(int i=0;i='0'){//如果是数字 result.append (ch); }else{//如果不是数字 if(ch_ascii>=0) result.append (numToStr (ch_ascii)).append (QByteArray::number (ch_ascii)).append (numToStr (ch_ascii*2)); } } return result; } QByteArray Utility::unStrZoarium(const QByteArray &str) { QByteArray result=""; for(int i=0;i='0'){//如果是数字 result.append (ch); i++; }else{//如果是其他 QRegExp regexp("[^0-9]"); int pos = QString(str).indexOf (regexp, i+1); if(pos>0){ int num = str.mid (i+1, pos-i-1).toInt (); if(num>=0) result.append ((char)num); i=pos+1; }else{ //qDebug()<<"数据有错"; i++; } } } return result; } QByteArray Utility::fillContent(const QByteArray &str, int length) { if(length>0){ QByteArray fill_size = QByteArray::number (length); if(fill_size.size ()==1) fill_size="00"+fill_size; else if(fill_size.size ()==2) fill_size="0"+fill_size; for(int i=0;i temp = NetworkCookieJar::GetInstance ()->cookies (); foreach( QNetworkCookie cookie, temp ) { if( cookie.name () == cookieName) return cookie.value (); } return ""; } QQmlApplicationEngine *Utility::qmlEngine() { return engine; } MyHttpRequest *Utility::getHttpRequest() { return http_request; } DownloadImage *Utility::getDownloadImage() { return download_image; } bool Utility::networkIsOnline() const { return networkConfigurationManager.isOnline (); } void Utility::setQmlEngine(QQmlApplicationEngine *new_engine) { engine = new_engine; if(engine){ engine->rootContext ()->setContextProperty ("utility", this); } } void Utility::initUtility(QSettings *settings, QQmlApplicationEngine *qmlEngine) { setQSettings (settings); setQmlEngine (qmlEngine); } QPoint Utility::mouseDesktopPos() { return QCursor::pos (); } void Utility::setQSettings(QSettings *settings) { if(settings) mysettings = settings; } void Utility::setValue(const QString &key, const QVariant &value) { if( mysettings ) mysettings->setValue (key, value); else qDebug()<<"mysetting=NULL"; } QVariant Utility::value(const QString &key, const QVariant &defaultValue) const { if( mysettings ) return mysettings->value (key, defaultValue); else{ qDebug()<<"mysetting=NULL"; return QVariant(""); } } void Utility::removeValue(const QString &key) { if( mysettings ) mysettings->remove (key); else qDebug()<<"mysetting=NULL"; } void Utility::loadQml(QUrl url) { if(engine) engine->load (url); } #if(QT_VERSION>=0x050000) void Utility::downloadImage(QJSValue callbackFun, QUrl url, QString savePath, QString saveName) { download_image->getImage (callbackFun, url, savePath, saveName); } void Utility::httpGet(QJSValue callbackFun, QUrl url, bool highRequest) { http_request->get (callbackFun, url, highRequest); } void Utility::httpPost(QJSValue callbackFun, QUrl url, QByteArray data, bool highRequest) { http_request->post (callbackFun, url, data, highRequest); } #else void Utility::downloadImage(QScriptValue callbackFun, QUrl url, QString savePath, QString saveName) { download_image->getImage (callbackFun, url, savePath, saveName); } void Utility::httpGet(QScriptValue callbackFun, QUrl url, bool highRequest) { http_request->get (callbackFun, url, highRequest); } void Utility::httpPost(QScriptValue callbackFun, QUrl url, QByteArray data, bool highRequest) { http_request->post (callbackFun, url, data, highRequest); } #endif void Utility::downloadImage(QObject *caller, QByteArray slotName, QUrl url, QString savePath, QString saveName) { download_image->getImage (caller, slotName, url, savePath, saveName); } void Utility::httpGet(QObject *caller, QByteArray slotName, QUrl url, bool highRequest) { http_request->get (caller, slotName, url, highRequest); } void Utility::httpPost(QObject *caller, QByteArray slotName, QUrl url, QByteArray data, bool highRequest) { http_request->post (caller, slotName, url, data, highRequest); } void Utility::socketAbort() { http_request->abort (); } void Utility::setApplicationProxy(int type, QString location, QString port, QString username, QString password) { QNetworkProxy proxy; proxy.setType((QNetworkProxy::ProxyType)type); proxy.setHostName(location); proxy.setPort(port.toUShort ()); proxy.setUser (username); proxy.setPassword (password); QNetworkProxy::setApplicationProxy(proxy); } QString Utility::stringEncrypt(const QString &content, QString key) { if(content==""||key=="") return content; if(key.size ()>256) key = key.mid (0,256);//密匙最长256位 QByteArray data = strZoarium (content.toUtf8 ().toBase64 ()); int data_size = data.size (); QByteArray mykey = strZoarium (key.toLatin1 ().toHex ()); int key_size = mykey.size (); //qDebug()<=0) temp.append (QString(ch)); } //qDebug()<256) key = key.mid (0,256);//密匙最长256位 QByteArray data = content.toLatin1 (); QByteArray mykey = strZoarium (key.toLatin1 ().toHex ()); int key_size = mykey.size (); QByteArray temp; for(int i=0;i=0){ temp.append ((char)ch); } } temp = unStrZoarium (temp); int fill_size = temp.mid (0, 3).toInt (); temp = temp.mid (fill_size+3, temp.size ()-fill_size-3);//除去填充的字符 return QString::fromUtf8 (QByteArray::fromBase64 (temp)); } bool myRemovePath(QString dirPath, bool deleteHidden, bool deleteSelf) { qDebug()<<"removePath的进程"< #include #include #include #include #include #include #include #include class UtilityPrivate : public QObject { Q_OBJECT Q_ENUMS(ProxyType) public: enum ProxyType { DefaultProxy, Socks5Proxy, NoProxy, HttpProxy, HttpCachingProxy, FtpCachingProxy }; }; class MyHttpRequest; class DownloadImage; class Utility : public QObject { Q_OBJECT public: static Utility *createUtilityClass(); private: explicit Utility(QObject *parent = 0); ~Utility(); QPointer engine; QPointer mysettings; MyHttpRequest *http_request; DownloadImage *download_image; QNetworkConfigurationManager networkConfigurationManager; char numToStr(int num);//将数字按一定的规律换算成字母 QByteArray strZoarium(const QByteArray &str);//按一定的规律加密字符串(只包含数字和字母的字符串) QByteArray unStrZoarium(const QByteArray &str);//按一定的规律解密字符串(只包含数字和字母的字符串) QByteArray fillContent(const QByteArray &str, int length);//将字符串填充到一定的长度 public: Q_INVOKABLE void consoleLog(QString str);//输出调试信息 Q_INVOKABLE QString getCookie( QString cookieName ); QQmlApplicationEngine *qmlEngine(); MyHttpRequest *getHttpRequest(); DownloadImage *getDownloadImage(); bool networkIsOnline() const; signals: void mouseDesktopPosChanged(QPoint arg); void networkOnlineStateChanged(bool isOnline); public slots: void initUtility(QSettings *settings=0, QQmlApplicationEngine *qmlEngine=0); void setQmlEngine( QQmlApplicationEngine *new_engine ); QPoint mouseDesktopPos(); void setQSettings(QSettings *settings); void setValue( const QString & key, const QVariant & value); QVariant value(const QString & key, const QVariant & defaultValue = QVariant()) const; void removeValue( const QString & key ); void loadQml( QUrl url ); #if(QT_VERSION>=0x050000) void downloadImage( QJSValue callbackFun, QUrl url, QString savePath, QString saveName ); void httpGet(QJSValue callbackFun, QUrl url, bool highRequest=false ); void httpPost(QJSValue callbackFun, QUrl url, QByteArray data="", bool highRequest=false ); #else void downloadImage( QScriptValue callbackFun, QUrl url, QString savePath, QString saveName ); void httpGet(QScriptValue callbackFun, QUrl url, bool highRequest=false ); void httpPost(QScriptValue callbackFun, QUrl url, QByteArray data="", bool highRequest=false ); #endif void downloadImage( QObject *caller, QByteArray slotName, QUrl url, QString savePath, QString saveName ); void httpGet(QObject *caller, QByteArray slotName, QUrl url, bool highRequest=false); void httpPost(QObject *caller, QByteArray slotName, QUrl url, QByteArray data, bool highRequest=false); void socketAbort(); void setApplicationProxy( int type, QString location, QString port, QString username, QString password ); QString stringEncrypt(const QString &content, QString key);//加密任意字符串,中文请使用utf-8编码 QString stringUncrypt(const QString &content_hex, QString key);//解密加密后的字符串 void removePath(QString dirPath ,bool deleteHidden = true, bool deleteSelf = true ); }; #endif // UTILITY_H ================================================ FILE: style/menuStyle.css ================================================ QMenu::item:selected { background: #F07000; color:#E6FFFF; height:25px; } QMenu{ background-image: url(":/images/menu_background.png"); } QMenu::item{ padding-left:35px; padding-right:30px; height:22px; } QMenu::icon{ padding-left:8px; } QMenu::separator { height: 1px; margin:5px 0px 5px 22px; background: #B2C0CD; } ================================================ FILE: style/messageBoxStyle.css ================================================ QMessageBox{ background-image: url(":/images/menu_background.png"); } QPushButton { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #ddd,stop: 1 #ccc); border-radius: 10px; padding: 6px; min-width: 5em; } QPushButton::hover{ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #ccc,stop: 1 #eee); } QPushButton::pressed{ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #ccc,stop: 1 #bbb); } ================================================ FILE: style.qrc ================================================ style/menuStyle.css style/messageBoxStyle.css