def __init__(self, peerName, peerPubKey, listenerStream, reactor): QWidget.__init__(self) FlashWindow.__init__(self, reactor) self.ui = Ui_IMWindow() self.ui.setupUi(self) self.peerName = peerName self.peerPubKey = peerPubKey self.listenerStream = listenerStream self.reactor = reactor self.listenerStream.setCloseCallback(self._onListenerClose) self.listenerStream.setErrorCallback(self._onListenerError) self.listenerStream.setInputCallback(self._onListenerInput) self.connect(self.ui.chatInputEdit, SIGNAL('textChanged()'), self._onChatInputChanged) self.baseTitle = self.peerName + ' - ' + str(self.windowTitle()) self.setWindowTitle(self.baseTitle) self.connecting = [] self.connected = [] self.pendingMessages = [] self.lastSentTyping = False self.lastSentTypingTime = 0 self.lastReceivedTyping = False self.lastReceivedTypingTime = 0 self.timerOp = self.reactor.addTimer(1, self._updateTypingStatus) self.ui.chatLogView.installEventFilter(self) self.ui.chatInputEdit.installEventFilter(self)
def __init__( self, peerName, peerPubKey, listenerStream, reactor ) : QWidget.__init__( self ) FlashWindow.__init__( self, reactor ) self.ui = Ui_IMWindow() self.ui.setupUi( self ) self.peerName = peerName self.peerPubKey = peerPubKey self.listenerStream = listenerStream self.reactor = reactor self.listenerStream.setCloseCallback( self._onListenerClose ) self.listenerStream.setErrorCallback( self._onListenerError ) self.listenerStream.setInputCallback( self._onListenerInput ) self.connect( self.ui.chatInputEdit, SIGNAL('textChanged()'), self._onChatInputChanged ) self.baseTitle = self.peerName + ' - ' + str(self.windowTitle()) self.setWindowTitle( self.baseTitle ) self.connecting = [] self.connected = [] self.pendingMessages = [] self.lastSentTyping = False self.lastSentTypingTime = 0 self.lastReceivedTyping = False self.lastReceivedTypingTime = 0 self.timerOp = self.reactor.addTimer( 1, self._updateTypingStatus ) self.ui.chatLogView.installEventFilter( self ) self.ui.chatInputEdit.installEventFilter( self )
class IMWindow(QWidget, FlashWindow): def __init__(self, peerName, peerPubKey, listenerStream, reactor): QWidget.__init__(self) FlashWindow.__init__(self, reactor) self.ui = Ui_IMWindow() self.ui.setupUi(self) self.peerName = peerName self.peerPubKey = peerPubKey self.listenerStream = listenerStream self.reactor = reactor self.listenerStream.setCloseCallback(self._onListenerClose) self.listenerStream.setErrorCallback(self._onListenerError) self.listenerStream.setInputCallback(self._onListenerInput) self.connect(self.ui.chatInputEdit, SIGNAL('textChanged()'), self._onChatInputChanged) self.baseTitle = self.peerName + ' - ' + str(self.windowTitle()) self.setWindowTitle(self.baseTitle) self.connecting = [] self.connected = [] self.pendingMessages = [] self.lastSentTyping = False self.lastSentTypingTime = 0 self.lastReceivedTyping = False self.lastReceivedTypingTime = 0 self.timerOp = self.reactor.addTimer(1, self._updateTypingStatus) self.ui.chatLogView.installEventFilter(self) self.ui.chatInputEdit.installEventFilter(self) def shutdown(self): self.listenerStream.close() for conn in self.connecting + self.connected: conn.shutdown() del self.connecting[:] del self.connected[:] self.ui.chatInputEdit.setReadOnly(True) self.setWindowTitle('DISCONNECTED: ' + self.baseTitle) self.timerOp.cancel() self.timerOp = None if self.isHidden(): self.close() def _onListenerClose(self): self.shutdown() def _onListenerError(self, err, errMsg): self._onListenerClose() def _onListenerInput(self, line): words = line.split() if len(words) < 2: return if words[0].lower() != 'msg': return words = words[1:] cmd, args = words[0].lower(), words[1:] if cmd == 'contactaction': if len(args) != 0: return self._onContactAction() elif cmd == 'incoming': if len(args) != 1: return connectionId = args[0] self._onIncoming(connectionId) def _onContactAction(self): if self.isHidden(): self.show() if self.isMinimized(): self.showNormal() self.activateWindow() if self.connecting or self.connected: return conn = IMConnection(self.reactor) conn.setCloseCallback(lambda: self._onConnClose(conn)) conn.setInputCallback(lambda line: self._onConnInput(conn, line)) conn.connectTo(self.peerPubKey, lambda: self._onConnected(conn)) self.connecting.append(conn) self.ui.statusLabel.setText('Connecting...') def _onIncoming(self, connectionId): conn = IMConnection(self.reactor) conn.setCloseCallback(lambda: self._onConnClose(conn)) conn.setInputCallback(lambda line: self._onConnInput(conn, line)) conn.acceptConnection(connectionId, lambda: self._onConnected(conn)) self.connecting.append(conn) self.ui.statusLabel.setText('Receiving connection...') def _onConnClose(self, conn): if conn in self.connecting: self.connecting.remove(conn) self.ui.statusLabel.setText('Connection failed') else: assert conn in self.connected self.connected.remove(conn) #self.ui.statusLabel.setText( 'Connection closed.' ) self.ui.statusLabel.setText('') if (not self.connecting) and (not self.connected) and self.isHidden(): self.shutdown() def _onConnected(self, conn): assert conn in self.connecting self.connecting.remove(conn) self.connected.append(conn) self.ui.statusLabel.setText('Connected.') if self.pendingMessages: assert len(self.connected) == 1 for msg in self.pendingMessages: conn.writeLine('msg %s' % wordEncode(msg)) del self.pendingMessages[:] def _onConnInput(self, conn, line): words = line.split() if len(words) == 0: return cmd = words[0].lower() if cmd == 'msg': if len(words) != 2: return try: msg = wordDecode(words[1]) except WordDecodeError: return if self.isHidden(): self.show() self._chatMessage(msg, self.peerName) self.lastReceivedTyping = False self._updateTypingStatus() elif cmd == 'typing': if len(words) != 1: return self.lastReceivedTyping = True self.lastReceivedTypingTime = time.time() self._updateTypingStatus() def _updateTypingStatus(self): if not self.connected: return typing = self.lastReceivedTyping if (time.time() - self.lastReceivedTypingTime) > 5: typing = False if typing: status = '%s is typing...' % self.peerName else: status = '' self.ui.statusLabel.setText(status) def _onChatInputEnter(self): msg = unicode(self.ui.chatInputEdit.toPlainText()).encode('utf8') self.ui.chatInputEdit.clear() if msg.endswith('\n'): msg = msg[:-1] if msg.endswith('\r'): msg = msg[:-1] if not msg: return self._chatMessage(msg, env.user) self.lastSentTyping = False if not self.connected: self.pendingMessages.append(msg) self._onContactAction() return for conn in self.connected: conn.writeLine('msg %s' % wordEncode(msg)) def _onChatInputChanged(self): curTime = time.time() if self.lastSentTyping and (curTime - self.lastSentTypingTime < 2): return self.lastSentTyping = True self.lastSentTypingTime = curTime for conn in self.connected: conn.writeLine('typing') def eventFilter(self, obj, event): if event.type() == QEvent.FocusIn: if obj is self.ui.chatLogView: if event.reason() == Qt.ActiveWindowFocusReason: self.ui.chatInputEdit.setFocus() if event.type() == QEvent.KeyPress: if obj is self.ui.chatInputEdit: if event.key() in (Qt.Key_Enter, Qt.Key_Return): if event.modifiers() & Qt.ShiftModifier: pass elif event.modifiers() & Qt.ControlModifier: # QTextEdit inserts <br/> with Ctrl+Enter, and a new # <p> element with Shift+Enter. There seem to be some # rendering issues with <br/>, so convert all Ctrl+Enter # keys to Shift+Enter. mod = event.modifiers() & (~Qt.ControlModifier) mod = mod | Qt.ShiftModifier newEvent = QKeyEvent(event.type(), event.key(), mod, event.text(), event.isAutoRepeat(), event.count()) QApplication.sendEvent(self.ui.chatInputEdit, newEvent) return True else: self._onChatInputEnter() return True return False def _chatMessage(self, msg, fromUser): msg = unicode(msg, 'utf8', 'replace') msg = unicode(Qt.escape(msg)) msg = msg.replace('\r\n', '<br/>') msg = msg.replace('\n', '<br/>') msg = msg.replace('\t', ' ') msg = msg.replace(' ', ' ') color = (fromUser == self.peerName) and '#FF821C' or 'black' self.ui.chatLogView.append( u'<font face="Verdana" size=-1 color="%s"><b>%s: </b></font>%s' % (color, fromUser, msg)) self.flash() def closeEvent(self, ev): self.cancelFlash() ev.accept() delaygc(self)
class IMWindow( QWidget, FlashWindow ) : def __init__( self, peerName, peerPubKey, listenerStream, reactor ) : QWidget.__init__( self ) FlashWindow.__init__( self, reactor ) self.ui = Ui_IMWindow() self.ui.setupUi( self ) self.peerName = peerName self.peerPubKey = peerPubKey self.listenerStream = listenerStream self.reactor = reactor self.listenerStream.setCloseCallback( self._onListenerClose ) self.listenerStream.setErrorCallback( self._onListenerError ) self.listenerStream.setInputCallback( self._onListenerInput ) self.connect( self.ui.chatInputEdit, SIGNAL('textChanged()'), self._onChatInputChanged ) self.baseTitle = self.peerName + ' - ' + str(self.windowTitle()) self.setWindowTitle( self.baseTitle ) self.connecting = [] self.connected = [] self.pendingMessages = [] self.lastSentTyping = False self.lastSentTypingTime = 0 self.lastReceivedTyping = False self.lastReceivedTypingTime = 0 self.timerOp = self.reactor.addTimer( 1, self._updateTypingStatus ) self.ui.chatLogView.installEventFilter( self ) self.ui.chatInputEdit.installEventFilter( self ) def shutdown( self ) : self.listenerStream.close() for conn in self.connecting+self.connected : conn.shutdown() del self.connecting[:] del self.connected[:] self.ui.chatInputEdit.setReadOnly( True ) self.setWindowTitle( 'DISCONNECTED: ' + self.baseTitle ) self.timerOp.cancel() self.timerOp = None if self.isHidden() : self.close() def _onListenerClose( self ) : self.shutdown() def _onListenerError( self, err, errMsg ) : self._onListenerClose() def _onListenerInput( self, line ) : words = line.split() if len(words) < 2 : return if words[0].lower() != 'msg' : return words = words[1:] cmd,args = words[0].lower(),words[1:] if cmd == 'contactaction' : if len(args) != 0 : return self._onContactAction() elif cmd == 'incoming' : if len(args) != 1 : return connectionId = args[0] self._onIncoming( connectionId ) def _onContactAction( self ) : if self.isHidden() : self.show() if self.isMinimized() : self.showNormal() self.activateWindow() if self.connecting or self.connected : return conn = IMConnection( self.reactor ) conn.setCloseCallback( lambda : self._onConnClose(conn) ) conn.setInputCallback( lambda line : self._onConnInput(conn,line) ) conn.connectTo( self.peerPubKey, lambda : self._onConnected(conn) ) self.connecting.append( conn ) self.ui.statusLabel.setText( 'Connecting...' ) def _onIncoming( self, connectionId ) : conn = IMConnection( self.reactor ) conn.setCloseCallback( lambda : self._onConnClose(conn) ) conn.setInputCallback( lambda line : self._onConnInput(conn,line) ) conn.acceptConnection( connectionId, lambda : self._onConnected(conn) ) self.connecting.append( conn ) self.ui.statusLabel.setText( 'Receiving connection...' ) def _onConnClose( self, conn ) : if conn in self.connecting : self.connecting.remove( conn ) self.ui.statusLabel.setText( 'Connection failed' ) else : assert conn in self.connected self.connected.remove( conn ) #self.ui.statusLabel.setText( 'Connection closed.' ) self.ui.statusLabel.setText( '' ) if (not self.connecting) and (not self.connected) and self.isHidden() : self.shutdown() def _onConnected( self, conn ) : assert conn in self.connecting self.connecting.remove( conn ) self.connected.append( conn ) self.ui.statusLabel.setText( 'Connected.' ) if self.pendingMessages : assert len(self.connected) == 1 for msg in self.pendingMessages : conn.writeLine( 'msg %s' % wordEncode(msg) ) del self.pendingMessages[:] def _onConnInput( self, conn, line ) : words = line.split() if len(words) == 0 : return cmd = words[0].lower() if cmd == 'msg' : if len(words) != 2 : return try : msg = wordDecode( words[1] ) except WordDecodeError : return if self.isHidden() : self.show() self._chatMessage( msg, self.peerName ) self.lastReceivedTyping = False self._updateTypingStatus() elif cmd == 'typing' : if len(words) != 1 : return self.lastReceivedTyping = True self.lastReceivedTypingTime = time.time() self._updateTypingStatus() def _updateTypingStatus( self ) : if not self.connected : return typing = self.lastReceivedTyping if (time.time() - self.lastReceivedTypingTime) > 5 : typing = False if typing : status = '%s is typing...' % self.peerName else : status = '' self.ui.statusLabel.setText( status ) def _onChatInputEnter( self ) : msg = unicode(self.ui.chatInputEdit.toPlainText()).encode('utf8') self.ui.chatInputEdit.clear() if msg.endswith('\n') : msg = msg[:-1] if msg.endswith('\r') : msg = msg[:-1] if not msg : return self._chatMessage( msg, env.user ) self.lastSentTyping = False if not self.connected : self.pendingMessages.append( msg ) self._onContactAction() return for conn in self.connected : conn.writeLine( 'msg %s' % wordEncode(msg) ) def _onChatInputChanged( self ) : curTime = time.time() if self.lastSentTyping and (curTime - self.lastSentTypingTime < 2) : return self.lastSentTyping = True self.lastSentTypingTime = curTime for conn in self.connected : conn.writeLine( 'typing' ) def eventFilter( self, obj, event ) : if event.type() == QEvent.FocusIn : if obj is self.ui.chatLogView : if event.reason() == Qt.ActiveWindowFocusReason : self.ui.chatInputEdit.setFocus() if event.type() == QEvent.KeyPress : if obj is self.ui.chatInputEdit : if event.key() in (Qt.Key_Enter,Qt.Key_Return) : if event.modifiers() & Qt.ShiftModifier : pass elif event.modifiers() & Qt.ControlModifier : # QTextEdit inserts <br/> with Ctrl+Enter, and a new # <p> element with Shift+Enter. There seem to be some # rendering issues with <br/>, so convert all Ctrl+Enter # keys to Shift+Enter. mod = event.modifiers() & (~Qt.ControlModifier) mod = mod | Qt.ShiftModifier newEvent = QKeyEvent( event.type(), event.key(), mod, event.text(), event.isAutoRepeat(), event.count() ) QApplication.sendEvent( self.ui.chatInputEdit, newEvent ) return True else : self._onChatInputEnter() return True return False def _chatMessage( self, msg, fromUser ) : msg = unicode( msg, 'utf8', 'replace' ) msg = unicode( Qt.escape(msg) ) msg = msg.replace( '\r\n', '<br/>' ) msg = msg.replace( '\n', '<br/>' ) msg = msg.replace( '\t', ' ' ) msg = msg.replace( ' ', ' ' ) color = (fromUser == self.peerName) and '#FF821C' or 'black' self.ui.chatLogView.append( u'<font face="Verdana" size=-1 color="%s"><b>%s: </b></font>%s' % (color,fromUser,msg) ) self.flash() def closeEvent( self, ev ) : self.cancelFlash() ev.accept() delaygc( self )