1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 Riverbank Computing Limited. 7## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). 8## All rights reserved. 9## 10## This file is part of the examples of PyQt. 11## 12## $QT_BEGIN_LICENSE:BSD$ 13## You may use this file under the terms of the BSD license as follows: 14## 15## "Redistribution and use in source and binary forms, with or without 16## modification, are permitted provided that the following conditions are 17## met: 18## * Redistributions of source code must retain the above copyright 19## notice, this list of conditions and the following disclaimer. 20## * Redistributions in binary form must reproduce the above copyright 21## notice, this list of conditions and the following disclaimer in 22## the documentation and/or other materials provided with the 23## distribution. 24## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 25## the names of its contributors may be used to endorse or promote 26## products derived from this software without specific prior written 27## permission. 28## 29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 40## $QT_END_LICENSE$ 41## 42############################################################################# 43 44 45from PyQt5.QtCore import pyqtSignal, pyqtSlot, Q_CLASSINFO 46from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow, QMessageBox 47from PyQt5.QtDBus import (QDBusAbstractAdaptor, QDBusAbstractInterface, 48 QDBusConnection, QDBusMessage) 49 50from ui_chatmainwindow import Ui_ChatMainWindow 51from ui_chatsetnickname import Ui_NicknameDialog 52 53 54class ChatAdaptor(QDBusAbstractAdaptor): 55 56 Q_CLASSINFO("D-Bus Interface", 'org.example.chat') 57 58 Q_CLASSINFO("D-Bus Introspection", '' 59 ' <interface name="org.example.chat">\n' 60 ' <signal name="message">\n' 61 ' <arg direction="out" type="s" name="nickname"/>\n' 62 ' <arg direction="out" type="s" name="text"/>\n' 63 ' </signal>\n' 64 ' <signal name="action">\n' 65 ' <arg direction="out" type="s" name="nickname"/>\n' 66 ' <arg direction="out" type="s" name="text"/>\n' 67 ' </signal>\n' 68 ' </interface>\n' 69 '') 70 71 action = pyqtSignal(str, str) 72 73 message = pyqtSignal(str, str) 74 75 def __init__(self, parent): 76 super(ChatAdaptor, self).__init__(parent) 77 78 self.setAutoRelaySignals(True) 79 80 81class ChatInterface(QDBusAbstractInterface): 82 83 action = pyqtSignal(str, str) 84 85 message = pyqtSignal(str, str) 86 87 def __init__(self, service, path, connection, parent=None): 88 super(ChatInterface, self).__init__(service, path, 'org.example.chat', 89 connection, parent) 90 91 92class ChatMainWindow(QMainWindow, Ui_ChatMainWindow): 93 94 action = pyqtSignal(str, str) 95 96 message = pyqtSignal(str, str) 97 98 def __init__(self): 99 super(ChatMainWindow, self).__init__() 100 101 self.m_nickname = "nickname" 102 self.m_messages = [] 103 104 self.setupUi(self) 105 self.sendButton.setEnabled(False) 106 107 self.messageLineEdit.textChanged.connect(self.textChangedSlot) 108 self.sendButton.clicked.connect(self.sendClickedSlot) 109 self.actionChangeNickname.triggered.connect(self.changeNickname) 110 self.actionAboutQt.triggered.connect(self.aboutQt) 111 QApplication.instance().lastWindowClosed.connect(self.exiting) 112 113 # Add our D-Bus interface and connect to D-Bus. 114 ChatAdaptor(self) 115 QDBusConnection.sessionBus().registerObject('/', self) 116 117 iface = ChatInterface('', '', QDBusConnection.sessionBus(), self) 118 QDBusConnection.sessionBus().connect('', '', 'org.example.chat', 119 'message', self.messageSlot) 120 iface.action.connect(self.actionSlot) 121 122 dialog = NicknameDialog() 123 dialog.cancelButton.setVisible(False) 124 dialog.exec_() 125 self.m_nickname = dialog.nickname.text().strip() 126 self.action.emit(self.m_nickname, "joins the chat") 127 128 def rebuildHistory(self): 129 history = '\n'.join(self.m_messages) 130 self.chatHistory.setPlainText(history) 131 132 @pyqtSlot(str, str) 133 def messageSlot(self, nickname, text): 134 self.m_messages.append("<%s> %s" % (nickname, text)) 135 136 if len(self.m_messages) > 100: 137 self.m_messages.pop(0) 138 139 self.rebuildHistory() 140 141 @pyqtSlot(str, str) 142 def actionSlot(self, nickname, text): 143 self.m_messages.append("* %s %s" % (nickname, text)) 144 145 if len(self.m_messages) > 100: 146 self.m_messages.pop(0) 147 148 self.rebuildHistory() 149 150 @pyqtSlot(str) 151 def textChangedSlot(self, newText): 152 self.sendButton.setEnabled(newText != '') 153 154 @pyqtSlot() 155 def sendClickedSlot(self): 156 msg = QDBusMessage.createSignal('/', 'org.example.chat', 'message') 157 msg << self.m_nickname << self.messageLineEdit.text() 158 QDBusConnection.sessionBus().send(msg) 159 self.messageLineEdit.setText('') 160 161 @pyqtSlot() 162 def changeNickname(self): 163 dialog = NicknameDialog(self) 164 165 if dialog.exec_() == QDialog.Accepted: 166 old = self.m_nickname 167 self.m_nickname = dialog.nickname.text().strip() 168 self.action.emit(old, "is now known as %s" % self.m_nickname) 169 170 @pyqtSlot() 171 def aboutQt(self): 172 QMessageBox.aboutQt(self) 173 174 @pyqtSlot() 175 def exiting(self): 176 self.action.emit(self.m_nickname, "leaves the chat") 177 178 179class NicknameDialog(QDialog, Ui_NicknameDialog): 180 181 def __init__(self, parent=None): 182 super(NicknameDialog, self).__init__(parent) 183 184 self.setupUi(self) 185 186 187if __name__ == '__main__': 188 import sys 189 190 app = QApplication(sys.argv) 191 192 if not QDBusConnection.sessionBus().isConnected(): 193 sys.stderr.write("Cannot connect to the D-Bus session bus.\n" 194 "Please check your system settings and try again.\n") 195 sys.exit(1) 196 197 chat = ChatMainWindow() 198 chat.show() 199 200 sys.exit(app.exec_()) 201