非常感谢【Qml+Qt】聊天机器人|http://www.qtcn.org/bbs/read-htm-tid-62920.html提供的思路
自己增加了需要的一些功能:实现了listview的delegate文字可以鼠标选择并复制、接受键盘ctrl+c复制,item的删除,鼠标上滑到最顶端时加载更多item,摒弃了互动聊天方式,只用于推送,若需要建议参考以上链接
直接贴代码:
import QtQuick 2.6
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.0
Rectangle {
id: root
color: "#f5f5f5"
function add_message(timetext,issame,text,who,istip,type,id)
{
if(text.length <= 0)
{
return;
}
listView.model.insert(0, {"content": text, "send": who,"msgTime":timetext,"same":issame,"tip":istip,
"send_type": type,"id": id});
//verScrollBar.increase()
//滚动条移动到最下面
verScrollBar.setPosition(1.0)
}
function add_more_message(timetext,issame,text,who,istip,type,id)
{
if(text.length <= 0)
{
return;
}
var count=listView.model.count
listView.model.insert(count, {"content": text, "send": who,"msgTime":timetext,"same":issame,"tip":istip,
"send_type": type,"id": id});
//verScrollBar.increase()
//verScrollBar.setPosition(1.0)
}
function clearListview()
{
listView.model.clear()
}
function re_edit_item(index)
{
var count=listView.model.count
if(count>0){
listView.model.remove(index,1)
}
}
property bool showtip: false
function show_tip()
{
showtip=true
}
function hide_tip()
{
showtip=false
}
ColumnLayout {
id:page
anchors.fill: parent
//接受键盘事件focus必须为true
focus: true
//由于messageText接受不到键盘事件,定义一个信号
//page接收到ctrl+c使通知messagetext copy
signal ctrl_c()
Keys.onPressed: {
if((event.modifiers===Qt.ControlModifier) && (event.key===Qt.Key_C)){
//发射信号
page.ctrl_c()
}
event.accepted=true
}
Rectangle {
id:tiprect
width: tiptext.implicitWidth+10
height: 30
color: "#f5f5f5"
anchors.horizontalCenter: parent.horizontalCenter
visible:showtip
Label {
id: tiptext
text: "查看更多消息请前往消息记录"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "#1f61ff"
font.family: "微软雅黑"
font.pointSize: 9
anchors.fill: parent
anchors.margins:0
}
}
ListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins:14
Layout.rightMargin: 0
displayMarginBeginning: 40
displayMarginEnd: 40
verticalLayoutDirection: ListView.BottomToTop
spacing:14
model:ListModel{}
delegate: Column {
anchors.left: parent.left
spacing: 14
width: parent.width
property bool sendmessage: send
property bool sameTime: same
//时间
Rectangle {
width: timetext.implicitWidth+10
height: timetext.implicitHeight+2
color: tip ? "lightgrey" : "#d1cece"
anchors.horizontalCenter: parent.horizontalCenter
visible: !same
Label {
id: timetext
text: msgTime
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: tip ? "red" : "white"
font.family: "微软雅黑"
font.pointSize: 9
anchors.fill: parent
anchors.margins:0
}
}
//消息内容
Row {
id: messageRow
spacing: 6
anchors.left: parent.left
visible: !tip
//头像
Image {
id: avatarLeft
height: 36
width: height
source: "qrc:/image/touxiang.jpg"
}
//消息框
Rectangle {
width: Math.min(messageText.implicitWidth +14, listView.width -98)
height: messageText.implicitHeight + 16
color: send ? "#9dea6a" : "#eeeeee"
radius: 3
TextEdit {
id: messageText
text: content
font.family: "华文细黑"
//color: "#9dea6a"
font.pointSize: 11
anchors.fill: parent
anchors.topMargin: 9
anchors.bottomMargin: 7
anchors.rightMargin: 7
anchors.leftMargin: 7
wrapMode: TextEdit.Wrap
enabled: true
readOnly: true
selectByKeyboard: true
selectByMouse: true
selectedTextColor: "white"
persistentSelection:true
selectionColor: "#3396FF"
//设置选择文本的时候使得不滑动listview
Drag.active: text_area.drag.active
}
//鼠标区域,支持的功能是选择复制和撤销
MouseArea{
id:text_area
width: parent.width
height: parent.height
acceptedButtons: Qt.LeftButton|Qt.RightButton
//把拖动的对象设为起父亲,由于一样打,所以使得拖动不会影响界面显示
drag.target: messageText
hoverEnabled: true
property int _startpos: 0
property int _endpos: 0
property bool canselect: false
onClicked: {
//三个=为全等于,js语法
if(mouse.button===Qt.RightButton){
//右键按下弹出右键菜单的的时候选择会清空,所以只好重新选一遍
if(_startpos<_endpos){
messageText.select(_startpos-1,_endpos)
} else {
if(_startpos>_endpos){
messageText.select(_endpos-1,_startpos)
} else {
messageText.select(_startpos,_endpos)
}
}
//使用mainwindow中的函数
mainwindow.listview_item_clicked(index,id,send_type,messageText.selectedText,messageText.text)
}
}
onEntered: {
//连接信号和槽,只有当鼠标进入区域时才响应信号
page.ctrl_c.connect(message_cpoy)
}
//复制
function message_cpoy(){
messageText.copy()
}
//鼠标离开,清空选择,解绑信号
onExited: {
messageText.deselect()
_startpos=0
_endpos=0
page.ctrl_c.disconnect(message_cpoy)
}
//跟随鼠标进行选择
onMouseXChanged: {
if(canselect){
_endpos=messageText.positionAt(mouseX,mouseY)
//使开始位置减1,符合视觉效果
if(_startpos<_endpos){
messageText.select(_startpos-1,_endpos)
} else {
if(_startpos>_endpos){
messageText.select(_endpos-1,_startpos)
} else {
messageText.select(_startpos,_endpos)
}
}
}
}
//只有鼠标左键按下时才可以选择
onPressed: {
if(mouse.button===Qt.LeftButton){
canselect=true
_startpos=messageText.positionAt(mouseX,mouseY)
}
}
//鼠标左键弹起后不可选择
onReleased: {
if(mouse.button===Qt.LeftButton){
//messageText.copy()
canselect=false
}
}
}
}
}
}
ScrollBar.vertical: ScrollBar {
id: verScrollBar
}
//鼠标上滑加载更多数据
onContentYChanged: {
//console.log("contenty:"+contentY)
//console.log("originy"+originY)
var x=contentY-originY
if(x==0){
mainwindow.load_more_for_chatwidget();
}
}
}
/*接收cpp发来信号显示的方法
Connections{
target: mywindows
onShowmymsg:add_message(arg_1,arg_2,arg_3,arg_4,arg_5)
}*/
}
}
//cpp中的函数:(在cpp中设置上下文属性后可用)
//quickwidget载入qml
QUrl source("qrc:/myqml/1.qml");
//设置上下文属性c++与qml,使qml能使用c++的Q_INVOKABLE声明的public函数或者public槽函数
ui->quickWidget->rootContext()->setContextProperty("mainwindow",this);
//下面此方法目前有问题不做
//qmlRegisterType("io.qt.mainwindow", 1, 0, "Mwindow");
ui->quickWidget->setSource(source);
//添加一项
QQuickItem *item=ui->quickWidget->rootObject();
QVariant returnVar;
QString send_time1=QDateTime::fromString(send_time,"yyyyMMddhhmmss").toString("hh:mm");
QVariant arg1 = send_time1;
QVariant arg2;
QVariant arg3 =send_text;
QVariant arg4 =true;
QVariant arg5 =false;
QVariant arg6="no_reedit";
QVariant arg7=-1;
if(send_time1==lastSend_time)
{
arg2= true;
}
else
{
arg2=false;
lastSend_time=send_time1;
}
QMetaObject::invokeMethod(item, "add_message",
Q_RETURN_ARG(QVariant, returnVar),Q_ARG(QVariant, arg1),Q_ARG(QVariant, arg2),
Q_ARG(QVariant, arg3),Q_ARG(QVariant, arg4),Q_ARG(QVariant, arg5),
Q_ARG(QVariant, arg6),Q_ARG(QVariant, arg7));
//cpp中的函数
void mainwindow::listview_item_clicked(const int index, const int id, const QString sendtype,
const QString selectedtext,const QString itemtext)
{
qml_id=id;
qml_index=index;
qml_sendtype=sendtype;
qml_selected_text=selectedtext;
qml_item_text=itemtext;
//qDebug()<
//qDebug()<
ui->quickWidget->customContextMenuRequested(QPoint(0,0));
}
对qml的值赋给私有变量后就可以实现复制以及定向撤销功能了
//撤销一项
QQuickItem *item=ui->quickWidget->rootObject();
QVariant returnVar;
QVariant arg1 = qml_item_index;
QMetaObject::invokeMethod(item, "re_edit_item",
Q_RETURN_ARG(QVariant, returnVar),Q_ARG(QVariant, arg1));
label_who *label_w1 = (label_who *)(ui->label_2->userData(Qt::UserRole));
//QString send_type=label_w1->send_type;
QString send_who=label_w1->send_who;
//复制选中的文字;qml文件中实现了选中以后按ctrl+c可以直接复制
void mainwindow::copy_content()
{
QClipboard *clipboard=QApplication::clipboard();
clipboard->setText(qml_selected_text,QClipboard::Clipboard);
}
void mainwindow::load_more_for_chatwidget()
{
if(last_query_number<5){ //只可加载5次
QQuickItem *item=ui->quickWidget->rootObject();
QString current_to_usr_name=ui->listWidget->currentItem()->data(Qt::UserRole+1).toString();
QString item_type=ui->listWidget->currentItem()->data(Qt::UserRole).toString();
QString item_mail=ui->listWidget->currentItem()->data(Qt::UserRole+2).toString();
//再取十条数据
QSqlQuery sql_query;
QString select_his_content;
if(item_type=="user")
{
int last_number=last_query_number*10;
//limit 必须放在最后...
select_his_content="select send_time,send_text from his_tousr "
"where tousrmail=:mail "
"order by send_time desc "
"limit 10 offset :last_number";
sql_query.prepare(select_his_content);
sql_query.bindValue(":mail",item_mail);
sql_query.bindValue(":last_number",last_number);
}
else
{
if(item_type=="group")
{
int last_number=last_query_number*10;
select_his_content="select send_time,send_text from his_group "
"where usrgroupname=:groupname "
"order by send_time desc "
"limit 10 offset :last_number";
sql_query.prepare(select_his_content);
sql_query.bindValue(":groupname",current_to_usr_name);
sql_query.bindValue(":last_number",last_number);
}
}
if(sql_query.exec())
{
while(sql_query.next())
{
QString send_time=sql_query.value(0).toString();
QString his_content=sql_query.value(1).toString();
//时间显示控件的格式转换,今天昨天及月日
QString yestoday_date=QDateTime::currentDateTime().addDays(-1).toString("yyyyMMdd");
QString today_date=QDateTime::currentDateTime().toString("yyyyMMdd");
if(send_time.left(8)==today_date)
{
send_time=(send_time.mid(8,2)+":"+send_time.mid(10,2));
}
else {
if(send_time.left(8)==yestoday_date)
{
send_time="昨天 "+send_time.mid(8,2)+":"+send_time.mid(10,2);
}
else
{
if(send_time.left(4)==today_date.left(4))
{
QDateTime send_time1=QDateTime::fromString(send_time,"yyyyMMddhhmmss");
send_time=send_time1.toString("M月dd日 hh:mm");
}
else
{
QDateTime send_time1=QDateTime::fromString(send_time,"yyyyMMddhhmmss");
send_time=send_time1.toString("yyyy年M月dd日 hh:mm");
}
}
}
QVariant returnVar;
QVariant arg1 = send_time;
QVariant arg2;
QVariant arg3 =his_content;
QVariant arg4 =true;
QVariant arg5 =false;
QVariant arg6="no_reedit";
QVariant arg7=-1;
if(send_time==lastSend_time)
{
arg2= true;
}
else
{
arg2=false;
lastSend_time=send_time;
}
QMetaObject::invokeMethod(item, "add_more_message",
Q_RETURN_ARG(QVariant, returnVar),Q_ARG(QVariant, arg1),Q_ARG(QVariant, arg2),
Q_ARG(QVariant, arg3),Q_ARG(QVariant, arg4),Q_ARG(QVariant, arg5),
Q_ARG(QVariant, arg6),Q_ARG(QVariant, arg7));
ui->quickWidget->repaint();
}
last_query_number++;
}
else{
qDebug()<<sql_query.lastError();
}
} else {
qDebug()<<"nidaye";
QQuickItem *item=ui->quickWidget->rootObject();
QMetaObject::invokeMethod(item,"show_tip");
}
}
//记录下吧,