使用PyQt4和twisted写的局域网聊天室(2)

公共模块qt4reactor.py:

# -*- coding: utf-8 -*-
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
# See LICENSE for details.

# Comes from http://sourcecodebrowser.com/openvswitch/1.1.0~pre2.g2.ea763e0e/qt4reactor_8py_source.html

# The referred licence file contains:
# 
#Copyright (c) 2001-2010

#Permission is hereby granted, free of charge, to any person obtaining
#a copy of this software and associated documentation files (the
#"Software"), to deal in the Software without restriction, including
#without limitation the rights to use, copy, modify, merge, publish,
#distribute, sublicense, and/or sell copies of the Software, and to
#permit persons to whom the Software is furnished to do so, subject to
#the following conditions:
#
#The above copyright notice and this permission notice shall be
#included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
#NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
#LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
#WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""
This module provides support for Twisted to be driven by the Qt mainloop.

In order to use this support, simply do the following::
    |  app = QApplication(sys.argv) # your code to init Qt
    |  import qt4reactor
    |  qt4reactor.install()
    
alternatively:

    |  from twisted.application import reactors
    |  reactors.installReactor('qt4')

Then use twisted.internet APIs as usual.  The other methods here are not
intended to be called directly.

If you don't instantiate a QApplication or QCoreApplication prior to
installing the reactor, a QCoreApplication will be constructed
by the reactor.  QCoreApplication does not require a GUI so trial testing
can occur normally.

Twisted can be initialized after QApplication.exec_() with a call to
reactor.runReturn().  calling reactor.stop() will unhook twisted but
leave your Qt application running

API Stability: stable

Maintainer: U{Glenn H Tarbox, PhD}

Previous maintainer: U{Itamar Shtull-Trauring}
Original port to QT4: U{Gabe Rudy}
Subsequent port by therve
"""

__all__ = ['install']


import sys, time

try:
    from zope.interface import implements                                       #接口实现
except:
    print('+++ Python Zope interface module is required\n')
    raise

from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
from PyQt4.QtCore import QEventLoop

try:
    from twisted.internet.interfaces import IReactorFDSet                       #Implement me to be able to use IFileDescriptor type resources.
    from twisted.python import log
    from twisted.internet.posixbase import PosixReactorBase                     #A basis for reactors that use file descriptors.
except:
    print('+++ Python Twisted Conch module is required\n')
    raise
    
class TwistedSocketNotifier(QSocketNotifier):
    """
    Connection between an fd event and reader/writer callbacks.
    """

    def __init__(self, reactor, watcher, type):
        QSocketNotifier.__init__(self, watcher.fileno(), type)                  # fileno方法返回整数的底层实现使用请求从操作系统的I / O操作的文件描述符
        self.reactor = reactor
        self.watcher = watcher
        self.fn = None
        if type == QSocketNotifier.Read:                                        # QSocketNotifier.Read	0	There is data to be read.
            self.fn = self.read
        elif type == QSocketNotifier.Write:                                     #QSocketNotifier.Write	1	Data can be written.
            self.fn = self.write
        QObject.connect(self, SIGNAL("activated(int)"), self.fn)                #bool QObject.connect (self, QObject, SIGNAL(), SLOT(), Qt.ConnectionType = Qt.AutoConnection)


    def shutdown(self):
        QObject.disconnect(self, SIGNAL("activated(int)"), self.fn)
        self.setEnabled(False)                                                  #bool QSocketNotifier.isEnabled (self). Returns true if the notifier is enabled; otherwise returns false.
        self.fn = self.watcher = None
        self.deleteLater()                                                      #QObject.deleteLater (self). The object will be deleted when control returns to the event loop. If the event loop is not running when this function is called


    def read(self, sock):
        w = self.watcher
        #self.setEnabled(False)    # ??? do I need this?            
        def _read():
            why = None
            try:
                why = w.doRead()                                                 #twisted.internet.abstract.FileDescriptor.
            except:
                log.err()
                why = sys.exc_info()[1]
            if why:
                self.reactor._disconnectSelectable(w, why, True)                 #twisted.internet.posixbase. def _disconnectSelectable(self, selectable, why, isRead, faildict={error.ConnectionDone:failure.Failure(error.ConnectionDone()),error.ConnectionLost:failure.Failure(error.ConnectionLost()),})
            elif self.watcher:
                pass
                #self.setEnabled(True)
        log.callWithLogger(w, _read)
        self.reactor.reactorInvocation()                                         #类QTReactor中方法

    def write(self, sock):
        w = self.watcher
        self.setEnabled(False)
        def _write():
            why = None
            try:
                why = w.doWrite()                                                 
            except:
                log.err()
                why = sys.exc_info()[1]
            if why:
                self.reactor._disconnectSelectable(w, why, False)
            elif self.watcher:
                self.setEnabled(True)
        log.callWithLogger(w, _write)
        self.reactor.reactorInvocation()

class fakeApplication(QEventLoop):                                          #The QEventLoop class provides a means of entering and leaving an event loop.
    def __init__(self):
        QEventLoop.__init__(self)
        
    def exec_(self):                                                        #Enters the main event loop and waits until exit() is called. Returns the value that was passed to exit().
        QEventLoop.exec_(self)
        
class QTReactor(PosixReactorBase):
    """
    Qt based reactor.
    """
    implements(IReactorFDSet)

    _timer = None

    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._timer=QTimer()                                                # The QTimer class provides repetitive and single-shot timers.
        self._timer.setSingleShot(True)                                     # You can set a timer to time out only once by calling setSingleShot(true).表示在时间结束之后只发送一次time out信息. 否则缺省计时器会重复启发直至他们停止或者销毁. 
        if QCoreApplication.startingUp():                                   # bool QCoreApplication.startingUp (). Returns true if an application object has not been created yet; otherwise returns false.
            self.qApp=QCoreApplication([])                                  # The QCoreApplication class provides an event loop for console Qt applications.
            self._ownApp=True
        else:
            self.qApp = QCoreApplication.instance()                         # QCoreApplication QCoreApplication.instance () . Returns a pointer to the application's QCoreApplication (or QApplication) instance.
            self._ownApp=False
        self._blockApp = None
        self._readWriteQ=[]
        
        """ some debugging instrumentation """
        self._doSomethingCount=0
        
        PosixReactorBase.__init__(self)

    def addReader(self, reader):
        if not reader in self._reads:
            self._reads[reader] = TwistedSocketNotifier(self, reader,
                                                       QSocketNotifier.Read)


    def addWriter(self, writer):
        if not writer in self._writes:
            self._writes[writer] = TwistedSocketNotifier(self, writer,
                                                        QSocketNotifier.Write)


    def removeReader(self, reader):
        if reader in self._reads:
            #self._reads[reader].shutdown()
            #del self._reads[reader]
            self._reads.pop(reader).shutdown()

    def removeWriter(self, writer):
        if writer in self._writes:
            self._writes[writer].shutdown()
            #del self._writes[writer]
            self._writes.pop(writer)


    def removeAll(self):
        return self._removeAll(self._reads, self._writes)


    def getReaders(self):
        return self._reads.keys()


    def getWriters(self):
        return self._writes.keys()
    
    def callLater(self,howlong, *args, **kargs):
        rval = super(QTReactor,self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval
    
    def crash(self):
        super(QTReactor,self).crash()
        
    def iterate(self,delay=0.0):
        t=self.running # not sure I entirely get the state of running
        self.running=True
        self._timer.stop() # in case its not (rare?)
        try:
            if delay == 0.0:
                self.reactorInvokePrivate()
                self._timer.stop() # supports multiple invocations
            else:
                endTime = delay + time.time()
                self.reactorInvokePrivate()
                while True:
                    t = endTime - time.time()
                    if t <= 0.0: return
                    self.qApp.processEvents(QEventLoop.AllEvents | 
                                      QEventLoop.WaitForMoreEvents,t*1010)
        finally:
            self.running=t
            
    def addReadWrite(self,t):
        self._readWriteQ.append(t)
        
    def runReturn(self, installSignalHandlers=True):
        QObject.connect(self._timer, SIGNAL("timeout()"), 
                        self.reactorInvokePrivate)
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self._timer.start(0)
        
    def run(self, installSignalHandlers=True):
        try:
            if self._ownApp:
                self._blockApp=self.qApp
            else:
                self._blockApp = fakeApplication()
            self.runReturn(installSignalHandlers)
            self._blockApp.exec_()
        finally:
            self._timer.stop() # should already be stopped

    def reactorInvocation(self):
        self._timer.setInterval(0)
        
    def reactorInvokePrivate(self):
        if not self.running:
            if self._blockApp is None:
                # Andy's fix for Ctrl-C quit
                self.qApp.quit()
            else:
                self._blockApp.quit()
        self._doSomethingCount += 1
        self.runUntilCurrent()
        t = self.timeout()
        if t is None: t=0.1
        else: t = min(t,0.1)
        self._timer.setInterval(int(t*1010))
        self.qApp.processEvents() # could change interval
        self._timer.start()
                
    def doIteration(self):
        assert False, "doiteration is invalid call"
            
def install():
    """
    Configure the twisted mainloop to be run inside the qt mainloop.
    """
    from twisted.internet import main
    reactor = QTReactor()
    main.installReactor(reactor)
    return reactor
    
if __name__ == "__main__":
    from PyQt4 import QtGui
    app = QtGui.QApplication(sys.argv) 
    import qt4reactor
    reactor=qt4reactor.install()
    reactor.runReturn() 
posted @ 2014-11-02 10:27  WeiJY  阅读(788)  评论(0编辑  收藏  举报